hyperclaw 5.2.5 → 5.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/audit-NPINyRh4.js +445 -0
  2. package/dist/chat-8E4H6nqx.js +325 -0
  3. package/dist/connector-1_9a4Mhv.js +276 -0
  4. package/dist/connector-BeHsEhpz.js +164 -0
  5. package/dist/connector-DJ63fLj9.js +555 -0
  6. package/dist/connector-Ic8H84de.js +204 -0
  7. package/dist/daemon-DRhU750_.js +7 -0
  8. package/dist/daemon-bJ8IYnkd.js +421 -0
  9. package/dist/delivery-1vTBQ0a0.js +95 -0
  10. package/dist/delivery-BbOfKejh.js +4 -0
  11. package/dist/engine-BjzV25HS.js +7 -0
  12. package/dist/engine-DJSr69DF.js +327 -0
  13. package/dist/heartbeat-engine-0swQl6wg.js +89 -0
  14. package/dist/hub-BuUwiTxh.js +6 -0
  15. package/dist/hub-DIoASRn6.js +512 -0
  16. package/dist/hyperclawbot-CIvGq2IG.js +516 -0
  17. package/dist/inference-BSWFHqzs.js +2854 -0
  18. package/dist/inference-DQiqWbqu.js +8 -0
  19. package/dist/loader-Bpju2Xqs.js +6 -0
  20. package/dist/loader-DRfmh8hU.js +410 -0
  21. package/dist/logger-CnxILOPV.js +86 -0
  22. package/dist/mcp-loader-D-uIqYwB.js +93 -0
  23. package/dist/memory-auto-CK5M1YV8.js +5 -0
  24. package/dist/memory-auto-Cs6XiIxb.js +306 -0
  25. package/dist/node-4_wJsNEN.js +226 -0
  26. package/dist/oauth-flow-CJ7dFXKT.js +148 -0
  27. package/dist/onboard-C1RArB82.js +3865 -0
  28. package/dist/onboard-CQkUrkNk.js +13 -0
  29. package/dist/orchestrator-D9R2u9yL.js +6 -0
  30. package/dist/orchestrator-DMDgfB8j.js +189 -0
  31. package/dist/pairing-CNNtZ8JR.js +6 -0
  32. package/dist/pairing-fGaxBlgG.js +207 -0
  33. package/dist/pc-access-CaE4x3Vt.js +8 -0
  34. package/dist/pc-access-OIwXRyAD.js +858 -0
  35. package/dist/run-main.js +50 -44
  36. package/dist/runner-CFvEFt23.js +1274 -0
  37. package/dist/server-BSCeWSlZ.js +1304 -0
  38. package/dist/server-DIwR4tT3.js +4 -0
  39. package/dist/skill-runtime-CCwGR7iX.js +5 -0
  40. package/dist/skill-runtime-vmBIhuVk.js +104 -0
  41. package/dist/src-3dXyf5GQ.js +458 -0
  42. package/dist/src-BVeLalMV.js +63 -0
  43. package/dist/sub-agent-tools-C1dWyUAR.js +39 -0
  44. package/dist/tts-elevenlabs-F_xjKQ-I.js +64 -0
  45. package/dist/vision-BR5Gdb2s.js +169 -0
  46. package/dist/vision-tools-DuB1QtlE.js +51 -0
  47. package/dist/vision-tools-LvL8RMWR.js +5 -0
  48. package/dist/voice-transcription-D6dK7b9A.js +171 -0
  49. package/dist/website-watch-tools-Cqp7RPvn.js +176 -0
  50. package/dist/website-watch-tools-UPSrnBk2.js +5 -0
  51. package/package.json +1 -1
