@projectservan8n/cnapse 0.6.3 → 0.8.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/index.js CHANGED
@@ -1,18 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ ProviderSelector,
3
4
  getApiKey,
4
5
  getConfig,
5
6
  setApiKey,
6
7
  setModel,
7
8
  setProvider
8
- } from "./chunk-COKO6V5J.js";
9
+ } from "./chunk-OPX7FFL6.js";
9
10
 
10
11
  // src/index.tsx
11
12
  import { render } from "ink";
12
13
 
13
14
  // src/components/App.tsx
14
- import { useState as useState7, useCallback as useCallback5 } from "react";
15
- import { Box as Box7, Text as Text7, useApp, useInput as useInput3 } from "ink";
15
+ import { useState as useState6, useCallback as useCallback5 } from "react";
16
+ import { Box as Box6, Text as Text6, useApp, useInput as useInput2 } from "ink";
16
17
 
17
18
  // src/components/Header.tsx
18
19
  import { Box, Text } from "ink";
@@ -191,354 +192,14 @@ function HelpMenu({ onClose, onSelect }) {
191
192
  );
192
193
  }
193
194
 
194
- // src/components/ProviderSelector.tsx
195
- import { useState as useState2, useEffect } from "react";
196
- import { Box as Box6, Text as Text6, useInput as useInput2 } from "ink";
197
- import TextInput2 from "ink-text-input";
198
- import Spinner from "ink-spinner";
199
-
200
- // src/lib/ollama.ts
201
- import { exec } from "child_process";
202
- import { promisify } from "util";
203
- var execAsync = promisify(exec);
204
- async function checkOllamaStatus() {
205
- try {
206
- const { stdout } = await execAsync("ollama list", { timeout: 1e4 });
207
- const lines = stdout.trim().split("\n");
208
- const models = [];
209
- for (let i = 1; i < lines.length; i++) {
210
- const line = lines[i];
211
- if (!line?.trim()) continue;
212
- const parts = line.split(/\s{2,}/);
213
- if (parts.length >= 3) {
214
- models.push({
215
- name: parts[0]?.trim() || "",
216
- size: parts[2]?.trim() || "",
217
- modified: parts[3]?.trim() || ""
218
- });
219
- }
220
- }
221
- return {
222
- installed: true,
223
- running: true,
224
- models
225
- };
226
- } catch (err2) {
227
- const errorMsg = err2 instanceof Error ? err2.message : "Unknown error";
228
- if (errorMsg.includes("connect") || errorMsg.includes("refused")) {
229
- return {
230
- installed: true,
231
- running: false,
232
- models: [],
233
- error: "Ollama is not running. Start it with: ollama serve"
234
- };
235
- }
236
- if (errorMsg.includes("not found") || errorMsg.includes("not recognized")) {
237
- return {
238
- installed: false,
239
- running: false,
240
- models: [],
241
- error: "Ollama not installed. Get it at: https://ollama.ai"
242
- };
243
- }
244
- return {
245
- installed: false,
246
- running: false,
247
- models: [],
248
- error: errorMsg
249
- };
250
- }
251
- }
252
- function hasModel(status, modelId) {
253
- const modelName = modelId.split(":")[0]?.toLowerCase() || "";
254
- return status.models.some((m) => m.name.toLowerCase().startsWith(modelName));
255
- }
256
-
257
- // src/components/ProviderSelector.tsx
258
- import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
259
- var PROVIDERS = [
260
- {
261
- id: "ollama",
262
- name: "Ollama",
263
- description: "Local AI - Free, private",
264
- needsApiKey: false,
265
- models: [
266
- { id: "qwen2.5:0.5b", name: "Qwen 2.5 0.5B (fast)", recommended: true },
267
- { id: "qwen2.5:1.5b", name: "Qwen 2.5 1.5B" },
268
- { id: "qwen2.5:7b", name: "Qwen 2.5 7B (quality)" },
269
- { id: "llama3.2:1b", name: "Llama 3.2 1B" },
270
- { id: "llama3.2:3b", name: "Llama 3.2 3B" },
271
- { id: "codellama:7b", name: "Code Llama 7B" },
272
- { id: "llava:7b", name: "LLaVA 7B (vision)" }
273
- ]
274
- },
275
- {
276
- id: "openrouter",
277
- name: "OpenRouter",
278
- description: "Many models, budget-friendly",
279
- needsApiKey: true,
280
- models: [
281
- { id: "openai/gpt-5-nano", name: "GPT-5 Nano ($0.05/1M) + Vision", recommended: true },
282
- { id: "google/gemini-2.5-flash-lite", name: "Gemini 2.5 Flash Lite ($0.10/1M)" },
283
- { id: "qwen/qwen-2.5-coder-32b-instruct", name: "Qwen Coder 32B ($0.07/1M)" },
284
- { id: "meta-llama/llama-3.3-70b-instruct", name: "Llama 3.3 70B ($0.10/1M)" },
285
- { id: "deepseek/deepseek-chat", name: "DeepSeek V3 ($0.14/1M)" }
286
- ]
287
- },
288
- {
289
- id: "anthropic",
290
- name: "Anthropic",
291
- description: "Claude - Best reasoning",
292
- needsApiKey: true,
293
- models: [
294
- { id: "claude-3-5-sonnet-20241022", name: "Claude 3.5 Sonnet", recommended: true },
295
- { id: "claude-3-opus-20240229", name: "Claude 3 Opus" },
296
- { id: "claude-3-haiku-20240307", name: "Claude 3 Haiku" }
297
- ]
298
- },
299
- {
300
- id: "openai",
301
- name: "OpenAI",
302
- description: "GPT models",
303
- needsApiKey: true,
304
- models: [
305
- { id: "gpt-4o", name: "GPT-4o", recommended: true },
306
- { id: "gpt-4o-mini", name: "GPT-4o Mini" },
307
- { id: "gpt-4-turbo", name: "GPT-4 Turbo" }
308
- ]
309
- }
310
- ];
311
- function ProviderSelector({ onClose, onSelect }) {
312
- const config = getConfig();
313
- const [step, setStep] = useState2("provider");
314
- const [providerIndex, setProviderIndex] = useState2(() => {
315
- const idx = PROVIDERS.findIndex((p) => p.id === config.provider);
316
- return idx >= 0 ? idx : 0;
317
- });
318
- const [modelIndex, setModelIndex] = useState2(0);
319
- const [apiKeyInput, setApiKeyInput] = useState2("");
320
- const [selectedProvider, setSelectedProvider] = useState2(null);
321
- const [ollamaStatus, setOllamaStatus] = useState2(null);
322
- const [checkingOllama, setCheckingOllama] = useState2(false);
323
- useEffect(() => {
324
- if (step === "model" && selectedProvider?.id === "ollama" && !ollamaStatus) {
325
- setCheckingOllama(true);
326
- checkOllamaStatus().then((status) => {
327
- setOllamaStatus(status);
328
- setCheckingOllama(false);
329
- if (!status.running) {
330
- setStep("ollamaError");
331
- }
332
- });
333
- }
334
- }, [step, selectedProvider, ollamaStatus]);
335
- useInput2((input, key) => {
336
- if (key.escape) {
337
- onClose();
338
- return;
339
- }
340
- if (step === "provider") {
341
- if (key.upArrow) {
342
- setProviderIndex((prev) => prev > 0 ? prev - 1 : PROVIDERS.length - 1);
343
- } else if (key.downArrow) {
344
- setProviderIndex((prev) => prev < PROVIDERS.length - 1 ? prev + 1 : 0);
345
- } else if (key.return) {
346
- const provider = PROVIDERS[providerIndex];
347
- setSelectedProvider(provider);
348
- const currentIdx = provider.models.findIndex((m) => m.id === config.model);
349
- const recommendedIdx = provider.models.findIndex((m) => m.recommended);
350
- setModelIndex(currentIdx >= 0 ? currentIdx : recommendedIdx >= 0 ? recommendedIdx : 0);
351
- if (provider.needsApiKey) {
352
- const apiKeyProvider = provider.id;
353
- if (!config.apiKeys[apiKeyProvider]) {
354
- setStep("apiKey");
355
- } else {
356
- setStep("model");
357
- }
358
- } else {
359
- setStep("model");
360
- }
361
- }
362
- } else if (step === "model" && selectedProvider) {
363
- if (key.upArrow) {
364
- setModelIndex((prev) => prev > 0 ? prev - 1 : selectedProvider.models.length - 1);
365
- } else if (key.downArrow) {
366
- setModelIndex((prev) => prev < selectedProvider.models.length - 1 ? prev + 1 : 0);
367
- } else if (key.return) {
368
- const model = selectedProvider.models[modelIndex];
369
- if (selectedProvider.id === "ollama" && ollamaStatus && !hasModel(ollamaStatus, model.id)) {
370
- }
371
- setProvider(selectedProvider.id);
372
- setModel(model.id);
373
- setStep("done");
374
- onSelect(selectedProvider.id, model.id);
375
- setTimeout(() => onClose(), 1500);
376
- } else if (key.leftArrow || input === "b") {
377
- setStep("provider");
378
- setOllamaStatus(null);
379
- }
380
- } else if (step === "ollamaError") {
381
- if (key.return || input === "b") {
382
- setStep("provider");
383
- setOllamaStatus(null);
384
- }
385
- }
386
- });
387
- const handleApiKeySubmit = (value) => {
388
- if (value.trim() && selectedProvider) {
389
- setApiKey(selectedProvider.id, value.trim());
390
- setStep("model");
391
- }
392
- };
393
- if (step === "provider") {
394
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, width: 60, children: [
395
- /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: "Select Provider" }) }),
396
- /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: "Arrows to navigate, Enter to select" }) }),
397
- PROVIDERS.map((provider, index) => {
398
- const isSelected = index === providerIndex;
399
- const isCurrent = provider.id === config.provider;
400
- const hasKey = provider.needsApiKey && provider.id !== "ollama" ? !!config.apiKeys[provider.id] : true;
401
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
402
- /* @__PURE__ */ jsxs5(Text6, { color: isSelected ? "cyan" : "white", children: [
403
- isSelected ? "\u276F " : " ",
404
- provider.name,
405
- isCurrent && /* @__PURE__ */ jsx6(Text6, { color: "green", children: " (current)" }),
406
- provider.needsApiKey && !hasKey && /* @__PURE__ */ jsx6(Text6, { color: "red", children: " (needs key)" }),
407
- provider.needsApiKey && hasKey && !isCurrent && /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: " (key saved)" })
408
- ] }),
409
- isSelected && /* @__PURE__ */ jsxs5(Text6, { color: "gray", children: [
410
- " ",
411
- provider.description
412
- ] })
413
- ] }, provider.id);
414
- }),
415
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: "Press Esc to cancel" }) })
416
- ] });
417
- }
418
- if (step === "apiKey" && selectedProvider) {
419
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, width: 60, children: [
420
- /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: "Enter API Key" }) }),
421
- /* @__PURE__ */ jsxs5(Text6, { children: [
422
- /* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713" }),
423
- " Provider: ",
424
- selectedProvider.name
425
- ] }),
426
- /* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
427
- /* @__PURE__ */ jsxs5(Text6, { color: "gray", dimColor: true, children: [
428
- selectedProvider.id === "openrouter" && "Get key: openrouter.ai/keys",
429
- selectedProvider.id === "anthropic" && "Get key: console.anthropic.com",
430
- selectedProvider.id === "openai" && "Get key: platform.openai.com/api-keys"
431
- ] }),
432
- /* @__PURE__ */ jsxs5(Box6, { marginTop: 1, children: [
433
- /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u276F " }),
434
- /* @__PURE__ */ jsx6(
435
- TextInput2,
436
- {
437
- value: apiKeyInput,
438
- onChange: setApiKeyInput,
439
- onSubmit: handleApiKeySubmit,
440
- mask: "*"
441
- }
442
- )
443
- ] })
444
- ] }),
445
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: "Press Esc to cancel" }) })
446
- ] });
447
- }
448
- if (step === "ollamaError" && ollamaStatus) {
449
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "red", padding: 1, width: 60, children: [
450
- /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { bold: true, color: "red", children: "Ollama Not Available" }) }),
451
- /* @__PURE__ */ jsx6(Text6, { color: "red", children: ollamaStatus.error }),
452
- /* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
453
- !ollamaStatus.installed && /* @__PURE__ */ jsxs5(Fragment, { children: [
454
- /* @__PURE__ */ jsx6(Text6, { children: "1. Install Ollama from https://ollama.ai" }),
455
- /* @__PURE__ */ jsx6(Text6, { children: "2. Run: ollama pull qwen2.5:0.5b" }),
456
- /* @__PURE__ */ jsx6(Text6, { children: "3. Try again" })
457
- ] }),
458
- ollamaStatus.installed && !ollamaStatus.running && /* @__PURE__ */ jsxs5(Fragment, { children: [
459
- /* @__PURE__ */ jsx6(Text6, { children: "1. Start Ollama: ollama serve" }),
460
- /* @__PURE__ */ jsx6(Text6, { children: "2. Or run any model: ollama run qwen2.5:0.5b" }),
461
- /* @__PURE__ */ jsx6(Text6, { children: "3. Try again" })
462
- ] })
463
- ] }),
464
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: "Press Enter or B to go back" }) })
465
- ] });
466
- }
467
- if (step === "model" && selectedProvider) {
468
- const isOllama = selectedProvider.id === "ollama";
469
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1, width: 60, children: [
470
- /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: "Select Model" }) }),
471
- /* @__PURE__ */ jsxs5(Text6, { children: [
472
- /* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713" }),
473
- " Provider: ",
474
- selectedProvider.name
475
- ] }),
476
- isOllama && checkingOllama && /* @__PURE__ */ jsxs5(Box6, { marginY: 1, children: [
477
- /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: /* @__PURE__ */ jsx6(Spinner, { type: "dots" }) }),
478
- /* @__PURE__ */ jsx6(Text6, { children: " Checking Ollama status..." })
479
- ] }),
480
- isOllama && ollamaStatus && ollamaStatus.running && /* @__PURE__ */ jsxs5(Text6, { color: "green", children: [
481
- "\u2713 Ollama running (",
482
- ollamaStatus.models.length,
483
- " models installed)"
484
- ] }),
485
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: "Arrows to navigate, Enter to select, B to go back" }) }),
486
- selectedProvider.models.map((model, index) => {
487
- const isSelected = index === modelIndex;
488
- const isCurrent = model.id === config.model && selectedProvider.id === config.provider;
489
- let modelStatus = "";
490
- if (isOllama && ollamaStatus) {
491
- const available = hasModel(ollamaStatus, model.id);
492
- modelStatus = available ? " (installed)" : " (not installed)";
493
- }
494
- return /* @__PURE__ */ jsxs5(Text6, { color: isSelected ? "cyan" : "white", children: [
495
- isSelected ? "\u276F " : " ",
496
- model.name,
497
- model.recommended && /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: " *" }),
498
- isCurrent && /* @__PURE__ */ jsx6(Text6, { color: "green", children: " (current)" }),
499
- isOllama && ollamaStatus && (hasModel(ollamaStatus, model.id) ? /* @__PURE__ */ jsx6(Text6, { color: "green", children: modelStatus }) : /* @__PURE__ */ jsx6(Text6, { color: "red", children: modelStatus }))
500
- ] }, model.id);
501
- }),
502
- isOllama && /* @__PURE__ */ jsxs5(Box6, { marginTop: 1, flexDirection: "column", children: [
503
- /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: "* = Recommended" }),
504
- ollamaStatus && !hasModel(ollamaStatus, selectedProvider.models[modelIndex]?.id || "") && /* @__PURE__ */ jsxs5(Text6, { color: "yellow", children: [
505
- "Run: ollama pull ",
506
- selectedProvider.models[modelIndex]?.id
507
- ] })
508
- ] }),
509
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: "Press Esc to cancel" }) })
510
- ] });
511
- }
512
- if (step === "done" && selectedProvider) {
513
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "green", padding: 1, width: 60, children: [
514
- /* @__PURE__ */ jsx6(Text6, { color: "green", bold: true, children: "Configuration Updated!" }),
515
- /* @__PURE__ */ jsxs5(Text6, { children: [
516
- /* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713" }),
517
- " Provider: ",
518
- selectedProvider.name
519
- ] }),
520
- /* @__PURE__ */ jsxs5(Text6, { children: [
521
- /* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u2713" }),
522
- " Model: ",
523
- selectedProvider.models[modelIndex]?.name
524
- ] }),
525
- selectedProvider.id === "ollama" && ollamaStatus && !hasModel(ollamaStatus, selectedProvider.models[modelIndex]?.id || "") && /* @__PURE__ */ jsxs5(Text6, { color: "yellow", children: [
526
- "Remember to run: ollama pull ",
527
- selectedProvider.models[modelIndex]?.id
528
- ] })
529
- ] });
530
- }
531
- return null;
532
- }
533
-
534
195
  // src/hooks/useChat.ts
535
- import { useState as useState3, useCallback, useRef, useEffect as useEffect2 } from "react";
196
+ import { useState as useState2, useCallback, useRef, useEffect } from "react";
536
197
 
537
198
  // src/lib/system.ts
538
199
  import os from "os";
539
- import { exec as exec2 } from "child_process";
540
- import { promisify as promisify2 } from "util";
541
- var execAsync2 = promisify2(exec2);
200
+ import { exec } from "child_process";
201
+ import { promisify } from "util";
202
+ var execAsync = promisify(exec);
542
203
  var cachedSystemInfo = null;
543
204
  async function getSystemInfo() {
544
205
  if (cachedSystemInfo) return cachedSystemInfo;
@@ -548,7 +209,7 @@ async function getSystemInfo() {
548
209
  const osVersion = os.release();
549
210
  if (platform === "win32") {
550
211
  try {
551
- const { stdout } = await execAsync2("wmic os get Caption /value", { timeout: 5e3 });
212
+ const { stdout } = await execAsync("wmic os get Caption /value", { timeout: 5e3 });
552
213
  const match = stdout.match(/Caption=(.+)/);
553
214
  if (match) osName = match[1].trim();
554
215
  } catch {
@@ -556,7 +217,7 @@ async function getSystemInfo() {
556
217
  }
557
218
  } else if (platform === "darwin") {
558
219
  try {
559
- const { stdout } = await execAsync2("sw_vers -productName && sw_vers -productVersion", { timeout: 5e3 });
220
+ const { stdout } = await execAsync("sw_vers -productName && sw_vers -productVersion", { timeout: 5e3 });
560
221
  const lines = stdout.trim().split("\n");
561
222
  osName = `${lines[0]} ${lines[1]}`;
562
223
  } catch {
@@ -564,7 +225,7 @@ async function getSystemInfo() {
564
225
  }
565
226
  } else if (platform === "linux") {
566
227
  try {
567
- const { stdout } = await execAsync2("cat /etc/os-release | grep PRETTY_NAME", { timeout: 5e3 });
228
+ const { stdout } = await execAsync("cat /etc/os-release | grep PRETTY_NAME", { timeout: 5e3 });
568
229
  const match = stdout.match(/PRETTY_NAME="(.+)"/);
569
230
  if (match) osName = match[1];
570
231
  } catch {
@@ -922,17 +583,17 @@ async function captureScreenshot() {
922
583
  }
923
584
  }
924
585
  async function captureScreenFallback() {
925
- const { exec: exec6 } = await import("child_process");
926
- const { promisify: promisify6 } = await import("util");
586
+ const { exec: exec5 } = await import("child_process");
587
+ const { promisify: promisify5 } = await import("util");
927
588
  const { tmpdir } = await import("os");
928
589
  const { join: join3 } = await import("path");
929
590
  const { readFile: readFile2, unlink } = await import("fs/promises");
930
- const execAsync6 = promisify6(exec6);
591
+ const execAsync5 = promisify5(exec5);
931
592
  const tempFile = join3(tmpdir(), `cnapse-screen-${Date.now()}.png`);
932
593
  try {
933
594
  const platform = process.platform;
934
595
  if (platform === "win32") {
935
- await execAsync6(`
596
+ await execAsync5(`
936
597
  Add-Type -AssemblyName System.Windows.Forms
937
598
  $screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
938
599
  $bitmap = New-Object System.Drawing.Bitmap($screen.Width, $screen.Height)
@@ -943,9 +604,9 @@ async function captureScreenFallback() {
943
604
  $bitmap.Dispose()
944
605
  `, { shell: "powershell.exe" });
945
606
  } else if (platform === "darwin") {
946
- await execAsync6(`screencapture -x "${tempFile}"`);
607
+ await execAsync5(`screencapture -x "${tempFile}"`);
947
608
  } else {
948
- await execAsync6(`gnome-screenshot -f "${tempFile}" 2>/dev/null || scrot "${tempFile}" 2>/dev/null || import -window root "${tempFile}"`);
609
+ await execAsync5(`gnome-screenshot -f "${tempFile}" 2>/dev/null || scrot "${tempFile}" 2>/dev/null || import -window root "${tempFile}"`);
949
610
  }
950
611
  const imageBuffer = await readFile2(tempFile);
951
612
  await unlink(tempFile).catch(() => {
@@ -1114,11 +775,11 @@ var WELCOME_MESSAGE = {
1114
775
  timestamp: /* @__PURE__ */ new Date()
1115
776
  };
1116
777
  function useChat(screenWatch = false) {
1117
- const [messages, setMessages] = useState3([WELCOME_MESSAGE]);
1118
- const [isProcessing, setIsProcessing] = useState3(false);
1119
- const [error, setError] = useState3(null);
778
+ const [messages, setMessages] = useState2([WELCOME_MESSAGE]);
779
+ const [isProcessing, setIsProcessing] = useState2(false);
780
+ const [error, setError] = useState2(null);
1120
781
  const screenWatchRef = useRef(screenWatch);
1121
- useEffect2(() => {
782
+ useEffect(() => {
1122
783
  screenWatchRef.current = screenWatch;
1123
784
  }, [screenWatch]);
1124
785
  const addSystemMessage = useCallback((content) => {
@@ -1197,12 +858,12 @@ function useChat(screenWatch = false) {
1197
858
  }
1198
859
 
1199
860
  // src/hooks/useVision.ts
1200
- import { useState as useState4, useCallback as useCallback2 } from "react";
861
+ import { useState as useState3, useCallback as useCallback2 } from "react";
1201
862
  function useVision() {
1202
- const [isAnalyzing, setIsAnalyzing] = useState4(false);
1203
- const [lastDescription, setLastDescription] = useState4(null);
1204
- const [lastScreenshot, setLastScreenshot] = useState4(null);
1205
- const [error, setError] = useState4(null);
863
+ const [isAnalyzing, setIsAnalyzing] = useState3(false);
864
+ const [lastDescription, setLastDescription] = useState3(null);
865
+ const [lastScreenshot, setLastScreenshot] = useState3(null);
866
+ const [error, setError] = useState3(null);
1206
867
  const analyze = useCallback2(async () => {
1207
868
  setIsAnalyzing(true);
1208
869
  setError(null);
@@ -1229,14 +890,14 @@ function useVision() {
1229
890
  }
1230
891
 
1231
892
  // src/hooks/useTelegram.ts
1232
- import { useState as useState5, useCallback as useCallback3, useEffect as useEffect3, useRef as useRef2 } from "react";
893
+ import { useState as useState4, useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2 } from "react";
1233
894
 
1234
895
  // src/services/telegram.ts
1235
896
  import { EventEmitter } from "events";
1236
897
 
1237
898
  // src/tools/shell.ts
1238
- import { exec as exec5 } from "child_process";
1239
- import { promisify as promisify5 } from "util";
899
+ import { exec as exec4 } from "child_process";
900
+ import { promisify as promisify4 } from "util";
1240
901
 
1241
902
  // src/tools/filesystem.ts
1242
903
  import { promises as fs } from "fs";
@@ -1292,22 +953,22 @@ async function listDir(path2, recursive = false) {
1292
953
  import clipboardy from "clipboardy";
1293
954
 
1294
955
  // src/tools/process.ts
956
+ import { exec as exec2 } from "child_process";
957
+ import { promisify as promisify2 } from "util";
958
+ var execAsync2 = promisify2(exec2);
959
+
960
+ // src/tools/computer.ts
1295
961
  import { exec as exec3 } from "child_process";
1296
962
  import { promisify as promisify3 } from "util";
1297
963
  var execAsync3 = promisify3(exec3);
1298
-
1299
- // src/tools/computer.ts
1300
- import { exec as exec4 } from "child_process";
1301
- import { promisify as promisify4 } from "util";
1302
- var execAsync4 = promisify4(exec4);
1303
964
  async function moveMouse(x, y) {
1304
965
  try {
1305
966
  if (process.platform === "win32") {
1306
- await execAsync4(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${x}, ${y})"`, { shell: "cmd.exe" });
967
+ await execAsync3(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${x}, ${y})"`, { shell: "cmd.exe" });
1307
968
  } else if (process.platform === "darwin") {
1308
- await execAsync4(`cliclick m:${x},${y}`);
969
+ await execAsync3(`cliclick m:${x},${y}`);
1309
970
  } else {
1310
- await execAsync4(`xdotool mousemove ${x} ${y}`);
971
+ await execAsync3(`xdotool mousemove ${x} ${y}`);
1311
972
  }
1312
973
  return ok(`Mouse moved to (${x}, ${y})`);
1313
974
  } catch (error) {
@@ -1323,12 +984,12 @@ Add-Type -MemberDefinition @"
1323
984
  public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
1324
985
  "@ -Name Mouse -Namespace Win32
1325
986
  ${button === "left" ? "[Win32.Mouse]::mouse_event(0x02, 0, 0, 0, 0); [Win32.Mouse]::mouse_event(0x04, 0, 0, 0, 0)" : button === "right" ? "[Win32.Mouse]::mouse_event(0x08, 0, 0, 0, 0); [Win32.Mouse]::mouse_event(0x10, 0, 0, 0, 0)" : "[Win32.Mouse]::mouse_event(0x20, 0, 0, 0, 0); [Win32.Mouse]::mouse_event(0x40, 0, 0, 0, 0)"}`;
1326
- await execAsync4(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
987
+ await execAsync3(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1327
988
  } else if (process.platform === "darwin") {
1328
- await execAsync4(`cliclick c:.`);
989
+ await execAsync3(`cliclick c:.`);
1329
990
  } else {
1330
991
  const btn = button === "left" ? "1" : button === "right" ? "3" : "2";
1331
- await execAsync4(`xdotool click ${btn}`);
992
+ await execAsync3(`xdotool click ${btn}`);
1332
993
  }
1333
994
  return ok(`Clicked ${button} button`);
1334
995
  } catch (error) {
@@ -1346,11 +1007,11 @@ public static extern void mouse_event(long dwFlags, long dx, long dy, long cButt
1346
1007
  [Win32.Mouse]::mouse_event(0x02, 0, 0, 0, 0); [Win32.Mouse]::mouse_event(0x04, 0, 0, 0, 0)
1347
1008
  Start-Sleep -Milliseconds 50
1348
1009
  [Win32.Mouse]::mouse_event(0x02, 0, 0, 0, 0); [Win32.Mouse]::mouse_event(0x04, 0, 0, 0, 0)`;
1349
- await execAsync4(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1010
+ await execAsync3(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1350
1011
  } else if (process.platform === "darwin") {
1351
- await execAsync4(`cliclick dc:.`);
1012
+ await execAsync3(`cliclick dc:.`);
1352
1013
  } else {
1353
- await execAsync4(`xdotool click --repeat 2 --delay 50 1`);
1014
+ await execAsync3(`xdotool click --repeat 2 --delay 50 1`);
1354
1015
  }
1355
1016
  return ok("Double clicked");
1356
1017
  } catch (error) {
@@ -1361,13 +1022,13 @@ async function typeText(text) {
1361
1022
  try {
1362
1023
  if (process.platform === "win32") {
1363
1024
  const escapedText = text.replace(/'/g, "''").replace(/[+^%~(){}[\]]/g, "{$&}");
1364
- await execAsync4(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${escapedText}')"`, { shell: "cmd.exe" });
1025
+ await execAsync3(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${escapedText}')"`, { shell: "cmd.exe" });
1365
1026
  } else if (process.platform === "darwin") {
1366
1027
  const escaped = text.replace(/'/g, "'\\''");
1367
- await execAsync4(`osascript -e 'tell application "System Events" to keystroke "${escaped}"'`);
1028
+ await execAsync3(`osascript -e 'tell application "System Events" to keystroke "${escaped}"'`);
1368
1029
  } else {
1369
1030
  const escaped = text.replace(/'/g, "'\\''");
1370
- await execAsync4(`xdotool type '${escaped}'`);
1031
+ await execAsync3(`xdotool type '${escaped}'`);
1371
1032
  }
1372
1033
  return ok(`Typed: ${text}`);
1373
1034
  } catch (error) {
@@ -1408,7 +1069,7 @@ async function pressKey(key) {
1408
1069
  "f12": "{F12}"
1409
1070
  };
1410
1071
  const winKey = winKeyMap[key.toLowerCase()] || key;
1411
- await execAsync4(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${winKey}')"`, { shell: "cmd.exe" });
1072
+ await execAsync3(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${winKey}')"`, { shell: "cmd.exe" });
1412
1073
  } else if (process.platform === "darwin") {
1413
1074
  const macKeyMap = {
1414
1075
  "return": 36,
@@ -1426,12 +1087,12 @@ async function pressKey(key) {
1426
1087
  };
1427
1088
  const keyCode = macKeyMap[key.toLowerCase()];
1428
1089
  if (keyCode) {
1429
- await execAsync4(`osascript -e 'tell application "System Events" to key code ${keyCode}'`);
1090
+ await execAsync3(`osascript -e 'tell application "System Events" to key code ${keyCode}'`);
1430
1091
  } else {
1431
- await execAsync4(`osascript -e 'tell application "System Events" to keystroke "${key}"'`);
1092
+ await execAsync3(`osascript -e 'tell application "System Events" to keystroke "${key}"'`);
1432
1093
  }
1433
1094
  } else {
1434
- await execAsync4(`xdotool key ${key}`);
1095
+ await execAsync3(`xdotool key ${key}`);
1435
1096
  }
1436
1097
  return ok(`Pressed: ${key}`);
1437
1098
  } catch (error) {
@@ -1444,7 +1105,7 @@ async function keyCombo(keys) {
1444
1105
  const hasWin = keys.some((k) => k.toLowerCase() === "meta" || k.toLowerCase() === "win");
1445
1106
  const hasR = keys.some((k) => k.toLowerCase() === "r");
1446
1107
  if (hasWin && hasR) {
1447
- await execAsync4(`powershell -Command "$shell = New-Object -ComObject WScript.Shell; $shell.Run('explorer shell:::{2559a1f3-21d7-11d4-bdaf-00c04f60b9f0}')"`, { shell: "cmd.exe" });
1108
+ await execAsync3(`powershell -Command "$shell = New-Object -ComObject WScript.Shell; $shell.Run('explorer shell:::{2559a1f3-21d7-11d4-bdaf-00c04f60b9f0}')"`, { shell: "cmd.exe" });
1448
1109
  return ok(`Pressed: ${keys.join("+")}`);
1449
1110
  }
1450
1111
  const modifierMap = {
@@ -1464,7 +1125,7 @@ async function keyCombo(keys) {
1464
1125
  }
1465
1126
  }
1466
1127
  combo += regularKeys.join("");
1467
- await execAsync4(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${combo}')"`, { shell: "cmd.exe" });
1128
+ await execAsync3(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${combo}')"`, { shell: "cmd.exe" });
1468
1129
  } else if (process.platform === "darwin") {
1469
1130
  const modifiers = keys.filter((k) => ["control", "ctrl", "alt", "shift", "command", "meta"].includes(k.toLowerCase()));
1470
1131
  const regular = keys.filter((k) => !["control", "ctrl", "alt", "shift", "command", "meta"].includes(k.toLowerCase()));
@@ -1480,9 +1141,9 @@ async function keyCombo(keys) {
1480
1141
  };
1481
1142
  cmd += " using {" + modifiers.map((m) => modMap[m.toLowerCase()]).join(", ") + "}";
1482
1143
  }
1483
- await execAsync4(`osascript -e '${cmd}'`);
1144
+ await execAsync3(`osascript -e '${cmd}'`);
1484
1145
  } else {
1485
- await execAsync4(`xdotool key ${keys.join("+")}`);
1146
+ await execAsync3(`xdotool key ${keys.join("+")}`);
1486
1147
  }
1487
1148
  return ok(`Pressed: ${keys.join("+")}`);
1488
1149
  } catch (error) {
@@ -1508,13 +1169,13 @@ $hwnd = [Win32]::GetForegroundWindow()
1508
1169
  $sb = New-Object System.Text.StringBuilder 256
1509
1170
  [Win32]::GetWindowText($hwnd, $sb, 256)
1510
1171
  $sb.ToString()`;
1511
- const { stdout } = await execAsync4(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1172
+ const { stdout } = await execAsync3(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1512
1173
  return ok(stdout.trim() || "Unknown window");
1513
1174
  } else if (process.platform === "darwin") {
1514
- const { stdout } = await execAsync4(`osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true'`);
1175
+ const { stdout } = await execAsync3(`osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true'`);
1515
1176
  return ok(stdout.trim());
1516
1177
  } else {
1517
- const { stdout } = await execAsync4(`xdotool getactivewindow getwindowname`);
1178
+ const { stdout } = await execAsync3(`xdotool getactivewindow getwindowname`);
1518
1179
  return ok(stdout.trim());
1519
1180
  }
1520
1181
  } catch (error) {
@@ -1524,13 +1185,13 @@ $sb.ToString()`;
1524
1185
  async function listWindows() {
1525
1186
  try {
1526
1187
  if (process.platform === "win32") {
1527
- const { stdout } = await execAsync4(`powershell -Command "Get-Process | Where-Object {$_.MainWindowTitle} | Select-Object ProcessName, MainWindowTitle | Format-Table -AutoSize"`, { shell: "cmd.exe" });
1188
+ const { stdout } = await execAsync3(`powershell -Command "Get-Process | Where-Object {$_.MainWindowTitle} | Select-Object ProcessName, MainWindowTitle | Format-Table -AutoSize"`, { shell: "cmd.exe" });
1528
1189
  return ok(stdout);
1529
1190
  } else if (process.platform === "darwin") {
1530
- const { stdout } = await execAsync4(`osascript -e 'tell application "System Events" to get name of every application process whose visible is true'`);
1191
+ const { stdout } = await execAsync3(`osascript -e 'tell application "System Events" to get name of every application process whose visible is true'`);
1531
1192
  return ok(stdout);
1532
1193
  } else {
1533
- const { stdout } = await execAsync4(`wmctrl -l`);
1194
+ const { stdout } = await execAsync3(`wmctrl -l`);
1534
1195
  return ok(stdout);
1535
1196
  }
1536
1197
  } catch (error) {
@@ -1541,11 +1202,11 @@ async function focusWindow(title) {
1541
1202
  try {
1542
1203
  if (process.platform === "win32") {
1543
1204
  const escaped = title.replace(/'/g, "''");
1544
- await execAsync4(`powershell -Command "$wshell = New-Object -ComObject wscript.shell; $wshell.AppActivate('${escaped}')"`, { shell: "cmd.exe" });
1205
+ await execAsync3(`powershell -Command "$wshell = New-Object -ComObject wscript.shell; $wshell.AppActivate('${escaped}')"`, { shell: "cmd.exe" });
1545
1206
  } else if (process.platform === "darwin") {
1546
- await execAsync4(`osascript -e 'tell application "${title}" to activate'`);
1207
+ await execAsync3(`osascript -e 'tell application "${title}" to activate'`);
1547
1208
  } else {
1548
- await execAsync4(`wmctrl -a "${title}"`);
1209
+ await execAsync3(`wmctrl -a "${title}"`);
1549
1210
  }
1550
1211
  return ok(`Focused window: ${title}`);
1551
1212
  } catch (error) {
@@ -1573,27 +1234,27 @@ if ($proc) {
1573
1234
  } else {
1574
1235
  Write-Output "NOT_FOUND"
1575
1236
  }`;
1576
- const { stdout } = await execAsync4(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1237
+ const { stdout } = await execAsync3(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1577
1238
  if (stdout.includes("NOT_FOUND")) {
1578
1239
  return err(`Window containing "${title}" not found`);
1579
1240
  }
1580
1241
  return ok(stdout.trim());
1581
1242
  } else {
1582
- await execAsync4(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('% n')"`, { shell: "cmd.exe" });
1243
+ await execAsync3(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('% n')"`, { shell: "cmd.exe" });
1583
1244
  return ok("Minimized active window");
1584
1245
  }
1585
1246
  } else if (process.platform === "darwin") {
1586
1247
  if (title) {
1587
- await execAsync4(`osascript -e 'tell application "${title}" to set miniaturized of window 1 to true'`);
1248
+ await execAsync3(`osascript -e 'tell application "${title}" to set miniaturized of window 1 to true'`);
1588
1249
  } else {
1589
- await execAsync4(`osascript -e 'tell application "System Events" to keystroke "m" using command down'`);
1250
+ await execAsync3(`osascript -e 'tell application "System Events" to keystroke "m" using command down'`);
1590
1251
  }
1591
1252
  return ok(`Minimized window${title ? `: ${title}` : ""}`);
1592
1253
  } else {
1593
1254
  if (title) {
1594
- await execAsync4(`wmctrl -r "${title}" -b add,hidden`);
1255
+ await execAsync3(`wmctrl -r "${title}" -b add,hidden`);
1595
1256
  } else {
1596
- await execAsync4(`xdotool getactivewindow windowminimize`);
1257
+ await execAsync3(`xdotool getactivewindow windowminimize`);
1597
1258
  }
1598
1259
  return ok(`Minimized window${title ? `: ${title}` : ""}`);
1599
1260
  }
@@ -1622,27 +1283,27 @@ if ($proc) {
1622
1283
  } else {
1623
1284
  Write-Output "NOT_FOUND"
1624
1285
  }`;
1625
- const { stdout } = await execAsync4(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1286
+ const { stdout } = await execAsync3(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1626
1287
  if (stdout.includes("NOT_FOUND")) {
1627
1288
  return err(`Window containing "${title}" not found`);
1628
1289
  }
1629
1290
  return ok(stdout.trim());
1630
1291
  } else {
1631
- await execAsync4(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('% x')"`, { shell: "cmd.exe" });
1292
+ await execAsync3(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('% x')"`, { shell: "cmd.exe" });
1632
1293
  return ok("Maximized active window");
1633
1294
  }
1634
1295
  } else if (process.platform === "darwin") {
1635
1296
  if (title) {
1636
- await execAsync4(`osascript -e 'tell application "${title}" to set zoomed of window 1 to true'`);
1297
+ await execAsync3(`osascript -e 'tell application "${title}" to set zoomed of window 1 to true'`);
1637
1298
  } else {
1638
- await execAsync4(`osascript -e 'tell application "System Events" to keystroke "f" using {control down, command down}'`);
1299
+ await execAsync3(`osascript -e 'tell application "System Events" to keystroke "f" using {control down, command down}'`);
1639
1300
  }
1640
1301
  return ok(`Maximized window${title ? `: ${title}` : ""}`);
1641
1302
  } else {
1642
1303
  if (title) {
1643
- await execAsync4(`wmctrl -r "${title}" -b add,maximized_vert,maximized_horz`);
1304
+ await execAsync3(`wmctrl -r "${title}" -b add,maximized_vert,maximized_horz`);
1644
1305
  } else {
1645
- await execAsync4(`wmctrl -r :ACTIVE: -b add,maximized_vert,maximized_horz`);
1306
+ await execAsync3(`wmctrl -r :ACTIVE: -b add,maximized_vert,maximized_horz`);
1646
1307
  }
1647
1308
  return ok(`Maximized window${title ? `: ${title}` : ""}`);
1648
1309
  }
@@ -1655,24 +1316,24 @@ async function closeWindow(title) {
1655
1316
  if (process.platform === "win32") {
1656
1317
  if (title) {
1657
1318
  const escaped = title.replace(/'/g, "''");
1658
- await execAsync4(`powershell -Command "Get-Process | Where-Object { $_.MainWindowTitle -like '*${escaped}*' } | ForEach-Object { $_.CloseMainWindow() }"`, { shell: "cmd.exe" });
1319
+ await execAsync3(`powershell -Command "Get-Process | Where-Object { $_.MainWindowTitle -like '*${escaped}*' } | ForEach-Object { $_.CloseMainWindow() }"`, { shell: "cmd.exe" });
1659
1320
  return ok(`Closed window: ${title}`);
1660
1321
  } else {
1661
- await execAsync4(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('%{F4}')"`, { shell: "cmd.exe" });
1322
+ await execAsync3(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('%{F4}')"`, { shell: "cmd.exe" });
1662
1323
  return ok("Closed active window");
1663
1324
  }
1664
1325
  } else if (process.platform === "darwin") {
1665
1326
  if (title) {
1666
- await execAsync4(`osascript -e 'tell application "${title}" to close window 1'`);
1327
+ await execAsync3(`osascript -e 'tell application "${title}" to close window 1'`);
1667
1328
  } else {
1668
- await execAsync4(`osascript -e 'tell application "System Events" to keystroke "w" using command down'`);
1329
+ await execAsync3(`osascript -e 'tell application "System Events" to keystroke "w" using command down'`);
1669
1330
  }
1670
1331
  return ok(`Closed window${title ? `: ${title}` : ""}`);
1671
1332
  } else {
1672
1333
  if (title) {
1673
- await execAsync4(`wmctrl -c "${title}"`);
1334
+ await execAsync3(`wmctrl -c "${title}"`);
1674
1335
  } else {
1675
- await execAsync4(`xdotool getactivewindow windowclose`);
1336
+ await execAsync3(`xdotool getactivewindow windowclose`);
1676
1337
  }
1677
1338
  return ok(`Closed window${title ? `: ${title}` : ""}`);
1678
1339
  }
@@ -1700,16 +1361,16 @@ if ($proc) {
1700
1361
  } else {
1701
1362
  Write-Output "NOT_FOUND"
1702
1363
  }`;
1703
- const { stdout } = await execAsync4(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1364
+ const { stdout } = await execAsync3(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1704
1365
  if (stdout.includes("NOT_FOUND")) {
1705
1366
  return err(`Window containing "${title}" not found`);
1706
1367
  }
1707
1368
  return ok(stdout.trim());
1708
1369
  } else if (process.platform === "darwin") {
1709
- await execAsync4(`osascript -e 'tell application "${title}" to set miniaturized of window 1 to false'`);
1370
+ await execAsync3(`osascript -e 'tell application "${title}" to set miniaturized of window 1 to false'`);
1710
1371
  return ok(`Restored window: ${title}`);
1711
1372
  } else {
1712
- await execAsync4(`wmctrl -r "${title}" -b remove,hidden`);
1373
+ await execAsync3(`wmctrl -r "${title}" -b remove,hidden`);
1713
1374
  return ok(`Restored window: ${title}`);
1714
1375
  }
1715
1376
  } catch (error) {
@@ -1726,13 +1387,13 @@ Add-Type -MemberDefinition @"
1726
1387
  public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
1727
1388
  "@ -Name Mouse -Namespace Win32
1728
1389
  [Win32.Mouse]::mouse_event(0x0800, 0, 0, ${direction}, 0)`;
1729
- await execAsync4(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1390
+ await execAsync3(`powershell -Command "${script.replace(/\n/g, " ")}"`, { shell: "cmd.exe" });
1730
1391
  } else if (process.platform === "darwin") {
1731
1392
  const dir = amount > 0 ? "u" : "d";
1732
- await execAsync4(`cliclick -r ${dir}:${Math.abs(amount)}`);
1393
+ await execAsync3(`cliclick -r ${dir}:${Math.abs(amount)}`);
1733
1394
  } else {
1734
1395
  const btn = amount > 0 ? "4" : "5";
1735
- await execAsync4(`xdotool click --repeat ${Math.abs(amount)} ${btn}`);
1396
+ await execAsync3(`xdotool click --repeat ${Math.abs(amount)} ${btn}`);
1736
1397
  }
1737
1398
  return ok(`Scrolled ${amount > 0 ? "up" : "down"} by ${Math.abs(amount)}`);
1738
1399
  } catch (error) {
@@ -1742,13 +1403,13 @@ public static extern void mouse_event(long dwFlags, long dx, long dy, long cButt
1742
1403
  async function getMousePosition() {
1743
1404
  try {
1744
1405
  if (process.platform === "win32") {
1745
- const { stdout } = await execAsync4(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; $p = [System.Windows.Forms.Cursor]::Position; Write-Output \\"$($p.X),$($p.Y)\\""`, { shell: "cmd.exe" });
1406
+ const { stdout } = await execAsync3(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; $p = [System.Windows.Forms.Cursor]::Position; Write-Output \\"$($p.X),$($p.Y)\\""`, { shell: "cmd.exe" });
1746
1407
  return ok(`Mouse position: ${stdout.trim()}`);
1747
1408
  } else if (process.platform === "darwin") {
1748
- const { stdout } = await execAsync4(`cliclick p`);
1409
+ const { stdout } = await execAsync3(`cliclick p`);
1749
1410
  return ok(`Mouse position: ${stdout.trim()}`);
1750
1411
  } else {
1751
- const { stdout } = await execAsync4(`xdotool getmouselocation --shell`);
1412
+ const { stdout } = await execAsync3(`xdotool getmouselocation --shell`);
1752
1413
  return ok(stdout);
1753
1414
  }
1754
1415
  } catch (error) {
@@ -1765,13 +1426,13 @@ function err(error) {
1765
1426
  }
1766
1427
 
1767
1428
  // src/tools/shell.ts
1768
- var execAsync5 = promisify5(exec5);
1429
+ var execAsync4 = promisify4(exec4);
1769
1430
  async function runCommand(cmd, timeout = 3e4) {
1770
1431
  try {
1771
1432
  const isWindows = process.platform === "win32";
1772
1433
  const shell = isWindows ? "cmd.exe" : "/bin/sh";
1773
1434
  const shellArg = isWindows ? "/C" : "-c";
1774
- const { stdout, stderr } = await execAsync5(cmd, {
1435
+ const { stdout, stderr } = await execAsync4(cmd, {
1775
1436
  shell,
1776
1437
  timeout,
1777
1438
  maxBuffer: 10 * 1024 * 1024
@@ -2194,12 +1855,12 @@ function getTelegramBot() {
2194
1855
 
2195
1856
  // src/hooks/useTelegram.ts
2196
1857
  function useTelegram(onMessage) {
2197
- const [isEnabled, setIsEnabled] = useState5(false);
2198
- const [isStarting, setIsStarting] = useState5(false);
2199
- const [error, setError] = useState5(null);
2200
- const [lastMessage, setLastMessage] = useState5(null);
1858
+ const [isEnabled, setIsEnabled] = useState4(false);
1859
+ const [isStarting, setIsStarting] = useState4(false);
1860
+ const [error, setError] = useState4(null);
1861
+ const [lastMessage, setLastMessage] = useState4(null);
2201
1862
  const onMessageRef = useRef2(onMessage);
2202
- useEffect3(() => {
1863
+ useEffect2(() => {
2203
1864
  onMessageRef.current = onMessage;
2204
1865
  }, [onMessage]);
2205
1866
  const start = useCallback3(async () => {
@@ -2256,7 +1917,324 @@ function useTelegram(onMessage) {
2256
1917
  }
2257
1918
 
2258
1919
  // src/hooks/useTasks.ts
2259
- import { useState as useState6, useCallback as useCallback4 } from "react";
1920
+ import { useState as useState5, useCallback as useCallback4 } from "react";
1921
+
1922
+ // src/services/browser.ts
1923
+ import { chromium } from "playwright";
1924
+ var browser = null;
1925
+ var context = null;
1926
+ var activePage = null;
1927
+ var defaultConfig = {
1928
+ headless: false,
1929
+ // Show browser so user can see what's happening
1930
+ slowMo: 50,
1931
+ // Slight delay for visibility
1932
+ viewport: { width: 1280, height: 800 }
1933
+ };
1934
+ async function initBrowser(config = {}) {
1935
+ const cfg = { ...defaultConfig, ...config };
1936
+ if (!browser) {
1937
+ browser = await chromium.launch({
1938
+ headless: cfg.headless,
1939
+ slowMo: cfg.slowMo
1940
+ });
1941
+ }
1942
+ if (!context) {
1943
+ context = await browser.newContext({
1944
+ viewport: cfg.viewport,
1945
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
1946
+ });
1947
+ }
1948
+ if (!activePage) {
1949
+ activePage = await context.newPage();
1950
+ }
1951
+ return activePage;
1952
+ }
1953
+ async function getPage() {
1954
+ if (!activePage) {
1955
+ return initBrowser();
1956
+ }
1957
+ return activePage;
1958
+ }
1959
+ async function navigateTo(url) {
1960
+ const page = await getPage();
1961
+ await page.goto(url, { waitUntil: "domcontentloaded" });
1962
+ }
1963
+ async function takeScreenshot() {
1964
+ const page = await getPage();
1965
+ const buffer = await page.screenshot({ type: "png" });
1966
+ return buffer.toString("base64");
1967
+ }
1968
+ async function clickElement(selector, timeout = 1e4) {
1969
+ const page = await getPage();
1970
+ try {
1971
+ await page.click(selector, { timeout });
1972
+ return true;
1973
+ } catch {
1974
+ return false;
1975
+ }
1976
+ }
1977
+ async function typeInElement(selector, text, timeout = 1e4) {
1978
+ const page = await getPage();
1979
+ try {
1980
+ await page.fill(selector, text, { timeout });
1981
+ return true;
1982
+ } catch {
1983
+ return false;
1984
+ }
1985
+ }
1986
+ async function pressKey2(key) {
1987
+ const page = await getPage();
1988
+ await page.keyboard.press(key);
1989
+ }
1990
+ async function scroll(direction, amount = 500) {
1991
+ const page = await getPage();
1992
+ await page.mouse.wheel(0, direction === "down" ? amount : -amount);
1993
+ }
1994
+ async function getPageText() {
1995
+ const page = await getPage();
1996
+ return await page.evaluate(() => document.body.innerText);
1997
+ }
1998
+ async function elementExists(selector) {
1999
+ const page = await getPage();
2000
+ try {
2001
+ const element = await page.$(selector);
2002
+ return element !== null;
2003
+ } catch {
2004
+ return false;
2005
+ }
2006
+ }
2007
+ var aiChatConfigs = {
2008
+ perplexity: {
2009
+ url: "https://www.perplexity.ai",
2010
+ inputSelector: 'textarea[placeholder*="Ask"]',
2011
+ submitKey: "Enter",
2012
+ responseSelector: '.prose, [class*="answer"], [class*="response"]',
2013
+ waitForResponse: 15e3
2014
+ },
2015
+ chatgpt: {
2016
+ url: "https://chat.openai.com",
2017
+ inputSelector: 'textarea[id="prompt-textarea"], textarea[data-id="root"]',
2018
+ submitSelector: 'button[data-testid="send-button"]',
2019
+ responseSelector: '[data-message-author-role="assistant"]',
2020
+ waitForResponse: 2e4
2021
+ },
2022
+ claude: {
2023
+ url: "https://claude.ai",
2024
+ inputSelector: '[contenteditable="true"], textarea',
2025
+ submitKey: "Enter",
2026
+ responseSelector: '[data-testid="message-content"]',
2027
+ waitForResponse: 2e4
2028
+ },
2029
+ copilot: {
2030
+ url: "https://copilot.microsoft.com",
2031
+ inputSelector: 'textarea, [contenteditable="true"]',
2032
+ submitKey: "Enter",
2033
+ responseSelector: '[class*="response"], [class*="message"]',
2034
+ waitForResponse: 15e3
2035
+ },
2036
+ google: {
2037
+ url: "https://www.google.com",
2038
+ inputSelector: 'textarea[name="q"], input[name="q"]',
2039
+ submitKey: "Enter",
2040
+ responseSelector: "#search",
2041
+ waitForResponse: 5e3
2042
+ }
2043
+ };
2044
+ async function askAI(site, question, includeScreenshot = false) {
2045
+ const config = aiChatConfigs[site];
2046
+ if (!config) {
2047
+ throw new Error(`Unknown AI site: ${site}`);
2048
+ }
2049
+ const page = await getPage();
2050
+ await page.goto(config.url, { waitUntil: "domcontentloaded" });
2051
+ await page.waitForTimeout(2e3);
2052
+ try {
2053
+ await page.waitForSelector(config.inputSelector, { timeout: 1e4 });
2054
+ await page.fill(config.inputSelector, question);
2055
+ } catch {
2056
+ await page.click(config.inputSelector);
2057
+ await page.type(config.inputSelector, question, { delay: 30 });
2058
+ }
2059
+ if (config.submitSelector) {
2060
+ await page.click(config.submitSelector);
2061
+ } else if (config.submitKey) {
2062
+ await page.keyboard.press(config.submitKey);
2063
+ }
2064
+ await page.waitForTimeout(config.waitForResponse);
2065
+ let response = "";
2066
+ try {
2067
+ const elements = await page.$$(config.responseSelector);
2068
+ if (elements.length > 0) {
2069
+ const lastElement = elements[elements.length - 1];
2070
+ response = await lastElement.textContent() || "";
2071
+ }
2072
+ } catch {
2073
+ response = await getPageText();
2074
+ }
2075
+ let screenshot;
2076
+ if (includeScreenshot) {
2077
+ screenshot = await takeScreenshot();
2078
+ }
2079
+ return { response: response.trim(), screenshot };
2080
+ }
2081
+ async function getFullAIResponse(site, maxScrolls = 5) {
2082
+ const config = aiChatConfigs[site];
2083
+ const page = await getPage();
2084
+ const responseParts = [];
2085
+ for (let i = 0; i < maxScrolls; i++) {
2086
+ try {
2087
+ const elements = await page.$$(config.responseSelector);
2088
+ if (elements.length > 0) {
2089
+ const lastElement = elements[elements.length - 1];
2090
+ const text = await lastElement.textContent();
2091
+ if (text) {
2092
+ responseParts.push(text.trim());
2093
+ }
2094
+ }
2095
+ await page.mouse.wheel(0, 500);
2096
+ await page.waitForTimeout(1e3);
2097
+ const atBottom = await page.evaluate(() => {
2098
+ return window.innerHeight + window.scrollY >= document.body.scrollHeight - 100;
2099
+ });
2100
+ if (atBottom) break;
2101
+ } catch {
2102
+ break;
2103
+ }
2104
+ }
2105
+ return responseParts;
2106
+ }
2107
+ async function sendGmail(email) {
2108
+ const page = await getPage();
2109
+ try {
2110
+ await page.goto("https://mail.google.com/mail/u/0/#inbox?compose=new");
2111
+ await page.waitForTimeout(3e3);
2112
+ await page.waitForSelector('input[aria-label*="To"]', { timeout: 1e4 });
2113
+ await page.fill('input[aria-label*="To"]', email.to);
2114
+ await page.keyboard.press("Tab");
2115
+ await page.fill('input[name="subjectbox"]', email.subject);
2116
+ await page.keyboard.press("Tab");
2117
+ await page.fill('[aria-label*="Message Body"], [role="textbox"]', email.body);
2118
+ await page.keyboard.press("Control+Enter");
2119
+ await page.waitForTimeout(2e3);
2120
+ return true;
2121
+ } catch {
2122
+ return false;
2123
+ }
2124
+ }
2125
+ async function sendOutlook(email) {
2126
+ const page = await getPage();
2127
+ try {
2128
+ await page.goto("https://outlook.office.com/mail/0/inbox");
2129
+ await page.waitForTimeout(3e3);
2130
+ await page.click('button[aria-label*="New mail"], button[title*="New mail"]');
2131
+ await page.waitForTimeout(2e3);
2132
+ await page.fill('input[aria-label*="To"]', email.to);
2133
+ await page.keyboard.press("Tab");
2134
+ await page.fill('input[aria-label*="Subject"], input[placeholder*="Subject"]', email.subject);
2135
+ await page.keyboard.press("Tab");
2136
+ await page.fill('[aria-label*="Message body"], [role="textbox"]', email.body);
2137
+ await page.click('button[aria-label*="Send"], button[title*="Send"]');
2138
+ await page.waitForTimeout(2e3);
2139
+ return true;
2140
+ } catch {
2141
+ return false;
2142
+ }
2143
+ }
2144
+ async function googleSheetsType(cellData) {
2145
+ const page = await getPage();
2146
+ try {
2147
+ await page.goto("https://docs.google.com/spreadsheets/create");
2148
+ await page.waitForTimeout(5e3);
2149
+ for (const { cell, value } of cellData) {
2150
+ await page.click("input#t-name-box");
2151
+ await page.fill("input#t-name-box", cell);
2152
+ await page.keyboard.press("Enter");
2153
+ await page.waitForTimeout(500);
2154
+ await page.keyboard.type(value);
2155
+ await page.keyboard.press("Enter");
2156
+ await page.waitForTimeout(300);
2157
+ }
2158
+ return true;
2159
+ } catch {
2160
+ return false;
2161
+ }
2162
+ }
2163
+ async function googleDocsType(text) {
2164
+ const page = await getPage();
2165
+ try {
2166
+ await page.goto("https://docs.google.com/document/create");
2167
+ await page.waitForTimeout(5e3);
2168
+ await page.click(".kix-appview-editor");
2169
+ await page.waitForTimeout(500);
2170
+ await page.keyboard.type(text, { delay: 20 });
2171
+ return true;
2172
+ } catch {
2173
+ return false;
2174
+ }
2175
+ }
2176
+ async function webSearch(query, engine = "google") {
2177
+ const page = await getPage();
2178
+ const results = [];
2179
+ const urls = {
2180
+ google: "https://www.google.com",
2181
+ bing: "https://www.bing.com",
2182
+ duckduckgo: "https://duckduckgo.com"
2183
+ };
2184
+ const selectors = {
2185
+ google: { input: 'textarea[name="q"]', results: "#search .g h3" },
2186
+ bing: { input: 'input[name="q"]', results: "#b_results h2 a" },
2187
+ duckduckgo: { input: 'input[name="q"]', results: "[data-result] h2" }
2188
+ };
2189
+ try {
2190
+ await page.goto(urls[engine]);
2191
+ await page.waitForTimeout(2e3);
2192
+ await page.fill(selectors[engine].input, query);
2193
+ await page.keyboard.press("Enter");
2194
+ await page.waitForTimeout(3e3);
2195
+ const elements = await page.$$(selectors[engine].results);
2196
+ for (const el of elements.slice(0, 10)) {
2197
+ const text = await el.textContent();
2198
+ if (text) results.push(text);
2199
+ }
2200
+ } catch {
2201
+ }
2202
+ return results;
2203
+ }
2204
+ async function research(topic, maxSources = 3) {
2205
+ const page = await getPage();
2206
+ const sources = [];
2207
+ await webSearch(topic);
2208
+ await page.waitForTimeout(2e3);
2209
+ for (let i = 0; i < maxSources; i++) {
2210
+ try {
2211
+ const results = await page.$$("#search .g");
2212
+ if (results[i]) {
2213
+ const titleEl = await results[i].$("h3");
2214
+ const linkEl = await results[i].$("a");
2215
+ const title = await titleEl?.textContent() || "Unknown";
2216
+ const url = await linkEl?.getAttribute("href") || "";
2217
+ await titleEl?.click();
2218
+ await page.waitForTimeout(3e3);
2219
+ const content = await page.evaluate(() => {
2220
+ const article = document.querySelector("article, main, .content, #content");
2221
+ return article?.textContent?.slice(0, 2e3) || document.body.innerText.slice(0, 2e3);
2222
+ });
2223
+ sources.push({ title, url, content: content.trim() });
2224
+ await page.goBack();
2225
+ await page.waitForTimeout(1500);
2226
+ }
2227
+ } catch {
2228
+ continue;
2229
+ }
2230
+ }
2231
+ return {
2232
+ query: topic,
2233
+ sources,
2234
+ summary: ""
2235
+ // To be filled by AI
2236
+ };
2237
+ }
2260
2238
 
2261
2239
  // src/lib/tasks.ts
2262
2240
  import * as fs2 from "fs";
@@ -2380,7 +2358,27 @@ Before outputting steps, THINK through these questions:
2380
2358
  ### Web Browsing
2381
2359
  - open_url: Open URL in default browser (e.g., "open_url:https://perplexity.ai")
2382
2360
  - browse_and_ask: Open AI website, type question, wait for response (e.g., "browse_and_ask:perplexity|What is the capital of France?")
2383
- - browse_and_ask: Supports: perplexity, chatgpt, claude, google
2361
+ - browse_and_ask: Supports: perplexity, chatgpt, claude, google, copilot, bard
2362
+ - web_search: Search Google and extract results (e.g., "web_search:best restaurants in NYC")
2363
+
2364
+ ### Email
2365
+ - send_email: Send email via Gmail or Outlook web (e.g., "send_email:gmail|to@email.com|Subject|Body text here")
2366
+ - send_email: Supports: gmail, outlook
2367
+
2368
+ ### Google Apps (via browser)
2369
+ - google_sheets: Interact with Google Sheets (e.g., "google_sheets:new|My Spreadsheet" or "google_sheets:type|A1|Hello World")
2370
+ - google_sheets: Commands: new (create), open (open existing), type (type in cell), read (screenshot current view)
2371
+ - google_docs: Interact with Google Docs (e.g., "google_docs:new|My Document" or "google_docs:type|Hello World")
2372
+ - google_docs: Commands: new (create), open (open existing), type (type text)
2373
+
2374
+ ### Research
2375
+ - research: Multi-step web research - searches, gathers info, summarizes (e.g., "research:What are the latest AI trends in 2024?")
2376
+
2377
+ ### Adaptive/Learning
2378
+ - ask_llm: Ask another LLM for help with a screenshot (e.g., "ask_llm:perplexity|How do I do X in this app?")
2379
+ - ask_llm: Supports: perplexity, chatgpt, claude, copilot - sends screenshot + question, gets answer
2380
+ - adaptive_do: Try to accomplish something, if stuck ask LLMs for help (e.g., "adaptive_do:book a flight to NYC on kayak.com")
2381
+ - learn_ui: Take screenshot and learn how to interact with current UI (e.g., "learn_ui:What buttons can I click here?")
2384
2382
 
2385
2383
  ### Utility
2386
2384
  - wait: Wait N seconds (e.g., "wait:2" - use 1-3s for app loads)
@@ -2471,14 +2469,104 @@ Output:
2471
2469
  ### Example 7: "search google for weather today"
2472
2470
  Thinking:
2473
2471
  - Goal: Open Google and search for something
2474
- - How: Use browse_and_ask with google target
2475
- - Sequence: Open Google, search, capture results
2472
+ - How: Use web_search for quick results extraction
2473
+ - Sequence: Search and get results
2474
+
2475
+ Output:
2476
+ [
2477
+ { "description": "Search Google for weather", "action": "web_search:weather today" }
2478
+ ]
2479
+
2480
+ ### Example 8: "send an email to john@example.com about the meeting tomorrow"
2481
+ Thinking:
2482
+ - Goal: Compose and send an email via Gmail
2483
+ - How: Use send_email with gmail, recipient, subject, body
2484
+ - Sequence: Open Gmail, compose, fill fields, send
2485
+
2486
+ Output:
2487
+ [
2488
+ { "description": "Send email via Gmail", "action": "send_email:gmail|john@example.com|Meeting Tomorrow|Hi John, this is a reminder about our meeting tomorrow. Please let me know if you have any questions." }
2489
+ ]
2490
+
2491
+ ### Example 9: "create a new google sheet called Sales Report and add headers"
2492
+ Thinking:
2493
+ - Goal: Create a new Google Sheet and add content
2494
+ - How: Use google_sheets to create new, then type in cells
2495
+ - Sequence: Create sheet -> Navigate to cells -> Type headers
2476
2496
 
2477
2497
  Output:
2478
2498
  [
2479
- { "description": "Search Google", "action": "browse_and_ask:google|weather today" },
2480
- { "description": "Wait for results", "action": "wait:2" },
2481
- { "description": "Capture search results", "action": "screenshot" }
2499
+ { "description": "Create new Google Sheet", "action": "google_sheets:new|Sales Report" },
2500
+ { "description": "Wait for sheet to load", "action": "wait:3" },
2501
+ { "description": "Type header in A1", "action": "google_sheets:type|A1|Product" },
2502
+ { "description": "Type header in B1", "action": "google_sheets:type|B1|Quantity" },
2503
+ { "description": "Type header in C1", "action": "google_sheets:type|C1|Price" }
2504
+ ]
2505
+
2506
+ ### Example 10: "research the latest news about AI regulations"
2507
+ Thinking:
2508
+ - Goal: Do multi-step research on a topic
2509
+ - How: Use research action which handles searching, gathering, summarizing
2510
+ - Sequence: Single research action does it all
2511
+
2512
+ Output:
2513
+ [
2514
+ { "description": "Research AI regulations news", "action": "research:latest news about AI regulations 2024" }
2515
+ ]
2516
+
2517
+ ### Example 11: "write a document in google docs about project status"
2518
+ Thinking:
2519
+ - Goal: Create a Google Doc and write content
2520
+ - How: Use google_docs to create and type
2521
+ - Sequence: Create doc -> Type content
2522
+
2523
+ Output:
2524
+ [
2525
+ { "description": "Create new Google Doc", "action": "google_docs:new|Project Status Report" },
2526
+ { "description": "Wait for doc to load", "action": "wait:3" },
2527
+ { "description": "Type the content", "action": "google_docs:type|Project Status Report
2528
+
2529
+ Date: Today
2530
+
2531
+ Summary:
2532
+ The project is on track. All milestones have been met.
2533
+
2534
+ Next Steps:
2535
+ - Complete testing
2536
+ - Deploy to production" }
2537
+ ]
2538
+
2539
+ ### Example 12: "I don't know how to use this app, can you figure it out?"
2540
+ Thinking:
2541
+ - Goal: Learn the current UI and understand how to use it
2542
+ - How: Use learn_ui to take screenshot and analyze
2543
+ - Sequence: Screenshot -> AI analysis -> report back
2544
+
2545
+ Output:
2546
+ [
2547
+ { "description": "Analyze current UI", "action": "learn_ui:What are all the buttons, menus, and interactive elements I can use?" }
2548
+ ]
2549
+
2550
+ ### Example 13: "book a hotel on booking.com for next weekend"
2551
+ Thinking:
2552
+ - Goal: Complex task on unfamiliar website - need adaptive approach
2553
+ - How: Use adaptive_do which will try, and if stuck ask LLMs for help
2554
+ - Sequence: Single adaptive action handles the complexity
2555
+
2556
+ Output:
2557
+ [
2558
+ { "description": "Adaptively book hotel", "action": "adaptive_do:Go to booking.com and book a hotel for next weekend" }
2559
+ ]
2560
+
2561
+ ### Example 14: "I'm stuck, ask Claude how to proceed"
2562
+ Thinking:
2563
+ - Goal: Get help from another LLM with current screen context
2564
+ - How: Use ask_llm with claude and send screenshot
2565
+ - Sequence: Screenshot + question -> Get answer
2566
+
2567
+ Output:
2568
+ [
2569
+ { "description": "Ask Claude for help with screenshot", "action": "ask_llm:claude|I'm stuck on this screen. What should I do next to accomplish my task?" }
2482
2570
  ]
2483
2571
 
2484
2572
  ## YOUR TASK
@@ -2670,72 +2758,311 @@ ${existingResult.output}`;
2670
2758
  case "browse_and_ask": {
2671
2759
  const [site, ...questionParts] = params.split("|");
2672
2760
  const question = questionParts.join("|");
2673
- const sites = {
2674
- perplexity: { url: "https://www.perplexity.ai", loadTime: 3, responseTime: 10 },
2675
- chatgpt: { url: "https://chat.openai.com", loadTime: 4, responseTime: 15 },
2676
- claude: { url: "https://claude.ai", loadTime: 4, responseTime: 15 },
2677
- google: { url: "https://www.google.com", loadTime: 2, responseTime: 3 },
2678
- bing: { url: "https://www.bing.com", loadTime: 2, responseTime: 3 },
2679
- bard: { url: "https://bard.google.com", loadTime: 3, responseTime: 12 },
2680
- copilot: { url: "https://copilot.microsoft.com", loadTime: 3, responseTime: 12 }
2681
- };
2682
- const siteConfig = sites[site.toLowerCase()] || { url: `https://${site}`, loadTime: 3, responseTime: 10 };
2683
- if (process.platform === "win32") {
2684
- await runCommand(`start "" "${siteConfig.url}"`, 5e3);
2685
- } else if (process.platform === "darwin") {
2686
- await runCommand(`open "${siteConfig.url}"`, 5e3);
2761
+ const supportedSites = ["perplexity", "chatgpt", "claude", "copilot", "google"];
2762
+ const siteLower = site.toLowerCase();
2763
+ if (supportedSites.includes(siteLower)) {
2764
+ const result = await askAI(siteLower, question, true);
2765
+ if (result.response.length < 500) {
2766
+ const fullParts = await getFullAIResponse(siteLower, 5);
2767
+ if (fullParts.length > 0) {
2768
+ step.result = `\u{1F4DD} ${site.charAt(0).toUpperCase() + site.slice(1)} says:
2769
+
2770
+ ${fullParts.join("\n\n")}`;
2771
+ break;
2772
+ }
2773
+ }
2774
+ step.result = `\u{1F4DD} ${site.charAt(0).toUpperCase() + site.slice(1)} says:
2775
+
2776
+ ${result.response}`;
2687
2777
  } else {
2688
- await runCommand(`xdg-open "${siteConfig.url}"`, 5e3);
2778
+ await navigateTo(`https://${site}`);
2779
+ await sleep(2e3);
2780
+ const page = await getPage();
2781
+ const inputs = ["textarea", 'input[type="text"]', 'input[type="search"]', '[contenteditable="true"]'];
2782
+ for (const selector of inputs) {
2783
+ if (await elementExists(selector)) {
2784
+ await typeInElement(selector, question);
2785
+ await pressKey2("Enter");
2786
+ break;
2787
+ }
2788
+ }
2789
+ await sleep(5e3);
2790
+ const pageText = await getPageText();
2791
+ step.result = `\u{1F4DD} Response from ${site}:
2792
+
2793
+ ${pageText.slice(0, 3e3)}`;
2689
2794
  }
2690
- await sleep(siteConfig.loadTime * 1e3);
2691
- await typeText(question);
2692
- await sleep(300);
2693
- await pressKey("Return");
2694
- await sleep(siteConfig.responseTime * 1e3);
2695
- const extractedParts = [];
2696
- const maxScrolls = 5;
2697
- for (let scrollIndex = 0; scrollIndex < maxScrolls; scrollIndex++) {
2698
- const screenResult = await describeScreen();
2699
- const extractPrompt = `You are looking at screenshot ${scrollIndex + 1} of ${site}. The user asked: "${question}"
2700
-
2701
- Extract ONLY the AI's response/answer text visible on screen. Do NOT include:
2702
- - The user's question
2703
- - Any UI elements, buttons, navigation, or headers
2704
- - Any disclaimers, suggestions, or "related questions"
2705
- - Any "Sources" or citation links
2706
- - Any text you already extracted (avoid duplicates)
2707
-
2708
- ${scrollIndex > 0 ? `Previous parts already extracted:
2709
- ${extractedParts.join("\n---\n")}
2710
-
2711
- Only extract NEW text that continues from where we left off.` : ""}
2712
-
2713
- Just give me the actual answer text, word for word as it appears. If there's no more response text visible, respond with exactly: "END_OF_RESPONSE"`;
2714
- const extractResponse = await chat([{ role: "user", content: extractPrompt }]);
2715
- const extracted = extractResponse.content.trim();
2716
- if (extracted === "END_OF_RESPONSE" || extracted.includes("END_OF_RESPONSE")) {
2795
+ break;
2796
+ }
2797
+ case "screenshot":
2798
+ const vision = await describeScreen();
2799
+ step.result = vision.description;
2800
+ break;
2801
+ case "web_search": {
2802
+ const searchResults = await webSearch(params, "google");
2803
+ if (searchResults.length > 0) {
2804
+ step.result = `\u{1F50D} Search results for "${params}":
2805
+
2806
+ ${searchResults.map((r, i) => `${i + 1}. ${r}`).join("\n")}`;
2807
+ } else {
2808
+ const pageText = await getPageText();
2809
+ step.result = `\u{1F50D} Search results for "${params}":
2810
+
2811
+ ${pageText.slice(0, 2e3)}`;
2812
+ }
2813
+ break;
2814
+ }
2815
+ case "send_email": {
2816
+ const [provider, to, subject, ...bodyParts] = params.split("|");
2817
+ const body = bodyParts.join("|");
2818
+ const emailData = { to, subject, body };
2819
+ let success = false;
2820
+ if (provider.toLowerCase() === "gmail") {
2821
+ success = await sendGmail(emailData);
2822
+ } else if (provider.toLowerCase() === "outlook") {
2823
+ success = await sendOutlook(emailData);
2824
+ } else {
2825
+ throw new Error(`Unsupported email provider: ${provider}. Use gmail or outlook.`);
2826
+ }
2827
+ if (success) {
2828
+ step.result = `\u{1F4E7} Email sent via ${provider} to ${to}`;
2829
+ } else {
2830
+ throw new Error(`Failed to send email via ${provider}. Make sure you're logged in.`);
2831
+ }
2832
+ break;
2833
+ }
2834
+ case "google_sheets": {
2835
+ const [sheetCmd, ...sheetArgs] = params.split("|");
2836
+ switch (sheetCmd.toLowerCase()) {
2837
+ case "new": {
2838
+ const sheetName = sheetArgs[0] || "Untitled spreadsheet";
2839
+ await navigateTo("https://docs.google.com/spreadsheets/create");
2840
+ await sleep(5e3);
2841
+ step.result = `\u{1F4CA} Created Google Sheet: ${sheetName}`;
2717
2842
  break;
2718
2843
  }
2719
- if (extracted.toLowerCase().includes("response not ready") || extracted.toLowerCase().includes("no response visible") || extracted.toLowerCase().includes("no additional text")) {
2720
- if (scrollIndex === 0) {
2721
- extractedParts.push("Response not ready yet or page still loading.");
2722
- }
2844
+ case "type": {
2845
+ const cell = sheetArgs[0] || "A1";
2846
+ const cellValue = sheetArgs.slice(1).join("|");
2847
+ const success = await googleSheetsType([{ cell, value: cellValue }]);
2848
+ step.result = success ? `\u{1F4CA} Typed "${cellValue}" in cell ${cell}` : `\u{1F4CA} Could not type in cell ${cell}`;
2849
+ break;
2850
+ }
2851
+ case "read": {
2852
+ const screenshot = await takeScreenshot();
2853
+ const analysis = await chat([{
2854
+ role: "user",
2855
+ content: "Describe the contents of this Google Sheet. List visible data in the cells."
2856
+ }]);
2857
+ step.result = `\u{1F4CA} Current sheet view:
2858
+ ${analysis.content}`;
2723
2859
  break;
2724
2860
  }
2725
- extractedParts.push(extracted);
2726
- await scrollMouse(-5);
2727
- await sleep(1e3);
2861
+ default:
2862
+ throw new Error(`Unknown google_sheets command: ${sheetCmd}`);
2863
+ }
2864
+ break;
2865
+ }
2866
+ case "google_docs": {
2867
+ const [docCmd, ...docArgs] = params.split("|");
2868
+ switch (docCmd.toLowerCase()) {
2869
+ case "new": {
2870
+ const docName = docArgs[0] || "Untitled document";
2871
+ const success = await googleDocsType("");
2872
+ step.result = success ? `\u{1F4C4} Created Google Doc: ${docName}` : `\u{1F4C4} Could not create Google Doc`;
2873
+ break;
2874
+ }
2875
+ case "type": {
2876
+ const docText = docArgs.join("|");
2877
+ const success = await googleDocsType(docText);
2878
+ step.result = success ? `\u{1F4C4} Typed content in Google Doc` : `\u{1F4C4} Could not type in Google Doc`;
2879
+ break;
2880
+ }
2881
+ default:
2882
+ throw new Error(`Unknown google_docs command: ${docCmd}`);
2883
+ }
2884
+ break;
2885
+ }
2886
+ case "research": {
2887
+ const researchQuery = params;
2888
+ const researchData = await research(researchQuery, 3);
2889
+ const sourceSummaries = researchData.sources.map(
2890
+ (s, i) => `Source ${i + 1}: ${s.title}
2891
+ ${s.content.slice(0, 500)}...`
2892
+ ).join("\n\n");
2893
+ const synthesis = await chat([{
2894
+ role: "user",
2895
+ content: `Based on the following research gathered about "${researchQuery}", provide a comprehensive summary:
2896
+
2897
+ ${sourceSummaries}
2898
+
2899
+ Create a well-organized summary with:
2900
+ 1. Key findings
2901
+ 2. Important details
2902
+ 3. Any notable facts or statistics
2903
+ 4. Conclusion
2904
+
2905
+ Be thorough but concise.`
2906
+ }]);
2907
+ step.result = `\u{1F52C} Research Summary: ${researchQuery}
2908
+
2909
+ ${synthesis.content}`;
2910
+ break;
2911
+ }
2912
+ case "ask_llm": {
2913
+ const [llmName, ...questionParts] = params.split("|");
2914
+ const question = questionParts.join("|");
2915
+ const currentScreen = await describeScreen();
2916
+ const fullQuestion = `I'm looking at my screen and I need help. ${question}
2917
+
2918
+ Here's what I see on my screen: ${currentScreen.description}`;
2919
+ const supportedLLMs = ["perplexity", "chatgpt", "claude", "copilot"];
2920
+ const llmLower = llmName.toLowerCase();
2921
+ if (!supportedLLMs.includes(llmLower)) {
2922
+ throw new Error(`Unknown LLM: ${llmName}. Supported: ${supportedLLMs.join(", ")}`);
2728
2923
  }
2729
- const fullResponse = extractedParts.join("\n\n");
2730
- step.result = `\u{1F4DD} ${site.charAt(0).toUpperCase() + site.slice(1)} says:
2924
+ const result = await askAI(llmLower, fullQuestion, false);
2925
+ const fullParts = await getFullAIResponse(llmLower, 3);
2926
+ const finalResponse = fullParts.length > 0 ? fullParts.join("\n\n") : result.response;
2927
+ step.result = `\u{1F916} ${llmName} says:
2731
2928
 
2732
- ${fullResponse}`;
2929
+ ${finalResponse}`;
2733
2930
  break;
2734
2931
  }
2735
- case "screenshot":
2736
- const vision = await describeScreen();
2737
- step.result = vision.description;
2932
+ case "learn_ui": {
2933
+ const uiScreen = await describeScreen();
2934
+ const uiAnalysis = await chat([{
2935
+ role: "user",
2936
+ content: `Analyze this screenshot and identify all interactive UI elements. List:
2937
+ 1. All clickable buttons and their likely functions
2938
+ 2. Text input fields
2939
+ 3. Menus and dropdowns
2940
+ 4. Links
2941
+ 5. Any keyboard shortcuts visible
2942
+ 6. The main actions available in this interface
2943
+
2944
+ Question: ${params}
2945
+
2946
+ Be specific about locations (top-left, center, etc.) and what each element does.`
2947
+ }]);
2948
+ step.result = `\u{1F50D} UI Analysis:
2949
+
2950
+ ${uiAnalysis.content}`;
2951
+ break;
2952
+ }
2953
+ case "adaptive_do": {
2954
+ const goal = params;
2955
+ const maxAttempts = 5;
2956
+ const actionHistory = [];
2957
+ let accomplished = false;
2958
+ const page = await getPage();
2959
+ for (let attempt = 0; attempt < maxAttempts && !accomplished; attempt++) {
2960
+ const screenshot = await takeScreenshot();
2961
+ const currentState = await chat([{
2962
+ role: "user",
2963
+ content: `Describe what you see on this screen. What app/website is it? What elements are visible?`
2964
+ }]);
2965
+ const nextAction = await chat([{
2966
+ role: "user",
2967
+ content: `GOAL: ${goal}
2968
+
2969
+ CURRENT SCREEN: ${currentState.content}
2970
+
2971
+ PREVIOUS ACTIONS TAKEN:
2972
+ ${actionHistory.length > 0 ? actionHistory.join("\n") : "None yet"}
2973
+
2974
+ Based on what you see, what's the SINGLE next action to take?
2975
+ Options:
2976
+ - click: Click element (describe CSS selector or visible text)
2977
+ - type: Type something (specify selector and text)
2978
+ - press: Press a key (specify key)
2979
+ - scroll: Scroll up/down
2980
+ - navigate: Go to URL
2981
+ - done: Goal is accomplished
2982
+ - stuck: Can't figure out what to do
2983
+
2984
+ Respond in format:
2985
+ ACTION: <action_type>
2986
+ SELECTOR: <css selector or text to find>
2987
+ VALUE: <text to type or URL>
2988
+ REASONING: <why>`
2989
+ }]);
2990
+ const actionContent = nextAction.content;
2991
+ const actionMatch = actionContent.match(/ACTION:\s*(\w+)/i);
2992
+ const selectorMatch = actionContent.match(/SELECTOR:\s*(.+?)(?:\n|$)/i);
2993
+ const valueMatch = actionContent.match(/VALUE:\s*(.+?)(?:\n|$)/i);
2994
+ if (!actionMatch) {
2995
+ actionHistory.push(`Attempt ${attempt + 1}: Couldn't parse action`);
2996
+ continue;
2997
+ }
2998
+ const action = actionMatch[1].toLowerCase();
2999
+ const selector = selectorMatch?.[1]?.trim() || "";
3000
+ const value = valueMatch?.[1]?.trim() || "";
3001
+ if (action === "done") {
3002
+ accomplished = true;
3003
+ actionHistory.push(`Attempt ${attempt + 1}: Goal accomplished!`);
3004
+ break;
3005
+ }
3006
+ if (action === "stuck") {
3007
+ actionHistory.push(`Attempt ${attempt + 1}: Got stuck, asking Perplexity for help...`);
3008
+ const helpRequest = `I'm trying to: ${goal}
3009
+
3010
+ I'm stuck. What should I do next? Be specific about what to click or type.`;
3011
+ const advice = await askAI("perplexity", helpRequest, false);
3012
+ actionHistory.push(`Got advice: ${advice.response.slice(0, 200)}...`);
3013
+ await navigateTo(page.url());
3014
+ continue;
3015
+ }
3016
+ try {
3017
+ switch (action) {
3018
+ case "click":
3019
+ if (selector) {
3020
+ const clicked = await clickElement(selector);
3021
+ if (!clicked) {
3022
+ await page.getByText(selector).first().click({ timeout: 5e3 });
3023
+ }
3024
+ }
3025
+ actionHistory.push(`Attempt ${attempt + 1}: Clicked "${selector}"`);
3026
+ break;
3027
+ case "type":
3028
+ if (selector && value) {
3029
+ const typed = await typeInElement(selector, value);
3030
+ if (!typed) {
3031
+ await page.getByPlaceholder(selector).first().fill(value);
3032
+ }
3033
+ }
3034
+ actionHistory.push(`Attempt ${attempt + 1}: Typed "${value}" in "${selector}"`);
3035
+ break;
3036
+ case "press":
3037
+ await pressKey2(value || selector);
3038
+ actionHistory.push(`Attempt ${attempt + 1}: Pressed ${value || selector}`);
3039
+ break;
3040
+ case "scroll":
3041
+ await scroll(value.toLowerCase().includes("up") ? "up" : "down");
3042
+ actionHistory.push(`Attempt ${attempt + 1}: Scrolled ${value || "down"}`);
3043
+ break;
3044
+ case "navigate":
3045
+ const url = value.startsWith("http") ? value : `https://${value}`;
3046
+ await navigateTo(url);
3047
+ actionHistory.push(`Attempt ${attempt + 1}: Navigated to ${url}`);
3048
+ break;
3049
+ default:
3050
+ actionHistory.push(`Attempt ${attempt + 1}: Unknown action ${action}`);
3051
+ }
3052
+ } catch (e) {
3053
+ actionHistory.push(`Attempt ${attempt + 1}: Action failed - ${e}`);
3054
+ }
3055
+ await sleep(2e3);
3056
+ }
3057
+ step.result = `\u{1F3AF} Adaptive Agent Result:
3058
+
3059
+ Goal: ${goal}
3060
+ Accomplished: ${accomplished ? "Yes \u2705" : "Partial/No \u274C"}
3061
+
3062
+ Action Log:
3063
+ ${actionHistory.join("\n")}`;
2738
3064
  break;
3065
+ }
2739
3066
  case "chat":
2740
3067
  step.result = `Task noted: ${params}`;
2741
3068
  break;
@@ -2826,10 +3153,10 @@ function formatTask(task) {
2826
3153
 
2827
3154
  // src/hooks/useTasks.ts
2828
3155
  function useTasks(onProgress) {
2829
- const [isRunning, setIsRunning] = useState6(false);
2830
- const [currentTask, setCurrentTask] = useState6(null);
2831
- const [currentStep, setCurrentStep] = useState6(null);
2832
- const [error, setError] = useState6(null);
3156
+ const [isRunning, setIsRunning] = useState5(false);
3157
+ const [currentTask, setCurrentTask] = useState5(null);
3158
+ const [currentStep, setCurrentStep] = useState5(null);
3159
+ const [error, setError] = useState5(null);
2833
3160
  const run = useCallback4(async (description) => {
2834
3161
  setIsRunning(true);
2835
3162
  setError(null);
@@ -2865,13 +3192,13 @@ function useTasks(onProgress) {
2865
3192
  }
2866
3193
 
2867
3194
  // src/components/App.tsx
2868
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3195
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2869
3196
  function App() {
2870
3197
  const { exit } = useApp();
2871
- const [overlay, setOverlay] = useState7("none");
2872
- const [screenWatch, setScreenWatch] = useState7(false);
2873
- const [status, setStatus] = useState7("Ready");
2874
- const [inputValue, setInputValue] = useState7("");
3198
+ const [overlay, setOverlay] = useState6("none");
3199
+ const [screenWatch, setScreenWatch] = useState6(false);
3200
+ const [status, setStatus] = useState6("Ready");
3201
+ const [inputValue, setInputValue] = useState6("");
2875
3202
  const chat2 = useChat(screenWatch);
2876
3203
  const vision = useVision();
2877
3204
  const telegram = useTelegram((msg) => {
@@ -2882,7 +3209,7 @@ function App() {
2882
3209
  setStatus(`Running: ${step.description}`);
2883
3210
  }
2884
3211
  });
2885
- useInput3((inputChar, key) => {
3212
+ useInput2((inputChar, key) => {
2886
3213
  if (overlay !== "none") return;
2887
3214
  if (key.ctrl && inputChar === "c") exit();
2888
3215
  if (key.ctrl && inputChar === "l") chat2.clearMessages();
@@ -3060,7 +3387,7 @@ ${tasks.format(task)}`);
3060
3387
  chat2.addSystemMessage(`\u2705 Updated: ${provider} / ${model}`);
3061
3388
  }, [chat2]);
3062
3389
  if (overlay === "help") {
3063
- return /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: "100%", alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx7(
3390
+ return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: "100%", alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx6(
3064
3391
  HelpMenu,
3065
3392
  {
3066
3393
  onClose: () => setOverlay("none"),
@@ -3072,7 +3399,7 @@ ${tasks.format(task)}`);
3072
3399
  ) });
3073
3400
  }
3074
3401
  if (overlay === "provider") {
3075
- return /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", height: "100%", alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx7(
3402
+ return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: "100%", alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx6(
3076
3403
  ProviderSelector,
3077
3404
  {
3078
3405
  onClose: () => setOverlay("none"),
@@ -3082,11 +3409,11 @@ ${tasks.format(task)}`);
3082
3409
  }
3083
3410
  const visibleMessages = chat2.messages.slice(-20);
3084
3411
  const isProcessing = chat2.isProcessing || vision.isAnalyzing || tasks.isRunning || telegram.isStarting;
3085
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", height: "100%", children: [
3086
- /* @__PURE__ */ jsx7(Header, { screenWatch, telegramEnabled: telegram.isEnabled }),
3087
- /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "gray", padding: 1, children: [
3088
- /* @__PURE__ */ jsx7(Text7, { bold: true, color: "gray", children: " Chat " }),
3089
- visibleMessages.map((msg) => /* @__PURE__ */ jsx7(
3412
+ return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", height: "100%", children: [
3413
+ /* @__PURE__ */ jsx6(Header, { screenWatch, telegramEnabled: telegram.isEnabled }),
3414
+ /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", flexGrow: 1, borderStyle: "round", borderColor: "gray", padding: 1, children: [
3415
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: "gray", children: " Chat " }),
3416
+ visibleMessages.map((msg) => /* @__PURE__ */ jsx6(
3090
3417
  ChatMessage,
3091
3418
  {
3092
3419
  role: msg.role,
@@ -3097,11 +3424,11 @@ ${tasks.format(task)}`);
3097
3424
  msg.id
3098
3425
  ))
3099
3426
  ] }),
3100
- chat2.error && /* @__PURE__ */ jsx7(Box7, { marginY: 1, children: /* @__PURE__ */ jsxs6(Text7, { color: "red", children: [
3427
+ chat2.error && /* @__PURE__ */ jsx6(Box6, { marginY: 1, children: /* @__PURE__ */ jsxs5(Text6, { color: "red", children: [
3101
3428
  "Error: ",
3102
3429
  chat2.error
3103
3430
  ] }) }),
3104
- /* @__PURE__ */ jsx7(
3431
+ /* @__PURE__ */ jsx6(
3105
3432
  ChatInput,
3106
3433
  {
3107
3434
  value: inputValue,
@@ -3110,12 +3437,12 @@ ${tasks.format(task)}`);
3110
3437
  isProcessing
3111
3438
  }
3112
3439
  ),
3113
- /* @__PURE__ */ jsx7(StatusBar, { status })
3440
+ /* @__PURE__ */ jsx6(StatusBar, { status })
3114
3441
  ] });
3115
3442
  }
3116
3443
 
3117
3444
  // src/index.tsx
3118
- import { jsx as jsx8 } from "react/jsx-runtime";
3445
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3119
3446
  var args = process.argv.slice(2);
3120
3447
  async function main() {
3121
3448
  if (args.length > 0) {
@@ -3144,8 +3471,21 @@ async function main() {
3144
3471
  case "config": {
3145
3472
  const subcommand = args[1];
3146
3473
  if (!subcommand) {
3147
- const { ConfigUI } = await import("./ConfigUI-I2CJVODT.js");
3148
- render(/* @__PURE__ */ jsx8(ConfigUI, {}));
3474
+ const { ProviderSelector: ProviderSelector2 } = await import("./ProviderSelector-MXRZFAOB.js");
3475
+ const { Box: Box7 } = await import("ink");
3476
+ render(
3477
+ /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", padding: 1, children: /* @__PURE__ */ jsx7(
3478
+ ProviderSelector2,
3479
+ {
3480
+ onClose: () => process.exit(0),
3481
+ onSelect: (provider, model) => {
3482
+ console.log(`
3483
+ \u2713 Set to ${provider} / ${model}`);
3484
+ process.exit(0);
3485
+ }
3486
+ }
3487
+ ) })
3488
+ );
3149
3489
  return;
3150
3490
  }
3151
3491
  if (subcommand === "set") {
@@ -3243,12 +3583,29 @@ ${dim}GitHub: https://github.com/projectservan8n/C-napse${reset}
3243
3583
  case "version":
3244
3584
  case "--version":
3245
3585
  case "-v": {
3246
- console.log("cnapse v0.5.0");
3586
+ console.log("cnapse v0.8.0");
3247
3587
  process.exit(0);
3248
3588
  }
3249
3589
  case "init": {
3250
- const { Setup } = await import("./Setup-KGYXCA7Y.js");
3251
- render(/* @__PURE__ */ jsx8(Setup, {}));
3590
+ const { ProviderSelector: ProviderSelector2 } = await import("./ProviderSelector-MXRZFAOB.js");
3591
+ const { Box: Box7, Text: Text7 } = await import("ink");
3592
+ render(
3593
+ /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", padding: 1, children: [
3594
+ /* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "C-napse Setup" }) }),
3595
+ /* @__PURE__ */ jsx7(
3596
+ ProviderSelector2,
3597
+ {
3598
+ onClose: () => process.exit(0),
3599
+ onSelect: (provider, model) => {
3600
+ console.log(`
3601
+ \u2713 Setup complete! Provider: ${provider}, Model: ${model}`);
3602
+ console.log("Run `cnapse` to start chatting.");
3603
+ process.exit(0);
3604
+ }
3605
+ }
3606
+ )
3607
+ ] })
3608
+ );
3252
3609
  return;
3253
3610
  }
3254
3611
  default: {
@@ -3256,6 +3613,6 @@ ${dim}GitHub: https://github.com/projectservan8n/C-napse${reset}
3256
3613
  }
3257
3614
  }
3258
3615
  }
3259
- render(/* @__PURE__ */ jsx8(App, {}));
3616
+ render(/* @__PURE__ */ jsx7(App, {}));
3260
3617
  }
3261
3618
  main();