hyperclaw 5.0.1 → 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -1
- package/README.md +247 -32
- package/dist/a2ui-protocol-Gzm29Gaw.js +75 -0
- package/dist/agents-routing-Biy5ew4a.js +4 -0
- package/dist/agents-routing-CL3HQNoM.js +327 -0
- package/dist/api-keys-guide-ChbThbPj.js +149 -0
- package/dist/audit-NPIMmOSq.js +441 -0
- package/dist/bounty-tools-BUqUKjt0.js +211 -0
- package/dist/browser-tools-CxJY6pAn.js +5 -0
- package/dist/browser-tools-JZ9ji6AW.js +179 -0
- package/dist/chat-qVuqhlPu.js +258 -0
- package/dist/claw-tasks-B-8RRMdq.js +80 -0
- package/dist/connector-1x1rCBHz.js +162 -0
- package/dist/connector-B4jeCULG.js +305 -0
- package/dist/connector-B7qngfkT.js +286 -0
- package/dist/connector-B8BK0GBo.js +531 -0
- package/dist/connector-BE9eJs8-.js +182 -0
- package/dist/connector-BEe-DTGQ.js +189 -0
- package/dist/connector-BU7p5ZgB.js +167 -0
- package/dist/connector-BUzzq7Ij.js +568 -0
- package/dist/connector-BpDqLgnW.js +419 -0
- package/dist/connector-BpW88ut2.js +189 -0
- package/dist/connector-Bxv-gy8U.js +167 -0
- package/dist/connector-Bz14zcJv.js +213 -0
- package/dist/connector-C1zP5-5q.js +85 -0
- package/dist/connector-CAcpcovF.js +498 -0
- package/dist/connector-CJgVjS58.js +181 -0
- package/dist/connector-Cf53D6qV.js +425 -0
- package/dist/connector-CyHmlbNz.js +508 -0
- package/dist/connector-D22mJGVu.js +340 -0
- package/dist/connector-D6RtMmlL.js +225 -0
- package/dist/connector-D9EnT8A4.js +280 -0
- package/dist/connector-DNDwIh37.js +239 -0
- package/dist/connector-Di27MeO4.js +350 -0
- package/dist/connector-Do0BPiHt.js +194 -0
- package/dist/connector-DvLwOfJy.js +192 -0
- package/dist/connector-DvU83NSq.js +181 -0
- package/dist/connector-DxskpDc_.js +173 -0
- package/dist/connector-byy3eISx.js +552 -0
- package/dist/connector-vV89hsyd.js +218 -0
- package/dist/cost-tracker-fnaj_6M9.js +103 -0
- package/dist/credentials-store-BxijEirw.js +77 -0
- package/dist/credentials-store-CPkVO6-z.js +4 -0
- package/dist/cron-tasks-L0mz1yyU.js +82 -0
- package/dist/daemon-CNyunwkR.js +5 -0
- package/dist/daemon-CindY8OK.js +318 -0
- package/dist/delivery-DgiZcJBp.js +4 -0
- package/dist/delivery-otAU4alM.js +95 -0
- package/dist/destructive-gate-CA0DtA5K.js +101 -0
- package/dist/developer-keys-Cnd1kswV.js +127 -0
- package/dist/developer-keys-DENo3ZA6.js +8 -0
- package/dist/doctor-Dgjoc3DG.js +230 -0
- package/dist/doctor-RwsOhtAl.js +6 -0
- package/dist/engine-D_VeoZHw.js +305 -0
- package/dist/engine-JjRnhlsE.js +7 -0
- package/dist/env-resolve-BFJXWl94.js +115 -0
- package/dist/env-resolve-bDYssfih.js +10 -0
- package/dist/extraction-tools-DbxnxIco.js +5 -0
- package/dist/extraction-tools-Dg7AHS35.js +91 -0
- package/dist/form_data-CGAy4HE0.js +8657 -0
- package/dist/gmail-watch-setup-C3uSWznp.js +40 -0
- package/dist/health-DUjluWHQ.js +6 -0
- package/dist/health-DVfkpUQW.js +152 -0
- package/dist/heartbeat-engine-CrgL4mrP.js +83 -0
- package/dist/hub-BO6bj8Yj.js +515 -0
- package/dist/hub-Bu52YZqW.js +6 -0
- package/dist/hyperclawbot-BrcoYLOp.js +505 -0
- package/dist/inference-DHR82Gh7.js +6 -0
- package/dist/inference-DhA8jpfH.js +2692 -0
- package/dist/knowledge-graph-BrYpSgxW.js +131 -0
- package/dist/loader-9JqY6Nlq.js +4 -0
- package/dist/loader-Cjdd1kw4.js +400 -0
- package/dist/logger-DCT2l9GV.js +83 -0
- package/dist/manager-3cq3DydI.js +4 -0
- package/dist/manager-BUrFrPuq.js +117 -0
- package/dist/manager-Bi9UYyVR.js +105 -0
- package/dist/manager-Biz9ixWJ.js +40 -0
- package/dist/manager-CBUHJiY7.js +6 -0
- package/dist/manager-CVLLaKmq.js +218 -0
- package/dist/mcp-CUoTCMw-.js +139 -0
- package/dist/mcp-loader-BIz-450x.js +94 -0
- package/dist/memory-OL77OMOr.js +270 -0
- package/dist/memory-auto-D-L2q21G.js +306 -0
- package/dist/memory-auto-DTcy5VBy.js +5 -0
- package/dist/memory-gUi4VaIf.js +4 -0
- package/dist/memory-integration-B8RSN4pr.js +91 -0
- package/dist/moltbook-B-40gQOL.js +81 -0
- package/dist/node-TWxRm84k.js +222 -0
- package/dist/nodes-registry-DKRtsbNg.js +52 -0
- package/dist/oauth-flow-JCfporKq.js +150 -0
- package/dist/oauth-provider-4R0EJlsT.js +110 -0
- package/dist/observability-CDZmeHfa.js +89 -0
- package/dist/onboard-BVOtKQdh.js +3641 -0
- package/dist/onboard-CGNIw27w.js +11 -0
- package/dist/orchestrator-CcKx1Ovk.js +189 -0
- package/dist/orchestrator-DcFfDLTX.js +6 -0
- package/dist/osint-B6BZKQAD.js +277 -0
- package/dist/pairing-B6RArWhD.js +196 -0
- package/dist/pairing-BsQ08DLq.js +4 -0
- package/dist/pc-access-B0KocJNe.js +819 -0
- package/dist/pc-access-DkzmugZ7.js +8 -0
- package/dist/pending-approval-C_HkX1QL.js +22 -0
- package/dist/providers-DxiamZSL.js +5 -0
- package/dist/providers-Dy15rDb7.js +657 -0
- package/dist/reminders-store-CzUY0zYx.js +58 -0
- package/dist/renderer-ANNfXsHn.js +225 -0
- package/dist/rules-BSQwwAYC.js +103 -0
- package/dist/run-main.js +112 -117
- package/dist/runner-BHRSOPEU.js +1271 -0
- package/dist/security--oQObeJO.js +4 -0
- package/dist/security-wBOg0TA8.js +73 -0
- package/dist/server-CbTTpB5m.js +1255 -0
- package/dist/server-DP_bPzvI.js +4 -0
- package/dist/session-store-B09r5HgB.js +5 -0
- package/dist/session-store-DCTQIVur.js +113 -0
- package/dist/sessions-tools-BdlN6Pb6.js +95 -0
- package/dist/sessions-tools-JVLDKSJ_.js +5 -0
- package/dist/skill-loader-B5oeliGu.js +7 -0
- package/dist/skill-loader-Wf3brNOj.js +160 -0
- package/dist/skill-runtime-BGlvly2s.js +102 -0
- package/dist/skill-runtime-DhL2T76p.js +5 -0
- package/dist/src-BbPa6Q8p.js +63 -0
- package/dist/src-BeXtfkK2.js +458 -0
- package/dist/src-CGQjRI4N.js +20 -0
- package/dist/sub-agent-tools-CmE345s_.js +39 -0
- package/dist/theme-D0smfC_l.js +8 -0
- package/dist/theme-DajRRZbA.js +180 -0
- package/dist/tool-policy-DgNqFWYn.js +189 -0
- package/dist/tts-elevenlabs-JeFaGNJU.js +61 -0
- package/dist/update-check-BVEqHhFY.js +83 -0
- package/dist/vision-fky3elEo.js +121 -0
- package/dist/vision-tools-C8B3776g.js +5 -0
- package/dist/vision-tools-dwn9p4el.js +51 -0
- package/dist/voice-transcription-B6RtplmN.js +138 -0
- package/dist/website-watch-tools-B-jRAeTe.js +139 -0
- package/dist/website-watch-tools-BC9xAL67.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
+
|
|
3
|
+
//#region src/infra/tool-policy.ts
|
|
4
|
+
function expandToToolNames(entries) {
|
|
5
|
+
const out = /* @__PURE__ */ new Set();
|
|
6
|
+
for (const e of entries) {
|
|
7
|
+
const key = e.toLowerCase().trim();
|
|
8
|
+
if (key === "*" || key === "") continue;
|
|
9
|
+
if (key.startsWith("group:")) {
|
|
10
|
+
const group = TOOL_GROUPS[key];
|
|
11
|
+
if (group) group.forEach((t) => out.add(t));
|
|
12
|
+
} else out.add(key);
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
function matchesPattern(toolName, pattern) {
|
|
17
|
+
const p = pattern.toLowerCase();
|
|
18
|
+
const t = toolName.toLowerCase();
|
|
19
|
+
if (p === "*" || p === t) return true;
|
|
20
|
+
if (p.endsWith("*")) {
|
|
21
|
+
const prefix = p.slice(0, -1);
|
|
22
|
+
return t.startsWith(prefix);
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
function isAllowed(toolName, allowSet, denySet, profileAllowSet) {
|
|
27
|
+
if (denySet.size > 0) {
|
|
28
|
+
for (const d of denySet) if (matchesPattern(toolName, d)) return false;
|
|
29
|
+
}
|
|
30
|
+
if (profileAllowSet && profileAllowSet.size > 0) {
|
|
31
|
+
let inProfile = false;
|
|
32
|
+
for (const a of profileAllowSet) if (matchesPattern(toolName, a)) {
|
|
33
|
+
inProfile = true;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
if (!inProfile) return false;
|
|
37
|
+
}
|
|
38
|
+
if (allowSet.size > 0) {
|
|
39
|
+
for (const a of allowSet) if (matchesPattern(toolName, a)) return true;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
function applyToolPolicy(tools, config, context) {
|
|
45
|
+
if (!config) return tools;
|
|
46
|
+
let profile = config.profile ?? "full";
|
|
47
|
+
let allow = config.allow ?? [];
|
|
48
|
+
let deny = config.deny ?? [];
|
|
49
|
+
if (context?.provider && config.byProvider) {
|
|
50
|
+
const providerKey = context.provider + (context.model ? "/" + context.model : "");
|
|
51
|
+
const exact = config.byProvider[providerKey] ?? config.byProvider[context.provider];
|
|
52
|
+
if (exact) {
|
|
53
|
+
if (exact.profile) profile = exact.profile;
|
|
54
|
+
if (exact.allow?.length) allow = [...allow, ...exact.allow];
|
|
55
|
+
if (exact.deny?.length) deny = [...deny, ...exact.deny];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const denySet = expandToToolNames(deny);
|
|
59
|
+
const allowSet = expandToToolNames(allow);
|
|
60
|
+
const profileTools = profile === "full" ? null : new Set(PROFILE_TOOLS[profile]);
|
|
61
|
+
return tools.filter((t) => isAllowed(t.name, allowSet, denySet, profileTools));
|
|
62
|
+
}
|
|
63
|
+
/** For sandbox explain: return effective policy description. */
|
|
64
|
+
function describeToolPolicy(config, context) {
|
|
65
|
+
if (!config) return {
|
|
66
|
+
profile: "full",
|
|
67
|
+
allow: [],
|
|
68
|
+
deny: [],
|
|
69
|
+
source: "default"
|
|
70
|
+
};
|
|
71
|
+
let profile = config.profile ?? "full";
|
|
72
|
+
let allow = config.allow ?? [];
|
|
73
|
+
let deny = config.deny ?? [];
|
|
74
|
+
let source = "tools";
|
|
75
|
+
if (context?.provider && config.byProvider) {
|
|
76
|
+
const providerKey = context.provider + (context.model ? "/" + context.model : "");
|
|
77
|
+
const exact = config.byProvider[providerKey] ?? config.byProvider[context.provider];
|
|
78
|
+
if (exact) {
|
|
79
|
+
if (exact.profile) {
|
|
80
|
+
profile = exact.profile;
|
|
81
|
+
source = `tools.byProvider[${context.provider}]`;
|
|
82
|
+
}
|
|
83
|
+
if (exact.allow?.length) allow = [...allow, ...exact.allow];
|
|
84
|
+
if (exact.deny?.length) deny = [...deny, ...exact.deny];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
profile,
|
|
89
|
+
allow,
|
|
90
|
+
deny,
|
|
91
|
+
source
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
var TOOL_GROUPS, PROFILE_TOOLS;
|
|
95
|
+
var init_tool_policy = require_chunk.__esm({ "src/infra/tool-policy.ts"() {
|
|
96
|
+
TOOL_GROUPS = {
|
|
97
|
+
"group:fs": [
|
|
98
|
+
"read_file",
|
|
99
|
+
"write_file",
|
|
100
|
+
"edit_file",
|
|
101
|
+
"delete_file",
|
|
102
|
+
"list_directory"
|
|
103
|
+
],
|
|
104
|
+
"group:runtime": ["run_shell", "kill_process"],
|
|
105
|
+
"group:sessions": [
|
|
106
|
+
"sessions_list",
|
|
107
|
+
"sessions_send",
|
|
108
|
+
"sessions_history"
|
|
109
|
+
],
|
|
110
|
+
"group:memory": [
|
|
111
|
+
"read_memory",
|
|
112
|
+
"write_memory",
|
|
113
|
+
"memory_graph_add",
|
|
114
|
+
"memory_graph_query"
|
|
115
|
+
],
|
|
116
|
+
"group:ui": [
|
|
117
|
+
"canvas_add",
|
|
118
|
+
"browser_snapshot",
|
|
119
|
+
"browser_action"
|
|
120
|
+
],
|
|
121
|
+
"group:messaging": [
|
|
122
|
+
"sessions_send",
|
|
123
|
+
"sessions_list",
|
|
124
|
+
"sessions_history"
|
|
125
|
+
],
|
|
126
|
+
"group:automation": [
|
|
127
|
+
"add_reminder",
|
|
128
|
+
"list_reminders",
|
|
129
|
+
"complete_reminder",
|
|
130
|
+
"watch_website_add",
|
|
131
|
+
"watch_website_check",
|
|
132
|
+
"watch_website_list"
|
|
133
|
+
],
|
|
134
|
+
"group:pc": [
|
|
135
|
+
"run_shell",
|
|
136
|
+
"read_file",
|
|
137
|
+
"write_file",
|
|
138
|
+
"edit_file",
|
|
139
|
+
"list_directory",
|
|
140
|
+
"delete_file",
|
|
141
|
+
"kill_process",
|
|
142
|
+
"system_info",
|
|
143
|
+
"open",
|
|
144
|
+
"clipboard",
|
|
145
|
+
"search_files",
|
|
146
|
+
"screenshot",
|
|
147
|
+
"camera_capture",
|
|
148
|
+
"screen_record",
|
|
149
|
+
"contacts_list",
|
|
150
|
+
"calendar_events",
|
|
151
|
+
"photos_recent",
|
|
152
|
+
"app_updates",
|
|
153
|
+
"notify"
|
|
154
|
+
],
|
|
155
|
+
"group:extraction": ["extract_pdf", "extract_spreadsheet"],
|
|
156
|
+
"group:vision": ["analyze_image"]
|
|
157
|
+
};
|
|
158
|
+
PROFILE_TOOLS = {
|
|
159
|
+
full: [],
|
|
160
|
+
messaging: [
|
|
161
|
+
"get_current_time",
|
|
162
|
+
"sessions_list",
|
|
163
|
+
"sessions_history",
|
|
164
|
+
"sessions_send",
|
|
165
|
+
"read_memory",
|
|
166
|
+
"write_memory"
|
|
167
|
+
],
|
|
168
|
+
coding: [
|
|
169
|
+
...TOOL_GROUPS["group:fs"],
|
|
170
|
+
...TOOL_GROUPS["group:runtime"],
|
|
171
|
+
...TOOL_GROUPS["group:sessions"],
|
|
172
|
+
...TOOL_GROUPS["group:memory"],
|
|
173
|
+
"http_get",
|
|
174
|
+
"canvas_add",
|
|
175
|
+
"create_skill",
|
|
176
|
+
"get_current_time"
|
|
177
|
+
],
|
|
178
|
+
minimal: [
|
|
179
|
+
"get_current_time",
|
|
180
|
+
"sessions_list",
|
|
181
|
+
"read_memory"
|
|
182
|
+
]
|
|
183
|
+
};
|
|
184
|
+
} });
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
init_tool_policy();
|
|
188
|
+
exports.applyToolPolicy = applyToolPolicy;
|
|
189
|
+
exports.describeToolPolicy = describeToolPolicy;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
+
const https = require_chunk.__toESM(require("https"));
|
|
3
|
+
|
|
4
|
+
//#region src/services/tts-elevenlabs.ts
|
|
5
|
+
const DEFAULT_VOICE = "21m00Tcm4TlvDq8ikWAM";
|
|
6
|
+
const DEFAULT_MODEL = "eleven_multilingual_v2";
|
|
7
|
+
/**
|
|
8
|
+
|
|
9
|
+
* Convert text to speech via ElevenLabs API.
|
|
10
|
+
|
|
11
|
+
* Returns base64-encoded MP3 or null on error.
|
|
12
|
+
|
|
13
|
+
*/
|
|
14
|
+
async function textToSpeech(text, opts) {
|
|
15
|
+
const voiceId = opts.voiceId || DEFAULT_VOICE;
|
|
16
|
+
const modelId = opts.modelId || DEFAULT_MODEL;
|
|
17
|
+
const format = opts.outputFormat || "mp3_22050_32";
|
|
18
|
+
const body = JSON.stringify({
|
|
19
|
+
text,
|
|
20
|
+
model_id: modelId
|
|
21
|
+
});
|
|
22
|
+
const path = `/v1/text-to-speech/${voiceId}?output_format=${format}`;
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const req = https.default.request({
|
|
25
|
+
hostname: "api.elevenlabs.io",
|
|
26
|
+
port: 443,
|
|
27
|
+
path,
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
"xi-api-key": opts.apiKey,
|
|
32
|
+
"Content-Length": Buffer.byteLength(body)
|
|
33
|
+
}
|
|
34
|
+
}, (res) => {
|
|
35
|
+
const chunks = [];
|
|
36
|
+
res.on("data", (c) => chunks.push(c));
|
|
37
|
+
res.on("end", () => {
|
|
38
|
+
const buf = Buffer.concat(chunks);
|
|
39
|
+
if (res.statusCode !== 200) {
|
|
40
|
+
console.warn(`[tts] ElevenLabs error ${res.statusCode}: ${buf.slice(0, 200)}`);
|
|
41
|
+
resolve(null);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
resolve(buf.toString("base64"));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
req.on("error", (e) => {
|
|
48
|
+
console.warn("[tts] ElevenLabs request error:", e.message);
|
|
49
|
+
resolve(null);
|
|
50
|
+
});
|
|
51
|
+
req.setTimeout(3e4, () => {
|
|
52
|
+
req.destroy();
|
|
53
|
+
resolve(null);
|
|
54
|
+
});
|
|
55
|
+
req.write(body);
|
|
56
|
+
req.end();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
exports.textToSpeech = textToSpeech;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
+
const chalk = require_chunk.__toESM(require("chalk"));
|
|
3
|
+
const fs_extra = require_chunk.__toESM(require("fs-extra"));
|
|
4
|
+
const path = require_chunk.__toESM(require("path"));
|
|
5
|
+
const https = require_chunk.__toESM(require("https"));
|
|
6
|
+
|
|
7
|
+
//#region src/infra/update-check.ts
|
|
8
|
+
const NPM_REGISTRY = "https://registry.npmjs.org";
|
|
9
|
+
const PACKAGE_NAME = "hyperclaw";
|
|
10
|
+
function parseVersion(v) {
|
|
11
|
+
const match = v.replace(/^v/, "").match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
12
|
+
if (!match) return [
|
|
13
|
+
0,
|
|
14
|
+
0,
|
|
15
|
+
0
|
|
16
|
+
];
|
|
17
|
+
return [
|
|
18
|
+
parseInt(match[1], 10),
|
|
19
|
+
parseInt(match[2], 10),
|
|
20
|
+
parseInt(match[3], 10)
|
|
21
|
+
];
|
|
22
|
+
}
|
|
23
|
+
function isNewer(latest, current) {
|
|
24
|
+
const a = parseVersion(latest);
|
|
25
|
+
const b = parseVersion(current);
|
|
26
|
+
for (let i = 0; i < 3; i++) {
|
|
27
|
+
if (a[i] > b[i]) return true;
|
|
28
|
+
if (a[i] < b[i]) return false;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
async function checkForUpdates(currentVersion) {
|
|
33
|
+
if (process.env.HYPERCLAW_NO_UPDATE_CHECK === "1") return null;
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
const req = https.default.get(`${NPM_REGISTRY}/${PACKAGE_NAME}/latest`, { timeout: 3e3 }, (res) => {
|
|
36
|
+
let data = "";
|
|
37
|
+
res.on("data", (chunk) => data += chunk);
|
|
38
|
+
res.on("end", () => {
|
|
39
|
+
try {
|
|
40
|
+
const json = JSON.parse(data);
|
|
41
|
+
const latest = json?.version;
|
|
42
|
+
if (!latest || typeof latest !== "string") {
|
|
43
|
+
resolve(null);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
resolve({
|
|
47
|
+
latest,
|
|
48
|
+
available: isNewer(latest, currentVersion)
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
resolve(null);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
req.on("error", () => resolve(null));
|
|
56
|
+
req.on("timeout", () => {
|
|
57
|
+
req.destroy();
|
|
58
|
+
resolve(null);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function notifyUpdateAvailable(current, latest) {
|
|
63
|
+
console.log(chalk.default.yellow(` 🦅 Update available: ${latest} (you have ${current})`));
|
|
64
|
+
console.log(chalk.default.gray(" Run: npm i -g hyperclaw\n"));
|
|
65
|
+
}
|
|
66
|
+
/** Fire-and-forget: check for updates and notify user if available. Call after banner/startup. */
|
|
67
|
+
function maybeShowUpdateNotice(skipInDaemon = false) {
|
|
68
|
+
if (skipInDaemon) return;
|
|
69
|
+
(async () => {
|
|
70
|
+
try {
|
|
71
|
+
const pkgPath = path.default.join(__dirname, "../../package.json");
|
|
72
|
+
const pkg = await fs_extra.default.readJson(pkgPath).catch(() => null);
|
|
73
|
+
const current = pkg?.version ?? "0.0.0";
|
|
74
|
+
const result = await checkForUpdates(current);
|
|
75
|
+
if (result?.available) notifyUpdateAvailable(current, result.latest);
|
|
76
|
+
} catch {}
|
|
77
|
+
})().catch(() => {});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
exports.checkForUpdates = checkForUpdates;
|
|
82
|
+
exports.maybeShowUpdateNotice = maybeShowUpdateNotice;
|
|
83
|
+
exports.notifyUpdateAvailable = notifyUpdateAvailable;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
+
const fs_extra = require_chunk.__toESM(require("fs-extra"));
|
|
3
|
+
const path = require_chunk.__toESM(require("path"));
|
|
4
|
+
const os = require_chunk.__toESM(require("os"));
|
|
5
|
+
const http = require_chunk.__toESM(require("http"));
|
|
6
|
+
const https = require_chunk.__toESM(require("https"));
|
|
7
|
+
|
|
8
|
+
//#region src/services/vision.ts
|
|
9
|
+
async function imageToBase64(input) {
|
|
10
|
+
const trimmed = input.trim();
|
|
11
|
+
if (trimmed.startsWith("data:")) {
|
|
12
|
+
const match = trimmed.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
13
|
+
if (match) return {
|
|
14
|
+
data: match[2],
|
|
15
|
+
mediaType: match[1]
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (trimmed.startsWith("http")) {
|
|
19
|
+
const buf$1 = await new Promise((resolve, reject) => {
|
|
20
|
+
const mod = trimmed.startsWith("https") ? https.default : http.default;
|
|
21
|
+
const req = mod.get(trimmed, (res) => {
|
|
22
|
+
const chunks = [];
|
|
23
|
+
res.on("data", (c) => chunks.push(c));
|
|
24
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
25
|
+
res.on("error", reject);
|
|
26
|
+
});
|
|
27
|
+
req.on("error", reject);
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
data: buf$1.toString("base64"),
|
|
31
|
+
mediaType: "image/jpeg"
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const filePath = trimmed.replace(/^~/, os.default.homedir());
|
|
35
|
+
const buf = await fs_extra.default.readFile(path.default.resolve(filePath));
|
|
36
|
+
const ext = path.default.extname(filePath).toLowerCase();
|
|
37
|
+
const mediaType = {
|
|
38
|
+
".png": "image/png",
|
|
39
|
+
".jpg": "image/jpeg",
|
|
40
|
+
".jpeg": "image/jpeg",
|
|
41
|
+
".gif": "image/gif",
|
|
42
|
+
".webp": "image/webp"
|
|
43
|
+
}[ext] ?? "image/jpeg";
|
|
44
|
+
return {
|
|
45
|
+
data: buf.toString("base64"),
|
|
46
|
+
mediaType
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function analyzeImage(imageInput, prompt, apiKey, provider = "anthropic") {
|
|
50
|
+
const { data, mediaType } = await imageToBase64(imageInput);
|
|
51
|
+
const isAnthropic = provider === "anthropic";
|
|
52
|
+
const hostname = isAnthropic ? "api.anthropic.com" : "openrouter.ai";
|
|
53
|
+
const model = isAnthropic ? "claude-sonnet-4-20250514" : "openai/gpt-4o";
|
|
54
|
+
const body = {
|
|
55
|
+
model,
|
|
56
|
+
max_tokens: 1024,
|
|
57
|
+
messages: isAnthropic ? [{
|
|
58
|
+
role: "user",
|
|
59
|
+
content: [{
|
|
60
|
+
type: "image",
|
|
61
|
+
source: {
|
|
62
|
+
type: "base64",
|
|
63
|
+
media_type: mediaType,
|
|
64
|
+
data
|
|
65
|
+
}
|
|
66
|
+
}, {
|
|
67
|
+
type: "text",
|
|
68
|
+
text: prompt || "Describe this image."
|
|
69
|
+
}]
|
|
70
|
+
}] : [{
|
|
71
|
+
role: "user",
|
|
72
|
+
content: [{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: "[Image attached] " + (prompt || "Describe this image.")
|
|
75
|
+
}, {
|
|
76
|
+
type: "image_url",
|
|
77
|
+
image_url: { url: `data:${mediaType};base64,${data}` }
|
|
78
|
+
}]
|
|
79
|
+
}]
|
|
80
|
+
};
|
|
81
|
+
if (isAnthropic) body.anthropic_version = "2023-06-01";
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
const payload = JSON.stringify(body);
|
|
84
|
+
const req = https.default.request({
|
|
85
|
+
hostname,
|
|
86
|
+
port: 443,
|
|
87
|
+
path: isAnthropic ? "/v1/messages" : "/api/v1/chat/completions",
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: {
|
|
90
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
...isAnthropic ? { "anthropic-version": "2023-06-01" } : { "HTTP-Referer": "https://hyperclaw.ai" }
|
|
93
|
+
}
|
|
94
|
+
}, (res) => {
|
|
95
|
+
let raw = "";
|
|
96
|
+
res.on("data", (c) => raw += c);
|
|
97
|
+
res.on("end", () => {
|
|
98
|
+
try {
|
|
99
|
+
const j = JSON.parse(raw);
|
|
100
|
+
if (isAnthropic) {
|
|
101
|
+
const text = j.content?.[0]?.text;
|
|
102
|
+
resolve(text || j.error?.message || "(no description)");
|
|
103
|
+
} else {
|
|
104
|
+
const text = j.choices?.[0]?.message?.content;
|
|
105
|
+
resolve(text || j.error?.message || "(no description)");
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
resolve(raw || "(parse error)");
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
req.on("error", reject);
|
|
113
|
+
req.write(payload);
|
|
114
|
+
req.end();
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
var init_vision = require_chunk.__esm({ "src/services/vision.ts"() {} });
|
|
118
|
+
|
|
119
|
+
//#endregion
|
|
120
|
+
init_vision();
|
|
121
|
+
exports.analyzeImage = analyzeImage;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
+
|
|
3
|
+
//#region packages/core/src/agent/vision-tools.ts
|
|
4
|
+
function getVisionTools(opts = {}) {
|
|
5
|
+
const { apiKey = "", provider = "anthropic" } = opts;
|
|
6
|
+
return [{
|
|
7
|
+
name: "analyze_image",
|
|
8
|
+
description: "Analyze an image using a vision model. Describe scenes, receipts, documents, photos. Supports file path, URL, or data URI.",
|
|
9
|
+
input_schema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
image: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "Image path (~/path), URL (https://...), or data:image/...;base64,..."
|
|
15
|
+
},
|
|
16
|
+
prompt: {
|
|
17
|
+
type: "string",
|
|
18
|
+
description: "What to describe or extract (e.g. \"Describe this scene\", \"List items on this receipt\")"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
required: ["image"]
|
|
22
|
+
},
|
|
23
|
+
handler: async (input) => {
|
|
24
|
+
if (!apiKey) return "Error: No API key configured for vision. Set provider.apiKey or run hyperclaw auth add.";
|
|
25
|
+
const image = input.image?.trim();
|
|
26
|
+
const prompt = input.prompt?.trim() || "Describe this image concisely.";
|
|
27
|
+
if (!image) return "Error: image is required";
|
|
28
|
+
try {
|
|
29
|
+
const { analyzeImage } = await Promise.resolve().then(() => require("./vision-fky3elEo.js"));
|
|
30
|
+
return await analyzeImage(image, prompt, apiKey, provider);
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return `Error: ${e.message}`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}];
|
|
36
|
+
}
|
|
37
|
+
var init_vision_tools = require_chunk.__esm({ "packages/core/src/agent/vision-tools.ts"() {} });
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
Object.defineProperty(exports, 'getVisionTools', {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
get: function () {
|
|
43
|
+
return getVisionTools;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(exports, 'init_vision_tools', {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
get: function () {
|
|
49
|
+
return init_vision_tools;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-jS-bbMI5.js');
|
|
2
|
+
const fs_extra = require_chunk.__toESM(require("fs-extra"));
|
|
3
|
+
const path = require_chunk.__toESM(require("path"));
|
|
4
|
+
const os = require_chunk.__toESM(require("os"));
|
|
5
|
+
const https = require_chunk.__toESM(require("https"));
|
|
6
|
+
|
|
7
|
+
//#region src/services/voice-transcription.ts
|
|
8
|
+
const HC_DIR = path.default.join(os.default.homedir(), ".hyperclaw");
|
|
9
|
+
async function getConfig() {
|
|
10
|
+
try {
|
|
11
|
+
const cfg = await fs_extra.default.readJson(path.default.join(HC_DIR, "hyperclaw.json"));
|
|
12
|
+
const providerId = cfg?.provider?.providerId;
|
|
13
|
+
const apiKey = cfg?.provider?.apiKey;
|
|
14
|
+
return {
|
|
15
|
+
providerId,
|
|
16
|
+
apiKey
|
|
17
|
+
};
|
|
18
|
+
} catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function transcribeWithWhisper(buffer, apiKey) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
const boundary = "----HyperClaw" + Date.now();
|
|
25
|
+
const header = [
|
|
26
|
+
`--${boundary}`,
|
|
27
|
+
"Content-Disposition: form-data; name=\"file\"; filename=\"audio.ogg\"",
|
|
28
|
+
"Content-Type: application/octet-stream",
|
|
29
|
+
"",
|
|
30
|
+
""
|
|
31
|
+
].join("\r\n");
|
|
32
|
+
const footer = `\r\n--${boundary}\r\nContent-Disposition: form-data; name="model"\r\n\r\nwhisper-1\r\n--${boundary}--\r\n`;
|
|
33
|
+
const body = Buffer.concat([
|
|
34
|
+
Buffer.from(header, "utf8"),
|
|
35
|
+
buffer,
|
|
36
|
+
Buffer.from(footer, "utf8")
|
|
37
|
+
]);
|
|
38
|
+
const req = https.default.request({
|
|
39
|
+
hostname: "api.openai.com",
|
|
40
|
+
port: 443,
|
|
41
|
+
path: "/v1/audio/transcriptions",
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: {
|
|
44
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
45
|
+
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
46
|
+
"Content-Length": body.length
|
|
47
|
+
}
|
|
48
|
+
}, (res) => {
|
|
49
|
+
let data = "";
|
|
50
|
+
res.on("data", (c) => data += c);
|
|
51
|
+
res.on("end", () => {
|
|
52
|
+
try {
|
|
53
|
+
const j = JSON.parse(data);
|
|
54
|
+
resolve(j.text?.trim() || "[No transcription]");
|
|
55
|
+
} catch {
|
|
56
|
+
resolve(`[Transcription error: ${data.slice(0, 100)}]`);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
req.on("error", reject);
|
|
61
|
+
req.write(body);
|
|
62
|
+
req.end();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async function transcribeWithGemini(buffer, apiKey) {
|
|
66
|
+
const base64 = buffer.toString("base64");
|
|
67
|
+
const payload = JSON.stringify({
|
|
68
|
+
contents: [{ parts: [{ text: "Transcribe this audio to text. Output only the transcription, no other text." }, { inlineData: {
|
|
69
|
+
mimeType: "audio/ogg",
|
|
70
|
+
data: base64
|
|
71
|
+
} }] }],
|
|
72
|
+
generationConfig: { maxOutputTokens: 1024 }
|
|
73
|
+
});
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const req = https.default.request({
|
|
76
|
+
hostname: "generativelanguage.googleapis.com",
|
|
77
|
+
port: 443,
|
|
78
|
+
path: "/v1beta/models/gemini-2.0-flash:generateContent?key=" + encodeURIComponent(apiKey),
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: {
|
|
81
|
+
"Content-Type": "application/json",
|
|
82
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
83
|
+
}
|
|
84
|
+
}, (res) => {
|
|
85
|
+
let data = "";
|
|
86
|
+
res.on("data", (c) => data += c);
|
|
87
|
+
res.on("end", () => {
|
|
88
|
+
try {
|
|
89
|
+
const j = JSON.parse(data);
|
|
90
|
+
const text = j.candidates?.[0]?.content?.parts?.[0]?.text?.trim();
|
|
91
|
+
resolve(text || "[No transcription]");
|
|
92
|
+
} catch {
|
|
93
|
+
resolve(`[Transcription error: ${data.slice(0, 100)}]`);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
req.on("error", reject);
|
|
98
|
+
req.write(payload);
|
|
99
|
+
req.end();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
|
|
104
|
+
* Transcribe audio using configured provider or fallbacks.
|
|
105
|
+
|
|
106
|
+
* Providers: OpenAI (Whisper), Google (Gemini), OpenRouter.
|
|
107
|
+
|
|
108
|
+
* Env: OPENAI_API_KEY, WHISPER_API_KEY, GOOGLE_AI_API_KEY.
|
|
109
|
+
|
|
110
|
+
*/
|
|
111
|
+
async function transcribeVoiceNote(audioPathOrBuffer, apiKey) {
|
|
112
|
+
let buffer;
|
|
113
|
+
if (typeof audioPathOrBuffer === "string") buffer = await fs_extra.default.readFile(audioPathOrBuffer);
|
|
114
|
+
else buffer = audioPathOrBuffer;
|
|
115
|
+
const cfg = await getConfig();
|
|
116
|
+
const openaiKey = apiKey || process.env.OPENAI_API_KEY || process.env.WHISPER_API_KEY || (cfg.providerId === "openai" || cfg.providerId === "openrouter" ? cfg.apiKey : "");
|
|
117
|
+
const googleKey = process.env.GOOGLE_AI_API_KEY || (cfg.providerId === "google" ? cfg.apiKey : "");
|
|
118
|
+
if (cfg.providerId === "google" && googleKey) try {
|
|
119
|
+
return await transcribeWithGemini(buffer, googleKey);
|
|
120
|
+
} catch {}
|
|
121
|
+
if ((cfg.providerId === "openrouter" || cfg.providerId === "openai") && openaiKey) try {
|
|
122
|
+
return await transcribeWithWhisper(buffer, openaiKey);
|
|
123
|
+
} catch {}
|
|
124
|
+
if (openaiKey) try {
|
|
125
|
+
return await transcribeWithWhisper(buffer, openaiKey);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
return `[Transcription failed: ${e.message}]`;
|
|
128
|
+
}
|
|
129
|
+
if (googleKey) try {
|
|
130
|
+
return await transcribeWithGemini(buffer, googleKey);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
return `[Transcription failed: ${e.message}]`;
|
|
133
|
+
}
|
|
134
|
+
return "[Voice note — add OPENAI_API_KEY or GOOGLE_AI_API_KEY (or select OpenAI/Google provider in the wizard) for transcription]";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
exports.transcribeVoiceNote = transcribeVoiceNote;
|