@@ -0,0 +1,306 @@
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 os = require_chunk.__toESM(require("os"));
6
+
7
+ //#region packages/core/src/agent/memory-auto.ts
8
+ function isSensitive(text) {
9
+ return SENSITIVE_PATTERNS.some((p) => p.test(text));
10
+ }
11
+ function extractFactsLocally(turns) {
12
+ const facts = [];
13
+ const userMessages = turns.filter((t) => t.role === "user").map((t) => t.content);
14
+ for (const msg of userMessages) {
15
+ if (isSensitive(msg)) continue;
16
+ for (const pattern of PREFERENCE_PATTERNS) {
17
+ const m = msg.match(pattern);
18
+ if (m?.[1] && m[1].length > 4) facts.push({
19
+ fact: `User preference: ${m[1].trim()}`,
20
+ category: "preference",
21
+ confidence: "high"
22
+ });
23
+ }
24
+ for (const pattern of IDENTITY_PATTERNS) {
25
+ const m = msg.match(pattern);
26
+ if (m?.[1] && m[1].length > 2) facts.push({
27
+ fact: `About user: ${m[1].trim()}`,
28
+ category: "identity",
29
+ confidence: "high"
30
+ });
31
+ }
32
+ for (const pattern of GOAL_PATTERNS) {
33
+ const m = msg.match(pattern);
34
+ if (m?.[1] && m[1].length > 8) facts.push({
35
+ fact: `User goal: ${m[1].trim()}`,
36
+ category: "goal",
37
+ confidence: "medium"
38
+ });
39
+ }
40
+ }
41
+ const seen = /* @__PURE__ */ new Set();
42
+ return facts.filter((f) => {
43
+ const key = f.fact.toLowerCase().slice(0, 40);
44
+ if (seen.has(key)) return false;
45
+ seen.add(key);
46
+ return true;
47
+ });
48
+ }
49
+ async function readMemory() {
50
+ try {
51
+ return await fs_extra.default.readFile(MEMORY_FILE, "utf8");
52
+ } catch {
53
+ return "";
54
+ }
55
+ }
56
+ async function appendMemory(facts) {
57
+ if (facts.length === 0) return 0;
58
+ const existing = await readMemory();
59
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
60
+ let added = 0;
61
+ const newLines = [];
62
+ const addedFacts = [];
63
+ for (const f of facts) {
64
+ if (existing.toLowerCase().includes(f.fact.toLowerCase().slice(0, 30))) continue;
65
+ newLines.push(`- ${today} [${f.category}] ${f.fact}`);
66
+ addedFacts.push(f);
67
+ added++;
68
+ }
69
+ if (newLines.length === 0) return 0;
70
+ await fs_extra.default.ensureDir(HC_DIR);
71
+ if (!await fs_extra.default.pathExists(MEMORY_FILE)) await fs_extra.default.writeFile(MEMORY_FILE, "# HyperClaw Memory\n\n");
72
+ await fs_extra.default.appendFile(MEMORY_FILE, "\n" + newLines.join("\n") + "\n");
73
+ try {
74
+ const { onMemoryAppended } = await Promise.resolve().then(() => require("./memory-integration-_NOy60Fj.js"));
75
+ await onMemoryAppended(addedFacts.map((f) => ({ fact: f.fact })));
76
+ } catch {}
77
+ return added;
78
+ }
79
+ async function saveMemoryDirect(text) {
80
+ await fs_extra.default.ensureDir(HC_DIR);
81
+ if (!await fs_extra.default.pathExists(MEMORY_FILE)) await fs_extra.default.writeFile(MEMORY_FILE, "# HyperClaw Memory\n\n");
82
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
83
+ await fs_extra.default.appendFile(MEMORY_FILE, `\n- ${today} ${text}\n`);
84
+ try {
85
+ const { onMemoryAppended } = await Promise.resolve().then(() => require("./memory-integration-_NOy60Fj.js"));
86
+ await onMemoryAppended([{ fact: text }]);
87
+ } catch {}
88
+ }
89
+ async function extractFactsWithAI(turns, inferenceOpts) {
90
+ const totalText = turns.map((t) => t.content).join(" ");
91
+ if (totalText.length < 100) return [];
92
+ const localFacts = extractFactsLocally(turns);
93
+ const hasPersonalContext = /\b(I|my|me|mine|myself)\b/i.test(totalText);
94
+ if (!hasPersonalContext) return localFacts;
95
+ try {
96
+ const { InferenceEngine } = await Promise.resolve().then(() => require("./inference-DQiqWbqu.js"));
97
+ const engine = new InferenceEngine({
98
+ model: inferenceOpts.model,
99
+ apiKey: inferenceOpts.apiKey,
100
+ provider: inferenceOpts.provider,
101
+ maxTokens: 512,
102
+ tools: []
103
+ });
104
+ const conversationSummary = turns.slice(-6).map((t) => `${t.role === "user" ? "User" : "AI"}: ${t.content.slice(0, 300)}`).join("\n");
105
+ const result = await engine.run([{
106
+ role: "user",
107
+ content: `Extract ONLY concrete, reusable facts about the USER from this conversation.
108
+ Skip: passwords, API keys, one-off questions, assistant responses.
109
+ Include: name, job, preferences, ongoing projects, locations, recurring tasks.
110
+
111
+ Conversation:
112
+ ${conversationSummary}
113
+
114
+ Respond with a JSON array of strings, each a short fact starting with "User".
115
+ If nothing worth saving, respond: []
116
+ Example: ["User's name is Alex", "User prefers TypeScript over JavaScript", "User is building a SaaS app"]`
117
+ }]);
118
+ const clean = result.text.replace(/```json|```/g, "").trim();
119
+ const aiFactStrings = JSON.parse(clean);
120
+ const allFacts = [...localFacts];
121
+ const existingFactText = localFacts.map((f) => f.fact.toLowerCase());
122
+ for (const factStr of aiFactStrings) {
123
+ if (!factStr || factStr.length < 5) continue;
124
+ if (isSensitive(factStr)) continue;
125
+ if (existingFactText.some((e) => e.includes(factStr.toLowerCase().slice(0, 20)))) continue;
126
+ const cat = /prefer|like|hate|always|never|don't/i.test(factStr) ? "preference" : /goal|want|trying|working on|building/i.test(factStr) ? "goal" : /name|job|work|role|from|based/i.test(factStr) ? "identity" : "context";
127
+ allFacts.push({
128
+ fact: factStr,
129
+ category: cat,
130
+ confidence: "medium"
131
+ });
132
+ }
133
+ return allFacts;
134
+ } catch {
135
+ return localFacts;
136
+ }
137
+ }
138
+ async function showMemory() {
139
+ const content = await readMemory();
140
+ if (!content.trim()) {
141
+ console.log(chalk.default.gray("\n 🧠 MEMORY.md is empty\n"));
142
+ return;
143
+ }
144
+ console.log(chalk.default.bold.cyan("\n 🧠 MEMORY\n"));
145
+ const lines = content.split("\n").filter((l) => l.trim());
146
+ for (const line of lines) if (line.startsWith("#")) console.log(chalk.default.bold.white(line));
147
+ else if (line.startsWith("-")) {
148
+ const parts = line.match(/^-\s+(\S+)\s+\[(\w+)\]\s+(.+)$/);
149
+ if (parts) {
150
+ const [, date, cat, fact] = parts;
151
+ const catColor = cat === "preference" ? chalk.default.cyan : cat === "identity" ? chalk.default.yellow : cat === "goal" ? chalk.default.green : chalk.default.gray;
152
+ console.log(` ${chalk.default.gray(date)} ${catColor(`[${cat}]`)} ${fact}`);
153
+ } else console.log(` ${line}`);
154
+ }
155
+ console.log();
156
+ }
157
+ async function clearMemory() {
158
+ await fs_extra.default.writeFile(MEMORY_FILE, "# HyperClaw Memory\n\n");
159
+ console.log(chalk.default.green(" ✅ Memory cleared\n"));
160
+ }
161
+ async function searchMemory(query) {
162
+ const content = await readMemory();
163
+ const lines = content.split("\n").filter((l) => l.toLowerCase().includes(query.toLowerCase()));
164
+ if (lines.length === 0) {
165
+ console.log(chalk.default.gray(` No memories found for: "${query}"\n`));
166
+ return;
167
+ }
168
+ console.log(chalk.default.bold.cyan(`\n 🔍 Memory search: "${query}"\n`));
169
+ for (const line of lines) console.log(` ${line}`);
170
+ console.log();
171
+ }
172
+ var HC_DIR, MEMORY_FILE, SOUL_FILE, PREFERENCE_PATTERNS, IDENTITY_PATTERNS, GOAL_PATTERNS, SENSITIVE_PATTERNS, AutoMemory;
173
+ var init_memory_auto = require_chunk.__esm({ "packages/core/src/agent/memory-auto.ts"() {
174
+ HC_DIR = path.default.join(os.default.homedir(), ".hyperclaw");
175
+ MEMORY_FILE = path.default.join(HC_DIR, "MEMORY.md");
176
+ SOUL_FILE = path.default.join(HC_DIR, "SOUL.md");
177
+ PREFERENCE_PATTERNS = [
178
+ /i (?:prefer|like|love|hate|dislike|always|never)\s+(.{5,80})/i,
179
+ /(?:my favorite|my preferred)\s+(.{5,80})/i,
180
+ /(?:please )?always\s+((?:use|do|write|respond|answer|format|reply)\s+.{5,60})/i,
181
+ /don'?t (?:ever )?(.{5,60})/i,
182
+ /(?:i want you to always)\s+(.{5,80})/i
183
+ ];
184
+ IDENTITY_PATTERNS = [
185
+ /(?:my name is|i'?m called|call me)\s+([A-ZΑ-Ωa-zα-ω][a-zα-ω]{1,30})/i,
186
+ /i(?:'m| am) (?:a |an )?([A-Za-zΑ-Ωα-ω][\w\s]{3,40}?)(?:\.|,|$)/i,
187
+ /i work (?:at|for|as)\s+(.{5,60})/i,
188
+ /i(?:'m| am) (?:from|in|based in)\s+(.{3,40})/i,
189
+ /my (?:job|role|position|title) is\s+(.{5,60})/i
190
+ ];
191
+ GOAL_PATTERNS = [
192
+ /i(?:'m| am) (?:working on|building|creating|developing)\s+(.{10,100})/i,
193
+ /i want to\s+(.{10,100})/i,
194
+ /my goal is (?:to\s+)?(.{10,100})/i,
195
+ /i'?m trying to\s+(.{10,100})/i
196
+ ];
197
+ SENSITIVE_PATTERNS = [
198
+ /password/i,
199
+ /passwd/i,
200
+ /secret key/i,
201
+ /api.?key/i,
202
+ /credit.?card/i,
203
+ /\b\d{4}[\s-]\d{4}[\s-]\d{4}[\s-]\d{4}\b/,
204
+ /ssn|social security/i,
205
+ /\b\d{3}-\d{2}-\d{4}\b/
206
+ ];
207
+ AutoMemory = class {
208
+ turns = [];
209
+ turnsSinceLastExtract = 0;
210
+ extractEveryNTurns;
211
+ useAI;
212
+ aiOpts;
213
+ constructor(opts = {}) {
214
+ this.extractEveryNTurns = opts.extractEveryNTurns ?? 4;
215
+ this.useAI = opts.useAI ?? false;
216
+ this.aiOpts = opts.aiOpts;
217
+ }
218
+ addTurn(role, content) {
219
+ this.turns.push({
220
+ role,
221
+ content,
222
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
223
+ });
224
+ this.turnsSinceLastExtract++;
225
+ }
226
+ async maybeExtract() {
227
+ if (this.turnsSinceLastExtract < this.extractEveryNTurns) return 0;
228
+ this.turnsSinceLastExtract = 0;
229
+ return this.extract();
230
+ }
231
+ async extract() {
232
+ const facts = this.useAI && this.aiOpts ? await extractFactsWithAI(this.turns, this.aiOpts) : extractFactsLocally(this.turns);
233
+ const saved = await appendMemory(facts);
234
+ if (saved > 0) console.log(chalk.default.gray(` 🧠 Auto-saved ${saved} fact${saved === 1 ? "" : "s"} to MEMORY.md`));
235
+ return saved;
236
+ }
237
+ clearTurns() {
238
+ this.turns = [];
239
+ }
240
+ getTurns() {
241
+ return [...this.turns];
242
+ }
243
+ };
244
+ } });
245
+
246
+ //#endregion
247
+ Object.defineProperty(exports, 'AutoMemory', {
248
+ enumerable: true,
249
+ get: function () {
250
+ return AutoMemory;
251
+ }
252
+ });
253
+ Object.defineProperty(exports, 'appendMemory', {
254
+ enumerable: true,
255
+ get: function () {
256
+ return appendMemory;
257
+ }
258
+ });
259
+ Object.defineProperty(exports, 'clearMemory', {
260
+ enumerable: true,
261
+ get: function () {
262
+ return clearMemory;
263
+ }
264
+ });
265
+ Object.defineProperty(exports, 'extractFactsLocally', {
266
+ enumerable: true,
267
+ get: function () {
268
+ return extractFactsLocally;
269
+ }
270
+ });
271
+ Object.defineProperty(exports, 'extractFactsWithAI', {
272
+ enumerable: true,
273
+ get: function () {
274
+ return extractFactsWithAI;
275
+ }
276
+ });
277
+ Object.defineProperty(exports, 'init_memory_auto', {
278
+ enumerable: true,
279
+ get: function () {
280
+ return init_memory_auto;
281
+ }
282
+ });
283
+ Object.defineProperty(exports, 'readMemory', {
284
+ enumerable: true,
285
+ get: function () {
286
+ return readMemory;
287
+ }
288
+ });
289
+ Object.defineProperty(exports, 'saveMemoryDirect', {
290
+ enumerable: true,
291
+ get: function () {
292
+ return saveMemoryDirect;
293
+ }
294
+ });
295
+ Object.defineProperty(exports, 'searchMemory', {
296
+ enumerable: true,
297
+ get: function () {
298
+ return searchMemory;
299
+ }
300
+ });
301
+ Object.defineProperty(exports, 'showMemory', {
302
+ enumerable: true,
303
+ get: function () {
304
+ return showMemory;
305
+ }
306
+ });
@@ -0,0 +1,226 @@
1
+ const require_chunk = require('./chunk-jS-bbMI5.js');
2
+ const require_paths = require('./paths-AIyBxIzm.js');
3
+ const require_paths$1 = require('./paths-DPovhojT.js');
4
+ const chalk = require_chunk.__toESM(require("chalk"));
5
+ const inquirer = require_chunk.__toESM(require("inquirer"));
6
+ const ora = require_chunk.__toESM(require("ora"));
7
+ const fs_extra = require_chunk.__toESM(require("fs-extra"));
8
+ const path = require_chunk.__toESM(require("path"));
9
+ const os = require_chunk.__toESM(require("os"));
10
+
11
+ //#region src/commands/node.ts
12
+ require_paths$1.init_paths();
13
+ const getNodesFile = () => path.default.join(require_paths.getHyperClawDir(), "nodes.json");
14
+ async function loadNodes() {
15
+ try {
16
+ return await fs_extra.default.readJson(getNodesFile());
17
+ } catch {
18
+ return [];
19
+ }
20
+ }
21
+ async function saveNodes(nodes) {
22
+ const nodesFile = getNodesFile();
23
+ await fs_extra.default.ensureDir(path.default.dirname(nodesFile));
24
+ await fs_extra.default.writeJson(nodesFile, nodes, { spaces: 2 });
25
+ }
26
+ const TYPE_EMOJI = {
27
+ local: "💻",
28
+ remote: "🖥️",
29
+ android: "📱",
30
+ raspberrypi: "🍓",
31
+ docker: "🐳",
32
+ vm: "☁️"
33
+ };
34
+ const STATUS_COLOR = {
35
+ online: chalk.default.green,
36
+ offline: chalk.default.red,
37
+ unknown: chalk.default.gray,
38
+ degraded: chalk.default.yellow
39
+ };
40
+ async function nodeList() {
41
+ const nodes = await loadNodes();
42
+ console.log(chalk.default.bold.cyan("\n 📱 HYPERCLAW NODES\n"));
43
+ console.log(` ${chalk.default.green("●")} ${chalk.default.white("Local (this machine)".padEnd(22))} ${chalk.default.cyan("[local]")} ${chalk.default.green("online")}`);
44
+ console.log(` ${chalk.default.gray(`Node.js ${process.version} ${os.default.platform()} ${os.default.arch()} port 18789`)}`);
45
+ console.log();
46
+ if (nodes.length === 0) {
47
+ console.log(chalk.default.gray(" No additional nodes registered."));
48
+ console.log(chalk.default.gray(" Add a remote node: hyperclaw node add\n"));
49
+ return;
50
+ }
51
+ for (const node of nodes) {
52
+ const dot = STATUS_COLOR[node.status]("●");
53
+ const emoji = TYPE_EMOJI[node.type];
54
+ const status = STATUS_COLOR[node.status](node.status);
55
+ console.log(` ${dot} ${chalk.default.white(node.name.padEnd(22))} ${chalk.default.cyan(`[${node.type}]`)} ${status}`);
56
+ if (node.host) console.log(` ${chalk.default.gray(`${node.host}:${node.port || 18789}`)}`);
57
+ if (node.androidDeviceId) console.log(` ${chalk.default.gray(`adb: ${node.androidDeviceId}`)}`);
58
+ if (node.capabilities.length > 0) console.log(` ${chalk.default.gray("caps:")} ${node.capabilities.join(", ")}`);
59
+ if (node.lastSeenAt) {
60
+ const ago = Math.round((Date.now() - new Date(node.lastSeenAt).getTime()) / 1e3 / 60);
61
+ console.log(` ${chalk.default.gray(`last seen: ${ago}m ago`)}`);
62
+ }
63
+ console.log();
64
+ }
65
+ }
66
+ async function nodeAdd() {
67
+ console.log(chalk.default.bold.cyan("\n ➕ ADD NODE\n"));
68
+ const { type } = await inquirer.default.prompt([{
69
+ type: "list",
70
+ name: "type",
71
+ message: "Node type:",
72
+ choices: [
73
+ {
74
+ name: "🖥️ Remote server (SSH)",
75
+ value: "remote"
76
+ },
77
+ {
78
+ name: "📱 Android device (ADB)",
79
+ value: "android"
80
+ },
81
+ {
82
+ name: "🍓 Raspberry Pi",
83
+ value: "raspberrypi"
84
+ },
85
+ {
86
+ name: "🐳 Docker container",
87
+ value: "docker"
88
+ },
89
+ {
90
+ name: "☁️ VM / cloud instance",
91
+ value: "vm"
92
+ }
93
+ ]
94
+ }]);
95
+ const { name } = await inquirer.default.prompt([{
96
+ type: "input",
97
+ name: "name",
98
+ message: "Node name:",
99
+ validate: (v) => !!v.trim() || "Required"
100
+ }]);
101
+ let extras = {};
102
+ if (type === "android") {
103
+ const { deviceId } = await inquirer.default.prompt([{
104
+ type: "input",
105
+ name: "deviceId",
106
+ message: "ADB device ID (run: adb devices):",
107
+ validate: (v) => !!v.trim() || "Required"
108
+ }]);
109
+ extras.androidDeviceId = deviceId;
110
+ extras.capabilities = ["channel:host", "always-on"];
111
+ } else {
112
+ const { host, port } = await inquirer.default.prompt([{
113
+ type: "input",
114
+ name: "host",
115
+ message: "Hostname or IP:",
116
+ validate: (v) => !!v.trim() || "Required"
117
+ }, {
118
+ type: "number",
119
+ name: "port",
120
+ message: "Gateway port:",
121
+ default: 18789
122
+ }]);
123
+ const { caps } = await inquirer.default.prompt([{
124
+ type: "checkbox",
125
+ name: "caps",
126
+ message: "Capabilities:",
127
+ choices: [
128
+ {
129
+ name: "Run agent inference",
130
+ value: "agent:run",
131
+ checked: true
132
+ },
133
+ {
134
+ name: "Host channel connections",
135
+ value: "channel:host"
136
+ },
137
+ {
138
+ name: "Execute code",
139
+ value: "code:execute"
140
+ },
141
+ {
142
+ name: "GPU acceleration",
143
+ value: "gpu"
144
+ },
145
+ {
146
+ name: "Always-on (24/7)",
147
+ value: "always-on"
148
+ }
149
+ ]
150
+ }]);
151
+ extras = {
152
+ host,
153
+ port,
154
+ capabilities: caps
155
+ };
156
+ }
157
+ const node = {
158
+ id: name.toLowerCase().replace(/\s+/g, "-"),
159
+ name,
160
+ type,
161
+ status: "unknown",
162
+ addedAt: (/* @__PURE__ */ new Date()).toISOString(),
163
+ capabilities: extras.capabilities || [],
164
+ ...extras
165
+ };
166
+ const nodes = await loadNodes();
167
+ nodes.push(node);
168
+ await saveNodes(nodes);
169
+ console.log(chalk.default.green(`\n ✔ Node added: ${name}`));
170
+ console.log(chalk.default.gray(" Run: hyperclaw node probe to test the connection\n"));
171
+ }
172
+ async function nodeProbe(id) {
173
+ const nodes = await loadNodes();
174
+ const targets = id ? nodes.filter((n) => n.id === id) : nodes;
175
+ console.log(chalk.default.bold.cyan("\n 🔍 PROBING NODES\n"));
176
+ console.log(` ${chalk.default.cyan("○")} Local...`);
177
+ await new Promise((r) => setTimeout(r, 300));
178
+ console.log(` ${chalk.default.green("✔")} Local — online (${os.default.platform()} ${os.default.arch()})\n`);
179
+ for (const node of targets) {
180
+ const spinner = (0, ora.default)(` Probing ${node.name} (${node.host || node.androidDeviceId || node.type})...`).start();
181
+ await new Promise((r) => setTimeout(r, 1e3));
182
+ try {
183
+ if (node.host) {
184
+ const axios = (await import("axios")).default;
185
+ const res = await axios.get(`http://${node.host}:${node.port || 18789}/api/status`, { timeout: 3e3 });
186
+ node.status = "online";
187
+ node.version = res.data?.version;
188
+ node.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
189
+ spinner.succeed(`${node.name} — online${node.version ? ` (v${node.version})` : ""}`);
190
+ } else if (node.type === "android") {
191
+ const { exec } = await import("child_process");
192
+ const { promisify } = await import("util");
193
+ const execAsync = promisify(exec);
194
+ await execAsync(`adb -s ${node.androidDeviceId} shell echo ok`);
195
+ node.status = "online";
196
+ node.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
197
+ spinner.succeed(`${node.name} — online (ADB connected)`);
198
+ } else {
199
+ node.status = "unknown";
200
+ spinner.warn(`${node.name} — probe not supported for ${node.type}`);
201
+ }
202
+ } catch {
203
+ node.status = "offline";
204
+ spinner.fail(`${node.name} — offline / unreachable`);
205
+ }
206
+ console.log();
207
+ }
208
+ await saveNodes(nodes);
209
+ }
210
+ async function nodeRemove(id) {
211
+ const nodes = await loadNodes();
212
+ const idx = nodes.findIndex((n) => n.id === id);
213
+ if (idx === -1) {
214
+ console.log(chalk.default.red(`\n ✖ Node not found: ${id}\n`));
215
+ return;
216
+ }
217
+ nodes.splice(idx, 1);
218
+ await saveNodes(nodes);
219
+ console.log(chalk.default.green(`\n ✔ Node removed: ${id}\n`));
220
+ }
221
+
222
+ //#endregion
223
+ exports.nodeAdd = nodeAdd;
224
+ exports.nodeList = nodeList;
225
+ exports.nodeProbe = nodeProbe;
226
+ exports.nodeRemove = nodeRemove;
@@ -0,0 +1,148 @@
1
+ const require_chunk = require('./chunk-jS-bbMI5.js');
2
+ const crypto = require_chunk.__toESM(require("crypto"));
3
+ const http = require_chunk.__toESM(require("http"));
4
+
5
+ //#region src/services/oauth-flow.ts
6
+ const REDIRECT_PORT = 38789;
7
+ const REDIRECT_PATH = "/oauth/callback";
8
+ const PROVIDERS = {
9
+ google: {
10
+ authorize_url: "https://accounts.google.com/o/oauth2/v2/auth",
11
+ token_url: "https://oauth2.googleapis.com/token",
12
+ scopes: [
13
+ "openid",
14
+ "email",
15
+ "profile",
16
+ "https://www.googleapis.com/auth/aiplatform"
17
+ ],
18
+ client_id: process.env.GOOGLE_OAUTH_CLIENT_ID || "",
19
+ client_secret: process.env.GOOGLE_OAUTH_CLIENT_SECRET
20
+ },
21
+ "google-gmail": {
22
+ authorize_url: "https://accounts.google.com/o/oauth2/v2/auth",
23
+ token_url: "https://oauth2.googleapis.com/token",
24
+ scopes: [
25
+ "openid",
26
+ "email",
27
+ "profile",
28
+ "https://www.googleapis.com/auth/gmail.modify",
29
+ "https://mail.google.com/"
30
+ ],
31
+ client_id: process.env.GOOGLE_OAUTH_CLIENT_ID || "",
32
+ client_secret: process.env.GOOGLE_OAUTH_CLIENT_SECRET
33
+ },
34
+ microsoft: {
35
+ authorize_url: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
36
+ token_url: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
37
+ scopes: [
38
+ "openid",
39
+ "profile",
40
+ "offline_access",
41
+ "https://cognitiveservices.azure.com/.default"
42
+ ],
43
+ client_id: process.env.AZURE_OAUTH_CLIENT_ID || process.env.MICROSOFT_OAUTH_CLIENT_ID || "",
44
+ client_secret: process.env.AZURE_OAUTH_CLIENT_SECRET || process.env.MICROSOFT_OAUTH_CLIENT_SECRET
45
+ }
46
+ };
47
+ async function runOAuthFlow(providerId, opts) {
48
+ const cfg = PROVIDERS[providerId];
49
+ if (!cfg) throw new Error(`OAuth provider "${providerId}" not configured. Supported: google, google-gmail (Gmail Pub/Sub), microsoft. Anthropic/OpenAI: use "hyperclaw auth add" (API keys) or "hyperclaw auth setup-token anthropic" for Claude Pro/Max.`);
50
+ const clientId = opts?.clientId || cfg.client_id || process.env.OAUTH_CLIENT_ID;
51
+ const clientSecret = opts?.clientSecret || cfg.client_secret || process.env.OAUTH_CLIENT_SECRET;
52
+ const scopes = opts?.scopes || cfg.scopes;
53
+ if (!clientId) {
54
+ const hint = providerId === "google" ? "Set GOOGLE_OAUTH_CLIENT_ID or OAUTH_CLIENT_ID. Create at: https://console.cloud.google.com/apis/credentials" : providerId === "microsoft" ? "Set AZURE_OAUTH_CLIENT_ID. Create at: https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade" : "Set OAUTH_CLIENT_ID or pass --client-id";
55
+ throw new Error(`OAuth client_id required. ${hint}`);
56
+ }
57
+ const codeVerifier = crypto.default.randomBytes(32).toString("base64url");
58
+ const codeChallenge = crypto.default.createHash("sha256").update(codeVerifier).digest("base64url");
59
+ const authParams = new URLSearchParams({
60
+ client_id: clientId,
61
+ redirect_uri: `http://127.0.0.1:${REDIRECT_PORT}${REDIRECT_PATH}`,
62
+ response_type: "code",
63
+ scope: scopes.join(" "),
64
+ code_challenge: codeChallenge,
65
+ code_challenge_method: "S256",
66
+ access_type: "offline",
67
+ prompt: "consent"
68
+ });
69
+ const authUrl = `${cfg.authorize_url}?${authParams}`;
70
+ return new Promise((resolve, reject) => {
71
+ const server = http.default.createServer(async (req, res) => {
72
+ const url = new URL(req.url || "/", `http://127.0.0.1`);
73
+ if (url.pathname !== REDIRECT_PATH) {
74
+ res.writeHead(404);
75
+ res.end("Not found");
76
+ return;
77
+ }
78
+ const code = url.searchParams.get("code");
79
+ const error = url.searchParams.get("error");
80
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
81
+ if (error) {
82
+ const safeError = String(error).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
83
+ res.writeHead(400);
84
+ res.end(`<h1>OAuth error</h1><p>${safeError}</p><p>You can close this tab.</p>`);
85
+ server.close();
86
+ reject(new Error(`OAuth error: ${error}`));
87
+ return;
88
+ }
89
+ if (!code) {
90
+ res.writeHead(400);
91
+ res.end("<h1>No code received</h1><p>You can close this tab.</p>");
92
+ server.close();
93
+ reject(new Error("No authorization code received"));
94
+ return;
95
+ }
96
+ const body = new URLSearchParams({
97
+ grant_type: "authorization_code",
98
+ code,
99
+ redirect_uri: `http://127.0.0.1:${REDIRECT_PORT}${REDIRECT_PATH}`,
100
+ code_verifier: codeVerifier
101
+ });
102
+ if (clientSecret) body.set("client_secret", clientSecret);
103
+ body.set("client_id", clientId);
104
+ try {
105
+ const tokenRes = await fetch(cfg.token_url, {
106
+ method: "POST",
107
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
108
+ body: body.toString()
109
+ });
110
+ const tokenData = await tokenRes.json();
111
+ if (tokenData.error) {
112
+ res.writeHead(400);
113
+ res.end(`<h1>Token error</h1><p>${tokenData.error}</p><p>You can close this tab.</p>`);
114
+ server.close();
115
+ reject(new Error(tokenData.error_description || tokenData.error));
116
+ return;
117
+ }
118
+ res.writeHead(200);
119
+ res.end("<h1>Success!</h1><p>HyperClaw has received your tokens. You can close this tab and return to the terminal.</p>");
120
+ server.close();
121
+ resolve({
122
+ access_token: tokenData.access_token,
123
+ refresh_token: tokenData.refresh_token,
124
+ expires_in: tokenData.expires_in
125
+ });
126
+ } catch (e) {
127
+ res.writeHead(500);
128
+ res.end(`<h1>Error</h1><p>${e.message}</p><p>You can close this tab.</p>`);
129
+ server.close();
130
+ reject(e);
131
+ }
132
+ });
133
+ server.listen(REDIRECT_PORT, "127.0.0.1", () => {
134
+ try {
135
+ const { exec } = require("child_process");
136
+ const opener = process.platform === "win32" ? `start "" "${authUrl}"` : (process.platform === "darwin" ? "open" : "xdg-open") + ` "${authUrl}"`;
137
+ exec(opener);
138
+ } catch {}
139
+ });
140
+ server.on("error", (err) => {
141
+ server.close();
142
+ reject(err);
143
+ });
144
+ });
145
+ }
146
+
147
+ //#endregion
148
+ exports.runOAuthFlow = runOAuthFlow;