hyperclaw 4.0.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -4
- package/dist/a2ui-protocol-CT_jDEU9.js +75 -0
- package/dist/a2ui-protocol-CfBI44-Q.js +75 -0
- package/dist/agents-routing-683Q2JGp.js +129 -0
- package/dist/agents-routing-BpZBswBH.js +4 -0
- package/dist/agents-routing-ChHiZp36.js +327 -0
- package/dist/agents-routing-ChqZ6l2S.js +4 -0
- package/dist/api-keys-guide-BCcOl0Q7.js +149 -0
- package/dist/api-keys-guide-Dq5Obbp4.js +149 -0
- package/dist/audit-BYxPlnTQ.js +248 -0
- package/dist/audit-BaIiyWFu.js +441 -0
- package/dist/bounty-tools-C6LyzxM-.js +211 -0
- package/dist/bounty-tools-DWudyZie.js +211 -0
- package/dist/browser-tools-BsTeGMnX.js +5 -0
- package/dist/browser-tools-CQBSbIuO.js +5 -0
- package/dist/browser-tools-D8_rLe2p.js +179 -0
- package/dist/browser-tools-YQmwRLLM.js +179 -0
- package/dist/claw-tasks-BRLUvFRD.js +80 -0
- package/dist/claw-tasks-CgTsiNE8.js +80 -0
- package/dist/connector-3HnyH8fn.js +167 -0
- package/dist/connector-5N0-X_xs.js +194 -0
- package/dist/connector-6PMZo5Ky.js +189 -0
- package/dist/connector-B3v0qcXg.js +425 -0
- package/dist/connector-B6eoF3DD.js +181 -0
- package/dist/connector-B8R3iBY1.js +280 -0
- package/dist/connector-B9tLG8UZ.js +196 -0
- package/dist/connector-BAM-08NN.js +189 -0
- package/dist/connector-BC8FIVu4.js +181 -0
- package/dist/connector-BDmwwaVc.js +213 -0
- package/dist/connector-BGjbBy69.js +225 -0
- package/dist/connector-BO2SRzfG.js +218 -0
- package/dist/connector-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-BfXky0L3.js +167 -0
- package/dist/connector-BiiSJpx3.js +192 -0
- package/dist/connector-BliDVsJQ.js +239 -0
- package/dist/connector-BnDmIhIu.js +85 -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-C1HSoUyk.js +189 -0
- package/dist/connector-CKQHZOXg.js +568 -0
- package/dist/connector-CRRWY5Wv.js +167 -0
- package/dist/connector-CRl-iidy.js +239 -0
- package/dist/connector-CXPQVGyI.js +85 -0
- package/dist/connector-Cdk1CXKi.js +194 -0
- package/dist/connector-Ci9glMD-.js +340 -0
- package/dist/connector-CjtZIEDj.js +181 -0
- package/dist/connector-Ck6JtOsX.js +531 -0
- package/dist/connector-CwlgFgjx.js +181 -0
- package/dist/connector-D8Kelee0.js +286 -0
- package/dist/connector-DAnRJ0oP.js +162 -0
- package/dist/connector-DFchk6l7.js +178 -0
- package/dist/connector-DKw7tRAy.js +192 -0
- package/dist/connector-DRv1ahC_.js +2 -2
- package/dist/connector-DU63KW94.js +165 -0
- package/dist/connector-DXTp5PE8.js +508 -0
- package/dist/connector-Dbvb1Cj9.js +280 -0
- package/dist/connector-DcZdQcgR.js +173 -0
- package/dist/connector-Dih6dUPP.js +173 -0
- package/dist/connector-DqTH_tPX.js +182 -0
- package/dist/connector-DrnEiiyP.js +419 -0
- package/dist/connector-DtR5GGTX.js +167 -0
- package/dist/connector-DxKL8VvZ.js +182 -0
- package/dist/connector-T_YdZtzv.js +162 -0
- package/dist/connector-Tky_qS_K.js +350 -0
- package/dist/connector-ZSc3oTTy.js +305 -0
- package/dist/connector-i4gOS9xL.js +154 -0
- package/dist/connector-rHXE1ZD2.js +167 -0
- package/dist/connector-sW5yhU1m.js +498 -0
- package/dist/connector-u3ICd3Ic.js +552 -0
- package/dist/connector-wdUXChwa.js +172 -0
- package/dist/cost-tracker-DD9wtWsr.js +103 -0
- package/dist/cost-tracker-pVE15Yq4.js +103 -0
- package/dist/credentials-store-BvnMPJwi.js +4 -0
- package/dist/credentials-store-C6ir0Dae.js +4 -0
- package/dist/credentials-store-H13LqOwJ.js +77 -0
- package/dist/credentials-store-sb-TRLwR.js +77 -0
- package/dist/cron-tasks-Bli7Kzd2.js +82 -0
- package/dist/cron-tasks-BvDFNyiE.js +82 -0
- package/dist/daemon-Bg4GtCmc.js +318 -0
- package/dist/daemon-DhmwY8k4.js +5 -0
- package/dist/delivery-BmIYy9VQ.js +4 -0
- package/dist/delivery-D5Z98EVq.js +95 -0
- package/dist/delivery-DCOXhXEO.js +5 -0
- package/dist/delivery-pWUPBp1F.js +95 -0
- package/dist/destructive-gate-D6vWOdEl.js +101 -0
- package/dist/destructive-gate-m-dWqUFg.js +101 -0
- package/dist/developer-keys-CPWT7Q6S.js +8 -0
- package/dist/developer-keys-DrrcUqFa.js +127 -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-BvCe8BBk.js +230 -0
- package/dist/doctor-Cf1XSfp9.js +4 -0
- package/dist/doctor-CxyPLYsJ.js +6 -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-CEDSqXfw.js +256 -0
- package/dist/engine-D49jnSd_.js +256 -0
- package/dist/engine-Da4JMNpI.js +7 -0
- package/dist/env-resolve-CiXbWYwe.js +10 -0
- package/dist/env-resolve-CmGWhWXJ.js +115 -0
- package/dist/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/extraction-tools-HOZstZ0y.js +91 -0
- package/dist/extraction-tools-m4lmAv7l.js +5 -0
- package/dist/form_data-B_hIUrxU.js +8657 -0
- package/dist/form_data-Cz040rio.js +8657 -0
- package/dist/gmail-watch-setup-Czt8rXaX.js +40 -0
- package/dist/gmail-watch-setup-Du7DVV7S.js +40 -0
- package/dist/health-B-asI__D.js +6 -0
- package/dist/health-Ds2YlpTB.js +152 -0
- package/dist/heartbeat-engine-BYT5ayQH.js +83 -0
- package/dist/heartbeat-engine-CRqfPcFM.js +83 -0
- package/dist/hub-D0XwdjM-.js +515 -0
- package/dist/hub-DTsqe5Bt.js +6 -0
- package/dist/hub-FrPTA33j.js +515 -0
- package/dist/hub-LiD5Iztb.js +6 -0
- package/dist/hyperclawbot-D9KCtc4P.js +480 -0
- package/dist/hyperclawbot-Dw27pJo4.js +480 -0
- package/dist/hyperclawbot-zvczQgKx.js +505 -0
- package/dist/inference-BKVkBREb.js +6 -0
- package/dist/inference-CTWJeX9Q.js +922 -0
- package/dist/inference-DCXH4Q3x.js +922 -0
- package/dist/inference-ix607p7k.js +6 -0
- package/dist/knowledge-graph-DqA-Fztl.js +131 -0
- package/dist/knowledge-graph-iBG76fvm.js +131 -0
- package/dist/loader-CC45xGpC.js +4 -0
- package/dist/loader-CISCqBto.js +400 -0
- package/dist/loader-CYMQ8VOS.js +4 -0
- package/dist/loader-CnEdOyjT.js +400 -0
- package/dist/logger-8tEtAd3y.js +83 -0
- package/dist/logger-ybOp7VOC.js +83 -0
- package/dist/manager-03ipO9R0.js +105 -0
- package/dist/manager-BpDfbDjg.js +117 -0
- package/dist/manager-Bxl0sqlh.js +4 -0
- package/dist/manager-CPjeRe-6.js +4 -0
- package/dist/manager-CrVDn6eN.js +6 -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/manager-FCgF1plu.js +218 -0
- package/dist/manager-rgCsaWT1.js +40 -0
- package/dist/mcp-B_9Ber63.js +139 -0
- package/dist/mcp-CfoSU4Uz.js +139 -0
- package/dist/mcp-loader-DSM5UiFG.js +94 -0
- package/dist/mcp-loader-DkRBsLpk.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-BlHL7JCO.js +4 -0
- package/dist/memory-DsS_eFvJ.js +270 -0
- package/dist/memory-auto-Bc7euou4.js +306 -0
- package/dist/memory-auto-BkvtSFUw.js +5 -0
- package/dist/memory-auto-Bnz_-1wP.js +306 -0
- package/dist/memory-auto-DPfbkMVt.js +5 -0
- package/dist/memory-integration-DZExqWr4.js +91 -0
- package/dist/memory-integration-cSYkZyEo.js +91 -0
- package/dist/moltbook-B6ZeGN5_.js +81 -0
- package/dist/moltbook-BtLDZTfM.js +81 -0
- package/dist/node-Dw2Gi-cP.js +222 -0
- package/dist/node-pwL6O_KX.js +222 -0
- package/dist/nodes-registry-B8dmrlLv.js +52 -0
- package/dist/nodes-registry-CsPm_-CJ.js +52 -0
- package/dist/oauth-flow-CpWlgvNB.js +150 -0
- package/dist/oauth-flow-DQPvMHRH.js +150 -0
- package/dist/oauth-provider-BZb6qOw5.js +110 -0
- package/dist/oauth-provider-Uo4Nib_c.js +110 -0
- package/dist/observability-B43YvNQV.js +89 -0
- package/dist/observability-BV-Yx0V9.js +89 -0
- package/dist/onboard-0WoDxbv_.js +10 -0
- package/dist/onboard-BXNXCQp4.js +4070 -0
- package/dist/onboard-Bd_wsYdi.js +4086 -0
- package/dist/onboard-CAN7x3me.js +3026 -0
- package/dist/onboard-DnegOHMh.js +4 -4
- 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-DmnEvMaL.js +189 -0
- package/dist/orchestrator-NJQsmiBE.js +189 -0
- package/dist/orchestrator-RI3bpqqc.js +6 -0
- package/dist/pairing-6iM27aD8.js +196 -0
- package/dist/pairing-DU0_J28n.js +87 -0
- package/dist/pairing-DWllbSbO.js +4 -0
- package/dist/pairing-dGoiGepK.js +4 -0
- package/dist/pc-access-CgCsYrpt.js +8 -0
- package/dist/pc-access-Ly-uA8mn.js +8 -0
- package/dist/pc-access-NxBvTrRj.js +819 -0
- package/dist/pc-access-_iH2aorG.js +819 -0
- package/dist/pending-approval-CUXjysAo.js +22 -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/reminders-store-Drjed_-h.js +58 -0
- package/dist/renderer-BVQrd0_g.js +225 -0
- package/dist/renderer-pqlDRKbH.js +225 -0
- package/dist/rules-BE4GV6cV.js +103 -0
- package/dist/rules-BooT_qFP.js +103 -0
- package/dist/run-main.js +1649 -1227
- package/dist/runner-D1rjuMTJ.js +810 -0
- package/dist/runner-DatMMYYE.js +1271 -0
- package/dist/sdk/index.js +2 -2
- package/dist/sdk/index.mjs +2 -2
- package/dist/security-BqNyT4ID.js +4 -0
- package/dist/security-C-5URby1.js +73 -0
- package/dist/security-_xve79aq.js +4 -0
- package/dist/security-tpgqPWWH.js +73 -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 +2 -2
- package/dist/server-D4wVHiX9.js +4 -0
- package/dist/server-DU9POoWc.js +4 -0
- package/dist/server-Dh3JlBFB.js +1255 -0
- package/dist/session-store-BUiPz0Vv.js +5 -0
- package/dist/session-store-CujxByI6.js +113 -0
- package/dist/session-store-is4B6qmD.js +113 -0
- package/dist/session-store-qpJUg2M1.js +5 -0
- package/dist/sessions-tools-CB2qbwIk.js +5 -0
- package/dist/sessions-tools-CbUTFe4i.js +5 -0
- package/dist/sessions-tools-CeqD7iil.js +95 -0
- package/dist/sessions-tools-DHMaTZIs.js +95 -0
- package/dist/skill-loader-BaNLVmJy.js +7 -0
- package/dist/skill-loader-BkceKkIg.js +7 -0
- package/dist/skill-loader-DhgIwK4J.js +159 -0
- package/dist/skill-loader-HgpF6Vqs.js +159 -0
- package/dist/skill-runtime--LqxWrp5.js +102 -0
- package/dist/skill-runtime-C5l0Tgt-.js +5 -0
- package/dist/skill-runtime-CJN24QPW.js +102 -0
- package/dist/skill-runtime-DsXK_HYG.js +102 -0
- package/dist/skill-runtime-IVTiqrMR.js +5 -0
- package/dist/skill-runtime-w1ig_lcw.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-BxPHKO5x.js +63 -0
- package/dist/src-DIc-L2IG.js +20 -0
- package/dist/src-DWCUhnD4.js +20 -0
- package/dist/src-cfRTjFef.js +63 -0
- package/dist/src-g_rNx5rh.js +458 -0
- package/dist/sub-agent-tools-BD9DF8_g.js +39 -0
- package/dist/sub-agent-tools-CHQoHz9c.js +39 -0
- package/dist/sub-agent-tools-V7b3T9_s.js +39 -0
- package/dist/theme-DcxwcUgZ.js +180 -0
- package/dist/theme-cx0fkgWC.js +8 -0
- package/dist/tool-policy-CNT-mF2Z.js +189 -0
- package/dist/tool-policy-DNvNRnve.js +189 -0
- package/dist/tts-elevenlabs-BRosZv-f.js +61 -0
- package/dist/tts-elevenlabs-BUOGKL-k.js +61 -0
- package/dist/update-check-BD4qH7Am.js +81 -0
- package/dist/update-check-C2Dz85wJ.js +81 -0
- package/dist/vision-BMmiIKy7.js +121 -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/vision-tools-DVuYc17I.js +51 -0
- package/dist/vision-tools-U3YC4L-g.js +5 -0
- package/dist/voice-transcription-B555DbWR.js +138 -0
- package/dist/voice-transcription-CgWq54hn.js +138 -0
- package/dist/website-watch-tools-Bk_TnwuE.js +5 -0
- package/dist/website-watch-tools-DFMrJU-R.js +139 -0
- package/dist/website-watch-tools-DraMPxdl.js +139 -0
- package/dist/website-watch-tools-Du3W5sN7.js +5 -0
- package/package.json +1 -1
package/dist/run-main.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
require('./env-resolve-
|
|
5
|
-
const require_onboard = require('./onboard-
|
|
6
|
-
require('./server-
|
|
7
|
-
require('./
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
2
|
+
require('./paths-AIyBxIzm.js');
|
|
3
|
+
require('./paths-DPovhojT.js');
|
|
4
|
+
require('./env-resolve-CmGWhWXJ.js');
|
|
5
|
+
const require_onboard = require('./onboard-BXNXCQp4.js');
|
|
6
|
+
require('./server-Dh3JlBFB.js');
|
|
7
|
+
const require_daemon = require('./daemon-Bg4GtCmc.js');
|
|
8
|
+
require('./theme-DcxwcUgZ.js');
|
|
9
|
+
const require_hub = require('./hub-D0XwdjM-.js');
|
|
10
|
+
const require_manager = require('./manager-rgCsaWT1.js');
|
|
11
|
+
const require_manager$1 = require('./manager-03ipO9R0.js');
|
|
12
|
+
const require_memory = require('./memory-DsS_eFvJ.js');
|
|
13
|
+
const require_loader = require('./loader-CnEdOyjT.js');
|
|
14
|
+
const require_agents_routing = require('./agents-routing-ChHiZp36.js');
|
|
15
|
+
const require_pairing = require('./pairing-6iM27aD8.js');
|
|
16
|
+
const require_doctor = require('./doctor-BvCe8BBk.js');
|
|
17
|
+
const require_health = require('./health-Ds2YlpTB.js');
|
|
18
|
+
const require_security = require('./security-tpgqPWWH.js');
|
|
19
|
+
const require_developer_keys = require('./developer-keys-DrrcUqFa.js');
|
|
15
20
|
const commander = require_chunk.__toESM(require("commander"));
|
|
16
21
|
const chalk = require_chunk.__toESM(require("chalk"));
|
|
17
22
|
const inquirer = require_chunk.__toESM(require("inquirer"));
|
|
@@ -19,507 +24,14 @@ const ora = require_chunk.__toESM(require("ora"));
|
|
|
19
24
|
const fs_extra = require_chunk.__toESM(require("fs-extra"));
|
|
20
25
|
const path = require_chunk.__toESM(require("path"));
|
|
21
26
|
const os = require_chunk.__toESM(require("os"));
|
|
27
|
+
const crypto = require_chunk.__toESM(require("crypto"));
|
|
22
28
|
const child_process = require_chunk.__toESM(require("child_process"));
|
|
23
29
|
const util = require_chunk.__toESM(require("util"));
|
|
24
30
|
const http = require_chunk.__toESM(require("http"));
|
|
25
|
-
const https = require_chunk.__toESM(require("https"));
|
|
26
|
-
const readline = require_chunk.__toESM(require("readline"));
|
|
27
|
-
const tar = require_chunk.__toESM(require("tar"));
|
|
28
31
|
const fs = require_chunk.__toESM(require("fs"));
|
|
32
|
+
const readline = require_chunk.__toESM(require("readline"));
|
|
29
33
|
const net = require_chunk.__toESM(require("net"));
|
|
30
34
|
|
|
31
|
-
//#region src/skills/clawhub.ts
|
|
32
|
-
require_paths$1.init_paths();
|
|
33
|
-
const CLAWHUB_API = process.env.CLAWHUB_API_URL || "https://clawhub.com";
|
|
34
|
-
const WORKSPACE_SKILLS$1 = path.default.join(require_paths.getHyperClawDir(), "workspace", "skills");
|
|
35
|
-
async function searchSkills(query, category) {
|
|
36
|
-
const q = new URLSearchParams({ q: query });
|
|
37
|
-
if (category) q.set("category", category);
|
|
38
|
-
const url = `${CLAWHUB_API}/api/skills/search?${q}`;
|
|
39
|
-
try {
|
|
40
|
-
const body = await fetchJson(url);
|
|
41
|
-
return Array.isArray(body.skills) ? body.skills : Array.isArray(body) ? body : [];
|
|
42
|
-
} catch (e) {
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
async function installSkill(skillId, version) {
|
|
47
|
-
const ver = version ? `@${version}` : "";
|
|
48
|
-
const url = `${CLAWHUB_API}/api/skills/${encodeURIComponent(skillId)}/download${ver}`;
|
|
49
|
-
try {
|
|
50
|
-
const body = await fetchJson(url);
|
|
51
|
-
const tarballUrl = body.url || body.tarball;
|
|
52
|
-
if (!tarballUrl) throw new Error("No download URL in registry response");
|
|
53
|
-
await fs_extra.default.ensureDir(WORKSPACE_SKILLS$1);
|
|
54
|
-
const destDir = path.default.join(WORKSPACE_SKILLS$1, skillId);
|
|
55
|
-
await fs_extra.default.ensureDir(destDir);
|
|
56
|
-
if (body.content || body.skillMarkdown) {
|
|
57
|
-
const content = body.content || body.skillMarkdown;
|
|
58
|
-
await fs_extra.default.writeFile(path.default.join(destDir, "SKILL.md"), content, "utf8");
|
|
59
|
-
return destDir;
|
|
60
|
-
}
|
|
61
|
-
const tarballBuffer = await fetchBuffer(tarballUrl);
|
|
62
|
-
const extractDir = path.default.join(path.default.dirname(destDir), `.skill-extract-${skillId}-${Date.now()}`);
|
|
63
|
-
await fs_extra.default.ensureDir(extractDir);
|
|
64
|
-
try {
|
|
65
|
-
const tarPath = path.default.join(extractDir, "skill.tar.gz");
|
|
66
|
-
await fs_extra.default.writeFile(tarPath, tarballBuffer);
|
|
67
|
-
await tar.default.x({
|
|
68
|
-
file: tarPath,
|
|
69
|
-
cwd: extractDir
|
|
70
|
-
});
|
|
71
|
-
await fs_extra.default.remove(tarPath);
|
|
72
|
-
const entries = await fs_extra.default.readdir(extractDir);
|
|
73
|
-
let skillDir = extractDir;
|
|
74
|
-
const topSkill = path.default.join(extractDir, "SKILL.md");
|
|
75
|
-
if (!await fs_extra.default.pathExists(topSkill)) {
|
|
76
|
-
const sub = entries.find((e) => e !== "package.json" && !e.startsWith("."));
|
|
77
|
-
if (sub) {
|
|
78
|
-
const subPath = path.default.join(extractDir, sub);
|
|
79
|
-
if ((await fs_extra.default.stat(subPath)).isDirectory() && await fs_extra.default.pathExists(path.default.join(subPath, "SKILL.md"))) skillDir = subPath;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
await fs_extra.default.copy(skillDir, destDir, { filter: (src) => !src.includes("node_modules") });
|
|
83
|
-
if (!await fs_extra.default.pathExists(path.default.join(destDir, "SKILL.md"))) throw new Error("Tarball did not contain SKILL.md");
|
|
84
|
-
return destDir;
|
|
85
|
-
} finally {
|
|
86
|
-
await fs_extra.default.remove(extractDir).catch(() => {});
|
|
87
|
-
}
|
|
88
|
-
} catch (e) {
|
|
89
|
-
if (e.message?.includes("ENOTFOUND") || e.code === "ENOTFOUND") throw new Error(`ClawHub registry unavailable. Install manually: mkdir -p ~/.hyperclaw/workspace/skills/${skillId} && add SKILL.md`);
|
|
90
|
-
throw e;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
async function listInstalledFromClawHub() {
|
|
94
|
-
if (!await fs_extra.default.pathExists(WORKSPACE_SKILLS$1)) return [];
|
|
95
|
-
const dirs = await fs_extra.default.readdir(WORKSPACE_SKILLS$1);
|
|
96
|
-
const out = [];
|
|
97
|
-
for (const id of dirs) {
|
|
98
|
-
const p = path.default.join(WORKSPACE_SKILLS$1, id, "SKILL.md");
|
|
99
|
-
if (await fs_extra.default.pathExists(p)) out.push(id);
|
|
100
|
-
}
|
|
101
|
-
return out;
|
|
102
|
-
}
|
|
103
|
-
function fetchBuffer(url) {
|
|
104
|
-
return new Promise((resolve, reject) => {
|
|
105
|
-
const parsed = new URL(url);
|
|
106
|
-
const mod = parsed.protocol === "https:" ? https.default : http.default;
|
|
107
|
-
const req = mod.request({
|
|
108
|
-
hostname: parsed.hostname,
|
|
109
|
-
port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
|
|
110
|
-
path: parsed.pathname + parsed.search,
|
|
111
|
-
method: "GET",
|
|
112
|
-
headers: { "User-Agent": "HyperClaw/4.0.1" }
|
|
113
|
-
}, (res) => {
|
|
114
|
-
const chunks = [];
|
|
115
|
-
res.on("data", (c) => chunks.push(c));
|
|
116
|
-
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
117
|
-
});
|
|
118
|
-
req.on("error", reject);
|
|
119
|
-
req.setTimeout(3e4, () => {
|
|
120
|
-
req.destroy();
|
|
121
|
-
reject(new Error("Tarball download timeout"));
|
|
122
|
-
});
|
|
123
|
-
req.end();
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
function fetchJson(url) {
|
|
127
|
-
return new Promise((resolve, reject) => {
|
|
128
|
-
const parsed = new URL(url);
|
|
129
|
-
const req = https.default.request({
|
|
130
|
-
hostname: parsed.hostname,
|
|
131
|
-
port: 443,
|
|
132
|
-
path: parsed.pathname + parsed.search,
|
|
133
|
-
method: "GET",
|
|
134
|
-
headers: { "User-Agent": "HyperClaw/4.0.1" }
|
|
135
|
-
}, (res) => {
|
|
136
|
-
let data = "";
|
|
137
|
-
res.on("data", (c) => data += c);
|
|
138
|
-
res.on("end", () => {
|
|
139
|
-
try {
|
|
140
|
-
resolve(JSON.parse(data));
|
|
141
|
-
} catch {
|
|
142
|
-
reject(new Error("Invalid JSON from registry"));
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
req.on("error", reject);
|
|
147
|
-
req.setTimeout(15e3, () => {
|
|
148
|
-
req.destroy();
|
|
149
|
-
reject(new Error("Timeout"));
|
|
150
|
-
});
|
|
151
|
-
req.end();
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
//#endregion
|
|
156
|
-
//#region src/plugins/hub.ts
|
|
157
|
-
require_paths$1.init_paths();
|
|
158
|
-
const SKILL_REGISTRY = [
|
|
159
|
-
{
|
|
160
|
-
id: "web-search",
|
|
161
|
-
name: "Web Search (Tavily)",
|
|
162
|
-
version: "2.1.0",
|
|
163
|
-
description: "Real-time web search via Tavily API. Powers research and news queries.",
|
|
164
|
-
author: "hyperclaw-team",
|
|
165
|
-
category: "utility",
|
|
166
|
-
downloads: 48200,
|
|
167
|
-
rating: 4.8,
|
|
168
|
-
risk: "clean",
|
|
169
|
-
requiresKeys: ["TAVILY_API_KEY"],
|
|
170
|
-
tags: [
|
|
171
|
-
"search",
|
|
172
|
-
"internet",
|
|
173
|
-
"tavily"
|
|
174
|
-
]
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
id: "calendar",
|
|
178
|
-
name: "Google Calendar",
|
|
179
|
-
version: "1.4.0",
|
|
180
|
-
description: "Read and create Google Calendar events via OAuth.",
|
|
181
|
-
author: "hyperclaw-team",
|
|
182
|
-
category: "productivity",
|
|
183
|
-
downloads: 32100,
|
|
184
|
-
rating: 4.6,
|
|
185
|
-
risk: "clean",
|
|
186
|
-
requiresKeys: ["GOOGLE_CALENDAR_CREDS"],
|
|
187
|
-
tags: [
|
|
188
|
-
"calendar",
|
|
189
|
-
"schedule",
|
|
190
|
-
"google"
|
|
191
|
-
],
|
|
192
|
-
npmPackage: "googleapis"
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
id: "github",
|
|
196
|
-
name: "GitHub Integration",
|
|
197
|
-
version: "1.2.0",
|
|
198
|
-
description: "Create issues, PRs, read repos. Requires GitHub PAT.",
|
|
199
|
-
author: "hyperclaw-team",
|
|
200
|
-
category: "integration",
|
|
201
|
-
downloads: 27800,
|
|
202
|
-
rating: 4.7,
|
|
203
|
-
risk: "clean",
|
|
204
|
-
requiresKeys: ["GITHUB_TOKEN"],
|
|
205
|
-
tags: [
|
|
206
|
-
"github",
|
|
207
|
-
"git",
|
|
208
|
-
"code"
|
|
209
|
-
],
|
|
210
|
-
npmPackage: "@octokit/rest"
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
id: "home-assistant",
|
|
214
|
-
name: "Home Assistant",
|
|
215
|
-
version: "1.5.0",
|
|
216
|
-
description: "Control smart home devices via Home Assistant REST API.",
|
|
217
|
-
author: "hyperclaw-team",
|
|
218
|
-
category: "automation",
|
|
219
|
-
downloads: 19400,
|
|
220
|
-
rating: 4.5,
|
|
221
|
-
risk: "clean",
|
|
222
|
-
requiresKeys: ["HA_URL", "HA_TOKEN"],
|
|
223
|
-
tags: [
|
|
224
|
-
"smart-home",
|
|
225
|
-
"iot",
|
|
226
|
-
"automation"
|
|
227
|
-
]
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
id: "code-executor",
|
|
231
|
-
name: "Code Executor (Sandbox)",
|
|
232
|
-
version: "3.0.1",
|
|
233
|
-
description: "Execute Python/JS/Bash code in a sandboxed Docker container.",
|
|
234
|
-
author: "hyperclaw-team",
|
|
235
|
-
category: "utility",
|
|
236
|
-
downloads: 41e3,
|
|
237
|
-
rating: 4.9,
|
|
238
|
-
risk: "clean",
|
|
239
|
-
tags: [
|
|
240
|
-
"code",
|
|
241
|
-
"sandbox",
|
|
242
|
-
"python",
|
|
243
|
-
"bash"
|
|
244
|
-
],
|
|
245
|
-
npmPackage: "dockerode"
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
id: "translator",
|
|
249
|
-
name: "Real-time Translator",
|
|
250
|
-
version: "2.0.0",
|
|
251
|
-
description: "DeepL + Google Translate integration for 90+ languages.",
|
|
252
|
-
author: "hyperclaw-team",
|
|
253
|
-
category: "utility",
|
|
254
|
-
downloads: 38500,
|
|
255
|
-
rating: 4.7,
|
|
256
|
-
risk: "clean",
|
|
257
|
-
requiresKeys: ["DEEPL_API_KEY"],
|
|
258
|
-
tags: [
|
|
259
|
-
"translate",
|
|
260
|
-
"language",
|
|
261
|
-
"deepl"
|
|
262
|
-
],
|
|
263
|
-
installed: true
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
id: "reminders",
|
|
267
|
-
name: "Smart Reminders",
|
|
268
|
-
version: "2.1.0",
|
|
269
|
-
description: "Natural language reminders with cron scheduling.",
|
|
270
|
-
author: "hyperclaw-team",
|
|
271
|
-
category: "productivity",
|
|
272
|
-
downloads: 29e3,
|
|
273
|
-
rating: 4.6,
|
|
274
|
-
risk: "clean",
|
|
275
|
-
tags: [
|
|
276
|
-
"reminders",
|
|
277
|
-
"cron",
|
|
278
|
-
"schedule"
|
|
279
|
-
],
|
|
280
|
-
installed: true
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
id: "weather",
|
|
284
|
-
name: "Weather Forecast",
|
|
285
|
-
version: "1.3.0",
|
|
286
|
-
description: "OpenWeatherMap integration. Current + 7-day forecast.",
|
|
287
|
-
author: "hyperclaw-team",
|
|
288
|
-
category: "utility",
|
|
289
|
-
downloads: 22100,
|
|
290
|
-
rating: 4.4,
|
|
291
|
-
risk: "clean",
|
|
292
|
-
requiresKeys: ["OPENWEATHER_API_KEY"],
|
|
293
|
-
tags: ["weather", "forecast"]
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
id: "stealth-browser",
|
|
297
|
-
name: "Stealth Browser",
|
|
298
|
-
version: "1.0.3",
|
|
299
|
-
description: "Headless browser with fingerprint evasion. Can bypass bot detection.",
|
|
300
|
-
author: "unknown-dev",
|
|
301
|
-
category: "utility",
|
|
302
|
-
downloads: 3200,
|
|
303
|
-
rating: 3.1,
|
|
304
|
-
risk: "suspicious",
|
|
305
|
-
riskReason: "Fingerprint evasion may violate ToS on some sites. VirusTotal: 2/72 engines flagged.",
|
|
306
|
-
tags: [
|
|
307
|
-
"browser",
|
|
308
|
-
"puppeteer",
|
|
309
|
-
"stealth"
|
|
310
|
-
],
|
|
311
|
-
npmPackage: "puppeteer-extra-plugin-stealth"
|
|
312
|
-
},
|
|
313
|
-
{
|
|
314
|
-
id: "db-reader",
|
|
315
|
-
name: "Database Reader",
|
|
316
|
-
version: "1.1.0",
|
|
317
|
-
description: "Read from PostgreSQL/MySQL/SQLite databases.",
|
|
318
|
-
author: "hyperclaw-team",
|
|
319
|
-
category: "integration",
|
|
320
|
-
downloads: 15600,
|
|
321
|
-
rating: 4.5,
|
|
322
|
-
risk: "clean",
|
|
323
|
-
requiresKeys: ["DATABASE_URL"],
|
|
324
|
-
tags: [
|
|
325
|
-
"database",
|
|
326
|
-
"sql",
|
|
327
|
-
"postgres"
|
|
328
|
-
],
|
|
329
|
-
npmPackage: "pg"
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
id: "keylogger-util",
|
|
333
|
-
name: "Input Monitor Pro",
|
|
334
|
-
version: "0.9.1",
|
|
335
|
-
description: "Monitors keyboard events for automation triggers.",
|
|
336
|
-
author: "shadowy-scripts",
|
|
337
|
-
category: "automation",
|
|
338
|
-
downloads: 890,
|
|
339
|
-
rating: 1.8,
|
|
340
|
-
risk: "dangerous",
|
|
341
|
-
riskReason: "Detected keylogging behavior. VirusTotal: 31/72 engines flagged as malware.",
|
|
342
|
-
tags: ["keyboard", "monitor"]
|
|
343
|
-
}
|
|
344
|
-
];
|
|
345
|
-
const WORKSPACE_SKILLS = () => path.default.join(require_paths.getHyperClawDir(), "workspace", "skills");
|
|
346
|
-
var SkillHub = class {
|
|
347
|
-
installed = /* @__PURE__ */ new Set();
|
|
348
|
-
/** Sync installed set from workspace disk (persisted installs). */
|
|
349
|
-
async refreshInstalledFromDisk() {
|
|
350
|
-
const ids = await listInstalledFromClawHub();
|
|
351
|
-
this.installed = new Set(ids);
|
|
352
|
-
}
|
|
353
|
-
/** Persist bundled skill to workspace so it survives restarts and is loaded by skill-loader. */
|
|
354
|
-
async persistBundledSkill(skill) {
|
|
355
|
-
const destDir = path.default.join(WORKSPACE_SKILLS(), skill.id);
|
|
356
|
-
await fs_extra.default.ensureDir(destDir);
|
|
357
|
-
const skillPath = path.default.join(destDir, "SKILL.md");
|
|
358
|
-
const repoSkillPath = path.default.join(process.cwd(), "skills", skill.id, "SKILL.md");
|
|
359
|
-
const altRepoPath = path.default.join(__dirname, "..", "..", "skills", skill.id, "SKILL.md");
|
|
360
|
-
if (await fs_extra.default.pathExists(repoSkillPath)) await fs_extra.default.copy(repoSkillPath, skillPath);
|
|
361
|
-
else if (await fs_extra.default.pathExists(altRepoPath)) await fs_extra.default.copy(altRepoPath, skillPath);
|
|
362
|
-
else {
|
|
363
|
-
const content = `# ${skill.name}\n\n${skill.description}\n\n## Usage\n\nWhen the user needs ${skill.description.toLowerCase()}, use this skill.${skill.requiresKeys?.length ? `\n\nRequires: ${skill.requiresKeys.join(", ")}` : ""}\n`;
|
|
364
|
-
await fs_extra.default.writeFile(skillPath, content, "utf8");
|
|
365
|
-
}
|
|
366
|
-
this.installed.add(skill.id);
|
|
367
|
-
}
|
|
368
|
-
async showHub(hideSuspicious = false) {
|
|
369
|
-
await this.refreshInstalledFromDisk();
|
|
370
|
-
console.log(chalk.default.bold.cyan("\n╔═══════════════════════════════════════════╗"));
|
|
371
|
-
console.log(chalk.default.bold.cyan("║ 🧩 HYPERCLAW SKILL HUB ║"));
|
|
372
|
-
console.log(chalk.default.bold.cyan("╚═══════════════════════════════════════════╝\n"));
|
|
373
|
-
const skills = hideSuspicious ? SKILL_REGISTRY.filter((s) => s.risk === "clean") : SKILL_REGISTRY;
|
|
374
|
-
for (const skill of skills) this.printSkillCard(skill);
|
|
375
|
-
}
|
|
376
|
-
printSkillCard(skill) {
|
|
377
|
-
const riskBadge = {
|
|
378
|
-
"clean": chalk.default.green("✔ CLEAN"),
|
|
379
|
-
"suspicious": chalk.default.yellow("⚠ SUSPICIOUS"),
|
|
380
|
-
"dangerous": chalk.default.red("✖ DANGEROUS")
|
|
381
|
-
}[skill.risk];
|
|
382
|
-
const instBadge = this.installed.has(skill.id) ? chalk.default.green("[installed]") : chalk.default.gray("[available]");
|
|
383
|
-
const stars = "★".repeat(Math.round(skill.rating)) + "☆".repeat(5 - Math.round(skill.rating));
|
|
384
|
-
console.log(` ${chalk.default.bold(skill.name)} ${chalk.default.gray(`v${skill.version}`)} ${instBadge}`);
|
|
385
|
-
console.log(` ${chalk.default.gray(skill.description)}`);
|
|
386
|
-
console.log(` ${riskBadge} ${chalk.default.yellow(stars)} ${chalk.default.gray(`${(skill.downloads / 1e3).toFixed(1)}k downloads`)}`);
|
|
387
|
-
if (skill.riskReason) console.log(` ${chalk.default.yellow("⚠")} ${chalk.default.yellow(skill.riskReason)}`);
|
|
388
|
-
if (skill.requiresKeys?.length) console.log(` 🔑 Requires: ${chalk.default.cyan(skill.requiresKeys.join(", "))}`);
|
|
389
|
-
console.log();
|
|
390
|
-
}
|
|
391
|
-
async install(skillId, force = false) {
|
|
392
|
-
const skill = SKILL_REGISTRY.find((s) => s.id === skillId);
|
|
393
|
-
if (!skill) {
|
|
394
|
-
console.log(chalk.default.red(`❌ Skill not found: ${skillId}`));
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
if (skill.risk === "dangerous" && !force) {
|
|
398
|
-
console.log(chalk.default.red(`\n🚨 DANGEROUS SKILL BLOCKED: ${skill.name}`));
|
|
399
|
-
console.log(chalk.default.red(` ${skill.riskReason}`));
|
|
400
|
-
console.log(chalk.default.gray(" Use --force to override (NOT RECOMMENDED)\n"));
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
if (skill.risk === "suspicious" && !force) {
|
|
404
|
-
console.log(chalk.default.yellow(`\n⚠️ SUSPICIOUS SKILL: ${skill.name}`));
|
|
405
|
-
console.log(chalk.default.yellow(` ${skill.riskReason}`));
|
|
406
|
-
console.log(chalk.default.gray(" Use --force to install anyway\n"));
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
const spinner = (0, ora.default)(`Installing ${skill.name}...`).start();
|
|
410
|
-
if (skill.npmPackage) {
|
|
411
|
-
spinner.text = `Installing npm package: ${skill.npmPackage}`;
|
|
412
|
-
await new Promise((r) => setTimeout(r, 800));
|
|
413
|
-
}
|
|
414
|
-
await this.persistBundledSkill(skill);
|
|
415
|
-
spinner.succeed(`${skill.name} installed ✓`);
|
|
416
|
-
if (skill.requiresKeys?.length) {
|
|
417
|
-
console.log(chalk.default.yellow(`\n📋 Required API keys to activate:`));
|
|
418
|
-
skill.requiresKeys.forEach((k) => {
|
|
419
|
-
console.log(chalk.default.cyan(` hyperclaw config set-key ${k}`));
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
console.log();
|
|
423
|
-
}
|
|
424
|
-
async scan(skillId) {
|
|
425
|
-
const skill = SKILL_REGISTRY.find((s) => s.id === skillId);
|
|
426
|
-
if (!skill) return;
|
|
427
|
-
const spinner = (0, ora.default)(`Scanning ${skill.name}...`).start();
|
|
428
|
-
const stages = [
|
|
429
|
-
"Checking manifest...",
|
|
430
|
-
"Scanning for malicious patterns...",
|
|
431
|
-
"Checking VirusTotal...",
|
|
432
|
-
"Verifying author..."
|
|
433
|
-
];
|
|
434
|
-
for (const stage of stages) {
|
|
435
|
-
spinner.text = stage;
|
|
436
|
-
await new Promise((r) => setTimeout(r, 600));
|
|
437
|
-
}
|
|
438
|
-
const result = {
|
|
439
|
-
"clean": chalk.default.green("✅ All green — safe to install"),
|
|
440
|
-
"suspicious": chalk.default.yellow("⚠️ Suspicious patterns detected — proceed with caution"),
|
|
441
|
-
"dangerous": chalk.default.red("🚨 Malicious patterns detected — do NOT install")
|
|
442
|
-
}[skill.risk];
|
|
443
|
-
spinner.stop();
|
|
444
|
-
console.log(`\n🔬 Scan results for ${chalk.default.bold(skill.name)}:`);
|
|
445
|
-
console.log(` ${result}`);
|
|
446
|
-
if (skill.riskReason) console.log(chalk.default.gray(` Detail: ${skill.riskReason}`));
|
|
447
|
-
console.log();
|
|
448
|
-
}
|
|
449
|
-
async checkEligibility() {
|
|
450
|
-
const spinner = (0, ora.default)("Checking system eligibility...").start();
|
|
451
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
452
|
-
spinner.succeed("Eligibility check complete");
|
|
453
|
-
console.log(chalk.default.green("\n✅ All installed skills are eligible on this system\n"));
|
|
454
|
-
}
|
|
455
|
-
async getInstalled() {
|
|
456
|
-
await this.refreshInstalledFromDisk();
|
|
457
|
-
const ids = this.installed;
|
|
458
|
-
const fromRegistry = SKILL_REGISTRY.filter((s) => ids.has(s.id));
|
|
459
|
-
const fromWorkspace = (await listInstalledFromClawHub()).filter((id) => !SKILL_REGISTRY.some((s) => s.id === id));
|
|
460
|
-
return [...fromRegistry, ...fromWorkspace.map((id) => ({
|
|
461
|
-
id,
|
|
462
|
-
name: id,
|
|
463
|
-
version: "0",
|
|
464
|
-
description: "",
|
|
465
|
-
author: "",
|
|
466
|
-
category: "utility",
|
|
467
|
-
downloads: 0,
|
|
468
|
-
rating: 0,
|
|
469
|
-
risk: "clean",
|
|
470
|
-
tags: []
|
|
471
|
-
}))];
|
|
472
|
-
}
|
|
473
|
-
/** ClawHub integration: search remote registry, fallback to bundled when remote unavailable */
|
|
474
|
-
async searchClawHub(query, category) {
|
|
475
|
-
let remote = await searchSkills(query, category);
|
|
476
|
-
if (remote.length === 0) {
|
|
477
|
-
const q = (query || "").toLowerCase();
|
|
478
|
-
const filtered = SKILL_REGISTRY.filter((s) => !q || s.id.includes(q) || s.name.toLowerCase().includes(q) || s.tags.some((t) => t.includes(q))).filter((s) => !category || s.category === category);
|
|
479
|
-
remote = filtered.map((s) => ({
|
|
480
|
-
id: s.id,
|
|
481
|
-
name: s.name,
|
|
482
|
-
author: s.author,
|
|
483
|
-
description: s.description,
|
|
484
|
-
rating: s.rating,
|
|
485
|
-
downloads: s.downloads,
|
|
486
|
-
version: s.version,
|
|
487
|
-
categories: [s.category]
|
|
488
|
-
}));
|
|
489
|
-
}
|
|
490
|
-
return remote;
|
|
491
|
-
}
|
|
492
|
-
/** ClawHub integration: install from remote registry */
|
|
493
|
-
async installFromClawHub(skillId, version) {
|
|
494
|
-
return installSkill(skillId, version);
|
|
495
|
-
}
|
|
496
|
-
/** ClawHub marketplace UX: unified browse (bundled + remote) */
|
|
497
|
-
async showMarketplace(opts) {
|
|
498
|
-
await this.refreshInstalledFromDisk();
|
|
499
|
-
const installedClawHub = await listInstalledFromClawHub();
|
|
500
|
-
const bundled = (opts?.hideSuspicious ? SKILL_REGISTRY.filter((s) => s.risk === "clean") : SKILL_REGISTRY).filter((s) => !opts?.category || s.category === opts.category);
|
|
501
|
-
console.log(chalk.default.bold.cyan("\n╔══════════════════════════════════════════════════════════╗"));
|
|
502
|
-
console.log(chalk.default.bold.cyan("║ 🧩 CLAWHUB MARKETPLACE ║"));
|
|
503
|
-
console.log(chalk.default.bold.cyan("╚══════════════════════════════════════════════════════════╝\n"));
|
|
504
|
-
if (installedClawHub.length > 0) {
|
|
505
|
-
console.log(chalk.default.bold.green(" Installed (ClawHub):"));
|
|
506
|
-
installedClawHub.forEach((id) => console.log(chalk.default.gray(` • ${id}`)));
|
|
507
|
-
console.log();
|
|
508
|
-
}
|
|
509
|
-
console.log(chalk.default.bold(" Bundled skills:"));
|
|
510
|
-
for (const skill of bundled) {
|
|
511
|
-
const inst = this.installed.has(skill.id) || installedClawHub.includes(skill.id);
|
|
512
|
-
const badge = inst ? chalk.default.green("✓ installed") : chalk.default.cyan("hyperclaw skill install " + skill.id);
|
|
513
|
-
const risk = skill.risk === "clean" ? "" : chalk.default.yellow(` [${skill.risk}]`);
|
|
514
|
-
console.log(` ${chalk.default.bold(skill.name)} ${chalk.default.gray(`v${skill.version}`)} ${badge}${risk}`);
|
|
515
|
-
console.log(chalk.default.gray(` ${skill.description}`));
|
|
516
|
-
}
|
|
517
|
-
console.log(chalk.default.gray("\n Search remote: hyperclaw skill search <query>"));
|
|
518
|
-
console.log(chalk.default.gray(" Install: hyperclaw skill install <id>\n"));
|
|
519
|
-
}
|
|
520
|
-
};
|
|
521
|
-
|
|
522
|
-
//#endregion
|
|
523
35
|
//#region src/cli/dashboard.ts
|
|
524
36
|
var Dashboard = class {
|
|
525
37
|
async launch(live) {
|
|
@@ -533,7 +45,7 @@ var Dashboard = class {
|
|
|
533
45
|
async drawDashboard() {
|
|
534
46
|
const cfg = await new require_onboard.ConfigStore().load();
|
|
535
47
|
const gm = new require_onboard.GatewayManager();
|
|
536
|
-
const hub = new SkillHub();
|
|
48
|
+
const hub = new require_hub.SkillHub();
|
|
537
49
|
const installed = await hub.getInstalled();
|
|
538
50
|
const port = cfg?.gateway?.port || 1515;
|
|
539
51
|
const agent = cfg?.identity?.agentName || "Hyper";
|
|
@@ -552,7 +64,7 @@ var Dashboard = class {
|
|
|
552
64
|
return c(`║ `) + content + " ".repeat(pad) + c(`║`);
|
|
553
65
|
};
|
|
554
66
|
console.log(c(`╔${line}╗`));
|
|
555
|
-
console.log(c(`║`) + chalk.default.bold.hex("#06b6d4")(`${"🦅 HYPERCLAW
|
|
67
|
+
console.log(c(`║`) + chalk.default.bold.hex("#06b6d4")(`${"🦅 HYPERCLAW v5.0.0 — GATEWAY DASHBOARD".padStart(45).padEnd(w)}`) + c(`║`));
|
|
556
68
|
console.log(c(`╠${line}╣`));
|
|
557
69
|
console.log(row(`${statusDot} Gateway ${statusText} ${chalk.default.gray("│")} ws://localhost:${port} ${chalk.default.gray("│")} Agent: ${c(agent)}`));
|
|
558
70
|
console.log(row(`${c("◆")} Model ${chalk.default.gray(model.slice(0, 30))} ${chalk.default.gray("│")} User: ${c(user)}`));
|
|
@@ -633,7 +145,7 @@ async function recordAudio(outFile, seconds) {
|
|
|
633
145
|
async function transcribeWhisper(filePath, lang) {
|
|
634
146
|
const apiKey = process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
|
|
635
147
|
if (!apiKey) throw new Error("No OPENAI_API_KEY set");
|
|
636
|
-
const FormData = (await Promise.resolve().then(() => require_chunk.__toDynamicImportESM()(require("./form_data-
|
|
148
|
+
const FormData = (await Promise.resolve().then(() => require_chunk.__toDynamicImportESM()(require("./form_data-Cz040rio.js"))).catch(() => null))?.default;
|
|
637
149
|
if (!FormData) throw new Error("form-data not installed");
|
|
638
150
|
const form = new FormData();
|
|
639
151
|
form.append("file", fs.createReadStream(filePath), {
|
|
@@ -778,281 +290,183 @@ var VoiceEngine = class {
|
|
|
778
290
|
};
|
|
779
291
|
|
|
780
292
|
//#endregion
|
|
781
|
-
//#region src/
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
this.agents = [{
|
|
794
|
-
workspace: path.default.join(os.default.homedir(), ".hyperclaw", "workspace"),
|
|
795
|
-
name: "default",
|
|
796
|
-
model: void 0,
|
|
797
|
-
bindings: []
|
|
798
|
-
}];
|
|
799
|
-
}
|
|
293
|
+
//#region src/infra/device-pairing.ts
|
|
294
|
+
const DEVICES_DIR = path.default.join(os.default.homedir(), ".hyperclaw", "devices");
|
|
295
|
+
const PENDING_FILE = path.default.join(DEVICES_DIR, "pending.json");
|
|
296
|
+
const PAIRED_FILE = path.default.join(DEVICES_DIR, "paired.json");
|
|
297
|
+
const PENDING_EXPIRY_MS = 10 * 60 * 1e3;
|
|
298
|
+
const TOKEN_BYTES = 16;
|
|
299
|
+
async function readPending() {
|
|
300
|
+
try {
|
|
301
|
+
const data = await fs_extra.default.readJson(PENDING_FILE);
|
|
302
|
+
return Array.isArray(data) ? data : [];
|
|
303
|
+
} catch {
|
|
304
|
+
return [];
|
|
800
305
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
306
|
+
}
|
|
307
|
+
async function writePending(entries) {
|
|
308
|
+
await fs_extra.default.ensureDir(DEVICES_DIR);
|
|
309
|
+
await fs_extra.default.writeJson(PENDING_FILE, entries, {
|
|
310
|
+
spaces: 2,
|
|
311
|
+
mode: 384
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
async function readPaired() {
|
|
315
|
+
try {
|
|
316
|
+
const data = await fs_extra.default.readJson(PAIRED_FILE);
|
|
317
|
+
return Array.isArray(data) ? data : [];
|
|
318
|
+
} catch {
|
|
319
|
+
return [];
|
|
804
320
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
321
|
+
}
|
|
322
|
+
async function writePaired(devices) {
|
|
323
|
+
await fs_extra.default.ensureDir(DEVICES_DIR);
|
|
324
|
+
await fs_extra.default.writeJson(PAIRED_FILE, devices, {
|
|
325
|
+
spaces: 2,
|
|
326
|
+
mode: 384
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
var DevicePairingStore = class {
|
|
330
|
+
/**
|
|
331
|
+
|
|
332
|
+
* Creates a pending pairing request and returns:
|
|
333
|
+
|
|
334
|
+
* - requestId: show to user for approve/reject
|
|
335
|
+
|
|
336
|
+
* - setupCode: base64-encoded JSON {url, token} — send to device
|
|
337
|
+
|
|
338
|
+
*/
|
|
339
|
+
async createRequest(gatewayUrl, opts) {
|
|
340
|
+
const now = Date.now();
|
|
341
|
+
const all = (await readPending()).filter((e) => new Date(e.expiresAt).getTime() > now);
|
|
342
|
+
const requestId = crypto.default.randomBytes(6).toString("hex");
|
|
343
|
+
const token = crypto.default.randomBytes(TOKEN_BYTES).toString("hex");
|
|
344
|
+
const expiresAt = new Date(now + PENDING_EXPIRY_MS).toISOString();
|
|
345
|
+
const entry = {
|
|
346
|
+
requestId,
|
|
347
|
+
token,
|
|
348
|
+
expiresAt,
|
|
349
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
350
|
+
...opts?.deviceName ? { deviceName: opts.deviceName } : {},
|
|
351
|
+
...opts?.platform ? { platform: opts.platform } : {}
|
|
352
|
+
};
|
|
353
|
+
all.push(entry);
|
|
354
|
+
await writePending(all);
|
|
355
|
+
const payload = {
|
|
356
|
+
url: gatewayUrl,
|
|
357
|
+
token
|
|
358
|
+
};
|
|
359
|
+
const setupCode = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
360
|
+
return {
|
|
361
|
+
requestId,
|
|
362
|
+
setupCode,
|
|
363
|
+
expiresAt
|
|
364
|
+
};
|
|
821
365
|
}
|
|
822
|
-
async
|
|
823
|
-
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
name: `${b.channel} → ${b.agent}`,
|
|
884
|
-
value: b
|
|
885
|
-
}))
|
|
886
|
-
}]);
|
|
887
|
-
for (const { agent: agentName, channel } of toRemove) {
|
|
888
|
-
const agent = this.agents.find((a) => a.name === agentName);
|
|
889
|
-
if (agent) agent.bindings = agent.bindings.filter((b) => b.channelId !== channel);
|
|
366
|
+
async listPending() {
|
|
367
|
+
const now = Date.now();
|
|
368
|
+
const all = await readPending();
|
|
369
|
+
return all.filter((e) => new Date(e.expiresAt).getTime() > now);
|
|
370
|
+
}
|
|
371
|
+
async listPaired() {
|
|
372
|
+
return readPaired();
|
|
373
|
+
}
|
|
374
|
+
async approve(requestId) {
|
|
375
|
+
const now = Date.now();
|
|
376
|
+
const pending = await readPending();
|
|
377
|
+
const idx = pending.findIndex((e) => e.requestId === requestId && new Date(e.expiresAt).getTime() > now);
|
|
378
|
+
if (idx === -1) return null;
|
|
379
|
+
const entry = pending[idx];
|
|
380
|
+
pending.splice(idx, 1);
|
|
381
|
+
await writePending(pending);
|
|
382
|
+
const deviceId = `device-${crypto.default.randomBytes(4).toString("hex")}`;
|
|
383
|
+
const longToken = crypto.default.randomBytes(32).toString("hex");
|
|
384
|
+
const device = {
|
|
385
|
+
deviceId,
|
|
386
|
+
requestId: entry.requestId,
|
|
387
|
+
token: longToken,
|
|
388
|
+
pairedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
389
|
+
...entry.deviceName ? { deviceName: entry.deviceName } : {},
|
|
390
|
+
...entry.platform ? { platform: entry.platform } : {}
|
|
391
|
+
};
|
|
392
|
+
const paired = await readPaired();
|
|
393
|
+
paired.push(device);
|
|
394
|
+
await writePaired(paired);
|
|
395
|
+
return device;
|
|
396
|
+
}
|
|
397
|
+
async reject(requestId) {
|
|
398
|
+
const pending = await readPending();
|
|
399
|
+
const idx = pending.findIndex((e) => e.requestId === requestId);
|
|
400
|
+
if (idx === -1) return false;
|
|
401
|
+
pending.splice(idx, 1);
|
|
402
|
+
await writePending(pending);
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
async unpair(deviceId) {
|
|
406
|
+
const paired = await readPaired();
|
|
407
|
+
const idx = paired.findIndex((d) => d.deviceId === deviceId);
|
|
408
|
+
if (idx === -1) return false;
|
|
409
|
+
paired.splice(idx, 1);
|
|
410
|
+
await writePaired(paired);
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
async verifyToken(token) {
|
|
414
|
+
const now = Date.now();
|
|
415
|
+
const pending = await readPending();
|
|
416
|
+
const pendingMatch = pending.find((e) => e.token === token && new Date(e.expiresAt).getTime() > now);
|
|
417
|
+
if (pendingMatch) return this.approve(pendingMatch.requestId);
|
|
418
|
+
const paired = await readPaired();
|
|
419
|
+
return paired.find((d) => d.token === token) ?? null;
|
|
420
|
+
}
|
|
421
|
+
async touchDevice(deviceId) {
|
|
422
|
+
const paired = await readPaired();
|
|
423
|
+
const device = paired.find((d) => d.deviceId === deviceId);
|
|
424
|
+
if (device) {
|
|
425
|
+
device.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
426
|
+
await writePaired(paired);
|
|
890
427
|
}
|
|
891
|
-
this.save();
|
|
892
|
-
console.log(chalk.default.green(`\n ✔ Removed ${toRemove.length} binding(s)\n`));
|
|
893
428
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
//#endregion
|
|
897
|
-
//#region src/commands/doctor.ts
|
|
898
|
-
async function isPortOpen(port) {
|
|
899
|
-
return new Promise((resolve) => {
|
|
900
|
-
const s = new net.default.Socket();
|
|
901
|
-
s.setTimeout(500);
|
|
902
|
-
s.on("connect", () => {
|
|
903
|
-
s.destroy();
|
|
904
|
-
resolve(true);
|
|
905
|
-
});
|
|
906
|
-
s.on("error", () => resolve(false));
|
|
907
|
-
s.on("timeout", () => resolve(false));
|
|
429
|
+
static parseSetupCode(setupCode) {
|
|
908
430
|
try {
|
|
909
|
-
|
|
431
|
+
const json = Buffer.from(setupCode, "base64").toString("utf8");
|
|
432
|
+
const parsed = JSON.parse(json);
|
|
433
|
+
if (typeof parsed.url === "string" && typeof parsed.token === "string") return parsed;
|
|
434
|
+
return null;
|
|
910
435
|
} catch {
|
|
911
|
-
|
|
436
|
+
return null;
|
|
912
437
|
}
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
cfg = fs_extra.default.readJsonSync(configFile);
|
|
927
|
-
} catch {}
|
|
928
|
-
const issues = [];
|
|
929
|
-
if (!cfg) issues.push({
|
|
930
|
-
id: "no-config",
|
|
931
|
-
severity: "error",
|
|
932
|
-
title: "No configuration found",
|
|
933
|
-
detail: "Run: hyperclaw init",
|
|
934
|
-
fixable: false
|
|
935
|
-
});
|
|
936
|
-
else {
|
|
937
|
-
const hasToken = !!cfg.gateway?.authToken;
|
|
938
|
-
issues.push({
|
|
939
|
-
id: "gateway-token",
|
|
940
|
-
severity: hasToken ? "ok" : "warn",
|
|
941
|
-
title: hasToken ? "Gateway auth token set" : "Gateway auth token missing",
|
|
942
|
-
detail: hasToken ? "Token is configured" : "Set a strong token in gateway config",
|
|
943
|
-
fixable: !hasToken,
|
|
944
|
-
fix: async () => {
|
|
945
|
-
const crypto = await import("crypto");
|
|
946
|
-
cfg.gateway = cfg.gateway || {};
|
|
947
|
-
cfg.gateway.authToken = crypto.randomBytes(32).toString("hex");
|
|
948
|
-
fs_extra.default.writeJsonSync(configFile, cfg, { spaces: 2 });
|
|
949
|
-
console.log(chalk.default.green(" ✔ Generated and saved gateway auth token"));
|
|
438
|
+
}
|
|
439
|
+
async showCLI() {
|
|
440
|
+
const now = Date.now();
|
|
441
|
+
const pending = (await readPending()).filter((e) => new Date(e.expiresAt).getTime() > now);
|
|
442
|
+
const paired = await readPaired();
|
|
443
|
+
console.log(chalk.default.bold.cyan("\n 📱 DEVICE PAIRING\n"));
|
|
444
|
+
if (pending.length > 0) {
|
|
445
|
+
console.log(chalk.default.yellow(" Pending requests:\n"));
|
|
446
|
+
for (const e of pending) {
|
|
447
|
+
const expiresIn = Math.round((new Date(e.expiresAt).getTime() - now) / 6e4);
|
|
448
|
+
const name = e.deviceName ? chalk.default.white(` "${e.deviceName}"`) : "";
|
|
449
|
+
const plat = e.platform ? chalk.default.gray(` (${e.platform})`) : "";
|
|
450
|
+
console.log(` ${chalk.default.yellow("○")} ${chalk.default.bold(e.requestId)}${name}${plat} ${chalk.default.gray(`expires in ${expiresIn}m`)}`);
|
|
950
451
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
detail: `Anyone can DM your agent on ${ch}. Consider using "pairing" or "allowlist".`,
|
|
961
|
-
fixable: false
|
|
962
|
-
});
|
|
963
|
-
if (dmPolicy === "allowlist") {
|
|
964
|
-
const allowFrom = channelConfigs[ch]?.dmPolicy?.allowFrom || [];
|
|
965
|
-
if (allowFrom.length === 0) issues.push({
|
|
966
|
-
id: `dm-empty-allowlist-${ch}`,
|
|
967
|
-
severity: "error",
|
|
968
|
-
title: `Empty allowlist on ${ch} — DMs will be silently dropped`,
|
|
969
|
-
detail: `channel.${ch}.dmPolicy.allowFrom is empty. Add users or change policy.`,
|
|
970
|
-
fixable: true,
|
|
971
|
-
fix: async () => {
|
|
972
|
-
try {
|
|
973
|
-
const pairingEntries = fs_extra.default.readJsonSync(pairingFile);
|
|
974
|
-
const approved = pairingEntries.filter((e) => e.channelId === ch && e.status === "approved" && e.userId);
|
|
975
|
-
if (approved.length > 0) {
|
|
976
|
-
channelConfigs[ch].dmPolicy.allowFrom = approved.map((e) => e.userId);
|
|
977
|
-
cfg.channelConfigs = channelConfigs;
|
|
978
|
-
fs_extra.default.writeJsonSync(configFile, cfg, { spaces: 2 });
|
|
979
|
-
console.log(chalk.default.green(` ✔ Restored ${approved.length} user(s) from pairing store to ${ch} allowlist`));
|
|
980
|
-
}
|
|
981
|
-
} catch {}
|
|
982
|
-
}
|
|
983
|
-
});
|
|
452
|
+
console.log();
|
|
453
|
+
}
|
|
454
|
+
if (paired.length > 0) {
|
|
455
|
+
console.log(chalk.default.green(" Paired devices:\n"));
|
|
456
|
+
for (const d of paired) {
|
|
457
|
+
const name = d.deviceName ? chalk.default.white(` "${d.deviceName}"`) : "";
|
|
458
|
+
const plat = d.platform ? chalk.default.gray(` (${d.platform})`) : "";
|
|
459
|
+
const seen = d.lastSeenAt ? chalk.default.gray(` last seen: ${new Date(d.lastSeenAt).toLocaleDateString()}`) : "";
|
|
460
|
+
console.log(` ${chalk.default.green("●")} ${chalk.default.bold(d.deviceId)}${name}${plat} ${chalk.default.gray(`paired: ${new Date(d.pairedAt).toLocaleDateString()}`)}${seen}`);
|
|
984
461
|
}
|
|
462
|
+
console.log();
|
|
985
463
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
id: "no-api-key",
|
|
990
|
-
severity: "error",
|
|
991
|
-
title: "No AI provider API key configured",
|
|
992
|
-
detail: "Run: hyperclaw config set-key",
|
|
993
|
-
fixable: false
|
|
994
|
-
});
|
|
995
|
-
else issues.push({
|
|
996
|
-
id: "api-key",
|
|
997
|
-
severity: "ok",
|
|
998
|
-
title: "AI provider key configured",
|
|
999
|
-
detail: `Provider: ${cfg.provider?.providerId}`,
|
|
1000
|
-
fixable: false
|
|
1001
|
-
});
|
|
1002
|
-
issues.push({
|
|
1003
|
-
id: "agents-md",
|
|
1004
|
-
severity: await fs_extra.default.pathExists(agentsFile) ? "ok" : "warn",
|
|
1005
|
-
title: await fs_extra.default.pathExists(agentsFile) ? "AGENTS.md exists" : "AGENTS.md missing",
|
|
1006
|
-
detail: await fs_extra.default.pathExists(agentsFile) ? agentsFile : "Run: hyperclaw memory init to generate",
|
|
1007
|
-
fixable: false
|
|
1008
|
-
});
|
|
1009
|
-
const port = cfg.gateway?.port || 1515;
|
|
1010
|
-
const running = await isPortOpen(port);
|
|
1011
|
-
issues.push({
|
|
1012
|
-
id: "gateway-running",
|
|
1013
|
-
severity: running ? "ok" : "warn",
|
|
1014
|
-
title: running ? `Gateway running on port ${port}` : `Gateway not running on port ${port}`,
|
|
1015
|
-
detail: running ? `ws://127.0.0.1:${port}` : "Run: hyperclaw daemon start",
|
|
1016
|
-
fixable: false
|
|
1017
|
-
});
|
|
1018
|
-
if (await fs_extra.default.pathExists(authFile)) {
|
|
1019
|
-
const stat = await fs_extra.default.stat(authFile);
|
|
1020
|
-
const unsafe = (stat.mode & 63) !== 0;
|
|
1021
|
-
issues.push({
|
|
1022
|
-
id: "auth-permissions",
|
|
1023
|
-
severity: unsafe ? "warn" : "ok",
|
|
1024
|
-
title: unsafe ? "Auth store has unsafe permissions" : "Auth store permissions OK",
|
|
1025
|
-
detail: unsafe ? `chmod 600 ${authFile}` : `Mode: 600`,
|
|
1026
|
-
fixable: unsafe,
|
|
1027
|
-
fix: async () => {
|
|
1028
|
-
await fs_extra.default.chmod(authFile, 384);
|
|
1029
|
-
console.log(chalk.default.green(` ✔ Fixed permissions on ${authFile}`));
|
|
1030
|
-
}
|
|
1031
|
-
});
|
|
464
|
+
if (pending.length === 0 && paired.length === 0) {
|
|
465
|
+
console.log(chalk.default.gray(" No pending or paired devices."));
|
|
466
|
+
console.log(chalk.default.gray(" In Telegram, message your bot: /pair\n"));
|
|
1032
467
|
}
|
|
1033
468
|
}
|
|
1034
|
-
|
|
1035
|
-
let errorCount = 0, warnCount = 0;
|
|
1036
|
-
for (const issue of issues) {
|
|
1037
|
-
const icon = {
|
|
1038
|
-
error: chalk.default.red("✖"),
|
|
1039
|
-
warn: chalk.default.yellow("⚠"),
|
|
1040
|
-
ok: chalk.default.green("✔")
|
|
1041
|
-
}[issue.severity];
|
|
1042
|
-
console.log(` ${icon} ${chalk.default.white(issue.title)}`);
|
|
1043
|
-
console.log(` ${chalk.default.gray(issue.detail)}`);
|
|
1044
|
-
if (issue.fixable && fix && issue.fix) await issue.fix();
|
|
1045
|
-
else if (issue.fixable && !fix) console.log(chalk.default.gray(" Run with --fix to auto-repair"));
|
|
1046
|
-
if (issue.severity === "error") errorCount++;
|
|
1047
|
-
if (issue.severity === "warn") warnCount++;
|
|
1048
|
-
console.log();
|
|
1049
|
-
}
|
|
1050
|
-
const total = issues.length;
|
|
1051
|
-
const okCount = total - errorCount - warnCount;
|
|
1052
|
-
console.log(` ${chalk.default.bold("Summary:")} ${chalk.default.green(`${okCount} ok`)} ${chalk.default.yellow(`${warnCount} warnings`)} ${chalk.default.red(`${errorCount} errors`)}`);
|
|
1053
|
-
if (errorCount > 0 || warnCount > 0) console.log(chalk.default.gray("\n Run: hyperclaw doctor --fix to auto-repair fixable issues\n"));
|
|
1054
|
-
else console.log(chalk.default.green("\n ✔ All checks passed!\n"));
|
|
1055
|
-
}
|
|
469
|
+
};
|
|
1056
470
|
|
|
1057
471
|
//#endregion
|
|
1058
472
|
//#region src/commands/message-send.ts
|
|
@@ -1115,17 +529,28 @@ const CHANNELS = [
|
|
|
1115
529
|
platforms: ["all"],
|
|
1116
530
|
tokenLabel: "Telegram Bot Token",
|
|
1117
531
|
tokenHint: "Get from @BotFather → /newbot",
|
|
532
|
+
extraFields: [{
|
|
533
|
+
name: "dmPolicy",
|
|
534
|
+
label: "DM policy",
|
|
535
|
+
hint: "pairing (default) | allowlist | open | disabled",
|
|
536
|
+
required: false
|
|
537
|
+
}, {
|
|
538
|
+
name: "groupActivation",
|
|
539
|
+
label: "Group activation",
|
|
540
|
+
hint: "mention (default) | always",
|
|
541
|
+
required: false
|
|
542
|
+
}],
|
|
1118
543
|
setupSteps: [
|
|
1119
|
-
"1. Open Telegram
|
|
1120
|
-
"2.
|
|
1121
|
-
"3.
|
|
1122
|
-
"4.
|
|
1123
|
-
"5. Copy the token and paste it below.",
|
|
544
|
+
"1. Open Telegram → @BotFather → /newbot. Save the token.",
|
|
545
|
+
"2. Config: channels.telegram.botToken, dmPolicy (default: pairing), groups.",
|
|
546
|
+
"3. Start gateway, approve first DM: hyperclaw pairing approve telegram <CODE>",
|
|
547
|
+
"4. Add bot to groups; set groups[\"*\"].requireMention for mention gating.",
|
|
1124
548
|
"",
|
|
1125
|
-
" 🔗
|
|
549
|
+
" 🔗 docs/telegram.md — full setup"
|
|
1126
550
|
],
|
|
1127
551
|
status: "recommended",
|
|
1128
|
-
npmPackage: "node-telegram-bot-api"
|
|
552
|
+
npmPackage: "node-telegram-bot-api",
|
|
553
|
+
notes: "DMs + groups. Pairing, allowlist, voice notes. Long polling default."
|
|
1129
554
|
},
|
|
1130
555
|
{
|
|
1131
556
|
id: "discord",
|
|
@@ -1137,23 +562,37 @@ const CHANNELS = [
|
|
|
1137
562
|
tokenLabel: "Discord Bot Token",
|
|
1138
563
|
tokenHint: "discord.com/developers/applications",
|
|
1139
564
|
setupSteps: [
|
|
1140
|
-
"1.
|
|
1141
|
-
"2.
|
|
1142
|
-
"3.
|
|
1143
|
-
"4.
|
|
1144
|
-
"5.
|
|
1145
|
-
"6.
|
|
565
|
+
"1. Discord Developer Portal → New Application → Bot → Reset Token.",
|
|
566
|
+
"2. Enable Message Content Intent (and Server Members if needed).",
|
|
567
|
+
"3. OAuth2 URL Generator: scope bot + applications.commands, permissions: View Channels, Send Messages, Read Message History.",
|
|
568
|
+
"4. Add bot to server, enable Developer Mode, copy Server ID and User ID.",
|
|
569
|
+
"5. Enable DMs from server members (right‑click server → Privacy Settings).",
|
|
570
|
+
"6. hyperclaw gateway → DM the bot → hyperclaw pairing approve discord <CODE>",
|
|
1146
571
|
"",
|
|
1147
|
-
" 🔗 discord.
|
|
572
|
+
" 🔗 docs/discord-setup.md — full setup guide"
|
|
573
|
+
],
|
|
574
|
+
extraFields: [
|
|
575
|
+
{
|
|
576
|
+
name: "listenGuildIds",
|
|
577
|
+
label: "Guild IDs to listen in",
|
|
578
|
+
hint: "[] = all. Add Server IDs to restrict.",
|
|
579
|
+
required: false
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
name: "requireMentionInGuild",
|
|
583
|
+
label: "Require @mention in guild",
|
|
584
|
+
hint: "true (default) | false",
|
|
585
|
+
required: false
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
name: "dmPolicy",
|
|
589
|
+
label: "DM policy",
|
|
590
|
+
hint: "\"pairing\" (default) | \"allowlist\" | \"open\" | \"none\"",
|
|
591
|
+
required: false
|
|
592
|
+
}
|
|
1148
593
|
],
|
|
1149
|
-
extraFields: [{
|
|
1150
|
-
name: "clientId",
|
|
1151
|
-
label: "Client ID (Application ID)",
|
|
1152
|
-
hint: "From OAuth2 → General",
|
|
1153
|
-
required: true
|
|
1154
|
-
}],
|
|
1155
594
|
status: "recommended",
|
|
1156
|
-
npmPackage: "
|
|
595
|
+
npmPackage: "ws"
|
|
1157
596
|
},
|
|
1158
597
|
{
|
|
1159
598
|
id: "whatsapp",
|
|
@@ -1162,18 +601,38 @@ const CHANNELS = [
|
|
|
1162
601
|
requiresGateway: true,
|
|
1163
602
|
supportsDM: true,
|
|
1164
603
|
platforms: ["all"],
|
|
1165
|
-
tokenLabel: "
|
|
604
|
+
tokenLabel: "Access Token",
|
|
1166
605
|
tokenHint: "business.whatsapp.com",
|
|
606
|
+
extraFields: [
|
|
607
|
+
{
|
|
608
|
+
name: "phoneNumberId",
|
|
609
|
+
label: "Phone Number ID",
|
|
610
|
+
hint: "From Meta API Setup",
|
|
611
|
+
required: true
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
name: "verifyToken",
|
|
615
|
+
label: "Webhook verify token",
|
|
616
|
+
hint: "Any string for webhook verification",
|
|
617
|
+
required: false
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
name: "dmPolicy",
|
|
621
|
+
label: "DM policy",
|
|
622
|
+
hint: "pairing (default) | allowlist | open | disabled",
|
|
623
|
+
required: false
|
|
624
|
+
}
|
|
625
|
+
],
|
|
1167
626
|
setupSteps: [
|
|
1168
|
-
"1.
|
|
1169
|
-
"2.
|
|
1170
|
-
"3.
|
|
1171
|
-
"4.
|
|
1172
|
-
"5. You also need a Phone Number ID and WhatsApp Business Account ID.",
|
|
627
|
+
"1. Meta for Developers → Create App → Business → Add WhatsApp.",
|
|
628
|
+
"2. API Setup: copy Phone Number ID and Access Token.",
|
|
629
|
+
"3. Webhook: https://<host>/webhook/whatsapp, subscribe to messages.",
|
|
630
|
+
"4. Start gateway. Approve DMs: hyperclaw pairing approve whatsapp <CODE>",
|
|
1173
631
|
"",
|
|
1174
|
-
" 🔗
|
|
632
|
+
" 🔗 docs/whatsapp.md — full setup"
|
|
1175
633
|
],
|
|
1176
634
|
status: "available",
|
|
635
|
+
notes: "Meta Business API. Webhook required.",
|
|
1177
636
|
npmPackage: void 0
|
|
1178
637
|
},
|
|
1179
638
|
{
|
|
@@ -1183,42 +642,87 @@ const CHANNELS = [
|
|
|
1183
642
|
requiresGateway: true,
|
|
1184
643
|
supportsDM: true,
|
|
1185
644
|
platforms: ["all"],
|
|
645
|
+
extraFields: [{
|
|
646
|
+
name: "dmPolicy",
|
|
647
|
+
label: "DM policy",
|
|
648
|
+
hint: "pairing (default) | allowlist | open | disabled",
|
|
649
|
+
required: false
|
|
650
|
+
}],
|
|
1186
651
|
setupSteps: [
|
|
1187
|
-
"1. No Meta
|
|
1188
|
-
"2.
|
|
1189
|
-
"3.
|
|
1190
|
-
"4.
|
|
1191
|
-
"5. After connecting, the session is saved — no QR needed again.",
|
|
652
|
+
"1. No Meta API — uses WhatsApp Web. Install: npm install @whiskeysockets/baileys",
|
|
653
|
+
"2. hyperclaw channels add whatsapp-baileys, then hyperclaw gateway",
|
|
654
|
+
"3. Scan QR (WhatsApp → Linked Devices → Link a device)",
|
|
655
|
+
"4. Approve first DM: hyperclaw pairing approve whatsapp-baileys <CODE>",
|
|
1192
656
|
"",
|
|
1193
|
-
"
|
|
657
|
+
" 🔗 docs/whatsapp.md — full setup"
|
|
1194
658
|
],
|
|
1195
659
|
status: "available",
|
|
1196
|
-
notes: "WhatsApp Web via Baileys
|
|
660
|
+
notes: "WhatsApp Web via Baileys. No Meta Business. Pairing, voice notes.",
|
|
1197
661
|
npmPackage: "@whiskeysockets/baileys"
|
|
1198
662
|
},
|
|
1199
663
|
{
|
|
1200
664
|
id: "slack",
|
|
1201
665
|
name: "Slack",
|
|
1202
666
|
emoji: "💼",
|
|
1203
|
-
requiresGateway:
|
|
667
|
+
requiresGateway: true,
|
|
1204
668
|
supportsDM: true,
|
|
1205
669
|
platforms: ["all"],
|
|
1206
670
|
tokenLabel: "Slack Bot Token (xoxb-...)",
|
|
1207
|
-
extraFields: [
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
671
|
+
extraFields: [
|
|
672
|
+
{
|
|
673
|
+
name: "appToken",
|
|
674
|
+
label: "App Token (xapp-...)",
|
|
675
|
+
hint: "Required for Socket Mode (default) — connections:write scope",
|
|
676
|
+
required: false
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
name: "signingSecret",
|
|
680
|
+
label: "Signing Secret",
|
|
681
|
+
hint: "Required for HTTP Events API mode only",
|
|
682
|
+
required: false
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
name: "mode",
|
|
686
|
+
label: "Connection mode",
|
|
687
|
+
hint: "socket (default) | http",
|
|
688
|
+
required: false
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
name: "userToken",
|
|
692
|
+
label: "User Token (xoxp-...)",
|
|
693
|
+
hint: "Optional — for read operations",
|
|
694
|
+
required: false
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
name: "ackReaction",
|
|
698
|
+
label: "Ack reaction emoji",
|
|
699
|
+
hint: "Shortcode without colons, e.g. eyes",
|
|
700
|
+
required: false
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
name: "typingReaction",
|
|
704
|
+
label: "Typing reaction emoji",
|
|
705
|
+
hint: "Shortcode, e.g. hourglass_flowing_sand",
|
|
706
|
+
required: false
|
|
707
|
+
}
|
|
708
|
+
],
|
|
1212
709
|
setupSteps: [
|
|
1213
710
|
"1. Go to api.slack.com/apps → Create New App → From scratch.",
|
|
1214
|
-
"2.
|
|
1215
|
-
"
|
|
1216
|
-
"
|
|
1217
|
-
"
|
|
711
|
+
"2. Socket Mode (default — no public URL needed):",
|
|
712
|
+
" a. Settings → Socket Mode → Enable Socket Mode.",
|
|
713
|
+
" b. Settings → Basic Information → App-Level Tokens → Generate Token (connections:write) → copy xapp-...",
|
|
714
|
+
" c. OAuth & Permissions: add bot scopes (chat:write, im:read, im:history, channels:history, etc.).",
|
|
715
|
+
" d. Install App → copy Bot Token (xoxb-...).",
|
|
716
|
+
"3. HTTP mode (alternative):",
|
|
717
|
+
" a. Event Subscriptions → Request URL: https://<host>/webhook/slack.",
|
|
718
|
+
" b. Basic Information → Signing Secret — copy it.",
|
|
719
|
+
"4. Subscribe to bot events: app_mention, message.im, message.channels, message.groups, message.mpim, reaction_added.",
|
|
720
|
+
"5. App Home → Messages Tab → Enable.",
|
|
1218
721
|
"",
|
|
1219
722
|
" 🔗 api.slack.com/apps"
|
|
1220
723
|
],
|
|
1221
|
-
status: "
|
|
724
|
+
status: "recommended",
|
|
725
|
+
notes: "Socket Mode (default) requires appToken. HTTP mode requires signingSecret. Supports DMs, channels, threads, reactions, streaming.",
|
|
1222
726
|
npmPackage: "@slack/bolt"
|
|
1223
727
|
},
|
|
1224
728
|
{
|
|
@@ -1228,38 +732,160 @@ const CHANNELS = [
|
|
|
1228
732
|
requiresGateway: true,
|
|
1229
733
|
supportsDM: true,
|
|
1230
734
|
platforms: ["linux", "darwin"],
|
|
1231
|
-
tokenLabel: "
|
|
1232
|
-
tokenHint: "
|
|
735
|
+
tokenLabel: "Bot phone number (E.164)",
|
|
736
|
+
tokenHint: "e.g. +15551234567 — use a dedicated bot number",
|
|
1233
737
|
setupSteps: [
|
|
1234
738
|
"1. Install signal-cli: https://github.com/AsamK/signal-cli",
|
|
1235
|
-
"2. Register number: signal-cli -a +1XXXXXXXXX register",
|
|
1236
|
-
"3. Verify electronically (if available) or via SMS code.",
|
|
1237
|
-
"4. Enter your phone number here (e.g. +1XXXXXXXXX).",
|
|
1238
739
|
"",
|
|
1239
|
-
"
|
|
740
|
+
" Path A — Link existing Signal account (QR):",
|
|
741
|
+
" signal-cli link -n \"HyperClaw\" then scan in Signal.",
|
|
742
|
+
"",
|
|
743
|
+
" Path B — Register dedicated bot number (SMS):",
|
|
744
|
+
" signal-cli -a +<BOT_NUMBER> register",
|
|
745
|
+
" signal-cli -a +<BOT_NUMBER> register --captcha '<URL>' (if captcha required)",
|
|
746
|
+
" signal-cli -a +<BOT_NUMBER> verify <CODE>",
|
|
747
|
+
"",
|
|
748
|
+
"2. Set account: \"+<BOT_NUMBER>\" in config.",
|
|
749
|
+
"3. autoStart=true (default) spawns daemon automatically.",
|
|
750
|
+
" Or run daemon yourself and set httpUrl: \"http://127.0.0.1:8080\".",
|
|
751
|
+
"",
|
|
752
|
+
" Access control:",
|
|
753
|
+
" dmPolicy=pairing (default) — senders get pairing code (expires 1h)",
|
|
754
|
+
" groupPolicy=allowlist (default) — only groupAllowFrom senders trigger bot",
|
|
755
|
+
"",
|
|
756
|
+
" Chunking: textChunkLimit=4000, chunkMode=length|newline",
|
|
757
|
+
" Reactions: actions.reactions=true, reactionLevel=off|ack|minimal|extensive",
|
|
758
|
+
"",
|
|
759
|
+
" 🔗 github.com/AsamK/signal-cli",
|
|
760
|
+
" 🔗 github.com/AsamK/signal-cli/wiki/Registration-with-captcha"
|
|
761
|
+
],
|
|
762
|
+
extraFields: [
|
|
763
|
+
{
|
|
764
|
+
name: "account",
|
|
765
|
+
label: "Bot number (E.164)",
|
|
766
|
+
hint: "+15551234567",
|
|
767
|
+
required: true
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
name: "cliPath",
|
|
771
|
+
label: "signal-cli path",
|
|
772
|
+
hint: "signal-cli (if on PATH)",
|
|
773
|
+
required: false
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
name: "httpUrl",
|
|
777
|
+
label: "Daemon URL (external)",
|
|
778
|
+
hint: "http://127.0.0.1:8080 — skips autoStart",
|
|
779
|
+
required: false
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
name: "httpPort",
|
|
783
|
+
label: "Daemon port",
|
|
784
|
+
hint: "8080 (default)",
|
|
785
|
+
required: false
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
name: "autoStart",
|
|
789
|
+
label: "Auto-spawn daemon",
|
|
790
|
+
hint: "true (default) / false",
|
|
791
|
+
required: false
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
name: "startupTimeoutMs",
|
|
795
|
+
label: "Startup timeout (ms)",
|
|
796
|
+
hint: "15000 default, max 120000",
|
|
797
|
+
required: false
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
name: "dmPolicy",
|
|
801
|
+
label: "DM policy",
|
|
802
|
+
hint: "\"pairing\" (default) | \"allowlist\" | \"open\" | \"disabled\"",
|
|
803
|
+
required: false
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
name: "groupPolicy",
|
|
807
|
+
label: "Group policy",
|
|
808
|
+
hint: "\"allowlist\" (default) | \"open\" | \"disabled\"",
|
|
809
|
+
required: false
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
name: "textChunkLimit",
|
|
813
|
+
label: "Text chunk limit (chars)",
|
|
814
|
+
hint: "4000 default",
|
|
815
|
+
required: false
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
name: "chunkMode",
|
|
819
|
+
label: "Chunk mode",
|
|
820
|
+
hint: "\"length\" (default) | \"newline\"",
|
|
821
|
+
required: false
|
|
822
|
+
}
|
|
1240
823
|
],
|
|
1241
824
|
status: "available",
|
|
1242
|
-
notes: "
|
|
825
|
+
notes: "signal-cli HTTP daemon + SSE events. DMs, groups, typing, reactions, chunking, multi-account."
|
|
1243
826
|
},
|
|
1244
827
|
{
|
|
1245
828
|
id: "imessage",
|
|
1246
|
-
name: "iMessage",
|
|
829
|
+
name: "iMessage (BlueBubbles)",
|
|
1247
830
|
emoji: "💬",
|
|
1248
831
|
requiresGateway: true,
|
|
1249
832
|
supportsDM: true,
|
|
1250
833
|
platforms: ["darwin"],
|
|
834
|
+
tokenLabel: "BlueBubbles server URL",
|
|
835
|
+
extraFields: [{
|
|
836
|
+
name: "password",
|
|
837
|
+
label: "BlueBubbles password",
|
|
838
|
+
hint: "Set in BlueBubbles server settings",
|
|
839
|
+
required: true
|
|
840
|
+
}, {
|
|
841
|
+
name: "dmPolicy",
|
|
842
|
+
label: "DM policy",
|
|
843
|
+
hint: "pairing (default) | allowlist | open | disabled",
|
|
844
|
+
required: false
|
|
845
|
+
}],
|
|
1251
846
|
setupSteps: [
|
|
1252
|
-
"1. macOS only.
|
|
1253
|
-
"2. BlueBubbles:
|
|
1254
|
-
"3.
|
|
1255
|
-
"4.
|
|
847
|
+
"1. macOS only. Install BlueBubbles server: bluebubbles.app",
|
|
848
|
+
"2. BlueBubbles → Settings: enable web API, set password, note Server URL.",
|
|
849
|
+
"3. Add channel (imessage or bluebubbles), enter serverUrl + password.",
|
|
850
|
+
"4. hyperclaw gateway → hyperclaw pairing approve bluebubbles <CODE>",
|
|
1256
851
|
"",
|
|
1257
|
-
" 🔗 bluebubbles.
|
|
852
|
+
" 🔗 docs/bluebubbles.md — bluebubbles.app"
|
|
1258
853
|
],
|
|
1259
|
-
status: os.default.platform() === "darwin" ? "
|
|
1260
|
-
notes: "
|
|
854
|
+
status: os.default.platform() === "darwin" ? "recommended" : "unavailable",
|
|
855
|
+
notes: "BlueBubbles server on Mac. DMs, pairing. Groups planned.",
|
|
1261
856
|
npmPackage: "bluebubbles-api"
|
|
1262
857
|
},
|
|
858
|
+
{
|
|
859
|
+
id: "imessage-native",
|
|
860
|
+
name: "iMessage (imsg CLI — legacy)",
|
|
861
|
+
emoji: "💬",
|
|
862
|
+
requiresGateway: true,
|
|
863
|
+
supportsDM: true,
|
|
864
|
+
platforms: ["darwin"],
|
|
865
|
+
extraFields: [{
|
|
866
|
+
name: "cliPath",
|
|
867
|
+
label: "imsg binary path",
|
|
868
|
+
hint: "Default: imsg (must be in PATH)",
|
|
869
|
+
required: false
|
|
870
|
+
}, {
|
|
871
|
+
name: "dbPath",
|
|
872
|
+
label: "Messages DB path",
|
|
873
|
+
hint: "Default: ~/Library/Messages/chat.db",
|
|
874
|
+
required: false
|
|
875
|
+
}],
|
|
876
|
+
setupSteps: [
|
|
877
|
+
"1. macOS only. Install imsg: brew install steipete/tap/imsg",
|
|
878
|
+
"2. Verify: imsg rpc --help",
|
|
879
|
+
"3. Grant Full Disk Access + Automation to Terminal/Node (one-time: imsg chats --limit 1).",
|
|
880
|
+
"4. No token needed — imsg runs locally via JSON-RPC on stdio.",
|
|
881
|
+
"5. (Optional) Set cliPath if imsg is not in PATH.",
|
|
882
|
+
"",
|
|
883
|
+
" ⚠️ Legacy integration — for new setups use iMessage (BlueBubbles)",
|
|
884
|
+
" 🔗 github.com/steipete/imsg"
|
|
885
|
+
],
|
|
886
|
+
status: os.default.platform() === "darwin" ? "available" : "unavailable",
|
|
887
|
+
notes: "Legacy — gateway spawns imsg rpc over JSON-RPC stdio. For new setups prefer BlueBubbles."
|
|
888
|
+
},
|
|
1263
889
|
{
|
|
1264
890
|
id: "matrix",
|
|
1265
891
|
name: "Matrix",
|
|
@@ -1267,26 +893,84 @@ const CHANNELS = [
|
|
|
1267
893
|
requiresGateway: true,
|
|
1268
894
|
supportsDM: true,
|
|
1269
895
|
platforms: ["all"],
|
|
1270
|
-
tokenLabel:
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
896
|
+
tokenLabel: "Access Token (syt_...)",
|
|
897
|
+
tokenHint: "Or set userId + password instead",
|
|
898
|
+
extraFields: [
|
|
899
|
+
{
|
|
900
|
+
name: "homeserver",
|
|
901
|
+
label: "Homeserver URL",
|
|
902
|
+
hint: "e.g. https://matrix.example.org",
|
|
903
|
+
required: true
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
name: "accessToken",
|
|
907
|
+
label: "Access Token (syt_...)",
|
|
908
|
+
hint: "Preferred; userId auto-fetched via /whoami",
|
|
909
|
+
required: false
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
name: "userId",
|
|
913
|
+
label: "Matrix User ID",
|
|
914
|
+
hint: "@bot:example.org — required only for password login",
|
|
915
|
+
required: false
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
name: "password",
|
|
919
|
+
label: "Password (alternative to token)",
|
|
920
|
+
hint: "Token cached to credentials file on first login",
|
|
921
|
+
required: false
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
name: "deviceName",
|
|
925
|
+
label: "Device display name",
|
|
926
|
+
hint: "Shown in Matrix clients",
|
|
927
|
+
required: false
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
name: "encryption",
|
|
931
|
+
label: "Enable E2EE",
|
|
932
|
+
hint: "true | false — requires crypto native module",
|
|
933
|
+
required: false
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
name: "threadReplies",
|
|
937
|
+
label: "Thread replies",
|
|
938
|
+
hint: "off | inbound (default) | always",
|
|
939
|
+
required: false
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
name: "textChunkLimit",
|
|
943
|
+
label: "Text chunk limit (chars)",
|
|
944
|
+
hint: "Default: 16000",
|
|
945
|
+
required: false
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
name: "mediaMaxMb",
|
|
949
|
+
label: "Media size limit (MB)",
|
|
950
|
+
hint: "Default: 10",
|
|
951
|
+
required: false
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
name: "autoJoin",
|
|
955
|
+
label: "Auto-join invites",
|
|
956
|
+
hint: "always (default) | allowlist | off",
|
|
957
|
+
required: false
|
|
958
|
+
}
|
|
959
|
+
],
|
|
1281
960
|
setupSteps: [
|
|
1282
|
-
"1. Create a bot account on matrix.org
|
|
1283
|
-
"2.
|
|
1284
|
-
"
|
|
1285
|
-
"
|
|
961
|
+
"1. Create a Matrix bot account on any homeserver (matrix.org has free accounts).",
|
|
962
|
+
"2. Get an access token via the login API:",
|
|
963
|
+
" curl -X POST https://<homeserver>/_matrix/client/v3/login \\",
|
|
964
|
+
" -H \"Content-Type: application/json\" \\",
|
|
965
|
+
" -d '{\"type\":\"m.login.password\",\"identifier\":{\"type\":\"m.id.user\",\"user\":\"<username>\"},\"password\":\"<password>\"}'",
|
|
966
|
+
" Or set userId + password — HyperClaw will call the login API and cache the token.",
|
|
967
|
+
"3. Invite the bot account to a room or DM it from any Matrix client (Element, Beeper, etc.).",
|
|
968
|
+
"4. For Beeper, enable E2EE: set encryption: true and verify the device in Element.",
|
|
1286
969
|
"",
|
|
1287
|
-
" 🔗 matrix.org — element.io"
|
|
970
|
+
" 🔗 matrix.org/ecosystem/hosting/ — element.io"
|
|
1288
971
|
],
|
|
1289
972
|
status: "available",
|
|
973
|
+
notes: "Supports DMs, rooms, threads, media, reactions, polls, location, E2EE, multi-account.",
|
|
1290
974
|
npmPackage: "matrix-js-sdk"
|
|
1291
975
|
},
|
|
1292
976
|
{
|
|
@@ -1300,25 +984,81 @@ const CHANNELS = [
|
|
|
1300
984
|
extraFields: [
|
|
1301
985
|
{
|
|
1302
986
|
name: "server",
|
|
1303
|
-
label: "Server",
|
|
987
|
+
label: "Server (IRC_HOST)",
|
|
1304
988
|
hint: "e.g. irc.libera.chat",
|
|
1305
989
|
required: true
|
|
1306
990
|
},
|
|
991
|
+
{
|
|
992
|
+
name: "port",
|
|
993
|
+
label: "Port (IRC_PORT)",
|
|
994
|
+
hint: "Default: 6697 (TLS) or 6667",
|
|
995
|
+
required: false
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
name: "tls",
|
|
999
|
+
label: "TLS (IRC_TLS)",
|
|
1000
|
+
hint: "true / false — default: false",
|
|
1001
|
+
required: false
|
|
1002
|
+
},
|
|
1307
1003
|
{
|
|
1308
1004
|
name: "nick",
|
|
1309
|
-
label: "Nickname",
|
|
1005
|
+
label: "Nickname (IRC_NICK)",
|
|
1310
1006
|
required: true
|
|
1311
1007
|
},
|
|
1008
|
+
{
|
|
1009
|
+
name: "username",
|
|
1010
|
+
label: "Username / ident (IRC_USERNAME)",
|
|
1011
|
+
required: false
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
name: "realname",
|
|
1015
|
+
label: "Real name (IRC_REALNAME)",
|
|
1016
|
+
required: false
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
name: "password",
|
|
1020
|
+
label: "Server password (IRC_PASSWORD)",
|
|
1021
|
+
hint: "Not NickServ — leave blank if none",
|
|
1022
|
+
required: false
|
|
1023
|
+
},
|
|
1312
1024
|
{
|
|
1313
1025
|
name: "channels",
|
|
1314
|
-
label: "
|
|
1026
|
+
label: "Channels to join (IRC_CHANNELS)",
|
|
1027
|
+
hint: "#room1,#room2",
|
|
1028
|
+
required: false
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
name: "nickservPassword",
|
|
1032
|
+
label: "NickServ password (IRC_NICKSERV_PASSWORD)",
|
|
1033
|
+
hint: "Identify after connect",
|
|
1034
|
+
required: false
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
name: "groupPolicy",
|
|
1038
|
+
label: "Group policy",
|
|
1039
|
+
hint: "\"allowlist\" (default) or \"open\"",
|
|
1040
|
+
required: false
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
name: "dmPolicy",
|
|
1044
|
+
label: "DM policy",
|
|
1045
|
+
hint: "\"pairing\" (default) | \"allowlist\" | \"open\"",
|
|
1315
1046
|
required: false
|
|
1316
1047
|
}
|
|
1317
1048
|
],
|
|
1318
1049
|
setupSteps: [
|
|
1319
1050
|
"1. Choose an IRC server (e.g. irc.libera.chat, irc.oftc.net).",
|
|
1320
|
-
"2.
|
|
1321
|
-
"3.
|
|
1051
|
+
"2. Set a nickname for the bot and the channels it should join.",
|
|
1052
|
+
"3. Enable TLS (recommended): set port 6697 and tls: true.",
|
|
1053
|
+
"4. If your nick is registered, set nickservPassword to auto-identify.",
|
|
1054
|
+
"5. Access control defaults: groupPolicy=allowlist (bot only replies in",
|
|
1055
|
+
" configured groups), dmPolicy=pairing (new DMs need pairing approval).",
|
|
1056
|
+
"6. To allow everyone in a channel without mention, set per-channel:",
|
|
1057
|
+
" groups[\"#mychan\"].requireMention = false, allowFrom = [\"*\"].",
|
|
1058
|
+
"",
|
|
1059
|
+
" Env vars: IRC_HOST IRC_PORT IRC_TLS IRC_NICK IRC_USERNAME",
|
|
1060
|
+
" IRC_REALNAME IRC_PASSWORD IRC_CHANNELS",
|
|
1061
|
+
" IRC_NICKSERV_PASSWORD IRC_NICKSERV_REGISTER_EMAIL",
|
|
1322
1062
|
"",
|
|
1323
1063
|
" 🔗 libera.chat — oftc.net"
|
|
1324
1064
|
],
|
|
@@ -1332,31 +1072,71 @@ const CHANNELS = [
|
|
|
1332
1072
|
requiresGateway: true,
|
|
1333
1073
|
supportsDM: true,
|
|
1334
1074
|
platforms: ["all"],
|
|
1335
|
-
tokenLabel: "
|
|
1336
|
-
tokenHint: "
|
|
1075
|
+
tokenLabel: "Bot Token (MATTERMOST_BOT_TOKEN)",
|
|
1076
|
+
tokenHint: "System Console → Integrations → Bot Accounts → Add Bot Account",
|
|
1337
1077
|
setupSteps: [
|
|
1338
|
-
"1.
|
|
1339
|
-
"2.
|
|
1340
|
-
"3.
|
|
1341
|
-
"4.
|
|
1342
|
-
"5.
|
|
1078
|
+
"1. Create a Bot Account: System Console → Integrations → Bot Accounts → Add Bot Account.",
|
|
1079
|
+
"2. Copy the bot token shown after creation (not shown again).",
|
|
1080
|
+
"3. Note your Mattermost base URL (e.g. https://chat.example.com).",
|
|
1081
|
+
"4. The connector uses WebSocket events — no outgoing webhook needed.",
|
|
1082
|
+
"5. For slash commands: set commands.native=true and expose callbackUrl.",
|
|
1083
|
+
"6. For buttons: add capabilities: [\"inlineButtons\"] and set interactions.callbackBaseUrl.",
|
|
1084
|
+
"",
|
|
1085
|
+
" Env vars: MATTERMOST_BOT_TOKEN MATTERMOST_URL",
|
|
1343
1086
|
"",
|
|
1344
|
-
"
|
|
1087
|
+
" Chat modes:",
|
|
1088
|
+
" oncall (default) — reply only when @mentioned",
|
|
1089
|
+
" onmessage — reply to every channel message",
|
|
1090
|
+
" onchar — reply when message starts with a prefix (e.g. \">\", \"!\")",
|
|
1091
|
+
"",
|
|
1092
|
+
" Access control:",
|
|
1093
|
+
" dmPolicy=pairing (default) | allowlist | open | none",
|
|
1094
|
+
" groupPolicy=allowlist (default) | open",
|
|
1095
|
+
" groupAllowFrom=[userId1, ...] — sender gate for channels",
|
|
1096
|
+
"",
|
|
1097
|
+
" 🔗 docs.mattermost.com — mattermost.com"
|
|
1098
|
+
],
|
|
1099
|
+
extraFields: [
|
|
1100
|
+
{
|
|
1101
|
+
name: "baseUrl",
|
|
1102
|
+
label: "Base URL (MATTERMOST_URL)",
|
|
1103
|
+
hint: "https://chat.example.com",
|
|
1104
|
+
required: true
|
|
1105
|
+
},
|
|
1106
|
+
{
|
|
1107
|
+
name: "chatmode",
|
|
1108
|
+
label: "Chat mode",
|
|
1109
|
+
hint: "\"oncall\" (default) | \"onmessage\" | \"onchar\"",
|
|
1110
|
+
required: false
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
name: "oncharPrefixes",
|
|
1114
|
+
label: "onchar prefixes",
|
|
1115
|
+
hint: ">, ! (comma-separated, for chatmode=onchar)",
|
|
1116
|
+
required: false
|
|
1117
|
+
},
|
|
1118
|
+
{
|
|
1119
|
+
name: "dmPolicy",
|
|
1120
|
+
label: "DM policy",
|
|
1121
|
+
hint: "\"pairing\" (default) | \"open\" | \"allowlist\" | \"none\"",
|
|
1122
|
+
required: false
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
name: "groupPolicy",
|
|
1126
|
+
label: "Group policy",
|
|
1127
|
+
hint: "\"allowlist\" (default) | \"open\"",
|
|
1128
|
+
required: false
|
|
1129
|
+
},
|
|
1130
|
+
{
|
|
1131
|
+
name: "capabilities",
|
|
1132
|
+
label: "Capabilities",
|
|
1133
|
+
hint: "\"inlineButtons\" — comma-separated",
|
|
1134
|
+
required: false
|
|
1135
|
+
}
|
|
1345
1136
|
],
|
|
1346
|
-
extraFields: [{
|
|
1347
|
-
name: "serverUrl",
|
|
1348
|
-
label: "Server URL",
|
|
1349
|
-
hint: "https://mattermost.example.com",
|
|
1350
|
-
required: true
|
|
1351
|
-
}, {
|
|
1352
|
-
name: "webhookToken",
|
|
1353
|
-
label: "Outgoing Webhook Token",
|
|
1354
|
-
hint: "From Integrations > Outgoing Webhook",
|
|
1355
|
-
required: true
|
|
1356
|
-
}],
|
|
1357
1137
|
status: "available",
|
|
1358
|
-
notes: "
|
|
1359
|
-
npmPackage: "
|
|
1138
|
+
notes: "WebSocket + REST. Supports DMs, channels, buttons, reactions, slash commands, multi-account.",
|
|
1139
|
+
npmPackage: "ws"
|
|
1360
1140
|
},
|
|
1361
1141
|
{
|
|
1362
1142
|
id: "googlechat",
|
|
@@ -1379,18 +1159,27 @@ const CHANNELS = [
|
|
|
1379
1159
|
id: "msteams",
|
|
1380
1160
|
name: "Microsoft Teams",
|
|
1381
1161
|
emoji: "🟣",
|
|
1382
|
-
requiresGateway:
|
|
1383
|
-
supportsDM:
|
|
1162
|
+
requiresGateway: true,
|
|
1163
|
+
supportsDM: true,
|
|
1384
1164
|
platforms: ["all"],
|
|
1385
|
-
tokenLabel: "
|
|
1165
|
+
tokenLabel: "App ID (Azure Bot)",
|
|
1166
|
+
extraFields: [{
|
|
1167
|
+
name: "appPassword",
|
|
1168
|
+
label: "App Password (client secret)",
|
|
1169
|
+
hint: "From Azure Bot → Manage → Certificates & secrets",
|
|
1170
|
+
required: true
|
|
1171
|
+
}],
|
|
1386
1172
|
setupSteps: [
|
|
1387
|
-
"1.
|
|
1388
|
-
"2.
|
|
1389
|
-
"3.
|
|
1173
|
+
"1. Create an Azure Bot: portal.azure.com → Create a resource → Azure Bot",
|
|
1174
|
+
"2. Type of App: Single Tenant. Create new Microsoft App ID.",
|
|
1175
|
+
"3. Configuration → copy App ID. Manage → Certificates & secrets → New client secret → copy Value (appPassword).",
|
|
1176
|
+
"4. Channels → Microsoft Teams → Configure. Set Messaging endpoint: https://<your-host>/webhook/msteams",
|
|
1177
|
+
"5. Build a Teams app manifest with botId = App ID. Upload to Teams.",
|
|
1390
1178
|
"",
|
|
1391
|
-
" 🔗 docs.
|
|
1179
|
+
" 🔗 docs/msteams.md — full setup guide"
|
|
1392
1180
|
],
|
|
1393
|
-
status: "available"
|
|
1181
|
+
status: "available",
|
|
1182
|
+
notes: "Bot Framework. Text + DM. Channel/group files require Graph + SharePoint."
|
|
1394
1183
|
},
|
|
1395
1184
|
{
|
|
1396
1185
|
id: "nostr",
|
|
@@ -1420,24 +1209,61 @@ const CHANNELS = [
|
|
|
1420
1209
|
id: "line",
|
|
1421
1210
|
name: "LINE",
|
|
1422
1211
|
emoji: "🟩",
|
|
1423
|
-
requiresGateway:
|
|
1212
|
+
requiresGateway: true,
|
|
1424
1213
|
supportsDM: true,
|
|
1425
1214
|
platforms: ["all"],
|
|
1426
1215
|
tokenLabel: "LINE Channel access token",
|
|
1427
|
-
extraFields: [
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1216
|
+
extraFields: [
|
|
1217
|
+
{
|
|
1218
|
+
name: "channelSecret",
|
|
1219
|
+
label: "Channel Secret",
|
|
1220
|
+
hint: "From LINE Developers Console → Basic settings",
|
|
1221
|
+
required: true
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
name: "tokenFile",
|
|
1225
|
+
label: "Token file path (optional)",
|
|
1226
|
+
hint: "Alternative to pasting token directly",
|
|
1227
|
+
required: false
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
name: "secretFile",
|
|
1231
|
+
label: "Secret file path (optional)",
|
|
1232
|
+
hint: "Alternative to pasting secret directly",
|
|
1233
|
+
required: false
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
name: "webhookPath",
|
|
1237
|
+
label: "Webhook path",
|
|
1238
|
+
hint: "Default: /line/webhook",
|
|
1239
|
+
required: false
|
|
1240
|
+
},
|
|
1241
|
+
{
|
|
1242
|
+
name: "mediaMaxMb",
|
|
1243
|
+
label: "Media download limit (MB)",
|
|
1244
|
+
hint: "Default: 10",
|
|
1245
|
+
required: false
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
name: "groupPolicy",
|
|
1249
|
+
label: "Group policy",
|
|
1250
|
+
hint: "open | allowlist | disabled (default: allowlist)",
|
|
1251
|
+
required: false
|
|
1252
|
+
}
|
|
1253
|
+
],
|
|
1432
1254
|
setupSteps: [
|
|
1433
|
-
"1. Go to developers.line.biz → Console → Create provider & channel.",
|
|
1434
|
-
"2.
|
|
1435
|
-
"3. Channel
|
|
1436
|
-
"4.
|
|
1255
|
+
"1. Go to developers.line.biz → Console → Create provider & Messaging API channel.",
|
|
1256
|
+
"2. Channel access token: Issue or Regenerate — copy it.",
|
|
1257
|
+
"3. Channel secret: from Basic settings — copy it.",
|
|
1258
|
+
"4. Enable \"Use webhook\" in Messaging API settings.",
|
|
1259
|
+
"5. Set webhook URL (HTTPS required):",
|
|
1260
|
+
" https://<gateway-host>/line/webhook",
|
|
1261
|
+
"6. Paste token + secret below.",
|
|
1437
1262
|
"",
|
|
1438
1263
|
" 🔗 developers.line.biz"
|
|
1439
1264
|
],
|
|
1440
1265
|
status: "available",
|
|
1266
|
+
notes: "Webhook receiver. Supports DMs, groups, media, Flex messages, quick replies, locations.",
|
|
1441
1267
|
npmPackage: "@line/bot-sdk"
|
|
1442
1268
|
},
|
|
1443
1269
|
{
|
|
@@ -1465,27 +1291,68 @@ const CHANNELS = [
|
|
|
1465
1291
|
{
|
|
1466
1292
|
id: "synology-chat",
|
|
1467
1293
|
name: "Synology Chat",
|
|
1468
|
-
emoji: "
|
|
1294
|
+
emoji: "💬",
|
|
1469
1295
|
requiresGateway: true,
|
|
1470
1296
|
supportsDM: true,
|
|
1471
1297
|
platforms: ["all"],
|
|
1472
|
-
tokenLabel: "
|
|
1473
|
-
tokenHint: "Synology Chat
|
|
1474
|
-
extraFields: [
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1298
|
+
tokenLabel: "Outgoing Webhook Token (SYNOLOGY_CHAT_TOKEN)",
|
|
1299
|
+
tokenHint: "From Synology Chat Integrations Outgoing Webhook",
|
|
1300
|
+
extraFields: [
|
|
1301
|
+
{
|
|
1302
|
+
name: "incomingUrl",
|
|
1303
|
+
label: "Incoming Webhook URL (SYNOLOGY_CHAT_INCOMING_URL)",
|
|
1304
|
+
hint: "From Synology Chat Integrations Incoming Webhook",
|
|
1305
|
+
required: true
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
name: "webhookPath",
|
|
1309
|
+
label: "Gateway inbound path",
|
|
1310
|
+
hint: "/webhook/synology (default)",
|
|
1311
|
+
required: false
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
name: "dmPolicy",
|
|
1315
|
+
label: "DM policy",
|
|
1316
|
+
hint: "\"allowlist\" (default) | \"open\" | \"pairing\" | \"disabled\"",
|
|
1317
|
+
required: false
|
|
1318
|
+
},
|
|
1319
|
+
{
|
|
1320
|
+
name: "allowedUserIds",
|
|
1321
|
+
label: "Allowed user IDs (SYNOLOGY_ALLOWED_USER_IDS)",
|
|
1322
|
+
hint: "Numeric Synology Chat user IDs, comma-separated",
|
|
1323
|
+
required: false
|
|
1324
|
+
},
|
|
1325
|
+
{
|
|
1326
|
+
name: "rateLimitPerMinute",
|
|
1327
|
+
label: "Rate limit per sender/min (SYNOLOGY_RATE_LIMIT)",
|
|
1328
|
+
hint: "30 (default)",
|
|
1329
|
+
required: false
|
|
1330
|
+
},
|
|
1331
|
+
{
|
|
1332
|
+
name: "allowInsecureSsl",
|
|
1333
|
+
label: "Allow insecure SSL",
|
|
1334
|
+
hint: "false (default) — true only for self-signed NAS certs",
|
|
1335
|
+
required: false
|
|
1336
|
+
}
|
|
1337
|
+
],
|
|
1480
1338
|
setupSteps: [
|
|
1481
|
-
"1. Synology Chat
|
|
1482
|
-
"2.
|
|
1483
|
-
"
|
|
1339
|
+
"1. Synology Chat Integrations Incoming Webhook Create copy URL.",
|
|
1340
|
+
"2. Synology Chat Integrations Outgoing Webhook Create:",
|
|
1341
|
+
" Outgoing URL: https://<gateway>/webhook/synology",
|
|
1342
|
+
" Copy the generated token.",
|
|
1343
|
+
"3. Set token + incomingUrl in config (or env vars).",
|
|
1344
|
+
"4. dmPolicy=allowlist requires at least one allowedUserId.",
|
|
1345
|
+
"",
|
|
1346
|
+
" Env vars: SYNOLOGY_CHAT_TOKEN SYNOLOGY_CHAT_INCOMING_URL",
|
|
1347
|
+
" SYNOLOGY_ALLOWED_USER_IDS SYNOLOGY_RATE_LIMIT OPENCLAW_BOT_NAME",
|
|
1348
|
+
"",
|
|
1349
|
+
" Multi-account: channels.synology-chat.accounts.{ default, alerts }",
|
|
1350
|
+
" Targets: <numericId>, synology-chat:<id>, user:<id>",
|
|
1484
1351
|
"",
|
|
1485
1352
|
" 🔗 kb.synology.com"
|
|
1486
1353
|
],
|
|
1487
1354
|
status: "available",
|
|
1488
|
-
notes: "
|
|
1355
|
+
notes: "Gateway webhooks. Token verify, rate limiting, multi-account, allowInsecureSsl."
|
|
1489
1356
|
},
|
|
1490
1357
|
{
|
|
1491
1358
|
id: "twitch",
|
|
@@ -1496,61 +1363,84 @@ const CHANNELS = [
|
|
|
1496
1363
|
platforms: ["all"],
|
|
1497
1364
|
tokenLabel: "OAuth Token (oauth:...)",
|
|
1498
1365
|
tokenHint: "Generate a Twitch chat token for your bot account",
|
|
1499
|
-
extraFields: [
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1366
|
+
extraFields: [
|
|
1367
|
+
{
|
|
1368
|
+
name: "username",
|
|
1369
|
+
label: "Bot username",
|
|
1370
|
+
required: true
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
name: "channels",
|
|
1374
|
+
label: "Channels (comma-separated)",
|
|
1375
|
+
hint: "vevisk,secondchannel",
|
|
1376
|
+
required: true
|
|
1377
|
+
},
|
|
1378
|
+
{
|
|
1379
|
+
name: "commandPrefix",
|
|
1380
|
+
label: "Command prefix",
|
|
1381
|
+
hint: "! (default)",
|
|
1382
|
+
required: false
|
|
1383
|
+
}
|
|
1384
|
+
],
|
|
1508
1385
|
setupSteps: [
|
|
1509
|
-
"1. Create
|
|
1510
|
-
"2.
|
|
1511
|
-
"3. Add
|
|
1386
|
+
"1. Create Twitch bot account. Generate OAuth: twitchapps.com/tmi",
|
|
1387
|
+
"2. Config: username, oauthToken, channels (required).",
|
|
1388
|
+
"3. Add allowFrom (recommended) to restrict who can trigger.",
|
|
1389
|
+
"4. Public chat: prefix required (default !). Whispers: no prefix.",
|
|
1512
1390
|
"",
|
|
1513
|
-
" 🔗 dev.twitch.tv"
|
|
1391
|
+
" 🔗 docs/twitch.md — dev.twitch.tv"
|
|
1514
1392
|
],
|
|
1515
1393
|
status: "available",
|
|
1516
|
-
notes: "
|
|
1394
|
+
notes: "IRC over WebSocket. Channel chat + whispers. Pairing, allowlist, modsBypass."
|
|
1517
1395
|
},
|
|
1518
1396
|
{
|
|
1519
1397
|
id: "tlon",
|
|
1520
|
-
name: "Tlon",
|
|
1521
|
-
emoji: "
|
|
1398
|
+
name: "Tlon (Urbit Groups)",
|
|
1399
|
+
emoji: "🌊",
|
|
1522
1400
|
requiresGateway: true,
|
|
1523
1401
|
supportsDM: true,
|
|
1524
1402
|
platforms: ["all"],
|
|
1525
|
-
tokenLabel: "
|
|
1526
|
-
tokenHint: "
|
|
1403
|
+
tokenLabel: "Login Code",
|
|
1404
|
+
tokenHint: "Ship login code from Landscape → Settings → Access key (e.g. lidlut-tabwed-pillex-ridrup)",
|
|
1527
1405
|
extraFields: [
|
|
1528
1406
|
{
|
|
1529
|
-
name: "
|
|
1530
|
-
label: "
|
|
1531
|
-
hint: "e.g.
|
|
1407
|
+
name: "ship",
|
|
1408
|
+
label: "Ship Name",
|
|
1409
|
+
hint: "e.g. ~sampel-palnet",
|
|
1410
|
+
required: true
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
name: "url",
|
|
1414
|
+
label: "Ship URL",
|
|
1415
|
+
hint: "e.g. https://sampel-palnet.tlon.network or http://localhost:8080",
|
|
1532
1416
|
required: true
|
|
1533
1417
|
},
|
|
1534
1418
|
{
|
|
1535
|
-
name: "
|
|
1536
|
-
label: "
|
|
1419
|
+
name: "ownerShip",
|
|
1420
|
+
label: "Owner Ship",
|
|
1421
|
+
hint: "Your personal ship — always authorized, receives approval notifications",
|
|
1537
1422
|
required: false
|
|
1538
1423
|
},
|
|
1539
1424
|
{
|
|
1540
|
-
name: "
|
|
1541
|
-
label: "
|
|
1425
|
+
name: "allowPrivateNetwork",
|
|
1426
|
+
label: "Allow Private Network",
|
|
1427
|
+
hint: "Enable for localhost/LAN ships (SSRF opt-in)",
|
|
1542
1428
|
required: false
|
|
1543
1429
|
}
|
|
1544
1430
|
],
|
|
1545
1431
|
setupSteps: [
|
|
1546
|
-
"1.
|
|
1547
|
-
"2.
|
|
1548
|
-
"3.
|
|
1432
|
+
"1. Install plugin: hyperclaw plugins install @hyperclaw/extension-tlon",
|
|
1433
|
+
"2. Get your ship login code from Landscape (Settings → System → Access key).",
|
|
1434
|
+
"3. Configure channels.tlon with ship name, URL, and login code.",
|
|
1435
|
+
"4. Optionally set ownerShip to receive approval notifications.",
|
|
1436
|
+
"5. Restart gateway: hyperclaw gateway restart",
|
|
1437
|
+
"6. DM the bot ship in Tlon or mention it in a group channel.",
|
|
1549
1438
|
"",
|
|
1550
|
-
"
|
|
1439
|
+
" Plugin required — connects via Urbit Eyre HTTP API + SSE stream.",
|
|
1440
|
+
" See: docs/tlon.md"
|
|
1551
1441
|
],
|
|
1552
1442
|
status: "available",
|
|
1553
|
-
notes: "
|
|
1443
|
+
notes: "Urbit Eyre HTTP API + SSE. DMs, groups, reactions, owner approval, auto-discovery. Plugin: @hyperclaw/extension-tlon"
|
|
1554
1444
|
},
|
|
1555
1445
|
{
|
|
1556
1446
|
id: "instagram",
|
|
@@ -1684,46 +1574,136 @@ const CHANNELS = [
|
|
|
1684
1574
|
status: "available"
|
|
1685
1575
|
},
|
|
1686
1576
|
{
|
|
1687
|
-
id: "nextcloud",
|
|
1577
|
+
id: "nextcloud-talk",
|
|
1688
1578
|
name: "Nextcloud Talk",
|
|
1689
1579
|
emoji: "☁️",
|
|
1690
1580
|
requiresGateway: true,
|
|
1691
1581
|
supportsDM: true,
|
|
1692
1582
|
platforms: ["all"],
|
|
1693
|
-
tokenLabel: "Nextcloud URL",
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1583
|
+
tokenLabel: "Nextcloud instance URL",
|
|
1584
|
+
tokenHint: "e.g. https://cloud.example.com",
|
|
1585
|
+
extraFields: [
|
|
1586
|
+
{
|
|
1587
|
+
name: "botSecret",
|
|
1588
|
+
label: "Bot shared secret",
|
|
1589
|
+
hint: "From: occ talk:bot:install",
|
|
1590
|
+
required: true
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
name: "botSecretFile",
|
|
1594
|
+
label: "Bot secret file path (optional)",
|
|
1595
|
+
hint: "Alternative to inline secret",
|
|
1596
|
+
required: false
|
|
1597
|
+
},
|
|
1598
|
+
{
|
|
1599
|
+
name: "apiUser",
|
|
1600
|
+
label: "API username (optional)",
|
|
1601
|
+
hint: "For DM detection + fallback send via OCS API",
|
|
1602
|
+
required: false
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
name: "apiPassword",
|
|
1606
|
+
label: "API/app password (optional)",
|
|
1607
|
+
hint: "Nextcloud app password for apiUser",
|
|
1608
|
+
required: false
|
|
1609
|
+
},
|
|
1610
|
+
{
|
|
1611
|
+
name: "webhookPort",
|
|
1612
|
+
label: "Webhook listener port",
|
|
1613
|
+
hint: "Default: 8788",
|
|
1614
|
+
required: false
|
|
1615
|
+
},
|
|
1616
|
+
{
|
|
1617
|
+
name: "webhookPath",
|
|
1618
|
+
label: "Webhook path",
|
|
1619
|
+
hint: "Default: /nextcloud-talk-webhook",
|
|
1620
|
+
required: false
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
name: "webhookPublicUrl",
|
|
1624
|
+
label: "Webhook public URL",
|
|
1625
|
+
hint: "If behind a proxy — set this in occ talk:bot:install",
|
|
1626
|
+
required: false
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
name: "groupPolicy",
|
|
1630
|
+
label: "Room policy",
|
|
1631
|
+
hint: "allowlist (default) | open | disabled",
|
|
1632
|
+
required: false
|
|
1633
|
+
},
|
|
1634
|
+
{
|
|
1635
|
+
name: "textChunkLimit",
|
|
1636
|
+
label: "Text chunk limit (chars)",
|
|
1637
|
+
hint: "Default: 32000",
|
|
1638
|
+
required: false
|
|
1639
|
+
}
|
|
1640
|
+
],
|
|
1703
1641
|
setupSteps: [
|
|
1704
|
-
"1.
|
|
1705
|
-
"
|
|
1642
|
+
"1. On your Nextcloud server, create the bot:",
|
|
1643
|
+
" ./occ talk:bot:install \"HyperClaw\" \"<shared-secret>\" \"<webhook-url>\" --feature reaction",
|
|
1644
|
+
" Replace <webhook-url> with your gateway URL, e.g. https://yourhost/nextcloud-talk-webhook",
|
|
1645
|
+
"2. Enable the bot in the target room settings (room → ⋯ → Bots).",
|
|
1646
|
+
"3. Enter the Nextcloud URL (token field) and the shared secret below.",
|
|
1647
|
+
"4. (Optional) Add apiUser + app password to enable DM detection via OCS API.",
|
|
1706
1648
|
"",
|
|
1707
|
-
" 🔗 nextcloud.com"
|
|
1649
|
+
" 🔗 nextcloud.com — docs.nextcloud.com/server/latest/admin_manual/talk_bots.html"
|
|
1708
1650
|
],
|
|
1709
|
-
status: "available"
|
|
1651
|
+
status: "available",
|
|
1652
|
+
notes: "Webhook bot. Supports DMs (with apiUser), rooms, reactions. No media uploads."
|
|
1710
1653
|
},
|
|
1711
1654
|
{
|
|
1712
1655
|
id: "zalo",
|
|
1713
1656
|
name: "Zalo",
|
|
1714
1657
|
emoji: "🔵",
|
|
1715
|
-
requiresGateway:
|
|
1658
|
+
requiresGateway: true,
|
|
1716
1659
|
supportsDM: true,
|
|
1717
1660
|
platforms: ["all"],
|
|
1718
|
-
tokenLabel: "Zalo
|
|
1661
|
+
tokenLabel: "Zalo Bot Token (12345689:abc-xyz)",
|
|
1662
|
+
tokenHint: "From bot.zaloplatforms.com",
|
|
1663
|
+
extraFields: [
|
|
1664
|
+
{
|
|
1665
|
+
name: "tokenFile",
|
|
1666
|
+
label: "Token file path (optional)",
|
|
1667
|
+
hint: "Alternative to inline token",
|
|
1668
|
+
required: false
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
name: "groupPolicy",
|
|
1672
|
+
label: "Group policy",
|
|
1673
|
+
hint: "allowlist (default) | open | disabled",
|
|
1674
|
+
required: false
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
name: "webhookUrl",
|
|
1678
|
+
label: "Webhook URL (optional)",
|
|
1679
|
+
hint: "HTTPS required — leave blank for long-polling",
|
|
1680
|
+
required: false
|
|
1681
|
+
},
|
|
1682
|
+
{
|
|
1683
|
+
name: "webhookSecret",
|
|
1684
|
+
label: "Webhook secret (optional)",
|
|
1685
|
+
hint: "8-256 chars — required if webhookUrl is set",
|
|
1686
|
+
required: false
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
name: "mediaMaxMb",
|
|
1690
|
+
label: "Media size limit (MB)",
|
|
1691
|
+
hint: "Default: 5",
|
|
1692
|
+
required: false
|
|
1693
|
+
}
|
|
1694
|
+
],
|
|
1719
1695
|
setupSteps: [
|
|
1720
|
-
"1.
|
|
1721
|
-
"2. Create
|
|
1722
|
-
"3.
|
|
1696
|
+
"1. Go to https://bot.zaloplatforms.com and sign in.",
|
|
1697
|
+
"2. Create a new bot and configure its settings.",
|
|
1698
|
+
"3. Copy the bot token (format: 12345689:abc-xyz).",
|
|
1699
|
+
"4. Paste the token below.",
|
|
1700
|
+
"5. (Optional) Set webhookUrl for webhook mode (HTTPS required).",
|
|
1701
|
+
" Leave blank to use long-polling (no public URL needed).",
|
|
1723
1702
|
"",
|
|
1724
|
-
" 🔗
|
|
1703
|
+
" 🔗 bot.zaloplatforms.com"
|
|
1725
1704
|
],
|
|
1726
|
-
status: "available"
|
|
1705
|
+
status: "available",
|
|
1706
|
+
notes: "Experimental. DMs supported; groups with allowlist policy. Long-polling by default."
|
|
1727
1707
|
},
|
|
1728
1708
|
{
|
|
1729
1709
|
id: "web",
|
|
@@ -1812,18 +1792,24 @@ const ZALO_PERSONAL = {
|
|
|
1812
1792
|
requiresGateway: true,
|
|
1813
1793
|
supportsDM: true,
|
|
1814
1794
|
platforms: ["all"],
|
|
1815
|
-
tokenLabel: "
|
|
1816
|
-
tokenHint: "
|
|
1795
|
+
tokenLabel: "Cookie (from chat.zalo.me)",
|
|
1796
|
+
tokenHint: "DevTools → Application → Cookies",
|
|
1797
|
+
extraFields: [{
|
|
1798
|
+
name: "dmPolicy",
|
|
1799
|
+
label: "DM policy",
|
|
1800
|
+
hint: "pairing (default) | allowlist | open | disabled",
|
|
1801
|
+
required: false
|
|
1802
|
+
}],
|
|
1817
1803
|
setupSteps: [
|
|
1818
|
-
"1.
|
|
1819
|
-
"2. Open
|
|
1820
|
-
"3.
|
|
1821
|
-
"4.
|
|
1804
|
+
"1. ⚠️ Experimental — unofficial. Account ban risk. Use at your own risk.",
|
|
1805
|
+
"2. Open chat.zalo.me, log in. DevTools → Application → Cookies → copy.",
|
|
1806
|
+
"3. Add channel, set cookie (or ZALO_PERSONAL_COOKIE env).",
|
|
1807
|
+
"4. hyperclaw gateway → hyperclaw pairing approve zalo-personal <CODE>",
|
|
1822
1808
|
"",
|
|
1823
|
-
"
|
|
1809
|
+
" 🔗 docs/zalo-personal.md — full setup"
|
|
1824
1810
|
],
|
|
1825
1811
|
status: "available",
|
|
1826
|
-
notes: "
|
|
1812
|
+
notes: "Zalo Web cookie auth. DMs only. No groups. Text chunked ~2000 chars."
|
|
1827
1813
|
};
|
|
1828
1814
|
CHANNELS.push(ZALO_PERSONAL);
|
|
1829
1815
|
|
|
@@ -1955,6 +1941,180 @@ async function getConfiguredChannels() {
|
|
|
1955
1941
|
return [];
|
|
1956
1942
|
}
|
|
1957
1943
|
}
|
|
1944
|
+
/**
|
|
1945
|
+
* hyperclaw channels login [channel]
|
|
1946
|
+
* Shortcut for first-time login flows (QR pairing, bot tokens, OAuth).
|
|
1947
|
+
* Delegates to channelsAdd with a login-oriented preamble.
|
|
1948
|
+
*/
|
|
1949
|
+
async function channelsLogin(channelId) {
|
|
1950
|
+
console.log(chalk.default.bold.cyan("\n 🔑 Channel Login\n"));
|
|
1951
|
+
console.log(chalk.default.gray(" This wizard will guide you through first-time login for a channel.\n"));
|
|
1952
|
+
console.log(chalk.default.gray(" WhatsApp → QR code scan"));
|
|
1953
|
+
console.log(chalk.default.gray(" Telegram → bot token (from @BotFather)"));
|
|
1954
|
+
console.log(chalk.default.gray(" Discord → bot token (from Discord Developer Portal)"));
|
|
1955
|
+
console.log(chalk.default.gray(" Slack → bot + app tokens"));
|
|
1956
|
+
console.log(chalk.default.gray(" Signal → phone number + signal-cli daemon\n"));
|
|
1957
|
+
await channelsAdd(channelId);
|
|
1958
|
+
}
|
|
1959
|
+
function httpProbe(url, ms = 1200) {
|
|
1960
|
+
return new Promise((resolve) => {
|
|
1961
|
+
const req = http.default.get(url, { timeout: ms }, (res) => {
|
|
1962
|
+
res.resume();
|
|
1963
|
+
resolve((res.statusCode ?? 0) < 500);
|
|
1964
|
+
});
|
|
1965
|
+
req.on("error", () => resolve(false));
|
|
1966
|
+
req.on("timeout", () => {
|
|
1967
|
+
req.destroy();
|
|
1968
|
+
resolve(false);
|
|
1969
|
+
});
|
|
1970
|
+
});
|
|
1971
|
+
}
|
|
1972
|
+
/** Attempt a lightweight connectivity probe for a channel. */
|
|
1973
|
+
async function probeChannel(channelId, channelCfg) {
|
|
1974
|
+
switch (channelId) {
|
|
1975
|
+
case "telegram": {
|
|
1976
|
+
const token = channelCfg?.token || process.env.TELEGRAM_BOT_TOKEN;
|
|
1977
|
+
if (!token) return {
|
|
1978
|
+
status: "skipped",
|
|
1979
|
+
detail: "no token"
|
|
1980
|
+
};
|
|
1981
|
+
const ok = await httpProbe(`https://api.telegram.org/bot${token}/getMe`);
|
|
1982
|
+
return ok ? {
|
|
1983
|
+
status: "connected",
|
|
1984
|
+
detail: "api.telegram.org ok"
|
|
1985
|
+
} : {
|
|
1986
|
+
status: "unreachable",
|
|
1987
|
+
detail: "api.telegram.org unreachable"
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
case "discord": {
|
|
1991
|
+
const ok = await httpProbe("https://discord.com/api/v10/gateway");
|
|
1992
|
+
return ok ? {
|
|
1993
|
+
status: "connected",
|
|
1994
|
+
detail: "discord.com/api ok"
|
|
1995
|
+
} : {
|
|
1996
|
+
status: "unreachable",
|
|
1997
|
+
detail: "discord.com unreachable"
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
case "signal": {
|
|
2001
|
+
const daemonUrl = channelCfg?.daemonUrl || "http://127.0.0.1:8080";
|
|
2002
|
+
const ok = await httpProbe(`${daemonUrl}/v1/about`);
|
|
2003
|
+
return ok ? {
|
|
2004
|
+
status: "connected",
|
|
2005
|
+
detail: `signal-cli at ${daemonUrl}`
|
|
2006
|
+
} : {
|
|
2007
|
+
status: "unreachable",
|
|
2008
|
+
detail: `signal-cli not reachable at ${daemonUrl}`
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
case "mattermost": {
|
|
2012
|
+
const baseUrl = channelCfg?.baseUrl;
|
|
2013
|
+
if (!baseUrl) return {
|
|
2014
|
+
status: "skipped",
|
|
2015
|
+
detail: "no baseUrl"
|
|
2016
|
+
};
|
|
2017
|
+
const ok = await httpProbe(`${baseUrl}/api/v4/system/ping`);
|
|
2018
|
+
return ok ? {
|
|
2019
|
+
status: "connected",
|
|
2020
|
+
detail: "system/ping ok"
|
|
2021
|
+
} : {
|
|
2022
|
+
status: "unreachable",
|
|
2023
|
+
detail: "server unreachable"
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
case "matrix": {
|
|
2027
|
+
const homeserver = channelCfg?.homeserver;
|
|
2028
|
+
if (!homeserver) return {
|
|
2029
|
+
status: "skipped",
|
|
2030
|
+
detail: "no homeserver"
|
|
2031
|
+
};
|
|
2032
|
+
const ok = await httpProbe(`${homeserver}/_matrix/client/versions`);
|
|
2033
|
+
return ok ? {
|
|
2034
|
+
status: "connected",
|
|
2035
|
+
detail: "matrix client ok"
|
|
2036
|
+
} : {
|
|
2037
|
+
status: "unreachable",
|
|
2038
|
+
detail: "homeserver unreachable"
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
case "slack": {
|
|
2042
|
+
const ok = await httpProbe("https://slack.com/api/api.test");
|
|
2043
|
+
return ok ? {
|
|
2044
|
+
status: "connected",
|
|
2045
|
+
detail: "slack.com/api ok"
|
|
2046
|
+
} : {
|
|
2047
|
+
status: "unreachable",
|
|
2048
|
+
detail: "slack.com unreachable"
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
default: return {
|
|
2052
|
+
status: "skipped",
|
|
2053
|
+
detail: "probe not implemented"
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* hyperclaw channels status [--probe]
|
|
2059
|
+
* Show all configured channels with their status.
|
|
2060
|
+
* --probe attempts a real connectivity check for each.
|
|
2061
|
+
*/
|
|
2062
|
+
async function channelsStatus(opts = {}) {
|
|
2063
|
+
const configFile = path.default.join(os.default.homedir(), ".hyperclaw", "config.json");
|
|
2064
|
+
let cfg = {};
|
|
2065
|
+
try {
|
|
2066
|
+
cfg = fs_extra.default.readJsonSync(configFile);
|
|
2067
|
+
} catch {}
|
|
2068
|
+
const configured = cfg.channels || [];
|
|
2069
|
+
console.log(chalk.default.bold.cyan("\n 📡 CHANNEL STATUS\n"));
|
|
2070
|
+
if (configured.length === 0) {
|
|
2071
|
+
console.log(chalk.default.gray(" No channels configured.\n"));
|
|
2072
|
+
console.log(chalk.default.gray(" Add a channel: hyperclaw channels add\n"));
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
const spinner = opts.probe ? (0, ora.default)("Probing channels...").start() : null;
|
|
2076
|
+
const results = [];
|
|
2077
|
+
for (const id of configured) {
|
|
2078
|
+
const ch = getChannel(id);
|
|
2079
|
+
if (!ch) continue;
|
|
2080
|
+
const channelCfg = cfg.channelConfigs?.[id];
|
|
2081
|
+
const dmPolicy = channelCfg?.dmPolicy?.policy ?? channelCfg?.dmPolicy;
|
|
2082
|
+
const r = {
|
|
2083
|
+
id,
|
|
2084
|
+
name: ch.name,
|
|
2085
|
+
emoji: ch.emoji,
|
|
2086
|
+
configured: true,
|
|
2087
|
+
dmPolicy
|
|
2088
|
+
};
|
|
2089
|
+
if (opts.probe) {
|
|
2090
|
+
const { status, detail } = await probeChannel(id, channelCfg);
|
|
2091
|
+
r.probe = status;
|
|
2092
|
+
r.probeDetail = detail;
|
|
2093
|
+
}
|
|
2094
|
+
results.push(r);
|
|
2095
|
+
}
|
|
2096
|
+
if (spinner) spinner.stop();
|
|
2097
|
+
const allIds = CHANNELS.map((c) => c.id);
|
|
2098
|
+
const unconfigured = allIds.filter((id) => !configured.includes(id));
|
|
2099
|
+
for (const r of results) {
|
|
2100
|
+
const probeStr = opts.probe ? r.probe === "connected" ? chalk.default.green(" ✔ connected") : r.probe === "unreachable" ? chalk.default.red(" ✖ unreachable") : chalk.default.gray(" — skipped") : "";
|
|
2101
|
+
const dm = r.dmPolicy ? chalk.default.gray(` dm:${r.dmPolicy}`) : "";
|
|
2102
|
+
console.log(` ${chalk.default.green("●")} ${r.emoji} ${r.name.padEnd(18)}${dm}${probeStr}`);
|
|
2103
|
+
if (opts.probe && r.probeDetail) console.log(` ${chalk.default.gray(r.probeDetail)}`);
|
|
2104
|
+
}
|
|
2105
|
+
if (unconfigured.length > 0) console.log(chalk.default.gray(`\n ○ ${unconfigured.length} unconfigured channel(s) — add with: hyperclaw channels add`));
|
|
2106
|
+
console.log();
|
|
2107
|
+
if (opts.probe) {
|
|
2108
|
+
const unreachable = results.filter((r) => r.probe === "unreachable");
|
|
2109
|
+
if (unreachable.length > 0) {
|
|
2110
|
+
console.log(chalk.default.yellow(` ⚠ ${unreachable.length} channel(s) unreachable:`));
|
|
2111
|
+
for (const r of unreachable) console.log(chalk.default.gray(` ${r.id}: ${r.probeDetail}`));
|
|
2112
|
+
console.log();
|
|
2113
|
+
console.log(chalk.default.gray(" Troubleshoot: hyperclaw doctor"));
|
|
2114
|
+
console.log(chalk.default.gray(" Logs: hyperclaw logs --follow\n"));
|
|
2115
|
+
} else console.log(chalk.default.green(" ✔ All probed channels reachable\n"));
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
1958
2118
|
|
|
1959
2119
|
//#endregion
|
|
1960
2120
|
//#region src/infra/update-channels.ts
|
|
@@ -2266,38 +2426,93 @@ var init_queue = require_chunk.__esm({ "src/delivery/queue.ts"() {
|
|
|
2266
2426
|
//#endregion
|
|
2267
2427
|
//#region src/cli/run-main.ts
|
|
2268
2428
|
const program = new commander.Command();
|
|
2269
|
-
program.name("hyperclaw").description("⚡ HyperClaw — AI Gateway Platform. The Lobster Evolution 🦅").version("
|
|
2429
|
+
program.name("hyperclaw").description("⚡ HyperClaw — AI Gateway Platform. The Lobster Evolution 🦅").version("5.0.0").option("--profile <name>", "Use an isolated gateway profile. Auto-scopes HYPERCLAW_STATE_DIR and HYPERCLAW_CONFIG_PATH. Required for multi-gateway setups (rescue bot, staging, etc.). Example: hyperclaw --profile rescue gateway --port 19001").hook("preAction", (thisCommand) => {
|
|
2430
|
+
const profile = thisCommand.opts().profile;
|
|
2431
|
+
if (profile) {
|
|
2432
|
+
const os$8 = require("os");
|
|
2433
|
+
const path$7 = require("path");
|
|
2434
|
+
const home = os$8.homedir();
|
|
2435
|
+
if (!process.env.HYPERCLAW_STATE_DIR) process.env.HYPERCLAW_STATE_DIR = path$7.join(home, `.hyperclaw-${profile}`);
|
|
2436
|
+
if (!process.env.HYPERCLAW_CONFIG_PATH) process.env.HYPERCLAW_CONFIG_PATH = path$7.join(process.env.HYPERCLAW_STATE_DIR, "hyperclaw.json");
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2270
2439
|
program.command("init").description("Initialize HyperClaw with interactive wizard").option("-a, --auto-config", "Auto-configure with defaults").option("-d, --daemon", "Install as system daemon").option("-s, --start-now", "Start gateway after setup").action(async (opts) => {
|
|
2271
2440
|
await new require_onboard.Banner().showNeonBanner(false);
|
|
2272
2441
|
await new require_onboard.HyperClawWizard().run(opts);
|
|
2273
2442
|
process.exit(0);
|
|
2274
2443
|
});
|
|
2275
|
-
program.command("onboard").description("Full onboarding wizard — preferred setup path").option("--install-daemon", "Auto-install system daemon (starts on boot, grants full PC access)").option("--quick", "Use QuickStart mode (skip advanced options)").action(async (opts) => {
|
|
2444
|
+
program.command("onboard").description("Full onboarding wizard — preferred setup path").option("--install-daemon", "Auto-install system daemon (starts on boot, grants full PC access)").option("--quick", "Use QuickStart mode (skip advanced options)").option("--reset", "Reset config before running wizard (sends to trash, not deleted)").option("--reset-scope <scope>", "What to reset: config | config+creds | full", "config").option("--non-interactive", "Run in non-interactive mode (use flags for all options)").option("--json", "Output result as JSON (use with --non-interactive)").option("--anthropic-api-key <key>", "Anthropic API key (non-interactive)").option("--openai-api-key <key>", "OpenAI API key (non-interactive)").option("--gateway-port <port>", "Gateway port (non-interactive)", "18789").option("--gateway-bind <bind>", "Gateway bind: loopback | all (non-interactive)", "loopback").option("--daemon-runtime <runtime>", "Daemon runtime: node | bun (non-interactive)", "node").option("--skip-skills", "Skip skills setup (non-interactive)").option("--skip-search", "Skip web search setup (non-interactive)").action(async (opts) => {
|
|
2276
2445
|
await new require_onboard.Banner().showNeonBanner(false);
|
|
2446
|
+
if (opts.reset) {
|
|
2447
|
+
const fs$7 = require("fs-extra");
|
|
2448
|
+
const path$7 = require("path");
|
|
2449
|
+
const os$8 = require("os");
|
|
2450
|
+
const hcDir = path$7.join(os$8.homedir(), ".hyperclaw");
|
|
2451
|
+
const scope = opts.resetScope ?? "config";
|
|
2452
|
+
const filesToRemove = [path$7.join(hcDir, "hyperclaw.json")];
|
|
2453
|
+
if (scope === "config+creds" || scope === "full") {
|
|
2454
|
+
filesToRemove.push(path$7.join(hcDir, "credentials"));
|
|
2455
|
+
filesToRemove.push(path$7.join(hcDir, "sessions"));
|
|
2456
|
+
}
|
|
2457
|
+
if (scope === "full") filesToRemove.push(path$7.join(hcDir, "workspace"));
|
|
2458
|
+
const chalk$11 = require("chalk");
|
|
2459
|
+
console.log(chalk$11.yellow(`\n ⚠ Reset scope: ${chalk$11.bold(scope)}\n`));
|
|
2460
|
+
console.log(chalk$11.gray(" Files to remove:"));
|
|
2461
|
+
filesToRemove.forEach((f) => console.log(chalk$11.gray(` • ${f}`)));
|
|
2462
|
+
const inquirer$2 = require("inquirer");
|
|
2463
|
+
const { confirmReset } = await inquirer$2.prompt([{
|
|
2464
|
+
type: "confirm",
|
|
2465
|
+
name: "confirmReset",
|
|
2466
|
+
message: "Confirm reset? (files will be moved to trash/backup, not permanently deleted)",
|
|
2467
|
+
default: false
|
|
2468
|
+
}]);
|
|
2469
|
+
if (confirmReset) {
|
|
2470
|
+
const backupDir = path$7.join(hcDir, `backup-${Date.now()}`);
|
|
2471
|
+
await fs$7.ensureDir(backupDir);
|
|
2472
|
+
for (const f of filesToRemove) if (await fs$7.pathExists(f)) {
|
|
2473
|
+
const dest = path$7.join(backupDir, path$7.basename(f));
|
|
2474
|
+
await fs$7.move(f, dest);
|
|
2475
|
+
console.log(chalk$11.gray(` ✓ Moved ${path$7.basename(f)} → backup/`));
|
|
2476
|
+
}
|
|
2477
|
+
console.log(chalk$11.green("\n ✔ Reset complete. Starting fresh...\n"));
|
|
2478
|
+
} else {
|
|
2479
|
+
console.log(chalk$11.gray("\n Reset cancelled.\n"));
|
|
2480
|
+
process.exit(0);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2277
2483
|
if (opts.installDaemon) {
|
|
2278
|
-
const chalk$
|
|
2279
|
-
console.log(chalk$
|
|
2280
|
-
console.log(chalk$
|
|
2281
|
-
console.log(chalk$
|
|
2282
|
-
console.log(chalk$
|
|
2283
|
-
console.log(chalk$
|
|
2284
|
-
console.log(chalk$
|
|
2285
|
-
const inquirer$
|
|
2286
|
-
const { confirmed } = await inquirer$
|
|
2484
|
+
const chalk$11 = require("chalk");
|
|
2485
|
+
console.log(chalk$11.yellow("\n ⚠ --install-daemon mode\n"));
|
|
2486
|
+
console.log(chalk$11.gray(" The daemon will run as a background system service and will have:\n"));
|
|
2487
|
+
console.log(chalk$11.white(" • Full shell / command execution on this machine"));
|
|
2488
|
+
console.log(chalk$11.white(" • File system read & write access"));
|
|
2489
|
+
console.log(chalk$11.white(" • Network access (gateway WebSocket)"));
|
|
2490
|
+
console.log(chalk$11.white(" • Auto-start on every system boot\n"));
|
|
2491
|
+
const inquirer$2 = require("inquirer");
|
|
2492
|
+
const { confirmed } = await inquirer$2.prompt([{
|
|
2287
2493
|
type: "confirm",
|
|
2288
2494
|
name: "confirmed",
|
|
2289
2495
|
message: "I understand and want to continue with full PC access:",
|
|
2290
2496
|
default: false
|
|
2291
2497
|
}]);
|
|
2292
2498
|
if (!confirmed) {
|
|
2293
|
-
console.log(chalk$
|
|
2499
|
+
console.log(chalk$11.gray("\n Cancelled. Run without --install-daemon to choose during setup.\n"));
|
|
2294
2500
|
process.exit(0);
|
|
2295
2501
|
}
|
|
2296
2502
|
}
|
|
2297
2503
|
await new require_onboard.HyperClawWizard().run({
|
|
2298
2504
|
...opts,
|
|
2299
2505
|
wizard: true,
|
|
2300
|
-
installDaemon: opts.installDaemon ?? false
|
|
2506
|
+
installDaemon: opts.installDaemon ?? false,
|
|
2507
|
+
nonInteractive: opts.nonInteractive ?? false,
|
|
2508
|
+
jsonOutput: opts.json ?? false,
|
|
2509
|
+
skipSkills: opts.skipSkills ?? false,
|
|
2510
|
+
skipSearch: opts.skipSearch ?? false,
|
|
2511
|
+
daemonRuntime: opts.daemonRuntime ?? "node",
|
|
2512
|
+
gatewayPort: opts.gatewayPort ? parseInt(opts.gatewayPort) : void 0,
|
|
2513
|
+
gatewayBind: opts.gatewayBind ?? "loopback",
|
|
2514
|
+
anthropicApiKey: opts.anthropicApiKey,
|
|
2515
|
+
openaiApiKey: opts.openaiApiKey
|
|
2301
2516
|
});
|
|
2302
2517
|
process.exit(0);
|
|
2303
2518
|
});
|
|
@@ -2322,18 +2537,18 @@ gatewayCmd.command("status").description("Show gateway status").action(async ()
|
|
|
2322
2537
|
process.exit(0);
|
|
2323
2538
|
});
|
|
2324
2539
|
gatewayCmd.command("start").description("Start the gateway service").option("-p, --port <port>", "Override port").action(async (opts) => {
|
|
2325
|
-
await new
|
|
2540
|
+
await new require_daemon.DaemonManager().start();
|
|
2326
2541
|
});
|
|
2327
2542
|
gatewayCmd.command("stop").description("Stop the gateway service").action(async () => {
|
|
2328
|
-
await new
|
|
2543
|
+
await new require_daemon.DaemonManager().stop();
|
|
2329
2544
|
process.exit(0);
|
|
2330
2545
|
});
|
|
2331
2546
|
gatewayCmd.command("restart").description("Restart the gateway service").action(async () => {
|
|
2332
|
-
const dm = new
|
|
2547
|
+
const dm = new require_daemon.DaemonManager();
|
|
2333
2548
|
await dm.restart();
|
|
2334
2549
|
});
|
|
2335
|
-
program.command("daemon").description("Manage HyperClaw system service (alias: gateway)").argument("<action>", "start|stop|restart|status|logs").action(async (action) => {
|
|
2336
|
-
const dm = new
|
|
2550
|
+
program.command("daemon").description("Manage HyperClaw system service (alias: gateway)").argument("<action>", "start|stop|restart|status|logs|install|uninstall").action(async (action) => {
|
|
2551
|
+
const dm = new require_daemon.DaemonManager();
|
|
2337
2552
|
if (action === "start") await new require_onboard.Banner().showNeonBanner(true);
|
|
2338
2553
|
await dm.handle(action);
|
|
2339
2554
|
if (action === "start" || action === "restart") return;
|
|
@@ -2341,20 +2556,20 @@ program.command("daemon").description("Manage HyperClaw system service (alias: g
|
|
|
2341
2556
|
});
|
|
2342
2557
|
const sandboxCmd = program.command("sandbox").description("Debug sandbox and tool policy");
|
|
2343
2558
|
sandboxCmd.command("explain").description("Show effective sandbox mode, tool policy, and allowed tools").option("--json", "Output as JSON").action(async (opts) => {
|
|
2344
|
-
const chalk$
|
|
2345
|
-
const fs$
|
|
2346
|
-
const path$
|
|
2347
|
-
const os$
|
|
2559
|
+
const chalk$11 = require("chalk");
|
|
2560
|
+
const fs$7 = require("fs-extra");
|
|
2561
|
+
const path$7 = require("path");
|
|
2562
|
+
const os$8 = require("os");
|
|
2348
2563
|
const { getConfigPath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
|
|
2349
2564
|
const cfgPath = getConfigPath();
|
|
2350
2565
|
let cfg = {};
|
|
2351
2566
|
try {
|
|
2352
|
-
cfg = await fs$
|
|
2567
|
+
cfg = await fs$7.readJson(cfgPath);
|
|
2353
2568
|
} catch {}
|
|
2354
2569
|
const sandboxMode = cfg?.agents?.defaults?.sandbox?.mode ?? "non-main";
|
|
2355
2570
|
const toolsCfg = cfg?.tools ?? {};
|
|
2356
|
-
const { describeToolPolicy, applyToolPolicy } = await Promise.resolve().then(() => require("./tool-policy-
|
|
2357
|
-
const { getBuiltinTools, getSessionsTools, getPCAccessTools, getBrowserTools, getExtractionTools, getWebsiteWatchTools, getVisionTools } = await Promise.resolve().then(() => require("./src-
|
|
2571
|
+
const { describeToolPolicy, applyToolPolicy } = await Promise.resolve().then(() => require("./tool-policy-CNT-mF2Z.js"));
|
|
2572
|
+
const { getBuiltinTools, getSessionsTools, getPCAccessTools, getBrowserTools, getExtractionTools, getWebsiteWatchTools, getVisionTools } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
2358
2573
|
const allTools = [
|
|
2359
2574
|
...getBuiltinTools(),
|
|
2360
2575
|
...getSessionsTools(() => null),
|
|
@@ -2382,15 +2597,15 @@ sandboxCmd.command("explain").description("Show effective sandbox mode, tool pol
|
|
|
2382
2597
|
}, null, 2));
|
|
2383
2598
|
process.exit(0);
|
|
2384
2599
|
}
|
|
2385
|
-
console.log(chalk$
|
|
2600
|
+
console.log(chalk$11.bold.hex("#06b6d4")("\n 🔒 SANDBOX EXPLAIN\n"));
|
|
2386
2601
|
console.log(` Sandbox mode: ${sandboxMode} (non-main sessions get restricted pcTools)`);
|
|
2387
2602
|
console.log(` Tool profile: ${policy.profile}`);
|
|
2388
2603
|
console.log(` Policy source: ${policy.source}`);
|
|
2389
2604
|
if (policy.allow?.length) console.log(` Allow: ${policy.allow.join(", ")}`);
|
|
2390
2605
|
if (policy.deny?.length) console.log(` Deny: ${policy.deny.join(", ")}`);
|
|
2391
2606
|
console.log(` Tools: ${filtered.length} / ${allTools.length} allowed`);
|
|
2392
|
-
console.log(chalk$
|
|
2393
|
-
console.log(chalk$
|
|
2607
|
+
console.log(chalk$11.gray("\n Allowed: " + filtered.map((t) => t.name).join(", ")));
|
|
2608
|
+
console.log(chalk$11.gray("\n Elevated: " + ((cfg?.tools?.elevated)?.enabled ? "enabled" : "disabled")));
|
|
2394
2609
|
console.log();
|
|
2395
2610
|
process.exit(0);
|
|
2396
2611
|
});
|
|
@@ -2407,6 +2622,14 @@ channelsCmd.command("remove <channel>").description("Remove a channel").action(a
|
|
|
2407
2622
|
await channelsRemove(channel);
|
|
2408
2623
|
process.exit(0);
|
|
2409
2624
|
});
|
|
2625
|
+
channelsCmd.command("login [channel]").description("First-time login / QR pairing for a channel").action(async (channel) => {
|
|
2626
|
+
await channelsLogin(channel);
|
|
2627
|
+
process.exit(0);
|
|
2628
|
+
});
|
|
2629
|
+
channelsCmd.command("status").description("Show channel status (use --probe to test connectivity)").option("--probe", "Probe each channel for real connectivity").action(async (opts) => {
|
|
2630
|
+
await channelsStatus({ probe: !!opts.probe });
|
|
2631
|
+
process.exit(0);
|
|
2632
|
+
});
|
|
2410
2633
|
const hooksCmd = program.command("hooks").description("Hook management");
|
|
2411
2634
|
hooksCmd.command("list").description("List all hooks").option("--eligible", "Show only eligible hooks").action((opts) => {
|
|
2412
2635
|
new require_loader.HookLoader().list(opts.eligible);
|
|
@@ -2430,24 +2653,79 @@ hooksCmd.command("install <pack>").description("Install a hook pack").action(asy
|
|
|
2430
2653
|
});
|
|
2431
2654
|
const agentsCmd = program.command("agents").description("Multi-agent routing");
|
|
2432
2655
|
agentsCmd.command("bindings").description("List agent ↔ channel bindings").action(() => {
|
|
2433
|
-
new AgentRouter().listBindings();
|
|
2656
|
+
new require_agents_routing.AgentRouter().listBindings();
|
|
2434
2657
|
process.exit(0);
|
|
2435
2658
|
});
|
|
2436
2659
|
agentsCmd.command("bind").description("Bind a channel to an agent workspace").action(async () => {
|
|
2437
|
-
await new AgentRouter().bind();
|
|
2660
|
+
await new require_agents_routing.AgentRouter().bind();
|
|
2438
2661
|
process.exit(0);
|
|
2439
2662
|
});
|
|
2440
2663
|
agentsCmd.command("unbind").description("Remove channel ↔ agent bindings").action(async () => {
|
|
2441
|
-
await new AgentRouter().unbind();
|
|
2664
|
+
await new require_agents_routing.AgentRouter().unbind();
|
|
2442
2665
|
process.exit(0);
|
|
2443
2666
|
});
|
|
2444
2667
|
const pairingCmd = program.command("pairing").description("DM pairing codes");
|
|
2445
|
-
pairingCmd.command("list").description("List pending
|
|
2446
|
-
new require_pairing.
|
|
2668
|
+
pairingCmd.command("list [channel]").description("List pending DM pairing requests (optionally filter by channel)").action(async (channel) => {
|
|
2669
|
+
await new require_pairing.GlobalPairingManager().showList(channel);
|
|
2670
|
+
process.exit(0);
|
|
2671
|
+
});
|
|
2672
|
+
pairingCmd.command("approve <channel> <code>").description("Approve a pairing code and add sender to channel allowlist").option("--account <id>", "Account ID for multi-account channels", "default").action(async (channel, code, opts) => {
|
|
2673
|
+
await new require_pairing.GlobalPairingManager().cliApprove(channel, code, opts.account);
|
|
2674
|
+
process.exit(0);
|
|
2675
|
+
});
|
|
2676
|
+
const devicesCmd = program.command("devices").description("Node/device pairing (iOS, Android, macOS, headless)");
|
|
2677
|
+
devicesCmd.command("list").description("List pending and paired devices").action(async () => {
|
|
2678
|
+
await new DevicePairingStore().showCLI();
|
|
2679
|
+
process.exit(0);
|
|
2680
|
+
});
|
|
2681
|
+
devicesCmd.command("pair").description("Create a new device pairing request and print setup code").option("-u, --gateway-url <url>", "Gateway WebSocket URL", "ws://localhost:18789").option("-n, --name <name>", "Device name (optional)").option("-p, --platform <platform>", "Platform hint: ios|android|macos|headless (optional)").action(async (opts) => {
|
|
2682
|
+
const store = new DevicePairingStore();
|
|
2683
|
+
const result = await store.createRequest(opts.gatewayUrl, {
|
|
2684
|
+
deviceName: opts.name,
|
|
2685
|
+
platform: opts.platform
|
|
2686
|
+
});
|
|
2687
|
+
console.log(chalk.default.bold.cyan("\n 📱 DEVICE PAIR REQUEST\n"));
|
|
2688
|
+
console.log(` Request ID: ${chalk.default.bold(result.requestId)}`);
|
|
2689
|
+
console.log(` Expires: ${chalk.default.gray(new Date(result.expiresAt).toLocaleTimeString())}`);
|
|
2690
|
+
console.log();
|
|
2691
|
+
console.log(chalk.default.yellow(" Setup code (send to device or paste in app):"));
|
|
2692
|
+
console.log(chalk.default.bold(`\n ${result.setupCode}\n`));
|
|
2693
|
+
console.log(chalk.default.gray(" Approve: hyperclaw devices approve " + result.requestId));
|
|
2694
|
+
console.log(chalk.default.gray(" Reject: hyperclaw devices reject " + result.requestId));
|
|
2695
|
+
console.log(chalk.default.gray("\n Telegram: message your bot with /pair for guided flow.\n"));
|
|
2696
|
+
process.exit(0);
|
|
2697
|
+
});
|
|
2698
|
+
devicesCmd.command("approve <requestId>").description("Approve a pending device pairing request").action(async (requestId) => {
|
|
2699
|
+
const store = new DevicePairingStore();
|
|
2700
|
+
const device = await store.approve(requestId);
|
|
2701
|
+
if (device) {
|
|
2702
|
+
console.log(chalk.default.green(`\n ✔ Device approved: ${chalk.default.bold(device.deviceId)}`));
|
|
2703
|
+
if (device.deviceName) console.log(chalk.default.gray(` Name: ${device.deviceName}`));
|
|
2704
|
+
console.log(chalk.default.gray(` Paired at: ${device.pairedAt}\n`));
|
|
2705
|
+
} else {
|
|
2706
|
+
console.log(chalk.default.red(`\n ✖ Request not found or expired: ${requestId}\n`));
|
|
2707
|
+
process.exit(1);
|
|
2708
|
+
}
|
|
2709
|
+
process.exit(0);
|
|
2710
|
+
});
|
|
2711
|
+
devicesCmd.command("reject <requestId>").description("Reject a pending device pairing request").action(async (requestId) => {
|
|
2712
|
+
const store = new DevicePairingStore();
|
|
2713
|
+
const ok = await store.reject(requestId);
|
|
2714
|
+
if (ok) console.log(chalk.default.green(`\n ✔ Request rejected: ${requestId}\n`));
|
|
2715
|
+
else {
|
|
2716
|
+
console.log(chalk.default.red(`\n ✖ Request not found: ${requestId}\n`));
|
|
2717
|
+
process.exit(1);
|
|
2718
|
+
}
|
|
2447
2719
|
process.exit(0);
|
|
2448
2720
|
});
|
|
2449
|
-
|
|
2450
|
-
new
|
|
2721
|
+
devicesCmd.command("unpair <deviceId>").description("Remove a paired device").action(async (deviceId) => {
|
|
2722
|
+
const store = new DevicePairingStore();
|
|
2723
|
+
const ok = await store.unpair(deviceId);
|
|
2724
|
+
if (ok) console.log(chalk.default.green(`\n ✔ Device unpaired: ${deviceId}\n`));
|
|
2725
|
+
else {
|
|
2726
|
+
console.log(chalk.default.red(`\n ✖ Device not found: ${deviceId}\n`));
|
|
2727
|
+
process.exit(1);
|
|
2728
|
+
}
|
|
2451
2729
|
process.exit(0);
|
|
2452
2730
|
});
|
|
2453
2731
|
const msgCmd = program.command("message").description("Send messages");
|
|
@@ -2456,7 +2734,7 @@ msgCmd.command("send").description("Send a message via a configured channel").op
|
|
|
2456
2734
|
process.exit(0);
|
|
2457
2735
|
});
|
|
2458
2736
|
program.command("hub").description("Skill hub — browse marketplace, install, scan skills").option("-i, --install <id>", "Install skill").option("-s, --scan <id>", "Security scan a skill").option("-m, --marketplace", "ClawHub-style marketplace view (installed + bundled)").option("--force", "Force install (bypass risk block)").option("--hide-suspicious", "Hide suspicious/dangerous skills").action(async (opts) => {
|
|
2459
|
-
const hub = new SkillHub();
|
|
2737
|
+
const hub = new require_hub.SkillHub();
|
|
2460
2738
|
if (opts.scan) await hub.scan(opts.scan);
|
|
2461
2739
|
else if (opts.install) await hub.install(opts.install, opts.force);
|
|
2462
2740
|
else if (opts.marketplace) await hub.showMarketplace({ hideSuspicious: opts.hideSuspicious });
|
|
@@ -2465,7 +2743,7 @@ program.command("hub").description("Skill hub — browse marketplace, install, s
|
|
|
2465
2743
|
});
|
|
2466
2744
|
const skillCmd = program.command("skill").description("ClawHub — search and install skills from registry");
|
|
2467
2745
|
skillCmd.command("search [query]").description("Search ClawHub for skills").option("-c, --category <cat>", "Filter by category").action(async (query, opts) => {
|
|
2468
|
-
const hub = new SkillHub();
|
|
2746
|
+
const hub = new require_hub.SkillHub();
|
|
2469
2747
|
const q = query || "";
|
|
2470
2748
|
const skills = await hub.searchClawHub(q, opts.category);
|
|
2471
2749
|
if (skills.length === 0) {
|
|
@@ -2483,11 +2761,11 @@ skillCmd.command("search [query]").description("Search ClawHub for skills").opti
|
|
|
2483
2761
|
process.exit(0);
|
|
2484
2762
|
});
|
|
2485
2763
|
skillCmd.command("list").description("List installed skills (bundled + ClawHub)").action(async () => {
|
|
2486
|
-
const hub = new SkillHub();
|
|
2764
|
+
const hub = new require_hub.SkillHub();
|
|
2487
2765
|
const installed = await hub.getInstalled();
|
|
2488
2766
|
console.log(chalk.default.bold.hex("#06b6d4")("\n Installed skills:\n"));
|
|
2489
2767
|
for (const s of installed) {
|
|
2490
|
-
const src = SKILL_REGISTRY.some((r) => r.id === s.id) ? "" : " (ClawHub)";
|
|
2768
|
+
const src = require_hub.SKILL_REGISTRY.some((r) => r.id === s.id) ? "" : " (ClawHub)";
|
|
2491
2769
|
console.log(` ${chalk.default.hex("#06b6d4")("✓")} ${s.name} ${chalk.default.gray(`(${s.id})${src}`)}`);
|
|
2492
2770
|
}
|
|
2493
2771
|
if (installed.length === 0) console.log(chalk.default.gray(" No skills installed. Run: hyperclaw hub or hyperclaw skill search <query>\n"));
|
|
@@ -2495,14 +2773,14 @@ skillCmd.command("list").description("List installed skills (bundled + ClawHub)"
|
|
|
2495
2773
|
process.exit(0);
|
|
2496
2774
|
});
|
|
2497
2775
|
skillCmd.command("install <id>").description("Install skill from ClawHub (or bundled when registry unavailable)").option("-v, --version <ver>", "Pin version (e.g. 2.0.0)").option("--force", "Force install (bypass risk block for bundled)").action(async (id, opts) => {
|
|
2498
|
-
const hub = new SkillHub();
|
|
2499
|
-
const ora$
|
|
2500
|
-
const s = ora$
|
|
2776
|
+
const hub = new require_hub.SkillHub();
|
|
2777
|
+
const ora$4 = (await import("ora")).default;
|
|
2778
|
+
const s = ora$4(`Installing ${id}...`).start();
|
|
2501
2779
|
try {
|
|
2502
2780
|
const dest = await hub.installFromClawHub(id, opts.version);
|
|
2503
2781
|
s.succeed(`Installed to ${dest}`);
|
|
2504
2782
|
} catch (e) {
|
|
2505
|
-
const match = SKILL_REGISTRY.find((x) => x.id === id);
|
|
2783
|
+
const match = require_hub.SKILL_REGISTRY.find((x) => x.id === id);
|
|
2506
2784
|
if (match) {
|
|
2507
2785
|
await hub.install(id, !!opts?.force);
|
|
2508
2786
|
s.succeed(`Installed bundled skill: ${match.name}`);
|
|
@@ -2511,13 +2789,13 @@ skillCmd.command("install <id>").description("Install skill from ClawHub (or bun
|
|
|
2511
2789
|
process.exit(0);
|
|
2512
2790
|
});
|
|
2513
2791
|
program.command("menu-bar").description("Launch macOS menu bar companion (Electron tray app)").action(async () => {
|
|
2514
|
-
const path$
|
|
2792
|
+
const path$7 = await import("path");
|
|
2515
2793
|
const { spawn } = await import("child_process");
|
|
2516
|
-
const fs$
|
|
2517
|
-
const root = path$
|
|
2518
|
-
const altRoot = path$
|
|
2519
|
-
const macosDir = await fs$
|
|
2520
|
-
if (!macosDir || !await fs$
|
|
2794
|
+
const fs$7 = await import("fs-extra");
|
|
2795
|
+
const root = path$7.join(process.cwd(), "apps", "macos");
|
|
2796
|
+
const altRoot = path$7.join(__dirname, "..", "..", "apps", "macos");
|
|
2797
|
+
const macosDir = await fs$7.pathExists(root) ? root : await fs$7.pathExists(altRoot) ? altRoot : null;
|
|
2798
|
+
if (!macosDir || !await fs$7.pathExists(path$7.join(macosDir, "package.json"))) {
|
|
2521
2799
|
console.log(chalk.default.gray("\n macOS menu bar app not found."));
|
|
2522
2800
|
console.log(chalk.default.gray(" Run from HyperClaw repo root, or: cd apps/macos && npm start\n"));
|
|
2523
2801
|
process.exit(1);
|
|
@@ -2544,8 +2822,15 @@ program.command("update").description("Update HyperClaw").option("-c, --channel
|
|
|
2544
2822
|
await performUpdate(effective.channel, installKind);
|
|
2545
2823
|
process.exit(0);
|
|
2546
2824
|
});
|
|
2547
|
-
program.command("doctor").description("Health check — surfaces misconfigs
|
|
2548
|
-
await runDoctor(opts.fix
|
|
2825
|
+
program.command("doctor").description("Health check — surfaces misconfigs, risky DM policies, and repairs").option("--fix", "Auto-repair fixable issues").option("--repair", "Apply recommended repairs (same as --fix)").option("--force", "Apply aggressive repairs (use with --repair)").option("-y, --yes", "Accept defaults without prompting").option("--non-interactive", "Skip prompts; only run safe migrations").option("--deep", "Scan system services for extra gateway installs").action(async (opts) => {
|
|
2826
|
+
await require_doctor.runDoctor(opts.fix || opts.repair, {
|
|
2827
|
+
fix: opts.fix,
|
|
2828
|
+
repair: opts.repair,
|
|
2829
|
+
force: opts.force,
|
|
2830
|
+
yes: opts.yes,
|
|
2831
|
+
nonInteractive: opts.nonInteractive,
|
|
2832
|
+
deep: opts.deep
|
|
2833
|
+
});
|
|
2549
2834
|
process.exit(0);
|
|
2550
2835
|
});
|
|
2551
2836
|
const memCmd = program.command("memory").description("Agent memory management");
|
|
@@ -2591,12 +2876,12 @@ cfgCmd.command("set-key <KEY=value>").description("Set an API key or config valu
|
|
|
2591
2876
|
process.exit(0);
|
|
2592
2877
|
});
|
|
2593
2878
|
cfgCmd.command("set-service-key <serviceId> [apiKey]").description("Set API key for a service (hackerone, bugcrowd, synack, or custom). Prompts if apiKey omitted.").action(async (serviceId, apiKey) => {
|
|
2594
|
-
const inquirer$
|
|
2879
|
+
const inquirer$2 = (await import("inquirer")).default;
|
|
2595
2880
|
const config = new require_manager.ConfigManager();
|
|
2596
2881
|
const cfg = await config.load();
|
|
2597
2882
|
let key = apiKey;
|
|
2598
2883
|
if (!key || key.trim().length === 0) {
|
|
2599
|
-
const r = await inquirer$
|
|
2884
|
+
const r = await inquirer$2.prompt([{
|
|
2600
2885
|
type: "password",
|
|
2601
2886
|
name: "k",
|
|
2602
2887
|
message: `API key for ${serviceId}:`,
|
|
@@ -2627,7 +2912,7 @@ cfgCmd.command("set-service-key <serviceId> [apiKey]").description("Set API key
|
|
|
2627
2912
|
cfgCmd.command("schema").description("Show configuration schema").action(() => {
|
|
2628
2913
|
console.log(chalk.default.bold.hex("#06b6d4")("\n Config schema: ~/.hyperclaw/config.json\n"));
|
|
2629
2914
|
const schema = {
|
|
2630
|
-
version: "string (e.g. \"
|
|
2915
|
+
version: "string (e.g. \"5.0.0\")",
|
|
2631
2916
|
workspaceName: "string",
|
|
2632
2917
|
provider: {
|
|
2633
2918
|
providerId: "string",
|
|
@@ -2720,17 +3005,17 @@ program.command("deploy").description("Deploy gateway to cloud (Fly.io or Render
|
|
|
2720
3005
|
program.command("voice-call").description("Start voice call session — terminal mode, talks to gateway").option("-u, --gateway-url <url>", "Gateway URL", "http://localhost:18789").action(async (opts) => {
|
|
2721
3006
|
const axios = (await import("axios")).default;
|
|
2722
3007
|
const readline$2 = await import("readline");
|
|
2723
|
-
const chalk$
|
|
3008
|
+
const chalk$11 = require("chalk");
|
|
2724
3009
|
const url = opts.gatewayUrl || "http://localhost:18789";
|
|
2725
|
-
console.log(chalk$
|
|
2726
|
-
console.log(chalk$
|
|
2727
|
-
console.log(chalk$
|
|
3010
|
+
console.log(chalk$11.bold.cyan("\n 🎙️ HYPERCLAW VOICE CALL\n"));
|
|
3011
|
+
console.log(chalk$11.gray(` Gateway: ${url}`));
|
|
3012
|
+
console.log(chalk$11.gray(" Type a message and press Enter. Ctrl+C to exit.\n"));
|
|
2728
3013
|
const rl = readline$2.createInterface({
|
|
2729
3014
|
input: process.stdin,
|
|
2730
3015
|
output: process.stdout
|
|
2731
3016
|
});
|
|
2732
3017
|
const ask = () => {
|
|
2733
|
-
rl.question(chalk$
|
|
3018
|
+
rl.question(chalk$11.cyan(" You: "), async (input) => {
|
|
2734
3019
|
if (!input?.trim()) {
|
|
2735
3020
|
ask();
|
|
2736
3021
|
return;
|
|
@@ -2740,9 +3025,9 @@ program.command("voice-call").description("Start voice call session — terminal
|
|
|
2740
3025
|
message: input.trim(),
|
|
2741
3026
|
thinking: "none"
|
|
2742
3027
|
}, { timeout: 6e4 });
|
|
2743
|
-
console.log(chalk$
|
|
3028
|
+
console.log(chalk$11.green(` 🦅 Agent: ${(res.data?.response || "").slice(0, 500)}\n`));
|
|
2744
3029
|
} catch (e) {
|
|
2745
|
-
console.log(chalk$
|
|
3030
|
+
console.log(chalk$11.red(` Error: ${e.response?.data?.error || e.message}\n`));
|
|
2746
3031
|
}
|
|
2747
3032
|
ask();
|
|
2748
3033
|
});
|
|
@@ -2762,13 +3047,102 @@ program.command("dashboard").description("Launch live terminal dashboard").optio
|
|
|
2762
3047
|
await new Dashboard().launch(opts.live);
|
|
2763
3048
|
if (!opts.live) process.exit(0);
|
|
2764
3049
|
});
|
|
2765
|
-
program.command("status").description("System overview").action(async () => {
|
|
3050
|
+
program.command("status").description("System overview").option("--all", "Full local diagnosis (read-only)").option("--deep", "Also probe the running gateway").action(async (opts) => {
|
|
2766
3051
|
await new require_onboard.Banner().showStatus();
|
|
3052
|
+
if (opts.all || opts.deep) {
|
|
3053
|
+
const fs$7 = await import("fs-extra");
|
|
3054
|
+
const { getConfigPath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
|
|
3055
|
+
const t = (await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"))).getTheme(false);
|
|
3056
|
+
const configPath = getConfigPath();
|
|
3057
|
+
console.log(t.bold("\n ─── Deep status ───\n"));
|
|
3058
|
+
try {
|
|
3059
|
+
const cfg = await fs$7.readJson(configPath);
|
|
3060
|
+
console.log(t.muted(" Config: ") + (cfg ? t.success("loaded") : t.error("missing")));
|
|
3061
|
+
console.log(t.muted(" Channels: ") + JSON.stringify(cfg?.gateway?.enabledChannels || cfg?.channels || []));
|
|
3062
|
+
} catch {
|
|
3063
|
+
console.log(t.muted(" Config: ") + t.error("unreadable"));
|
|
3064
|
+
}
|
|
3065
|
+
if (opts.deep) {
|
|
3066
|
+
const http$2 = await import("http");
|
|
3067
|
+
const { resolveGatewayUrl } = await Promise.resolve().then(() => require("./health-B-asI__D.js"));
|
|
3068
|
+
const cfg = await new require_manager.ConfigManager().load();
|
|
3069
|
+
const { gatewayUrl } = resolveGatewayUrl(cfg);
|
|
3070
|
+
const u = new URL(gatewayUrl);
|
|
3071
|
+
const optsReq = {
|
|
3072
|
+
hostname: u.hostname,
|
|
3073
|
+
port: u.port || (u.protocol === "https:" ? 443 : 80),
|
|
3074
|
+
path: "/api/status",
|
|
3075
|
+
method: "GET",
|
|
3076
|
+
timeout: 3e3
|
|
3077
|
+
};
|
|
3078
|
+
if (u.protocol === "https:") {
|
|
3079
|
+
const https = await import("https");
|
|
3080
|
+
await new Promise((resolve) => {
|
|
3081
|
+
const req = https.request(`${gatewayUrl}/api/status`, { timeout: 3e3 }, (res) => {
|
|
3082
|
+
let d = "";
|
|
3083
|
+
res.on("data", (c) => d += c);
|
|
3084
|
+
res.on("end", () => {
|
|
3085
|
+
try {
|
|
3086
|
+
const j = JSON.parse(d);
|
|
3087
|
+
console.log(t.muted(" Gateway: ") + t.success("reachable") + ` (sessions: ${j.sessions ?? "-"}, uptime: ${j.uptime ?? "-"})`);
|
|
3088
|
+
} catch {
|
|
3089
|
+
console.log(t.muted(" Gateway: ") + t.error("unreachable or invalid response"));
|
|
3090
|
+
}
|
|
3091
|
+
resolve();
|
|
3092
|
+
});
|
|
3093
|
+
});
|
|
3094
|
+
req.on("error", () => {
|
|
3095
|
+
console.log(t.muted(" Gateway: ") + t.error("unreachable"));
|
|
3096
|
+
resolve();
|
|
3097
|
+
});
|
|
3098
|
+
req.on("timeout", () => {
|
|
3099
|
+
req.destroy();
|
|
3100
|
+
console.log(t.muted(" Gateway: ") + t.error("timeout"));
|
|
3101
|
+
resolve();
|
|
3102
|
+
});
|
|
3103
|
+
req.end();
|
|
3104
|
+
});
|
|
3105
|
+
} else await new Promise((resolve) => {
|
|
3106
|
+
const req = http$2.request(optsReq, (res) => {
|
|
3107
|
+
let d = "";
|
|
3108
|
+
res.on("data", (c) => d += c);
|
|
3109
|
+
res.on("end", () => {
|
|
3110
|
+
try {
|
|
3111
|
+
const j = JSON.parse(d);
|
|
3112
|
+
console.log(t.muted(" Gateway: ") + t.success("reachable") + ` (sessions: ${j.sessions ?? "-"}, uptime: ${j.uptime ?? "-"})`);
|
|
3113
|
+
} catch {
|
|
3114
|
+
console.log(t.muted(" Gateway: ") + t.error("unreachable or invalid response"));
|
|
3115
|
+
}
|
|
3116
|
+
resolve();
|
|
3117
|
+
});
|
|
3118
|
+
});
|
|
3119
|
+
req.on("error", () => {
|
|
3120
|
+
console.log(t.muted(" Gateway: ") + t.error("unreachable"));
|
|
3121
|
+
resolve();
|
|
3122
|
+
});
|
|
3123
|
+
req.on("timeout", () => {
|
|
3124
|
+
req.destroy();
|
|
3125
|
+
console.log(t.muted(" Gateway: ") + t.error("timeout"));
|
|
3126
|
+
resolve();
|
|
3127
|
+
});
|
|
3128
|
+
req.end();
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3131
|
+
console.log();
|
|
3132
|
+
}
|
|
2767
3133
|
process.exit(0);
|
|
2768
3134
|
});
|
|
3135
|
+
program.command("health").description("Quick gateway health probe (Runtime, RPC probe, channel count)").option("--json", "Output raw JSON").option("-v, --verbose", "Show state dir, config path, and env overrides").action(async (opts) => {
|
|
3136
|
+
const result = await require_health.runHealth({
|
|
3137
|
+
json: opts.json,
|
|
3138
|
+
verbose: opts.verbose
|
|
3139
|
+
});
|
|
3140
|
+
if (!result.allOk) process.exitCode = 1;
|
|
3141
|
+
process.exit(process.exitCode ?? 0);
|
|
3142
|
+
});
|
|
2769
3143
|
const themeCmd = program.command("theme").description("Switch CLI color theme");
|
|
2770
3144
|
themeCmd.command("list").description("List available themes").action(async () => {
|
|
2771
|
-
const { allThemes, getThemeName } = await Promise.resolve().then(() => require("./theme-
|
|
3145
|
+
const { allThemes, getThemeName } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
|
|
2772
3146
|
const current = getThemeName();
|
|
2773
3147
|
console.log(chalk.default.bold.hex("#06b6d4")("\n 🎨 AVAILABLE THEMES\n"));
|
|
2774
3148
|
for (const { name, label } of allThemes()) {
|
|
@@ -2780,7 +3154,7 @@ themeCmd.command("list").description("List available themes").action(async () =>
|
|
|
2780
3154
|
process.exit(0);
|
|
2781
3155
|
});
|
|
2782
3156
|
themeCmd.command("set <theme>").description("Set theme: dark | grey | white").action(async (name) => {
|
|
2783
|
-
const { setThemeName, allThemes } = await Promise.resolve().then(() => require("./theme-
|
|
3157
|
+
const { setThemeName, allThemes } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
|
|
2784
3158
|
const valid = allThemes().map((t) => t.name);
|
|
2785
3159
|
if (!valid.includes(name)) {
|
|
2786
3160
|
console.log(chalk.default.red(`\n ✖ Unknown theme: "${name}". Use: ${valid.join(" | ")}\n`));
|
|
@@ -2792,7 +3166,7 @@ themeCmd.command("set <theme>").description("Set theme: dark | grey | white").ac
|
|
|
2792
3166
|
process.exit(0);
|
|
2793
3167
|
});
|
|
2794
3168
|
themeCmd.command("preview").description("Preview all themes side-by-side").action(async () => {
|
|
2795
|
-
const { allThemes, getTheme, setThemeName, getThemeName } = await Promise.resolve().then(() => require("./theme-
|
|
3169
|
+
const { allThemes, getTheme, setThemeName, getThemeName } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
|
|
2796
3170
|
const current = getThemeName();
|
|
2797
3171
|
console.log(chalk.default.bold("\n 🎨 THEME PREVIEW\n"));
|
|
2798
3172
|
for (const { name, label } of allThemes()) {
|
|
@@ -2809,45 +3183,49 @@ themeCmd.command("preview").description("Preview all themes side-by-side").actio
|
|
|
2809
3183
|
});
|
|
2810
3184
|
const secretsCmd = program.command("secrets").description("External secrets management");
|
|
2811
3185
|
secretsCmd.command("audit").description("Audit all required secrets").option("--required-by <ids>", "Filter by skill/provider IDs (comma-separated)").action(async (opts) => {
|
|
2812
|
-
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-
|
|
3186
|
+
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
|
|
2813
3187
|
const filter = opts.requiredBy?.split(",");
|
|
2814
3188
|
await new SecretsManager().audit(filter);
|
|
2815
3189
|
process.exit(0);
|
|
2816
3190
|
});
|
|
2817
3191
|
secretsCmd.command("set <KEY=value>").description("Set a secret in .env file").action(async (kv) => {
|
|
2818
|
-
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-
|
|
3192
|
+
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
|
|
2819
3193
|
await new SecretsManager().set(kv);
|
|
2820
3194
|
process.exit(0);
|
|
2821
3195
|
});
|
|
2822
3196
|
secretsCmd.command("apply").description("Write secrets from .env to shell config (~/.bashrc, ~/.zshrc)").action(async () => {
|
|
2823
|
-
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-
|
|
3197
|
+
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
|
|
2824
3198
|
await new SecretsManager().apply();
|
|
2825
3199
|
process.exit(0);
|
|
2826
3200
|
});
|
|
2827
3201
|
secretsCmd.command("reload").description("Reload secrets into running gateway").action(async () => {
|
|
2828
|
-
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-
|
|
3202
|
+
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
|
|
2829
3203
|
await new SecretsManager().reload();
|
|
2830
3204
|
process.exit(0);
|
|
2831
3205
|
});
|
|
2832
3206
|
secretsCmd.command("remove <key>").description("Remove a secret from .env").action(async (key) => {
|
|
2833
|
-
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-
|
|
3207
|
+
const { SecretsManager } = await Promise.resolve().then(() => require("./manager-FCgF1plu.js"));
|
|
2834
3208
|
await new SecretsManager().remove(key);
|
|
2835
3209
|
process.exit(0);
|
|
2836
3210
|
});
|
|
2837
3211
|
secretsCmd.command("credentials").description("List provider credential files (credentials/*.json)").action(async () => {
|
|
2838
|
-
const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-
|
|
3212
|
+
const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-C6ir0Dae.js"));
|
|
2839
3213
|
await new CredentialsStore().showList();
|
|
2840
3214
|
process.exit(0);
|
|
2841
3215
|
});
|
|
2842
3216
|
const securityCmd = program.command("security").description("Security tools");
|
|
2843
|
-
securityCmd.command("audit").description("Security audit — file permissions, DM policies, embedded secrets").option("--deep", "Full deep scan including token entropy and installed skill risks").action(async (opts) => {
|
|
2844
|
-
const { runSecurityAudit } = await Promise.resolve().then(() => require("./audit-
|
|
2845
|
-
await runSecurityAudit(
|
|
3217
|
+
securityCmd.command("audit").description("Security audit — file permissions, DM policies, embedded secrets").option("--deep", "Full deep scan including token entropy and installed skill risks").option("--fix", "Auto-fix safe findings (file permissions etc.)").option("--json", "Machine-readable JSON output").action(async (opts) => {
|
|
3218
|
+
const { runSecurityAudit } = await Promise.resolve().then(() => require("./audit-BaIiyWFu.js"));
|
|
3219
|
+
await runSecurityAudit({
|
|
3220
|
+
deep: opts.deep,
|
|
3221
|
+
fix: opts.fix,
|
|
3222
|
+
json: opts.json
|
|
3223
|
+
});
|
|
2846
3224
|
process.exit(0);
|
|
2847
3225
|
});
|
|
2848
3226
|
const agentRunCmd = program.command("agent").description("Run agent with thinking control");
|
|
2849
3227
|
agentRunCmd.requiredOption("-m, --message <text>", "Message to send to the agent").option("--thinking <level>", "Thinking level: high|medium|low|none", "none").option("--model <model>", "Override model").option("--session <id>", "Session/thread ID").option("--multi-step", "Decompose into steps and run each (sequential)").option("--parallel", "Run sub-agents in parallel for independent subtasks").option("--verbose", "Show thinking blocks and request details").option("--workspace <dir>", "Override workspace directory").action(async (opts) => {
|
|
2850
|
-
const { runAgent } = await Promise.resolve().then(() => require("./src-
|
|
3228
|
+
const { runAgent } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
2851
3229
|
await runAgent({
|
|
2852
3230
|
message: opts.message,
|
|
2853
3231
|
thinking: opts.thinking,
|
|
@@ -2863,7 +3241,7 @@ agentRunCmd.requiredOption("-m, --message <text>", "Message to send to the agent
|
|
|
2863
3241
|
});
|
|
2864
3242
|
const threadsCmd = program.command("threads").description("ACP thread-bound agent sessions");
|
|
2865
3243
|
threadsCmd.command("list").description("List agent threads").option("--channel <id>", "Filter by channel").option("--active", "Show only active threads").action(async (opts) => {
|
|
2866
|
-
const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-
|
|
3244
|
+
const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
2867
3245
|
const mgr = new ACPThreadManager();
|
|
2868
3246
|
const threads = await mgr.list({
|
|
2869
3247
|
channelId: opts.channel,
|
|
@@ -2873,33 +3251,33 @@ threadsCmd.command("list").description("List agent threads").option("--channel <
|
|
|
2873
3251
|
process.exit(0);
|
|
2874
3252
|
});
|
|
2875
3253
|
threadsCmd.command("terminate <id>").description("Terminate a thread").action(async (id) => {
|
|
2876
|
-
const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-
|
|
3254
|
+
const { ACPThreadManager } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
2877
3255
|
await new ACPThreadManager().terminate(id);
|
|
2878
3256
|
console.log(require("chalk").green(`\n ✔ Thread terminated: ${id}\n`));
|
|
2879
3257
|
process.exit(0);
|
|
2880
3258
|
});
|
|
2881
3259
|
const canvasCmd = program.command("canvas").description("Live AI-driven UI canvas");
|
|
2882
3260
|
canvasCmd.command("show").description("Show current canvas components").action(async () => {
|
|
2883
|
-
const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-
|
|
3261
|
+
const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
|
|
2884
3262
|
await new CanvasRenderer().show();
|
|
2885
3263
|
process.exit(0);
|
|
2886
3264
|
});
|
|
2887
3265
|
canvasCmd.command("add <type> <title>").description("Add a canvas component (type: chart|table|form|markdown|image|custom)").action(async (type, title) => {
|
|
2888
|
-
const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-
|
|
3266
|
+
const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
|
|
2889
3267
|
await new CanvasRenderer().addComponent(type, title);
|
|
2890
3268
|
process.exit(0);
|
|
2891
3269
|
});
|
|
2892
3270
|
canvasCmd.command("clear").description("Clear all canvas components").action(async () => {
|
|
2893
|
-
const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-
|
|
3271
|
+
const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
|
|
2894
3272
|
await new CanvasRenderer().clear();
|
|
2895
3273
|
process.exit(0);
|
|
2896
3274
|
});
|
|
2897
3275
|
canvasCmd.command("export").description("Export canvas as HTML file").action(async () => {
|
|
2898
|
-
const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-
|
|
2899
|
-
const fs$
|
|
3276
|
+
const { CanvasRenderer } = await Promise.resolve().then(() => require("./renderer-BVQrd0_g.js"));
|
|
3277
|
+
const fs$7 = require("fs-extra");
|
|
2900
3278
|
const html = await new CanvasRenderer().exportHtml();
|
|
2901
3279
|
const outFile = require("path").join(require("os").homedir(), ".hyperclaw", "canvas", "export.html");
|
|
2902
|
-
await fs$
|
|
3280
|
+
await fs$7.writeFile(outFile, html);
|
|
2903
3281
|
console.log(require("chalk").green(`\n ✔ Canvas exported to ${outFile}\n`));
|
|
2904
3282
|
process.exit(0);
|
|
2905
3283
|
});
|
|
@@ -2916,137 +3294,137 @@ deliveryCmd.command("retry <id>").description("Retry a dead-lettered delivery it
|
|
|
2916
3294
|
});
|
|
2917
3295
|
const mcpCmd = program.command("mcp").description("MCP (Model Context Protocol) server management");
|
|
2918
3296
|
mcpCmd.command("list").action(async () => {
|
|
2919
|
-
const { mcpList } = await Promise.resolve().then(() => require("./mcp-
|
|
3297
|
+
const { mcpList } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
|
|
2920
3298
|
await mcpList();
|
|
2921
3299
|
process.exit(0);
|
|
2922
3300
|
});
|
|
2923
3301
|
mcpCmd.command("add").action(async () => {
|
|
2924
|
-
const { mcpAdd } = await Promise.resolve().then(() => require("./mcp-
|
|
3302
|
+
const { mcpAdd } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
|
|
2925
3303
|
await mcpAdd();
|
|
2926
3304
|
process.exit(0);
|
|
2927
3305
|
});
|
|
2928
3306
|
mcpCmd.command("remove <id>").action(async (id) => {
|
|
2929
|
-
const { mcpRemove } = await Promise.resolve().then(() => require("./mcp-
|
|
3307
|
+
const { mcpRemove } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
|
|
2930
3308
|
await mcpRemove(id);
|
|
2931
3309
|
process.exit(0);
|
|
2932
3310
|
});
|
|
2933
3311
|
mcpCmd.command("probe [id]").action(async (id) => {
|
|
2934
|
-
const { mcpProbe } = await Promise.resolve().then(() => require("./mcp-
|
|
3312
|
+
const { mcpProbe } = await Promise.resolve().then(() => require("./mcp-CfoSU4Uz.js"));
|
|
2935
3313
|
await mcpProbe(id);
|
|
2936
3314
|
process.exit(0);
|
|
2937
3315
|
});
|
|
2938
3316
|
const nodeCmd = program.command("node").description("HyperClaw node management (local, remote, android)");
|
|
2939
3317
|
nodeCmd.command("list").action(async () => {
|
|
2940
|
-
const { nodeList } = await Promise.resolve().then(() => require("./node-
|
|
3318
|
+
const { nodeList } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
|
|
2941
3319
|
await nodeList();
|
|
2942
3320
|
process.exit(0);
|
|
2943
3321
|
});
|
|
2944
3322
|
nodeCmd.command("add").action(async () => {
|
|
2945
|
-
const { nodeAdd } = await Promise.resolve().then(() => require("./node-
|
|
3323
|
+
const { nodeAdd } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
|
|
2946
3324
|
await nodeAdd();
|
|
2947
3325
|
process.exit(0);
|
|
2948
3326
|
});
|
|
2949
3327
|
nodeCmd.command("probe [id]").action(async (id) => {
|
|
2950
|
-
const { nodeProbe } = await Promise.resolve().then(() => require("./node-
|
|
3328
|
+
const { nodeProbe } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
|
|
2951
3329
|
await nodeProbe(id);
|
|
2952
3330
|
process.exit(0);
|
|
2953
3331
|
});
|
|
2954
3332
|
nodeCmd.command("remove <id>").action(async (id) => {
|
|
2955
|
-
const { nodeRemove } = await Promise.resolve().then(() => require("./node-
|
|
3333
|
+
const { nodeRemove } = await Promise.resolve().then(() => require("./node-Dw2Gi-cP.js"));
|
|
2956
3334
|
await nodeRemove(id);
|
|
2957
3335
|
process.exit(0);
|
|
2958
3336
|
});
|
|
2959
3337
|
const arCmd = program.command("auto-reply").description("Auto-reply rule engine");
|
|
2960
3338
|
arCmd.command("list").action(async () => {
|
|
2961
|
-
const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-
|
|
3339
|
+
const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BE4GV6cV.js"));
|
|
2962
3340
|
const e = new AutoReplyEngine();
|
|
2963
3341
|
await e.load();
|
|
2964
3342
|
e.showList();
|
|
2965
3343
|
process.exit(0);
|
|
2966
3344
|
});
|
|
2967
3345
|
arCmd.command("toggle <id>").action(async (id) => {
|
|
2968
|
-
const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-
|
|
3346
|
+
const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BE4GV6cV.js"));
|
|
2969
3347
|
const e = new AutoReplyEngine();
|
|
2970
3348
|
await e.toggle(id);
|
|
2971
3349
|
process.exit(0);
|
|
2972
3350
|
});
|
|
2973
3351
|
arCmd.command("remove <id>").action(async (id) => {
|
|
2974
|
-
const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-
|
|
3352
|
+
const { AutoReplyEngine } = await Promise.resolve().then(() => require("./rules-BE4GV6cV.js"));
|
|
2975
3353
|
const e = new AutoReplyEngine();
|
|
2976
3354
|
await e.remove(id);
|
|
2977
3355
|
process.exit(0);
|
|
2978
3356
|
});
|
|
2979
3357
|
const gmailCmd = program.command("gmail").description("Gmail Pub/Sub real-time notifications");
|
|
2980
3358
|
gmailCmd.command("watch-setup").description("Register Gmail watch for push notifications. Requires: hyperclaw auth oauth google-gmail").requiredOption("-t, --topic <name>", "Pub/Sub topic (e.g. projects/myproject/topics/gmail-push)").option("-l, --labels <ids>", "Label IDs to watch (comma-separated)", "INBOX").action(async (opts) => {
|
|
2981
|
-
const chalk$
|
|
3359
|
+
const chalk$11 = require("chalk");
|
|
2982
3360
|
try {
|
|
2983
|
-
const { setupGmailWatch } = await Promise.resolve().then(() => require("./gmail-watch-setup-
|
|
3361
|
+
const { setupGmailWatch } = await Promise.resolve().then(() => require("./gmail-watch-setup-Du7DVV7S.js"));
|
|
2984
3362
|
const labelIds = opts.labels.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2985
3363
|
const result = await setupGmailWatch({
|
|
2986
3364
|
topicName: opts.topic,
|
|
2987
3365
|
labelIds
|
|
2988
3366
|
});
|
|
2989
|
-
console.log(chalk$
|
|
2990
|
-
console.log(chalk$
|
|
2991
|
-
console.log(chalk$
|
|
2992
|
-
console.log(chalk$
|
|
2993
|
-
console.log(chalk$
|
|
3367
|
+
console.log(chalk$11.hex("#06b6d4")("\n ✔ Gmail watch registered"));
|
|
3368
|
+
console.log(chalk$11.gray(` historyId: ${result.historyId}`));
|
|
3369
|
+
console.log(chalk$11.gray(` expiration: ${new Date(parseInt(result.expiration, 10)).toISOString()}`));
|
|
3370
|
+
console.log(chalk$11.gray("\n Push endpoint: https://<your-server>/webhook/gmail-pubsub"));
|
|
3371
|
+
console.log(chalk$11.gray(" Ensure email channel is enabled and gateway is publicly accessible.\n"));
|
|
2994
3372
|
} catch (e) {
|
|
2995
|
-
console.error(chalk$
|
|
3373
|
+
console.error(chalk$11.red("\n ✖ " + e.message + "\n"));
|
|
2996
3374
|
process.exit(1);
|
|
2997
3375
|
}
|
|
2998
3376
|
process.exit(0);
|
|
2999
3377
|
});
|
|
3000
3378
|
const cronCmd = program.command("cron").description("Scheduled tasks (cron → agent prompt)");
|
|
3001
3379
|
cronCmd.command("list").action(async () => {
|
|
3002
|
-
const chalk$
|
|
3003
|
-
const { loadCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-
|
|
3380
|
+
const chalk$11 = require("chalk");
|
|
3381
|
+
const { loadCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bli7Kzd2.js"));
|
|
3004
3382
|
const tasks = await loadCronTasks();
|
|
3005
|
-
console.log(chalk$
|
|
3383
|
+
console.log(chalk$11.bold.cyan("\n ⏰ CRON TASKS\n"));
|
|
3006
3384
|
if (tasks.length === 0) {
|
|
3007
|
-
console.log(chalk$
|
|
3385
|
+
console.log(chalk$11.gray(" No tasks. Add: hyperclaw cron add \"0 9 * * 1-5\" \"Check calendar\"\n"));
|
|
3008
3386
|
process.exit(0);
|
|
3009
3387
|
return;
|
|
3010
3388
|
}
|
|
3011
3389
|
for (const t of tasks) {
|
|
3012
|
-
const dot = t.enabled ? chalk$
|
|
3013
|
-
console.log(` ${dot} ${chalk$
|
|
3014
|
-
console.log(` ${chalk$
|
|
3015
|
-
console.log(` ${chalk$
|
|
3016
|
-
if (t.lastRunAt) console.log(` ${chalk$
|
|
3390
|
+
const dot = t.enabled ? chalk$11.green("●") : chalk$11.gray("○");
|
|
3391
|
+
console.log(` ${dot} ${chalk$11.white(t.name || t.id)}`);
|
|
3392
|
+
console.log(` ${chalk$11.gray("Schedule:")} ${t.schedule}`);
|
|
3393
|
+
console.log(` ${chalk$11.gray("Prompt:")} ${t.prompt.slice(0, 60)}${t.prompt.length > 60 ? "..." : ""}`);
|
|
3394
|
+
if (t.lastRunAt) console.log(` ${chalk$11.gray("Last run:")} ${t.lastRunAt}`);
|
|
3017
3395
|
console.log();
|
|
3018
3396
|
}
|
|
3019
3397
|
process.exit(0);
|
|
3020
3398
|
});
|
|
3021
3399
|
cronCmd.command("add").arguments("<schedule> <prompt>").option("-n, --name <name>", "Task name").action(async (schedule, prompt, opts) => {
|
|
3022
|
-
const chalk$
|
|
3023
|
-
const { loadCronTasks, addCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-
|
|
3400
|
+
const chalk$11 = require("chalk");
|
|
3401
|
+
const { loadCronTasks, addCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bli7Kzd2.js"));
|
|
3024
3402
|
await loadCronTasks();
|
|
3025
3403
|
addCronTask(schedule, prompt, opts.name);
|
|
3026
3404
|
await saveCronTasks();
|
|
3027
|
-
console.log(chalk$
|
|
3028
|
-
console.log(chalk$
|
|
3405
|
+
console.log(chalk$11.green(`\n ✔ Cron task added: ${schedule} → "${prompt.slice(0, 40)}..."\n`));
|
|
3406
|
+
console.log(chalk$11.gray(" Restart gateway to apply.\n"));
|
|
3029
3407
|
process.exit(0);
|
|
3030
3408
|
});
|
|
3031
3409
|
cronCmd.command("remove <id>").action(async (id) => {
|
|
3032
|
-
const chalk$
|
|
3033
|
-
const { loadCronTasks, removeCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-
|
|
3410
|
+
const chalk$11 = require("chalk");
|
|
3411
|
+
const { loadCronTasks, removeCronTask, saveCronTasks } = await Promise.resolve().then(() => require("./cron-tasks-Bli7Kzd2.js"));
|
|
3034
3412
|
await loadCronTasks();
|
|
3035
3413
|
if (removeCronTask(id)) {
|
|
3036
3414
|
await saveCronTasks();
|
|
3037
|
-
console.log(chalk$
|
|
3038
|
-
} else console.log(chalk$
|
|
3415
|
+
console.log(chalk$11.green(`\n ✔ Task removed\n`));
|
|
3416
|
+
} else console.log(chalk$11.red(`\n ✖ Task not found: ${id}\n`));
|
|
3039
3417
|
process.exit(0);
|
|
3040
3418
|
});
|
|
3041
3419
|
program.command("nodes").description("List connected mobile nodes (iOS/Android Connect tab)").action(async () => {
|
|
3042
|
-
const chalk$
|
|
3420
|
+
const chalk$11 = require("chalk");
|
|
3043
3421
|
const http$2 = await import("http");
|
|
3044
|
-
const fs$
|
|
3045
|
-
const path$
|
|
3046
|
-
const os$
|
|
3422
|
+
const fs$7 = await import("fs-extra");
|
|
3423
|
+
const path$7 = await import("path");
|
|
3424
|
+
const os$8 = await import("os");
|
|
3047
3425
|
let port = 18789;
|
|
3048
3426
|
try {
|
|
3049
|
-
const cfg = await fs$
|
|
3427
|
+
const cfg = await fs$7.readJson(path$7.join(os$8.homedir(), ".hyperclaw", "hyperclaw.json"));
|
|
3050
3428
|
port = cfg?.gateway?.port ?? 18789;
|
|
3051
3429
|
} catch {}
|
|
3052
3430
|
return new Promise((resolve, reject) => {
|
|
@@ -3057,26 +3435,26 @@ program.command("nodes").description("List connected mobile nodes (iOS/Android C
|
|
|
3057
3435
|
try {
|
|
3058
3436
|
const j = JSON.parse(data);
|
|
3059
3437
|
const nodes = j.nodes || [];
|
|
3060
|
-
console.log(chalk$
|
|
3438
|
+
console.log(chalk$11.bold.cyan("\n 📱 CONNECTED NODES\n"));
|
|
3061
3439
|
if (nodes.length === 0) {
|
|
3062
|
-
console.log(chalk$
|
|
3063
|
-
console.log(chalk$
|
|
3440
|
+
console.log(chalk$11.gray(" No mobile nodes. Open iOS/Android app → Connect tab → pair with gateway."));
|
|
3441
|
+
console.log(chalk$11.gray(` Gateway: ws://localhost:${port}\n`));
|
|
3064
3442
|
} else {
|
|
3065
3443
|
for (const n of nodes) {
|
|
3066
|
-
console.log(` ${chalk$
|
|
3067
|
-
console.log(` ${chalk$
|
|
3068
|
-
console.log(` ${chalk$
|
|
3444
|
+
console.log(` ${chalk$11.green("●")} ${n.nodeId} ${chalk$11.gray(`(${n.platform || "?"})`)}`);
|
|
3445
|
+
console.log(` ${chalk$11.gray("Device:")} ${n.deviceName || "—"}`);
|
|
3446
|
+
console.log(` ${chalk$11.gray("Capabilities:")} ${Object.entries(n.capabilities || {}).filter(([, v]) => v).map(([k]) => k).join(", ") || "—"}`);
|
|
3069
3447
|
}
|
|
3070
3448
|
console.log();
|
|
3071
3449
|
}
|
|
3072
3450
|
} catch {
|
|
3073
|
-
console.log(chalk$
|
|
3451
|
+
console.log(chalk$11.red(" Could not reach gateway. Start with: hyperclaw daemon start\n"));
|
|
3074
3452
|
}
|
|
3075
3453
|
resolve();
|
|
3076
3454
|
});
|
|
3077
3455
|
});
|
|
3078
3456
|
req.on("error", () => {
|
|
3079
|
-
console.log(chalk$
|
|
3457
|
+
console.log(chalk$11.red(" Gateway offline. Start with: hyperclaw daemon start\n"));
|
|
3080
3458
|
resolve();
|
|
3081
3459
|
});
|
|
3082
3460
|
req.setTimeout(3e3, () => {
|
|
@@ -3087,20 +3465,20 @@ program.command("nodes").description("List connected mobile nodes (iOS/Android C
|
|
|
3087
3465
|
});
|
|
3088
3466
|
const whCmd = program.command("webhooks").description("Webhook endpoint management");
|
|
3089
3467
|
whCmd.command("list").action(async () => {
|
|
3090
|
-
const { WebhookManager } = await Promise.resolve().then(() => require("./manager-
|
|
3468
|
+
const { WebhookManager } = await Promise.resolve().then(() => require("./manager-BpDfbDjg.js"));
|
|
3091
3469
|
const m = new WebhookManager();
|
|
3092
3470
|
await m.load();
|
|
3093
3471
|
m.showList();
|
|
3094
3472
|
process.exit(0);
|
|
3095
3473
|
});
|
|
3096
3474
|
whCmd.command("remove <id>").action(async (id) => {
|
|
3097
|
-
const { WebhookManager } = await Promise.resolve().then(() => require("./manager-
|
|
3475
|
+
const { WebhookManager } = await Promise.resolve().then(() => require("./manager-BpDfbDjg.js"));
|
|
3098
3476
|
const m = new WebhookManager();
|
|
3099
3477
|
await m.remove(id);
|
|
3100
3478
|
process.exit(0);
|
|
3101
3479
|
});
|
|
3102
3480
|
whCmd.command("toggle <id>").action(async (id) => {
|
|
3103
|
-
const { WebhookManager } = await Promise.resolve().then(() => require("./manager-
|
|
3481
|
+
const { WebhookManager } = await Promise.resolve().then(() => require("./manager-BpDfbDjg.js"));
|
|
3104
3482
|
const m = new WebhookManager();
|
|
3105
3483
|
await m.toggle(id);
|
|
3106
3484
|
process.exit(0);
|
|
@@ -3109,7 +3487,7 @@ const logsCmd = program.command("logs").description("View gateway logs");
|
|
|
3109
3487
|
logsCmd.option("-n, --lines <n>", "Number of lines to show", "50");
|
|
3110
3488
|
logsCmd.option("-f, --follow", "Stream logs in real time");
|
|
3111
3489
|
logsCmd.action(async (opts) => {
|
|
3112
|
-
const { tailLog, streamLog } = await Promise.resolve().then(() => require("./logger-
|
|
3490
|
+
const { tailLog, streamLog } = await Promise.resolve().then(() => require("./logger-ybOp7VOC.js"));
|
|
3113
3491
|
if (opts.follow) await streamLog();
|
|
3114
3492
|
else {
|
|
3115
3493
|
await tailLog(parseInt(opts.lines));
|
|
@@ -3117,7 +3495,7 @@ logsCmd.action(async (opts) => {
|
|
|
3117
3495
|
}
|
|
3118
3496
|
});
|
|
3119
3497
|
program.command("gateway:serve").description("Start the gateway server in the foreground (used by daemon)").action(async () => {
|
|
3120
|
-
const { startGateway } = await Promise.resolve().then(() => require("./server-
|
|
3498
|
+
const { startGateway } = await Promise.resolve().then(() => require("./server-D4wVHiX9.js"));
|
|
3121
3499
|
await startGateway();
|
|
3122
3500
|
process.on("SIGINT", () => process.exit(0));
|
|
3123
3501
|
process.on("SIGTERM", () => process.exit(0));
|
|
@@ -3125,15 +3503,15 @@ program.command("gateway:serve").description("Start the gateway server in the fo
|
|
|
3125
3503
|
});
|
|
3126
3504
|
const gatewayCfgCmd = gatewayCmd.command("config").description("Configure gateway settings");
|
|
3127
3505
|
gatewayCfgCmd.option("--set-token <token>", "Set gateway auth token").option("--regenerate-token", "Generate a new random token").option("--set-port <port>", "Set gateway port").option("--set-bind <addr>", "Set gateway bind address").action(async (opts) => {
|
|
3128
|
-
const chalk$
|
|
3129
|
-
const fs$
|
|
3130
|
-
const path$
|
|
3131
|
-
const os$
|
|
3132
|
-
const crypto = require("crypto");
|
|
3133
|
-
const cfgFile = path$
|
|
3506
|
+
const chalk$11 = require("chalk");
|
|
3507
|
+
const fs$7 = require("fs-extra");
|
|
3508
|
+
const path$7 = require("path");
|
|
3509
|
+
const os$8 = require("os");
|
|
3510
|
+
const crypto$2 = require("crypto");
|
|
3511
|
+
const cfgFile = path$7.join(os$8.homedir(), ".hyperclaw", "hyperclaw.json");
|
|
3134
3512
|
let cfg = {};
|
|
3135
3513
|
try {
|
|
3136
|
-
cfg = await fs$
|
|
3514
|
+
cfg = await fs$7.readJson(cfgFile);
|
|
3137
3515
|
} catch {}
|
|
3138
3516
|
if (!cfg.gateway) cfg.gateway = {
|
|
3139
3517
|
port: 18789,
|
|
@@ -3144,53 +3522,53 @@ gatewayCfgCmd.option("--set-token <token>", "Set gateway auth token").option("--
|
|
|
3144
3522
|
hooks: true
|
|
3145
3523
|
};
|
|
3146
3524
|
if (opts.regenerateToken) {
|
|
3147
|
-
cfg.gateway.authToken = crypto.randomBytes(32).toString("hex");
|
|
3148
|
-
console.log(chalk$
|
|
3149
|
-
console.log(chalk$
|
|
3525
|
+
cfg.gateway.authToken = crypto$2.randomBytes(32).toString("hex");
|
|
3526
|
+
console.log(chalk$11.hex("#06b6d4")("\n ✔ New gateway token generated"));
|
|
3527
|
+
console.log(chalk$11.gray(` Token: ${cfg.gateway.authToken}`));
|
|
3150
3528
|
}
|
|
3151
3529
|
if (opts.setToken) {
|
|
3152
3530
|
cfg.gateway.authToken = opts.setToken;
|
|
3153
|
-
console.log(chalk$
|
|
3531
|
+
console.log(chalk$11.hex("#06b6d4")(`\n ✔ Gateway token set`));
|
|
3154
3532
|
}
|
|
3155
3533
|
if (opts.setPort) {
|
|
3156
3534
|
cfg.gateway.port = parseInt(opts.setPort);
|
|
3157
|
-
console.log(chalk$
|
|
3535
|
+
console.log(chalk$11.hex("#06b6d4")(`\n ✔ Port set to ${cfg.gateway.port}`));
|
|
3158
3536
|
}
|
|
3159
3537
|
if (opts.setBind) {
|
|
3160
3538
|
cfg.gateway.bind = opts.setBind;
|
|
3161
|
-
console.log(chalk$
|
|
3539
|
+
console.log(chalk$11.hex("#06b6d4")(`\n ✔ Bind set to ${cfg.gateway.bind}`));
|
|
3162
3540
|
}
|
|
3163
|
-
await fs$
|
|
3164
|
-
await fs$
|
|
3165
|
-
await fs$
|
|
3166
|
-
console.log(chalk$
|
|
3541
|
+
await fs$7.ensureDir(path$7.dirname(cfgFile));
|
|
3542
|
+
await fs$7.writeJson(cfgFile, cfg, { spaces: 2 });
|
|
3543
|
+
await fs$7.chmod(cfgFile, 384);
|
|
3544
|
+
console.log(chalk$11.gray(` Saved to ${cfgFile}\n`));
|
|
3167
3545
|
process.exit(0);
|
|
3168
3546
|
});
|
|
3169
3547
|
const authCmd = program.command("auth").description("OAuth and provider credentials");
|
|
3170
3548
|
authCmd.command("add <service_id>").description("Add API key for a service (any provider we do not ship). Stored in credentials/ and .env.").option("--key <api_key>", "API key (prompts if omitted)").option("--base-url <url>", "Base URL (optional, e.g. https://api.example.com)").option("--env-var <name>", "Env var name (default: <SERVICE_ID>_API_KEY)").action(async (serviceId, opts) => {
|
|
3171
|
-
const chalk$
|
|
3172
|
-
const inquirer$
|
|
3173
|
-
const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-
|
|
3174
|
-
const { getHyperClawDir
|
|
3175
|
-
const { getApiKeyGuide, GENERIC_API_KEY_STEPS } = await Promise.resolve().then(() => require("./api-keys-guide-
|
|
3176
|
-
const fs$
|
|
3177
|
-
const path$
|
|
3549
|
+
const chalk$11 = require("chalk");
|
|
3550
|
+
const inquirer$2 = require("inquirer");
|
|
3551
|
+
const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-C6ir0Dae.js"));
|
|
3552
|
+
const { getHyperClawDir, getEnvFilePath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
|
|
3553
|
+
const { getApiKeyGuide, GENERIC_API_KEY_STEPS } = await Promise.resolve().then(() => require("./api-keys-guide-BCcOl0Q7.js"));
|
|
3554
|
+
const fs$7 = await import("fs-extra");
|
|
3555
|
+
const path$7 = await import("path");
|
|
3178
3556
|
const guide = getApiKeyGuide(serviceId);
|
|
3179
3557
|
const steps = guide?.setupSteps ?? GENERIC_API_KEY_STEPS;
|
|
3180
|
-
console.log(chalk$
|
|
3181
|
-
console.log(chalk$
|
|
3182
|
-
for (const step of steps) if (step.startsWith(" 🔗")) console.log(chalk$
|
|
3183
|
-
else if (step.startsWith(" 💡")) console.log(chalk$
|
|
3184
|
-
else console.log(chalk$
|
|
3558
|
+
console.log(chalk$11.bold.hex("#06b6d4")(`\n 🔑 Add API key: ${guide?.name ?? serviceId}\n`));
|
|
3559
|
+
console.log(chalk$11.bold(" Steps:\n"));
|
|
3560
|
+
for (const step of steps) if (step.startsWith(" 🔗")) console.log(chalk$11.hex("#06b6d4")(step));
|
|
3561
|
+
else if (step.startsWith(" 💡")) console.log(chalk$11.gray(step));
|
|
3562
|
+
else console.log(chalk$11.gray(` ${step}`));
|
|
3185
3563
|
console.log();
|
|
3186
3564
|
const safeId = serviceId.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
|
|
3187
3565
|
if (!safeId) {
|
|
3188
|
-
console.log(chalk$
|
|
3566
|
+
console.log(chalk$11.red("\n ✖ Invalid service ID\n"));
|
|
3189
3567
|
process.exit(1);
|
|
3190
3568
|
}
|
|
3191
3569
|
let apiKey = opts.key || process.env[`${safeId.toUpperCase().replace(/-/g, "_")}_API_KEY`];
|
|
3192
3570
|
if (!apiKey) {
|
|
3193
|
-
const { key } = await inquirer$
|
|
3571
|
+
const { key } = await inquirer$2.prompt([{
|
|
3194
3572
|
type: "password",
|
|
3195
3573
|
name: "key",
|
|
3196
3574
|
message: `API key for ${serviceId}:`,
|
|
@@ -3199,7 +3577,7 @@ authCmd.command("add <service_id>").description("Add API key for a service (any
|
|
|
3199
3577
|
}]);
|
|
3200
3578
|
apiKey = key.trim();
|
|
3201
3579
|
}
|
|
3202
|
-
const creds = new CredentialsStore(getHyperClawDir
|
|
3580
|
+
const creds = new CredentialsStore(getHyperClawDir());
|
|
3203
3581
|
await creds.set(safeId, {
|
|
3204
3582
|
apiKey,
|
|
3205
3583
|
...opts.baseUrl ? { baseUrl: opts.baseUrl } : {},
|
|
@@ -3207,52 +3585,52 @@ authCmd.command("add <service_id>").description("Add API key for a service (any
|
|
|
3207
3585
|
});
|
|
3208
3586
|
const envVar = opts.envVar || `${safeId.toUpperCase().replace(/-/g, "_")}_API_KEY`;
|
|
3209
3587
|
const envPath = getEnvFilePath();
|
|
3210
|
-
await fs$
|
|
3588
|
+
await fs$7.ensureDir(path$7.dirname(envPath));
|
|
3211
3589
|
let envContent = "";
|
|
3212
|
-
if (await fs$
|
|
3590
|
+
if (await fs$7.pathExists(envPath)) envContent = await fs$7.readFile(envPath, "utf8");
|
|
3213
3591
|
const envLine = `${envVar}=${apiKey}`;
|
|
3214
3592
|
const re = new RegExp(`^${envVar}=.*$`, "m");
|
|
3215
3593
|
if (re.test(envContent)) envContent = envContent.replace(re, envLine);
|
|
3216
3594
|
else envContent = envContent.trimEnd() + (envContent ? "\n" : "") + envLine + "\n";
|
|
3217
|
-
await fs$
|
|
3218
|
-
console.log(chalk$
|
|
3219
|
-
console.log(chalk$
|
|
3220
|
-
console.log(chalk$
|
|
3221
|
-
console.log(chalk$
|
|
3222
|
-
console.log(chalk$
|
|
3595
|
+
await fs$7.writeFile(envPath, envContent, { mode: 384 });
|
|
3596
|
+
console.log(chalk$11.hex("#06b6d4")(`\n ✔ Added: ${safeId}`));
|
|
3597
|
+
console.log(chalk$11.gray(` Credentials: ~/.hyperclaw/credentials/${safeId}.json`));
|
|
3598
|
+
console.log(chalk$11.gray(` Env: ${envVar} (in .env — use in skills via process.env.${envVar.replace(/-/g, "_")})`));
|
|
3599
|
+
console.log(chalk$11.gray("\n Run: hyperclaw secrets apply to add to shell"));
|
|
3600
|
+
console.log(chalk$11.gray(" Run: hyperclaw secrets reload to inject into running gateway\n"));
|
|
3223
3601
|
process.exit(0);
|
|
3224
3602
|
});
|
|
3225
3603
|
authCmd.command("remove <service_id>").description("Remove API key for a service from credentials and .env").action(async (serviceId) => {
|
|
3226
|
-
const chalk$
|
|
3227
|
-
const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-
|
|
3228
|
-
const { getHyperClawDir
|
|
3229
|
-
const fs$
|
|
3604
|
+
const chalk$11 = require("chalk");
|
|
3605
|
+
const { CredentialsStore } = await Promise.resolve().then(() => require("./credentials-store-C6ir0Dae.js"));
|
|
3606
|
+
const { getHyperClawDir, getEnvFilePath } = await Promise.resolve().then(() => require("./paths-D-QecARF.js"));
|
|
3607
|
+
const fs$7 = await import("fs-extra");
|
|
3230
3608
|
const safeId = serviceId.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
|
|
3231
|
-
const creds = new CredentialsStore(getHyperClawDir
|
|
3609
|
+
const creds = new CredentialsStore(getHyperClawDir());
|
|
3232
3610
|
await creds.remove(safeId);
|
|
3233
3611
|
const envVar = `${safeId.toUpperCase().replace(/-/g, "_")}_API_KEY`;
|
|
3234
3612
|
const envPath = getEnvFilePath();
|
|
3235
|
-
if (await fs$
|
|
3236
|
-
let c = await fs$
|
|
3613
|
+
if (await fs$7.pathExists(envPath)) {
|
|
3614
|
+
let c = await fs$7.readFile(envPath, "utf8");
|
|
3237
3615
|
c = c.replace(new RegExp(`^${envVar}=.*\n?`, "gm"), "");
|
|
3238
|
-
await fs$
|
|
3616
|
+
await fs$7.writeFile(envPath, c);
|
|
3239
3617
|
}
|
|
3240
|
-
console.log(chalk$
|
|
3618
|
+
console.log(chalk$11.hex("#06b6d4")(`\n ✔ Removed: ${safeId}\n`));
|
|
3241
3619
|
process.exit(0);
|
|
3242
3620
|
});
|
|
3243
3621
|
authCmd.command("oauth <provider>").description("Run full OAuth flow. Providers: google, google-gmail (Gmail Pub/Sub), microsoft").option("--client-id <id>", "OAuth client ID (or GOOGLE_OAUTH_CLIENT_ID)").option("--client-secret <secret>", "OAuth client secret (optional for PKCE)").action(async (provider, opts) => {
|
|
3244
|
-
const chalk$
|
|
3245
|
-
const ora$
|
|
3622
|
+
const chalk$11 = require("chalk");
|
|
3623
|
+
const ora$4 = (await import("ora")).default;
|
|
3246
3624
|
try {
|
|
3247
|
-
const { runOAuthFlow } = await Promise.resolve().then(() => require("./oauth-flow-
|
|
3248
|
-
const spinner = ora$
|
|
3625
|
+
const { runOAuthFlow } = await Promise.resolve().then(() => require("./oauth-flow-DQPvMHRH.js"));
|
|
3626
|
+
const spinner = ora$4("Starting OAuth flow...").start();
|
|
3249
3627
|
spinner.text = "Opening browser — complete the consent and return here.";
|
|
3250
3628
|
const tokens = await runOAuthFlow(provider, {
|
|
3251
3629
|
clientId: opts.clientId,
|
|
3252
3630
|
clientSecret: opts.clientSecret
|
|
3253
3631
|
});
|
|
3254
3632
|
spinner.stop();
|
|
3255
|
-
const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-
|
|
3633
|
+
const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-Uo4Nib_c.js"));
|
|
3256
3634
|
const now = Math.floor(Date.now() / 1e3);
|
|
3257
3635
|
const expires_at = tokens.expires_in ? now + tokens.expires_in : void 0;
|
|
3258
3636
|
const tokenUrl = provider === "google" || provider === "google-gmail" ? "https://oauth2.googleapis.com/token" : provider === "microsoft" ? "https://login.microsoftonline.com/common/oauth2/v2.0/token" : void 0;
|
|
@@ -3262,46 +3640,46 @@ authCmd.command("oauth <provider>").description("Run full OAuth flow. Providers:
|
|
|
3262
3640
|
expires_at,
|
|
3263
3641
|
token_url: tokenUrl
|
|
3264
3642
|
});
|
|
3265
|
-
console.log(chalk$
|
|
3266
|
-
console.log(chalk$
|
|
3643
|
+
console.log(chalk$11.hex("#06b6d4")(`\n ✔ OAuth tokens saved for: ${provider}`));
|
|
3644
|
+
console.log(chalk$11.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
|
|
3267
3645
|
} catch (e) {
|
|
3268
|
-
console.error(chalk$
|
|
3646
|
+
console.error(chalk$11.red("\n ✖ OAuth failed: " + e.message + "\n"));
|
|
3269
3647
|
process.exit(1);
|
|
3270
3648
|
}
|
|
3271
3649
|
process.exit(0);
|
|
3272
3650
|
});
|
|
3273
3651
|
authCmd.command("setup-token <provider>").description("Save setup token (Anthropic Claude Pro/Max). Run: claude setup-token, paste result here.").action(async (provider) => {
|
|
3274
|
-
const chalk$
|
|
3275
|
-
const inquirer$
|
|
3652
|
+
const chalk$11 = require("chalk");
|
|
3653
|
+
const inquirer$2 = await import("inquirer");
|
|
3276
3654
|
if (provider !== "anthropic") {
|
|
3277
|
-
console.log(chalk$
|
|
3655
|
+
console.log(chalk$11.yellow(`\n Provider "${provider}" may not support setup-token. Use "hyperclaw auth add" for API keys.\n`));
|
|
3278
3656
|
process.exit(1);
|
|
3279
3657
|
}
|
|
3280
|
-
const { token } = await inquirer$
|
|
3658
|
+
const { token } = await inquirer$2.default.prompt([{
|
|
3281
3659
|
type: "password",
|
|
3282
3660
|
name: "token",
|
|
3283
3661
|
message: "Paste setup token from `claude setup-token`:",
|
|
3284
3662
|
mask: "●"
|
|
3285
3663
|
}]);
|
|
3286
3664
|
if (!token?.trim()) {
|
|
3287
|
-
console.log(chalk$
|
|
3665
|
+
console.log(chalk$11.red("\n ✖ No token provided.\n"));
|
|
3288
3666
|
process.exit(1);
|
|
3289
3667
|
}
|
|
3290
|
-
const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-
|
|
3668
|
+
const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-Uo4Nib_c.js"));
|
|
3291
3669
|
await writeOAuthToken("anthropic-setup", {
|
|
3292
3670
|
access_token: token.trim(),
|
|
3293
3671
|
token_url: "https://api.anthropic.com"
|
|
3294
3672
|
});
|
|
3295
|
-
console.log(chalk$
|
|
3296
|
-
console.log(chalk$
|
|
3673
|
+
console.log(chalk$11.hex("#06b6d4")("\n ✔ Anthropic setup token saved to ~/.hyperclaw/oauth-anthropic-setup.json"));
|
|
3674
|
+
console.log(chalk$11.gray(" Use providerId: anthropic with authType: oauth and oauthTokenPath for Claude Pro/Max.\n"));
|
|
3297
3675
|
process.exit(0);
|
|
3298
3676
|
});
|
|
3299
3677
|
authCmd.command("oauth-set <provider>").description("Save OAuth tokens manually (access_token, refresh_token, etc.) to ~/.hyperclaw/oauth-<provider>.json").option("--token <access_token>", "Access token").option("--refresh <refresh_token>", "Refresh token (optional)").option("--expires-in <seconds>", "Token lifetime in seconds (optional)").option("--token-url <url>", "Refresh endpoint URL (optional)").action(async (provider, opts) => {
|
|
3300
|
-
const chalk$
|
|
3301
|
-
const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-
|
|
3678
|
+
const chalk$11 = require("chalk");
|
|
3679
|
+
const { writeOAuthToken } = await Promise.resolve().then(() => require("./oauth-provider-Uo4Nib_c.js"));
|
|
3302
3680
|
const access_token = opts.token || process.env.OAUTH_ACCESS_TOKEN;
|
|
3303
3681
|
if (!access_token) {
|
|
3304
|
-
console.log(chalk$
|
|
3682
|
+
console.log(chalk$11.red("\n ✖ Provide --token <access_token> or set OAUTH_ACCESS_TOKEN\n"));
|
|
3305
3683
|
process.exit(1);
|
|
3306
3684
|
}
|
|
3307
3685
|
const expires_at = opts.expiresIn ? Math.floor(Date.now() / 1e3) + parseInt(opts.expiresIn, 10) : void 0;
|
|
@@ -3311,22 +3689,22 @@ authCmd.command("oauth-set <provider>").description("Save OAuth tokens manually
|
|
|
3311
3689
|
expires_at,
|
|
3312
3690
|
token_url: opts.tokenUrl || void 0
|
|
3313
3691
|
});
|
|
3314
|
-
console.log(chalk$
|
|
3315
|
-
console.log(chalk$
|
|
3692
|
+
console.log(chalk$11.hex("#06b6d4")(`\n ✔ OAuth tokens saved for provider: ${provider}`));
|
|
3693
|
+
console.log(chalk$11.gray(" Set in hyperclaw.json: \"provider\": { \"authType\": \"oauth\", \"providerId\": \"" + provider + "\" }\n"));
|
|
3316
3694
|
process.exit(0);
|
|
3317
3695
|
});
|
|
3318
3696
|
const workspaceCmd = program.command("workspace").description("Manage agent workspace files");
|
|
3319
3697
|
workspaceCmd.command("init [dir]").description("Initialize workspace files (SOUL.md, USER.md, TOOLS.md, HEARTBEAT.md, BOOTSTRAP.md) in a directory").action(async (dir) => {
|
|
3320
|
-
const chalk$
|
|
3321
|
-
const fs$
|
|
3322
|
-
const path$
|
|
3323
|
-
const os$
|
|
3324
|
-
const targetDir = dir || path$
|
|
3698
|
+
const chalk$11 = require("chalk");
|
|
3699
|
+
const fs$7 = require("fs-extra");
|
|
3700
|
+
const path$7 = require("path");
|
|
3701
|
+
const os$8 = require("os");
|
|
3702
|
+
const targetDir = dir || path$7.join(os$8.homedir(), ".hyperclaw");
|
|
3325
3703
|
let cfg = {};
|
|
3326
3704
|
try {
|
|
3327
|
-
cfg = await fs$
|
|
3705
|
+
cfg = await fs$7.readJson(path$7.join(os$8.homedir(), ".hyperclaw", "hyperclaw.json"));
|
|
3328
3706
|
} catch {}
|
|
3329
|
-
const { initWorkspaceFiles } = await Promise.resolve().then(() => require("./memory-
|
|
3707
|
+
const { initWorkspaceFiles } = await Promise.resolve().then(() => require("./memory-BlHL7JCO.js"));
|
|
3330
3708
|
await initWorkspaceFiles({
|
|
3331
3709
|
agentName: cfg.identity?.agentName || "Hyper",
|
|
3332
3710
|
personality: cfg.identity?.personality || "helpful and concise",
|
|
@@ -3334,17 +3712,17 @@ workspaceCmd.command("init [dir]").description("Initialize workspace files (SOUL
|
|
|
3334
3712
|
userName: cfg.identity?.userName || "User",
|
|
3335
3713
|
rules: cfg.identity?.rules ?? []
|
|
3336
3714
|
}, targetDir);
|
|
3337
|
-
console.log(chalk$
|
|
3338
|
-
console.log(chalk$
|
|
3715
|
+
console.log(chalk$11.hex("#06b6d4")(`\n ✔ Workspace files initialized in ${targetDir}`));
|
|
3716
|
+
console.log(chalk$11.gray(" Files: SOUL.md USER.md TOOLS.md HEARTBEAT.md BOOTSTRAP.md\n"));
|
|
3339
3717
|
process.exit(0);
|
|
3340
3718
|
});
|
|
3341
3719
|
workspaceCmd.command("show [dir]").description("Show workspace files summary").action(async (dir) => {
|
|
3342
|
-
const chalk$
|
|
3343
|
-
const fs$
|
|
3344
|
-
const path$
|
|
3345
|
-
const os$
|
|
3346
|
-
const targetDir = dir || path$
|
|
3347
|
-
console.log(chalk$
|
|
3720
|
+
const chalk$11 = require("chalk");
|
|
3721
|
+
const fs$7 = require("fs-extra");
|
|
3722
|
+
const path$7 = require("path");
|
|
3723
|
+
const os$8 = require("os");
|
|
3724
|
+
const targetDir = dir || path$7.join(os$8.homedir(), ".hyperclaw");
|
|
3725
|
+
console.log(chalk$11.bold.hex("#06b6d4")("\n 📁 WORKSPACE\n"));
|
|
3348
3726
|
for (const fname of [
|
|
3349
3727
|
"SOUL.md",
|
|
3350
3728
|
"USER.md",
|
|
@@ -3354,45 +3732,45 @@ workspaceCmd.command("show [dir]").description("Show workspace files summary").a
|
|
|
3354
3732
|
"AGENTS.md",
|
|
3355
3733
|
"MEMORY.md"
|
|
3356
3734
|
]) {
|
|
3357
|
-
const fpath = path$
|
|
3358
|
-
const exists = await fs$
|
|
3359
|
-
const size = exists ? (await fs$
|
|
3360
|
-
const dot = exists ? chalk$
|
|
3361
|
-
console.log(` ${dot} ${fname.padEnd(14)} ${exists ? chalk$
|
|
3735
|
+
const fpath = path$7.join(targetDir, fname);
|
|
3736
|
+
const exists = await fs$7.pathExists(fpath);
|
|
3737
|
+
const size = exists ? (await fs$7.stat(fpath)).size : 0;
|
|
3738
|
+
const dot = exists ? chalk$11.hex("#06b6d4")("✔") : chalk$11.gray("○");
|
|
3739
|
+
console.log(` ${dot} ${fname.padEnd(14)} ${exists ? chalk$11.gray(`${size} bytes`) : chalk$11.gray("(missing)")}`);
|
|
3362
3740
|
}
|
|
3363
3741
|
console.log();
|
|
3364
3742
|
process.exit(0);
|
|
3365
3743
|
});
|
|
3366
3744
|
const botCmd = program.command("bot").description("HyperClaw Bot — companion bot for remote gateway control");
|
|
3367
3745
|
botCmd.command("status").action(async () => {
|
|
3368
|
-
const { showBotStatus } = await Promise.resolve().then(() => require("./hyperclawbot-
|
|
3746
|
+
const { showBotStatus } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
|
|
3369
3747
|
await showBotStatus();
|
|
3370
3748
|
process.exit(0);
|
|
3371
3749
|
});
|
|
3372
3750
|
botCmd.command("setup").description("Configure HyperClaw Bot (Telegram token, allowed users)").action(async () => {
|
|
3373
|
-
const inquirer$
|
|
3374
|
-
const { saveBotConfig } = await Promise.resolve().then(() => require("./hyperclawbot-
|
|
3375
|
-
const chalk$
|
|
3376
|
-
console.log(chalk$
|
|
3377
|
-
console.log(chalk$
|
|
3378
|
-
const { platform } = await inquirer$
|
|
3751
|
+
const inquirer$2 = require("inquirer");
|
|
3752
|
+
const { saveBotConfig } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
|
|
3753
|
+
const chalk$11 = require("chalk");
|
|
3754
|
+
console.log(chalk$11.bold.hex("#06b6d4")("\n 🦅 HYPERCLAW BOT SETUP\n"));
|
|
3755
|
+
console.log(chalk$11.gray(" Create a bot at t.me/BotFather, then paste the token below.\n"));
|
|
3756
|
+
const { platform } = await inquirer$2.prompt([{
|
|
3379
3757
|
type: "list",
|
|
3380
3758
|
name: "platform",
|
|
3381
3759
|
message: "Platform:",
|
|
3382
3760
|
choices: ["telegram", "discord"]
|
|
3383
3761
|
}]);
|
|
3384
|
-
const { token } = await inquirer$
|
|
3762
|
+
const { token } = await inquirer$2.prompt([{
|
|
3385
3763
|
type: "input",
|
|
3386
3764
|
name: "token",
|
|
3387
3765
|
message: platform === "telegram" ? "Bot token (from @BotFather):" : "Discord bot token:",
|
|
3388
3766
|
validate: (v) => v.trim().length > 10 || "Required"
|
|
3389
3767
|
}]);
|
|
3390
|
-
const { userIds } = await inquirer$
|
|
3768
|
+
const { userIds } = await inquirer$2.prompt([{
|
|
3391
3769
|
type: "input",
|
|
3392
3770
|
name: "userIds",
|
|
3393
3771
|
message: "Allowed user IDs (comma-separated, leave empty for unrestricted):"
|
|
3394
3772
|
}]);
|
|
3395
|
-
const { gatewayUrl } = await inquirer$
|
|
3773
|
+
const { gatewayUrl } = await inquirer$2.prompt([{
|
|
3396
3774
|
type: "input",
|
|
3397
3775
|
name: "gatewayUrl",
|
|
3398
3776
|
message: "Gateway URL:",
|
|
@@ -3408,20 +3786,20 @@ botCmd.command("setup").description("Configure HyperClaw Bot (Telegram token, al
|
|
|
3408
3786
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3409
3787
|
};
|
|
3410
3788
|
await saveBotConfig(cfg);
|
|
3411
|
-
console.log(chalk$
|
|
3789
|
+
console.log(chalk$11.hex("#06b6d4")("\n ✔ HyperClaw Bot configured"));
|
|
3412
3790
|
if (platform === "discord") try {
|
|
3413
3791
|
require.resolve("discord.js");
|
|
3414
3792
|
} catch {
|
|
3415
|
-
console.log(chalk$
|
|
3793
|
+
console.log(chalk$11.yellow(" ⚠ For Discord: run: npm install discord.js"));
|
|
3416
3794
|
}
|
|
3417
|
-
console.log(chalk$
|
|
3795
|
+
console.log(chalk$11.gray(" Start with: hyperclaw bot start\n"));
|
|
3418
3796
|
process.exit(0);
|
|
3419
3797
|
});
|
|
3420
3798
|
botCmd.command("start").description("Start HyperClaw Bot (foreground or background)").option("--background", "Run bot in background (use hyperclaw bot stop to stop)").action(async (opts) => {
|
|
3421
3799
|
const { spawn } = await import("child_process");
|
|
3422
|
-
const path$
|
|
3800
|
+
const path$7 = await import("path");
|
|
3423
3801
|
if (opts?.background) {
|
|
3424
|
-
const entry = process.argv[1] || path$
|
|
3802
|
+
const entry = process.argv[1] || path$7.join(__dirname, "run-main.js");
|
|
3425
3803
|
const child = spawn(process.execPath, [
|
|
3426
3804
|
entry,
|
|
3427
3805
|
"bot",
|
|
@@ -3433,14 +3811,14 @@ botCmd.command("start").description("Start HyperClaw Bot (foreground or backgrou
|
|
|
3433
3811
|
cwd: process.cwd()
|
|
3434
3812
|
});
|
|
3435
3813
|
child.unref();
|
|
3436
|
-
const { writeBotPid } = await Promise.resolve().then(() => require("./hyperclawbot-
|
|
3814
|
+
const { writeBotPid } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
|
|
3437
3815
|
await writeBotPid(child.pid);
|
|
3438
3816
|
console.log(require("chalk").green(`\n ✔ HyperClaw Bot started in background (PID ${child.pid})`));
|
|
3439
3817
|
console.log(require("chalk").gray(" Stop with: hyperclaw bot stop\n"));
|
|
3440
3818
|
process.exit(0);
|
|
3441
3819
|
return;
|
|
3442
3820
|
}
|
|
3443
|
-
const { loadBotConfig, TelegramHyperClawBot, DiscordHyperClawBot } = await Promise.resolve().then(() => require("./hyperclawbot-
|
|
3821
|
+
const { loadBotConfig, TelegramHyperClawBot, DiscordHyperClawBot } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
|
|
3444
3822
|
const cfg = await loadBotConfig();
|
|
3445
3823
|
if (!cfg) {
|
|
3446
3824
|
console.log(require("chalk").red("\n ✖ HyperClaw Bot not configured. Run: hyperclaw bot setup\n"));
|
|
@@ -3466,42 +3844,42 @@ botCmd.command("start").description("Start HyperClaw Bot (foreground or backgrou
|
|
|
3466
3844
|
}
|
|
3467
3845
|
});
|
|
3468
3846
|
botCmd.command("stop").description("Stop HyperClaw Bot (when running in background)").action(async () => {
|
|
3469
|
-
const chalk$
|
|
3470
|
-
const { stopBotProcess } = await Promise.resolve().then(() => require("./hyperclawbot-
|
|
3847
|
+
const chalk$11 = require("chalk");
|
|
3848
|
+
const { stopBotProcess } = await Promise.resolve().then(() => require("./hyperclawbot-zvczQgKx.js"));
|
|
3471
3849
|
const stopped = await stopBotProcess();
|
|
3472
|
-
if (stopped) console.log(chalk$
|
|
3473
|
-
else console.log(chalk$
|
|
3850
|
+
if (stopped) console.log(chalk$11.green("\n ✔ HyperClaw Bot stopped\n"));
|
|
3851
|
+
else console.log(chalk$11.gray("\n Bot not running in background (no PID file). Use Ctrl+C to stop foreground bot.\n"));
|
|
3474
3852
|
process.exit(stopped ? 0 : 0);
|
|
3475
3853
|
});
|
|
3476
3854
|
memCmd.command("search <query>").description("Search MEMORY.md").action(async (query) => {
|
|
3477
|
-
const { searchMemory } = await Promise.resolve().then(() => require("./src-
|
|
3855
|
+
const { searchMemory } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
3478
3856
|
await searchMemory(query);
|
|
3479
3857
|
process.exit(0);
|
|
3480
3858
|
});
|
|
3481
3859
|
memCmd.command("auto-show").description("Show auto-extracted memories from MEMORY.md").action(async () => {
|
|
3482
|
-
const { showMemory } = await Promise.resolve().then(() => require("./src-
|
|
3860
|
+
const { showMemory } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
3483
3861
|
await showMemory();
|
|
3484
3862
|
process.exit(0);
|
|
3485
3863
|
});
|
|
3486
3864
|
memCmd.command("clear").description("Clear all auto-extracted memories").action(async () => {
|
|
3487
|
-
const { clearMemory } = await Promise.resolve().then(() => require("./src-
|
|
3865
|
+
const { clearMemory } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
3488
3866
|
await clearMemory();
|
|
3489
3867
|
process.exit(0);
|
|
3490
3868
|
});
|
|
3491
3869
|
memCmd.command("save <text>").description("Manually save a fact to MEMORY.md").action(async (text) => {
|
|
3492
|
-
const { saveMemoryDirect } = await Promise.resolve().then(() => require("./src-
|
|
3870
|
+
const { saveMemoryDirect } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
3493
3871
|
await saveMemoryDirect(text);
|
|
3494
3872
|
console.log(chalk.default.hex("#06b6d4")(` ✅ Saved: ${text}\n`));
|
|
3495
3873
|
process.exit(0);
|
|
3496
3874
|
});
|
|
3497
3875
|
const pcCmd = program.command("pc").description("PC access — give the AI access to your computer");
|
|
3498
3876
|
pcCmd.command("status").description("Show PC access status and config").action(async () => {
|
|
3499
|
-
const { showPCAccessStatus } = await Promise.resolve().then(() => require("./src-
|
|
3877
|
+
const { showPCAccessStatus } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
3500
3878
|
await showPCAccessStatus();
|
|
3501
3879
|
process.exit(0);
|
|
3502
3880
|
});
|
|
3503
3881
|
pcCmd.command("enable").description("Enable PC access for the AI").option("--level <level>", "Access level: read-only | sandboxed | full", "full").option("--paths <paths>", "Comma-separated allowed paths (sandboxed mode)").action(async (opts) => {
|
|
3504
|
-
const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-
|
|
3882
|
+
const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
3505
3883
|
const level = opts.level;
|
|
3506
3884
|
const allowed = [
|
|
3507
3885
|
"read-only",
|
|
@@ -3528,7 +3906,7 @@ pcCmd.command("enable").description("Enable PC access for the AI").option("--lev
|
|
|
3528
3906
|
process.exit(0);
|
|
3529
3907
|
});
|
|
3530
3908
|
pcCmd.command("disable").description("Disable PC access").action(async () => {
|
|
3531
|
-
const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-
|
|
3909
|
+
const { savePCAccessConfig } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
3532
3910
|
await savePCAccessConfig({ enabled: false });
|
|
3533
3911
|
console.log(chalk.default.hex("#06b6d4")("\n ✅ PC access disabled\n"));
|
|
3534
3912
|
process.exit(0);
|
|
@@ -3552,7 +3930,7 @@ pcCmd.command("log").description("Show PC access audit log").option("-n, --lines
|
|
|
3552
3930
|
process.exit(0);
|
|
3553
3931
|
});
|
|
3554
3932
|
pcCmd.command("run <command>").description("Run a shell command via PC access (must be enabled)").action(async (command) => {
|
|
3555
|
-
const { loadPCAccessConfig, getPCAccessTools } = await Promise.resolve().then(() => require("./src-
|
|
3933
|
+
const { loadPCAccessConfig, getPCAccessTools } = await Promise.resolve().then(() => require("./src-BxPHKO5x.js"));
|
|
3556
3934
|
const cfg = await loadPCAccessConfig();
|
|
3557
3935
|
if (!cfg.enabled) {
|
|
3558
3936
|
console.log(chalk.default.red("\n ✖ PC access disabled. Run: hyperclaw pc enable\n"));
|
|
@@ -3565,14 +3943,58 @@ pcCmd.command("run <command>").description("Run a shell command via PC access (m
|
|
|
3565
3943
|
console.log(result);
|
|
3566
3944
|
process.exit(0);
|
|
3567
3945
|
});
|
|
3946
|
+
function checkForUpdate() {
|
|
3947
|
+
const { execFile: execFile$1 } = require("child_process");
|
|
3948
|
+
const { readFileSync } = require("fs");
|
|
3949
|
+
const path$7 = require("path");
|
|
3950
|
+
try {
|
|
3951
|
+
const pkgPath = path$7.resolve(__dirname, "../../package.json");
|
|
3952
|
+
const current = JSON.parse(readFileSync(pkgPath, "utf8")).version;
|
|
3953
|
+
execFile$1("npm", [
|
|
3954
|
+
"view",
|
|
3955
|
+
"hyperclaw",
|
|
3956
|
+
"version",
|
|
3957
|
+
"--json"
|
|
3958
|
+
], { timeout: 5e3 }, (_err, stdout) => {
|
|
3959
|
+
if (_err || !stdout) return;
|
|
3960
|
+
try {
|
|
3961
|
+
const latest = JSON.parse(stdout.trim());
|
|
3962
|
+
if (latest && latest !== current) {
|
|
3963
|
+
const semver = (v) => v.split(".").map(Number);
|
|
3964
|
+
const [lMaj, lMin, lPat] = semver(latest);
|
|
3965
|
+
const [cMaj, cMin, cPat] = semver(current);
|
|
3966
|
+
const isNewer = lMaj > cMaj || lMaj === cMaj && lMin > cMin || lMaj === cMaj && lMin === cMin && lPat > cPat;
|
|
3967
|
+
if (isNewer) process.stdout.write(chalk.default.yellow(`\n ⬆ Update available: ${chalk.default.dim(current)} → ${chalk.default.green(latest)}\n`) + chalk.default.gray(` npm install -g hyperclaw@latest\n\n`));
|
|
3968
|
+
}
|
|
3969
|
+
} catch {}
|
|
3970
|
+
});
|
|
3971
|
+
} catch {}
|
|
3972
|
+
}
|
|
3973
|
+
program.command("setup").description("Setup wizard — alias for `hyperclaw onboard`").option("--install-daemon", "Auto-install system daemon").option("--reset", "Reset config before running wizard").option("--non-interactive", "Non-interactive mode").option("--json", "Output result as JSON (use with --non-interactive)").option("--anthropic-api-key <key>", "Anthropic API key (non-interactive)").option("--openai-api-key <key>", "OpenAI API key (non-interactive)").option("--gateway-port <port>", "Gateway port (non-interactive)", "18789").option("--gateway-bind <bind>", "Gateway bind: loopback | all", "loopback").action(async (opts) => {
|
|
3974
|
+
await new require_onboard.Banner().showNeonBanner(false);
|
|
3975
|
+
const wizardOpts = {
|
|
3976
|
+
wizard: true,
|
|
3977
|
+
installDaemon: opts.installDaemon ?? false,
|
|
3978
|
+
reset: opts.reset ?? false,
|
|
3979
|
+
nonInteractive: opts.nonInteractive ?? false,
|
|
3980
|
+
jsonOutput: opts.json ?? false,
|
|
3981
|
+
gatewayPort: opts.gatewayPort ? parseInt(opts.gatewayPort) : void 0,
|
|
3982
|
+
gatewayBind: opts.gatewayBind ?? "loopback",
|
|
3983
|
+
anthropicApiKey: opts.anthropicApiKey,
|
|
3984
|
+
openaiApiKey: opts.openaiApiKey
|
|
3985
|
+
};
|
|
3986
|
+
await new require_onboard.HyperClawWizard().run(wizardOpts);
|
|
3987
|
+
process.exit(0);
|
|
3988
|
+
});
|
|
3989
|
+
checkForUpdate();
|
|
3568
3990
|
if (process.argv.length === 2) (async () => {
|
|
3569
|
-
const { ConfigManager: ConfigManager$1 } = await Promise.resolve().then(() => require("./manager-
|
|
3991
|
+
const { ConfigManager: ConfigManager$1 } = await Promise.resolve().then(() => require("./manager-CrVDn6eN.js"));
|
|
3570
3992
|
const cfg = await new ConfigManager$1().load().catch(() => null);
|
|
3571
3993
|
if (cfg?.provider?.apiKey || cfg?.provider?.providerId) {
|
|
3572
3994
|
await new require_onboard.Banner().showNeonBanner(false);
|
|
3573
|
-
const { getTheme } = await Promise.resolve().then(() => require("./theme-
|
|
3995
|
+
const { getTheme } = await Promise.resolve().then(() => require("./theme-cx0fkgWC.js"));
|
|
3574
3996
|
const t = getTheme(false);
|
|
3575
|
-
const chalk$
|
|
3997
|
+
const chalk$11 = require("chalk");
|
|
3576
3998
|
console.log(t.bold(" Quick actions:\n"));
|
|
3577
3999
|
console.log(` ${t.c("hyperclaw onboard")} — re-run setup wizard`);
|
|
3578
4000
|
console.log(` ${t.c("hyperclaw onboard --install-daemon")} — wizard + daemon (full PC access)`);
|
|
@@ -3584,7 +4006,7 @@ if (process.argv.length === 2) (async () => {
|
|
|
3584
4006
|
console.log(` ${t.c("hyperclaw --help")} — all commands\n`);
|
|
3585
4007
|
} else {
|
|
3586
4008
|
await new require_onboard.Banner().showNeonBanner(false);
|
|
3587
|
-
const { HyperClawWizard: HyperClawWizard$1 } = await Promise.resolve().then(() => require("./onboard-
|
|
4009
|
+
const { HyperClawWizard: HyperClawWizard$1 } = await Promise.resolve().then(() => require("./onboard-0WoDxbv_.js"));
|
|
3588
4010
|
await new HyperClawWizard$1().run({ wizard: true });
|
|
3589
4011
|
}
|
|
3590
4012
|
process.exit(0);
|