@projectservan8n/cnapse 0.8.2 → 0.10.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/dist/ProviderSelector-GZYF26LL.js +7 -0
- package/dist/autonomous-VGEVIXXQ.js +419 -0
- package/dist/browser-YLFWQXIY.js +87 -0
- package/dist/{chunk-OPX7FFL6.js → chunk-7SDY7OPA.js} +14 -55
- package/dist/chunk-COKO6V5J.js +50 -0
- package/dist/chunk-GP73OJCZ.js +377 -0
- package/dist/chunk-MOKGR7WE.js +344 -0
- package/dist/chunk-OIVTPXE4.js +307 -0
- package/dist/chunk-TFHK5CYF.js +650 -0
- package/dist/chunk-WSBJFRQH.js +366 -0
- package/dist/index.js +579 -1733
- package/dist/learner-KH3TFTD7.js +14 -0
- package/dist/vision-S57PWSCU.js +19 -0
- package/package.json +1 -2
- package/src/agents/autonomous.ts +515 -0
- package/src/agents/learner.ts +489 -0
- package/src/lib/tasks.ts +217 -153
- package/src/lib/vision.ts +139 -0
- package/src/services/browser.ts +336 -584
- package/src/services/screen-monitor.ts +288 -0
- package/src/services/telegram.ts +312 -5
- package/src/tools/computer.ts +226 -0
- package/dist/ProviderSelector-MXRZFAOB.js +0 -6
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chat
|
|
3
|
+
} from "./chunk-GP73OJCZ.js";
|
|
4
|
+
import {
|
|
5
|
+
pressKey,
|
|
6
|
+
typeText
|
|
7
|
+
} from "./chunk-TFHK5CYF.js";
|
|
8
|
+
import {
|
|
9
|
+
describeScreen
|
|
10
|
+
} from "./chunk-OIVTPXE4.js";
|
|
11
|
+
|
|
12
|
+
// src/agents/learner.ts
|
|
13
|
+
import { EventEmitter } from "events";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
17
|
+
import { existsSync } from "fs";
|
|
18
|
+
var MEMORY_FILE = join(homedir(), ".cnapse", "agent-memory.json");
|
|
19
|
+
var MAX_MEMORY_SIZE = 500;
|
|
20
|
+
var AgentLearner = class extends EventEmitter {
|
|
21
|
+
memory;
|
|
22
|
+
loaded = false;
|
|
23
|
+
constructor() {
|
|
24
|
+
super();
|
|
25
|
+
this.memory = this.createEmptyMemory();
|
|
26
|
+
}
|
|
27
|
+
createEmptyMemory() {
|
|
28
|
+
return {
|
|
29
|
+
version: 1,
|
|
30
|
+
learned: [],
|
|
31
|
+
stats: {
|
|
32
|
+
totalAttempts: 0,
|
|
33
|
+
totalSuccesses: 0,
|
|
34
|
+
totalLearned: 0,
|
|
35
|
+
sourceCounts: {}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Load memory from disk
|
|
41
|
+
*/
|
|
42
|
+
async load() {
|
|
43
|
+
if (this.loaded) return;
|
|
44
|
+
try {
|
|
45
|
+
const dir = join(homedir(), ".cnapse");
|
|
46
|
+
if (!existsSync(dir)) {
|
|
47
|
+
await mkdir(dir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
if (existsSync(MEMORY_FILE)) {
|
|
50
|
+
const data = await readFile(MEMORY_FILE, "utf-8");
|
|
51
|
+
this.memory = JSON.parse(data);
|
|
52
|
+
}
|
|
53
|
+
this.loaded = true;
|
|
54
|
+
this.emit("loaded", this.memory.learned.length);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("Failed to load agent memory:", error);
|
|
57
|
+
this.memory = this.createEmptyMemory();
|
|
58
|
+
this.loaded = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Save memory to disk
|
|
63
|
+
*/
|
|
64
|
+
async save() {
|
|
65
|
+
try {
|
|
66
|
+
const dir = join(homedir(), ".cnapse");
|
|
67
|
+
if (!existsSync(dir)) {
|
|
68
|
+
await mkdir(dir, { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
if (this.memory.learned.length > MAX_MEMORY_SIZE) {
|
|
71
|
+
this.memory.learned.sort((a, b) => {
|
|
72
|
+
const aScore = a.successCount - a.failCount;
|
|
73
|
+
const bScore = b.successCount - b.failCount;
|
|
74
|
+
return bScore - aScore;
|
|
75
|
+
});
|
|
76
|
+
this.memory.learned = this.memory.learned.slice(0, MAX_MEMORY_SIZE);
|
|
77
|
+
}
|
|
78
|
+
await writeFile(MEMORY_FILE, JSON.stringify(this.memory, null, 2));
|
|
79
|
+
this.emit("saved", this.memory.learned.length);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error("Failed to save agent memory:", error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if we've solved something similar before
|
|
86
|
+
*/
|
|
87
|
+
async recall(goal, currentScreen) {
|
|
88
|
+
await this.load();
|
|
89
|
+
const goalLower = goal.toLowerCase();
|
|
90
|
+
const screenLower = currentScreen.toLowerCase();
|
|
91
|
+
const candidates = this.memory.learned.filter((m) => {
|
|
92
|
+
const goalSimilarity = this.calculateSimilarity(goalLower, m.goal.toLowerCase());
|
|
93
|
+
const screenSimilarity = this.calculateSimilarity(screenLower, m.situation.toLowerCase());
|
|
94
|
+
return goalSimilarity > 0.5 && screenSimilarity > 0.3;
|
|
95
|
+
});
|
|
96
|
+
if (candidates.length === 0) return null;
|
|
97
|
+
candidates.sort((a, b) => {
|
|
98
|
+
const aScore = a.successCount - a.failCount + a.successCount * 0.1;
|
|
99
|
+
const bScore = b.successCount - b.failCount + b.successCount * 0.1;
|
|
100
|
+
return bScore - aScore;
|
|
101
|
+
});
|
|
102
|
+
const best = candidates[0];
|
|
103
|
+
if (best.successCount > best.failCount) {
|
|
104
|
+
this.emit("recalled", best);
|
|
105
|
+
return best;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Simple word-based similarity (Jaccard-like)
|
|
111
|
+
*/
|
|
112
|
+
calculateSimilarity(a, b) {
|
|
113
|
+
const wordsA = new Set(a.split(/\s+/).filter((w) => w.length > 2));
|
|
114
|
+
const wordsB = new Set(b.split(/\s+/).filter((w) => w.length > 2));
|
|
115
|
+
if (wordsA.size === 0 || wordsB.size === 0) return 0;
|
|
116
|
+
let intersection = 0;
|
|
117
|
+
for (const word of wordsA) {
|
|
118
|
+
if (wordsB.has(word)) intersection++;
|
|
119
|
+
}
|
|
120
|
+
const union = wordsA.size + wordsB.size - intersection;
|
|
121
|
+
return intersection / union;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Learn from a successful action
|
|
125
|
+
*/
|
|
126
|
+
async learn(situation, goal, actionType, actionValue, source) {
|
|
127
|
+
await this.load();
|
|
128
|
+
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
129
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
130
|
+
const existing = this.memory.learned.find(
|
|
131
|
+
(m) => m.goal.toLowerCase() === goal.toLowerCase() && m.actionType === actionType && m.actionValue === actionValue
|
|
132
|
+
);
|
|
133
|
+
if (existing) {
|
|
134
|
+
existing.successCount++;
|
|
135
|
+
existing.lastUsed = now;
|
|
136
|
+
this.emit("reinforced", existing);
|
|
137
|
+
} else {
|
|
138
|
+
const learned = {
|
|
139
|
+
id,
|
|
140
|
+
situation: situation.slice(0, 500),
|
|
141
|
+
// Truncate long descriptions
|
|
142
|
+
goal,
|
|
143
|
+
solution: `${actionType}: ${actionValue}`,
|
|
144
|
+
actionType,
|
|
145
|
+
actionValue,
|
|
146
|
+
source,
|
|
147
|
+
successCount: 1,
|
|
148
|
+
failCount: 0,
|
|
149
|
+
lastUsed: now,
|
|
150
|
+
created: now
|
|
151
|
+
};
|
|
152
|
+
this.memory.learned.push(learned);
|
|
153
|
+
this.memory.stats.totalLearned++;
|
|
154
|
+
this.memory.stats.sourceCounts[source] = (this.memory.stats.sourceCounts[source] || 0) + 1;
|
|
155
|
+
this.emit("learned", learned);
|
|
156
|
+
}
|
|
157
|
+
await this.save();
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Record a failed attempt
|
|
161
|
+
*/
|
|
162
|
+
async recordFailure(goal, actionType, actionValue) {
|
|
163
|
+
await this.load();
|
|
164
|
+
const existing = this.memory.learned.find(
|
|
165
|
+
(m) => m.goal.toLowerCase() === goal.toLowerCase() && m.actionType === actionType && m.actionValue === actionValue
|
|
166
|
+
);
|
|
167
|
+
if (existing) {
|
|
168
|
+
existing.failCount++;
|
|
169
|
+
await this.save();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get help from multiple AI sources when stuck
|
|
174
|
+
*/
|
|
175
|
+
async getHelp(goal, currentScreen, triedActions) {
|
|
176
|
+
this.emit("seeking_help", { goal, triedActions: triedActions.length });
|
|
177
|
+
const query = this.buildHelpQuery(goal, currentScreen, triedActions);
|
|
178
|
+
const suggestions = [];
|
|
179
|
+
const sources = await Promise.allSettled([
|
|
180
|
+
this.askOwnAI(query),
|
|
181
|
+
this.askPerplexity(goal, currentScreen),
|
|
182
|
+
this.askWebSearch(goal)
|
|
183
|
+
]);
|
|
184
|
+
for (const result of sources) {
|
|
185
|
+
if (result.status === "fulfilled" && result.value) {
|
|
186
|
+
suggestions.push(result.value);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
suggestions.sort((a, b) => b.confidence - a.confidence);
|
|
190
|
+
this.emit("got_help", suggestions.length);
|
|
191
|
+
return suggestions;
|
|
192
|
+
}
|
|
193
|
+
buildHelpQuery(goal, screen, tried) {
|
|
194
|
+
return `I'm trying to: ${goal}
|
|
195
|
+
|
|
196
|
+
Current screen shows: ${screen.slice(0, 500)}
|
|
197
|
+
|
|
198
|
+
Actions I've already tried:
|
|
199
|
+
${tried.length > 0 ? tried.slice(-5).join("\n") : "None yet"}
|
|
200
|
+
|
|
201
|
+
What's the SINGLE next action I should take?
|
|
202
|
+
Be very specific - tell me exactly what to click, what to type, or what key to press.
|
|
203
|
+
|
|
204
|
+
Respond in this format:
|
|
205
|
+
ACTION: <click|type|press|navigate|scroll|wait>
|
|
206
|
+
VALUE: <what to click on / text to type / key to press / URL to navigate to>
|
|
207
|
+
REASONING: <brief explanation>`;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Ask our configured AI provider for help
|
|
211
|
+
*/
|
|
212
|
+
async askOwnAI(query) {
|
|
213
|
+
try {
|
|
214
|
+
const response = await chat([{ role: "user", content: query }]);
|
|
215
|
+
return this.parseSuggestion(response.content, "own_ai", 0.8);
|
|
216
|
+
} catch (error) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Ask Perplexity by opening browser (uses existing browser.ts)
|
|
222
|
+
*/
|
|
223
|
+
async askPerplexity(goal, screen) {
|
|
224
|
+
try {
|
|
225
|
+
const browser = await import("./browser-YLFWQXIY.js");
|
|
226
|
+
const question = `How do I ${goal}? I can see ${screen.slice(0, 200)}. Give me the exact next step.`;
|
|
227
|
+
const result = await browser.askAI("perplexity", question);
|
|
228
|
+
if (result.response) {
|
|
229
|
+
return {
|
|
230
|
+
action: "suggested",
|
|
231
|
+
value: result.response.slice(0, 500),
|
|
232
|
+
reasoning: "From Perplexity web search",
|
|
233
|
+
source: "perplexity",
|
|
234
|
+
confidence: 0.7
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Search Google for help
|
|
243
|
+
*/
|
|
244
|
+
async askWebSearch(goal) {
|
|
245
|
+
try {
|
|
246
|
+
const browser = await import("./browser-YLFWQXIY.js");
|
|
247
|
+
const searchQuery = `how to ${goal} step by step`;
|
|
248
|
+
const result = await browser.webSearch(searchQuery);
|
|
249
|
+
if (result.description) {
|
|
250
|
+
return {
|
|
251
|
+
action: "suggested",
|
|
252
|
+
value: result.description.slice(0, 500),
|
|
253
|
+
reasoning: "From web search results",
|
|
254
|
+
source: "google",
|
|
255
|
+
confidence: 0.5
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Parse AI response into structured suggestion
|
|
264
|
+
*/
|
|
265
|
+
parseSuggestion(content, source, baseConfidence) {
|
|
266
|
+
const actionMatch = content.match(/ACTION:\s*(\w+)/i);
|
|
267
|
+
const valueMatch = content.match(/VALUE:\s*(.+?)(?:\n|$)/i);
|
|
268
|
+
const reasoningMatch = content.match(/REASONING:\s*(.+?)(?:\n|$)/i);
|
|
269
|
+
if (!actionMatch) return null;
|
|
270
|
+
return {
|
|
271
|
+
action: actionMatch[1].toLowerCase(),
|
|
272
|
+
value: valueMatch?.[1]?.trim() || "",
|
|
273
|
+
reasoning: reasoningMatch?.[1]?.trim() || "No reasoning provided",
|
|
274
|
+
source,
|
|
275
|
+
confidence: baseConfidence
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Ask Claude.ai via browser automation
|
|
280
|
+
*/
|
|
281
|
+
async askClaude(query) {
|
|
282
|
+
try {
|
|
283
|
+
const browser = await import("./browser-YLFWQXIY.js");
|
|
284
|
+
await browser.openUrl("https://claude.ai");
|
|
285
|
+
await this.sleep(3e3);
|
|
286
|
+
await typeText(query);
|
|
287
|
+
await this.sleep(500);
|
|
288
|
+
await pressKey("Return");
|
|
289
|
+
await this.sleep(8e3);
|
|
290
|
+
const screen = await describeScreen();
|
|
291
|
+
return {
|
|
292
|
+
action: "suggested",
|
|
293
|
+
value: screen.description.slice(0, 500),
|
|
294
|
+
reasoning: "From Claude.ai",
|
|
295
|
+
source: "claude_web",
|
|
296
|
+
confidence: 0.75
|
|
297
|
+
};
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Ask ChatGPT via browser automation
|
|
304
|
+
*/
|
|
305
|
+
async askChatGPT(query) {
|
|
306
|
+
try {
|
|
307
|
+
const browser = await import("./browser-YLFWQXIY.js");
|
|
308
|
+
await browser.openUrl("https://chat.openai.com");
|
|
309
|
+
await this.sleep(3e3);
|
|
310
|
+
await typeText(query);
|
|
311
|
+
await this.sleep(500);
|
|
312
|
+
await pressKey("Return");
|
|
313
|
+
await this.sleep(8e3);
|
|
314
|
+
const screen = await describeScreen();
|
|
315
|
+
return {
|
|
316
|
+
action: "suggested",
|
|
317
|
+
value: screen.description.slice(0, 500),
|
|
318
|
+
reasoning: "From ChatGPT",
|
|
319
|
+
source: "chatgpt_web",
|
|
320
|
+
confidence: 0.75
|
|
321
|
+
};
|
|
322
|
+
} catch (error) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
sleep(ms) {
|
|
327
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Get memory stats
|
|
331
|
+
*/
|
|
332
|
+
getStats() {
|
|
333
|
+
return {
|
|
334
|
+
...this.memory.stats,
|
|
335
|
+
memorySize: this.memory.learned.length
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Get all learned actions (for debugging/display)
|
|
340
|
+
*/
|
|
341
|
+
getAllLearned() {
|
|
342
|
+
return [...this.memory.learned];
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Clear all memory (for testing)
|
|
346
|
+
*/
|
|
347
|
+
async clearMemory() {
|
|
348
|
+
this.memory = this.createEmptyMemory();
|
|
349
|
+
await this.save();
|
|
350
|
+
this.emit("cleared");
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
var learnerInstance = null;
|
|
354
|
+
function getLearner() {
|
|
355
|
+
if (!learnerInstance) {
|
|
356
|
+
learnerInstance = new AgentLearner();
|
|
357
|
+
}
|
|
358
|
+
return learnerInstance;
|
|
359
|
+
}
|
|
360
|
+
var learner_default = AgentLearner;
|
|
361
|
+
|
|
362
|
+
export {
|
|
363
|
+
AgentLearner,
|
|
364
|
+
getLearner,
|
|
365
|
+
learner_default
|
|
366
|
+
};
|