@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,419 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getLearner
|
|
3
|
+
} from "./chunk-WSBJFRQH.js";
|
|
4
|
+
import {
|
|
5
|
+
chat
|
|
6
|
+
} from "./chunk-GP73OJCZ.js";
|
|
7
|
+
import {
|
|
8
|
+
clickMouse,
|
|
9
|
+
getMousePosition,
|
|
10
|
+
keyCombo,
|
|
11
|
+
moveMouse,
|
|
12
|
+
pressKey,
|
|
13
|
+
scrollMouse,
|
|
14
|
+
typeText
|
|
15
|
+
} from "./chunk-TFHK5CYF.js";
|
|
16
|
+
import {
|
|
17
|
+
captureScreenshot,
|
|
18
|
+
describeScreen
|
|
19
|
+
} from "./chunk-OIVTPXE4.js";
|
|
20
|
+
import "./chunk-COKO6V5J.js";
|
|
21
|
+
|
|
22
|
+
// src/agents/autonomous.ts
|
|
23
|
+
import { EventEmitter } from "events";
|
|
24
|
+
var DEFAULT_CONFIG = {
|
|
25
|
+
maxAttempts: 25,
|
|
26
|
+
actionDelayMs: 1500,
|
|
27
|
+
stuckThreshold: 3,
|
|
28
|
+
verifyActions: true,
|
|
29
|
+
humanLikeTiming: true,
|
|
30
|
+
learnFromSuccess: true,
|
|
31
|
+
askForHelpWhenStuck: true
|
|
32
|
+
};
|
|
33
|
+
var AutonomousAgent = class extends EventEmitter {
|
|
34
|
+
state;
|
|
35
|
+
config;
|
|
36
|
+
learner;
|
|
37
|
+
abortController = null;
|
|
38
|
+
constructor(config = {}) {
|
|
39
|
+
super();
|
|
40
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
41
|
+
this.learner = getLearner();
|
|
42
|
+
this.state = this.createInitialState("");
|
|
43
|
+
}
|
|
44
|
+
createInitialState(goal) {
|
|
45
|
+
return {
|
|
46
|
+
goal,
|
|
47
|
+
isActive: false,
|
|
48
|
+
isPaused: false,
|
|
49
|
+
currentAction: null,
|
|
50
|
+
actionHistory: [],
|
|
51
|
+
stuckCount: 0,
|
|
52
|
+
attemptCount: 0,
|
|
53
|
+
lastScreenHash: "",
|
|
54
|
+
startTime: Date.now(),
|
|
55
|
+
confidence: 100
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Start pursuing a goal autonomously
|
|
60
|
+
*/
|
|
61
|
+
async start(goal) {
|
|
62
|
+
if (this.state.isActive) {
|
|
63
|
+
return { success: false, message: "Agent is already running" };
|
|
64
|
+
}
|
|
65
|
+
this.state = this.createInitialState(goal);
|
|
66
|
+
this.state.isActive = true;
|
|
67
|
+
this.abortController = new AbortController();
|
|
68
|
+
this.emit("started", { goal });
|
|
69
|
+
try {
|
|
70
|
+
await this.learner.load();
|
|
71
|
+
while (this.state.isActive && this.state.attemptCount < this.config.maxAttempts) {
|
|
72
|
+
if (this.state.isPaused) {
|
|
73
|
+
await this.sleep(500);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
this.state.attemptCount++;
|
|
77
|
+
this.emit("attempt", { count: this.state.attemptCount, max: this.config.maxAttempts });
|
|
78
|
+
const observation = await this.observe();
|
|
79
|
+
if (!observation) continue;
|
|
80
|
+
const remembered = await this.learner.recall(goal, observation.description);
|
|
81
|
+
if (remembered) {
|
|
82
|
+
this.emit("recalled", remembered);
|
|
83
|
+
const result2 = await this.executeAction(remembered.actionType, remembered.actionValue);
|
|
84
|
+
if (result2.success) {
|
|
85
|
+
await this.learner.learn(
|
|
86
|
+
observation.description,
|
|
87
|
+
goal,
|
|
88
|
+
remembered.actionType,
|
|
89
|
+
remembered.actionValue,
|
|
90
|
+
"memory"
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const decision = await this.think(observation.description);
|
|
96
|
+
if (decision.action === "done") {
|
|
97
|
+
this.state.isActive = false;
|
|
98
|
+
this.emit("completed", { success: true, attempts: this.state.attemptCount });
|
|
99
|
+
return { success: true, message: "Goal accomplished!" };
|
|
100
|
+
}
|
|
101
|
+
if (decision.action === "stuck") {
|
|
102
|
+
this.state.stuckCount++;
|
|
103
|
+
if (this.state.stuckCount >= this.config.stuckThreshold && this.config.askForHelpWhenStuck) {
|
|
104
|
+
this.emit("asking_help", { stuckCount: this.state.stuckCount });
|
|
105
|
+
const suggestions = await this.learner.getHelp(
|
|
106
|
+
goal,
|
|
107
|
+
observation.description,
|
|
108
|
+
this.state.actionHistory.slice(-5).map((a) => `${a.action}: ${a.value}`)
|
|
109
|
+
);
|
|
110
|
+
if (suggestions.length > 0) {
|
|
111
|
+
const suggestion = suggestions[0];
|
|
112
|
+
this.emit("trying_suggestion", suggestion);
|
|
113
|
+
const result2 = await this.executeAction(suggestion.action, suggestion.value);
|
|
114
|
+
if (result2.success && this.config.learnFromSuccess) {
|
|
115
|
+
await this.learner.learn(
|
|
116
|
+
observation.description,
|
|
117
|
+
goal,
|
|
118
|
+
suggestion.action,
|
|
119
|
+
suggestion.value,
|
|
120
|
+
suggestion.source
|
|
121
|
+
);
|
|
122
|
+
this.state.stuckCount = 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const result = await this.executeAction(decision.action, decision.value);
|
|
129
|
+
if (this.config.verifyActions) {
|
|
130
|
+
const afterScreen = await captureScreenshot();
|
|
131
|
+
const screenChanged = afterScreen !== observation.screenshot;
|
|
132
|
+
if (screenChanged) {
|
|
133
|
+
this.state.stuckCount = 0;
|
|
134
|
+
this.state.confidence = Math.min(100, this.state.confidence + 5);
|
|
135
|
+
if (result.success && this.config.learnFromSuccess) {
|
|
136
|
+
await this.learner.learn(
|
|
137
|
+
observation.description,
|
|
138
|
+
goal,
|
|
139
|
+
decision.action,
|
|
140
|
+
decision.value,
|
|
141
|
+
"self"
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
this.state.stuckCount++;
|
|
146
|
+
this.state.confidence = Math.max(0, this.state.confidence - 10);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (this.config.humanLikeTiming) {
|
|
150
|
+
const delay = this.config.actionDelayMs + Math.random() * 500;
|
|
151
|
+
await this.sleep(delay);
|
|
152
|
+
} else {
|
|
153
|
+
await this.sleep(this.config.actionDelayMs);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
this.state.isActive = false;
|
|
157
|
+
this.emit("completed", { success: false, attempts: this.state.attemptCount });
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
message: `Reached max attempts (${this.config.maxAttempts}). Goal may be partially complete.`
|
|
161
|
+
};
|
|
162
|
+
} catch (error) {
|
|
163
|
+
this.state.isActive = false;
|
|
164
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
165
|
+
this.emit("error", { error: message });
|
|
166
|
+
return { success: false, message };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Stop the agent
|
|
171
|
+
*/
|
|
172
|
+
stop() {
|
|
173
|
+
this.state.isActive = false;
|
|
174
|
+
this.abortController?.abort();
|
|
175
|
+
this.emit("stopped", { attempts: this.state.attemptCount });
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Pause the agent
|
|
179
|
+
*/
|
|
180
|
+
pause() {
|
|
181
|
+
this.state.isPaused = true;
|
|
182
|
+
this.emit("paused");
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Resume the agent
|
|
186
|
+
*/
|
|
187
|
+
resume() {
|
|
188
|
+
this.state.isPaused = false;
|
|
189
|
+
this.emit("resumed");
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Observe current screen state
|
|
193
|
+
*/
|
|
194
|
+
async observe() {
|
|
195
|
+
try {
|
|
196
|
+
this.emit("observing");
|
|
197
|
+
const result = await describeScreen();
|
|
198
|
+
this.emit("observed", { description: result.description.slice(0, 200) });
|
|
199
|
+
return result;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
this.emit("observe_error", { error });
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Think about what action to take next
|
|
207
|
+
*/
|
|
208
|
+
async think(screenDescription) {
|
|
209
|
+
this.emit("thinking");
|
|
210
|
+
const prompt = this.buildThinkingPrompt(screenDescription);
|
|
211
|
+
try {
|
|
212
|
+
const response = await chat([{ role: "user", content: prompt }]);
|
|
213
|
+
const decision = this.parseDecision(response.content);
|
|
214
|
+
this.emit("decided", decision);
|
|
215
|
+
return decision;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
return { action: "stuck", value: "", reasoning: "Failed to get AI decision" };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
buildThinkingPrompt(screenDescription) {
|
|
221
|
+
const recentActions = this.state.actionHistory.slice(-5).map((a) => `- ${a.action}: ${a.value} (${a.result})`).join("\n");
|
|
222
|
+
return `GOAL: ${this.state.goal}
|
|
223
|
+
|
|
224
|
+
CURRENT SCREEN: ${screenDescription}
|
|
225
|
+
|
|
226
|
+
PREVIOUS ACTIONS:
|
|
227
|
+
${recentActions || "None yet"}
|
|
228
|
+
|
|
229
|
+
ATTEMPT: ${this.state.attemptCount}/${this.config.maxAttempts}
|
|
230
|
+
STUCK COUNT: ${this.state.stuckCount}
|
|
231
|
+
|
|
232
|
+
Based on what you see, what's the SINGLE next action to take?
|
|
233
|
+
|
|
234
|
+
Available actions:
|
|
235
|
+
- click: Click at current mouse position
|
|
236
|
+
- clickAt: Click at specific coordinates (VALUE: x,y)
|
|
237
|
+
- type: Type text (VALUE: text to type)
|
|
238
|
+
- press: Press a key (VALUE: Enter, Tab, Escape, etc.)
|
|
239
|
+
- keyCombo: Press key combination (VALUE: command+s, control+c, etc.)
|
|
240
|
+
- scroll: Scroll (VALUE: up or down)
|
|
241
|
+
- navigate: Open URL (VALUE: full URL)
|
|
242
|
+
- moveTo: Move mouse (VALUE: x,y coordinates)
|
|
243
|
+
- wait: Wait for something (VALUE: seconds)
|
|
244
|
+
- done: Goal is accomplished
|
|
245
|
+
- stuck: Can't figure out what to do
|
|
246
|
+
|
|
247
|
+
Respond EXACTLY in this format:
|
|
248
|
+
ACTION: <action_type>
|
|
249
|
+
VALUE: <parameter>
|
|
250
|
+
REASONING: <brief why>`;
|
|
251
|
+
}
|
|
252
|
+
parseDecision(content) {
|
|
253
|
+
const actionMatch = content.match(/ACTION:\s*(\w+)/i);
|
|
254
|
+
const valueMatch = content.match(/VALUE:\s*(.+?)(?:\n|$)/i);
|
|
255
|
+
const reasoningMatch = content.match(/REASONING:\s*(.+?)(?:\n|$)/i);
|
|
256
|
+
return {
|
|
257
|
+
action: actionMatch?.[1]?.toLowerCase() || "stuck",
|
|
258
|
+
value: valueMatch?.[1]?.trim() || "",
|
|
259
|
+
reasoning: reasoningMatch?.[1]?.trim() || "No reasoning provided"
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Execute an action
|
|
264
|
+
*/
|
|
265
|
+
async executeAction(action, value) {
|
|
266
|
+
const record = {
|
|
267
|
+
timestamp: Date.now(),
|
|
268
|
+
action,
|
|
269
|
+
value,
|
|
270
|
+
result: "pending",
|
|
271
|
+
screenBefore: this.state.lastScreenHash,
|
|
272
|
+
reasoning: ""
|
|
273
|
+
};
|
|
274
|
+
this.state.currentAction = `${action}: ${value}`;
|
|
275
|
+
this.emit("executing", { action, value });
|
|
276
|
+
try {
|
|
277
|
+
switch (action) {
|
|
278
|
+
case "click":
|
|
279
|
+
await clickMouse("left");
|
|
280
|
+
break;
|
|
281
|
+
case "clickat":
|
|
282
|
+
case "clickAt": {
|
|
283
|
+
const [x, y] = value.split(",").map((n) => parseInt(n.trim()));
|
|
284
|
+
if (!isNaN(x) && !isNaN(y)) {
|
|
285
|
+
await moveMouse(x, y);
|
|
286
|
+
await this.sleep(100);
|
|
287
|
+
await clickMouse("left");
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
case "type":
|
|
292
|
+
if (this.config.humanLikeTiming) {
|
|
293
|
+
await this.typeHumanLike(value);
|
|
294
|
+
} else {
|
|
295
|
+
await typeText(value);
|
|
296
|
+
}
|
|
297
|
+
break;
|
|
298
|
+
case "press":
|
|
299
|
+
await pressKey(value || "Return");
|
|
300
|
+
break;
|
|
301
|
+
case "keycombo":
|
|
302
|
+
case "keyCombo": {
|
|
303
|
+
const keys = value.split("+").map((k) => k.trim().toLowerCase());
|
|
304
|
+
await keyCombo(keys);
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
case "scroll":
|
|
308
|
+
const amount = value.toLowerCase().includes("up") ? 3 : -3;
|
|
309
|
+
await scrollMouse(amount);
|
|
310
|
+
break;
|
|
311
|
+
case "navigate": {
|
|
312
|
+
const browser = await import("./browser-YLFWQXIY.js");
|
|
313
|
+
const url = value.startsWith("http") ? value : `https://${value}`;
|
|
314
|
+
await browser.openUrl(url);
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
case "moveto":
|
|
318
|
+
case "moveTo": {
|
|
319
|
+
const [mx, my] = value.split(",").map((n) => parseInt(n.trim()));
|
|
320
|
+
if (!isNaN(mx) && !isNaN(my)) {
|
|
321
|
+
if (this.config.humanLikeTiming) {
|
|
322
|
+
await this.moveMouseSmooth(mx, my);
|
|
323
|
+
} else {
|
|
324
|
+
await moveMouse(mx, my);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
case "wait": {
|
|
330
|
+
const seconds = parseFloat(value) || 2;
|
|
331
|
+
await this.sleep(seconds * 1e3);
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
case "done":
|
|
335
|
+
case "stuck":
|
|
336
|
+
break;
|
|
337
|
+
default:
|
|
338
|
+
record.result = "failure";
|
|
339
|
+
this.state.actionHistory.push(record);
|
|
340
|
+
return { success: false, error: `Unknown action: ${action}` };
|
|
341
|
+
}
|
|
342
|
+
record.result = "success";
|
|
343
|
+
this.state.actionHistory.push(record);
|
|
344
|
+
this.emit("executed", { action, value, success: true });
|
|
345
|
+
return { success: true };
|
|
346
|
+
} catch (error) {
|
|
347
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
348
|
+
record.result = "failure";
|
|
349
|
+
this.state.actionHistory.push(record);
|
|
350
|
+
this.emit("executed", { action, value, success: false, error: errorMsg });
|
|
351
|
+
return { success: false, error: errorMsg };
|
|
352
|
+
} finally {
|
|
353
|
+
this.state.currentAction = null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Type text with human-like timing
|
|
358
|
+
*/
|
|
359
|
+
async typeHumanLike(text) {
|
|
360
|
+
const baseDelay = 50;
|
|
361
|
+
for (const char of text) {
|
|
362
|
+
await typeText(char);
|
|
363
|
+
const delay = baseDelay + Math.random() * 30;
|
|
364
|
+
await this.sleep(delay);
|
|
365
|
+
if (Math.random() < 0.05) {
|
|
366
|
+
await this.sleep(200 + Math.random() * 300);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Move mouse smoothly (basic linear interpolation)
|
|
372
|
+
*/
|
|
373
|
+
async moveMouseSmooth(targetX, targetY) {
|
|
374
|
+
const currentPos = await getMousePosition();
|
|
375
|
+
const match = currentPos.output.match(/(\d+),\s*(\d+)/);
|
|
376
|
+
if (!match) {
|
|
377
|
+
await moveMouse(targetX, targetY);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const startX = parseInt(match[1]);
|
|
381
|
+
const startY = parseInt(match[2]);
|
|
382
|
+
const steps = 10;
|
|
383
|
+
for (let i = 1; i <= steps; i++) {
|
|
384
|
+
const t = i / steps;
|
|
385
|
+
const x = Math.round(startX + (targetX - startX) * t);
|
|
386
|
+
const y = Math.round(startY + (targetY - startY) * t);
|
|
387
|
+
await moveMouse(x, y);
|
|
388
|
+
await this.sleep(20);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
sleep(ms) {
|
|
392
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Get current state
|
|
396
|
+
*/
|
|
397
|
+
getState() {
|
|
398
|
+
return { ...this.state };
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Get action history
|
|
402
|
+
*/
|
|
403
|
+
getHistory() {
|
|
404
|
+
return [...this.state.actionHistory];
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
var agentInstance = null;
|
|
408
|
+
function getAutonomousAgent(config) {
|
|
409
|
+
if (!agentInstance) {
|
|
410
|
+
agentInstance = new AutonomousAgent(config);
|
|
411
|
+
}
|
|
412
|
+
return agentInstance;
|
|
413
|
+
}
|
|
414
|
+
var autonomous_default = AutonomousAgent;
|
|
415
|
+
export {
|
|
416
|
+
AutonomousAgent,
|
|
417
|
+
autonomous_default as default,
|
|
418
|
+
getAutonomousAgent
|
|
419
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
askAI,
|
|
3
|
+
browser_default,
|
|
4
|
+
click,
|
|
5
|
+
clickElement,
|
|
6
|
+
closeBrowser,
|
|
7
|
+
closeTab,
|
|
8
|
+
elementExists,
|
|
9
|
+
focusAddressBar,
|
|
10
|
+
getFullAIResponse,
|
|
11
|
+
getPage,
|
|
12
|
+
getPageText,
|
|
13
|
+
getTextContent,
|
|
14
|
+
goBack,
|
|
15
|
+
goForward,
|
|
16
|
+
googleDocsType,
|
|
17
|
+
googleSheetsType,
|
|
18
|
+
initBrowser,
|
|
19
|
+
navigateTo,
|
|
20
|
+
newTab,
|
|
21
|
+
nextTab,
|
|
22
|
+
openGmailCompose,
|
|
23
|
+
openGoogleDoc,
|
|
24
|
+
openGoogleSheet,
|
|
25
|
+
openOutlookCompose,
|
|
26
|
+
openUrl,
|
|
27
|
+
pressKey,
|
|
28
|
+
refresh,
|
|
29
|
+
research,
|
|
30
|
+
scroll,
|
|
31
|
+
searchGoogle,
|
|
32
|
+
sendGmail,
|
|
33
|
+
sendOutlook,
|
|
34
|
+
takeScreenshot,
|
|
35
|
+
typeInBrowser,
|
|
36
|
+
typeInElement,
|
|
37
|
+
typeSlowly,
|
|
38
|
+
typeUrl,
|
|
39
|
+
waitForNavigation,
|
|
40
|
+
waitForText,
|
|
41
|
+
webSearch
|
|
42
|
+
} from "./chunk-MOKGR7WE.js";
|
|
43
|
+
import "./chunk-TFHK5CYF.js";
|
|
44
|
+
import "./chunk-OIVTPXE4.js";
|
|
45
|
+
import "./chunk-COKO6V5J.js";
|
|
46
|
+
export {
|
|
47
|
+
askAI,
|
|
48
|
+
click,
|
|
49
|
+
clickElement,
|
|
50
|
+
closeBrowser,
|
|
51
|
+
closeTab,
|
|
52
|
+
browser_default as default,
|
|
53
|
+
elementExists,
|
|
54
|
+
focusAddressBar,
|
|
55
|
+
getFullAIResponse,
|
|
56
|
+
getPage,
|
|
57
|
+
getPageText,
|
|
58
|
+
getTextContent,
|
|
59
|
+
goBack,
|
|
60
|
+
goForward,
|
|
61
|
+
googleDocsType,
|
|
62
|
+
googleSheetsType,
|
|
63
|
+
initBrowser,
|
|
64
|
+
navigateTo,
|
|
65
|
+
newTab,
|
|
66
|
+
nextTab,
|
|
67
|
+
openGmailCompose,
|
|
68
|
+
openGoogleDoc,
|
|
69
|
+
openGoogleSheet,
|
|
70
|
+
openOutlookCompose,
|
|
71
|
+
openUrl,
|
|
72
|
+
pressKey,
|
|
73
|
+
refresh,
|
|
74
|
+
research,
|
|
75
|
+
scroll,
|
|
76
|
+
searchGoogle,
|
|
77
|
+
sendGmail,
|
|
78
|
+
sendOutlook,
|
|
79
|
+
takeScreenshot,
|
|
80
|
+
typeInBrowser,
|
|
81
|
+
typeInElement,
|
|
82
|
+
typeSlowly,
|
|
83
|
+
typeUrl,
|
|
84
|
+
waitForNavigation,
|
|
85
|
+
waitForText,
|
|
86
|
+
webSearch
|
|
87
|
+
};
|
|
@@ -1,52 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConfig,
|
|
3
|
+
setApiKey,
|
|
4
|
+
setModel,
|
|
5
|
+
setProvider
|
|
6
|
+
} from "./chunk-COKO6V5J.js";
|
|
7
|
+
|
|
1
8
|
// src/components/ProviderSelector.tsx
|
|
2
9
|
import { useState, useEffect } from "react";
|
|
3
10
|
import { Box, Text, useInput } from "ink";
|
|
4
11
|
import TextInput from "ink-text-input";
|
|
5
12
|
import Spinner from "ink-spinner";
|
|
6
13
|
|
|
7
|
-
// src/lib/config.ts
|
|
8
|
-
import Conf from "conf";
|
|
9
|
-
var config = new Conf({
|
|
10
|
-
projectName: "cnapse",
|
|
11
|
-
defaults: {
|
|
12
|
-
provider: "ollama",
|
|
13
|
-
model: "qwen2.5:0.5b",
|
|
14
|
-
apiKeys: {},
|
|
15
|
-
ollamaHost: "http://localhost:11434",
|
|
16
|
-
openrouter: {
|
|
17
|
-
siteUrl: "https://github.com/projectservan8n/C-napse",
|
|
18
|
-
appName: "C-napse"
|
|
19
|
-
},
|
|
20
|
-
telegram: {
|
|
21
|
-
enabled: false
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
function getConfig() {
|
|
26
|
-
return {
|
|
27
|
-
provider: config.get("provider"),
|
|
28
|
-
model: config.get("model"),
|
|
29
|
-
apiKeys: config.get("apiKeys"),
|
|
30
|
-
ollamaHost: config.get("ollamaHost"),
|
|
31
|
-
openrouter: config.get("openrouter"),
|
|
32
|
-
telegram: config.get("telegram")
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
function setProvider(provider) {
|
|
36
|
-
config.set("provider", provider);
|
|
37
|
-
}
|
|
38
|
-
function setModel(model) {
|
|
39
|
-
config.set("model", model);
|
|
40
|
-
}
|
|
41
|
-
function setApiKey(provider, key) {
|
|
42
|
-
const keys = config.get("apiKeys");
|
|
43
|
-
keys[provider] = key;
|
|
44
|
-
config.set("apiKeys", keys);
|
|
45
|
-
}
|
|
46
|
-
function getApiKey(provider) {
|
|
47
|
-
return config.get("apiKeys")[provider];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
14
|
// src/lib/ollama.ts
|
|
51
15
|
import { exec } from "child_process";
|
|
52
16
|
import { promisify } from "util";
|
|
@@ -159,10 +123,10 @@ var PROVIDERS = [
|
|
|
159
123
|
}
|
|
160
124
|
];
|
|
161
125
|
function ProviderSelector({ onClose, onSelect }) {
|
|
162
|
-
const
|
|
126
|
+
const config = getConfig();
|
|
163
127
|
const [step, setStep] = useState("provider");
|
|
164
128
|
const [providerIndex, setProviderIndex] = useState(() => {
|
|
165
|
-
const idx = PROVIDERS.findIndex((p) => p.id ===
|
|
129
|
+
const idx = PROVIDERS.findIndex((p) => p.id === config.provider);
|
|
166
130
|
return idx >= 0 ? idx : 0;
|
|
167
131
|
});
|
|
168
132
|
const [modelIndex, setModelIndex] = useState(0);
|
|
@@ -195,12 +159,12 @@ function ProviderSelector({ onClose, onSelect }) {
|
|
|
195
159
|
} else if (key.return) {
|
|
196
160
|
const provider = PROVIDERS[providerIndex];
|
|
197
161
|
setSelectedProvider(provider);
|
|
198
|
-
const currentIdx = provider.models.findIndex((m) => m.id ===
|
|
162
|
+
const currentIdx = provider.models.findIndex((m) => m.id === config.model);
|
|
199
163
|
const recommendedIdx = provider.models.findIndex((m) => m.recommended);
|
|
200
164
|
setModelIndex(currentIdx >= 0 ? currentIdx : recommendedIdx >= 0 ? recommendedIdx : 0);
|
|
201
165
|
if (provider.needsApiKey) {
|
|
202
166
|
const apiKeyProvider = provider.id;
|
|
203
|
-
if (!
|
|
167
|
+
if (!config.apiKeys[apiKeyProvider]) {
|
|
204
168
|
setStep("apiKey");
|
|
205
169
|
} else {
|
|
206
170
|
setStep("model");
|
|
@@ -246,8 +210,8 @@ function ProviderSelector({ onClose, onSelect }) {
|
|
|
246
210
|
/* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "Arrows to navigate, Enter to select" }) }),
|
|
247
211
|
PROVIDERS.map((provider, index) => {
|
|
248
212
|
const isSelected = index === providerIndex;
|
|
249
|
-
const isCurrent = provider.id ===
|
|
250
|
-
const hasKey = provider.needsApiKey && provider.id !== "ollama" ? !!
|
|
213
|
+
const isCurrent = provider.id === config.provider;
|
|
214
|
+
const hasKey = provider.needsApiKey && provider.id !== "ollama" ? !!config.apiKeys[provider.id] : true;
|
|
251
215
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
252
216
|
/* @__PURE__ */ jsxs(Text, { color: isSelected ? "cyan" : "white", children: [
|
|
253
217
|
isSelected ? "\u276F " : " ",
|
|
@@ -335,7 +299,7 @@ function ProviderSelector({ onClose, onSelect }) {
|
|
|
335
299
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "Arrows to navigate, Enter to select, B to go back" }) }),
|
|
336
300
|
selectedProvider.models.map((model, index) => {
|
|
337
301
|
const isSelected = index === modelIndex;
|
|
338
|
-
const isCurrent = model.id ===
|
|
302
|
+
const isCurrent = model.id === config.model && selectedProvider.id === config.provider;
|
|
339
303
|
let modelStatus = "";
|
|
340
304
|
if (isOllama && ollamaStatus) {
|
|
341
305
|
const available = hasModel(ollamaStatus, model.id);
|
|
@@ -382,10 +346,5 @@ function ProviderSelector({ onClose, onSelect }) {
|
|
|
382
346
|
}
|
|
383
347
|
|
|
384
348
|
export {
|
|
385
|
-
getConfig,
|
|
386
|
-
setProvider,
|
|
387
|
-
setModel,
|
|
388
|
-
setApiKey,
|
|
389
|
-
getApiKey,
|
|
390
349
|
ProviderSelector
|
|
391
350
|
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/lib/config.ts
|
|
2
|
+
import Conf from "conf";
|
|
3
|
+
var config = new Conf({
|
|
4
|
+
projectName: "cnapse",
|
|
5
|
+
defaults: {
|
|
6
|
+
provider: "ollama",
|
|
7
|
+
model: "qwen2.5:0.5b",
|
|
8
|
+
apiKeys: {},
|
|
9
|
+
ollamaHost: "http://localhost:11434",
|
|
10
|
+
openrouter: {
|
|
11
|
+
siteUrl: "https://github.com/projectservan8n/C-napse",
|
|
12
|
+
appName: "C-napse"
|
|
13
|
+
},
|
|
14
|
+
telegram: {
|
|
15
|
+
enabled: false
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
function getConfig() {
|
|
20
|
+
return {
|
|
21
|
+
provider: config.get("provider"),
|
|
22
|
+
model: config.get("model"),
|
|
23
|
+
apiKeys: config.get("apiKeys"),
|
|
24
|
+
ollamaHost: config.get("ollamaHost"),
|
|
25
|
+
openrouter: config.get("openrouter"),
|
|
26
|
+
telegram: config.get("telegram")
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function setProvider(provider) {
|
|
30
|
+
config.set("provider", provider);
|
|
31
|
+
}
|
|
32
|
+
function setModel(model) {
|
|
33
|
+
config.set("model", model);
|
|
34
|
+
}
|
|
35
|
+
function setApiKey(provider, key) {
|
|
36
|
+
const keys = config.get("apiKeys");
|
|
37
|
+
keys[provider] = key;
|
|
38
|
+
config.set("apiKeys", keys);
|
|
39
|
+
}
|
|
40
|
+
function getApiKey(provider) {
|
|
41
|
+
return config.get("apiKeys")[provider];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
getConfig,
|
|
46
|
+
setProvider,
|
|
47
|
+
setModel,
|
|
48
|
+
setApiKey,
|
|
49
|
+
getApiKey
|
|
50
|
+
};
|