@projectservan8n/cnapse 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +85 -37
- package/package.json +1 -1
- package/src/services/browser.ts +84 -15
package/dist/index.js
CHANGED
|
@@ -586,10 +586,10 @@ async function captureScreenFallback() {
|
|
|
586
586
|
const { exec: exec5 } = await import("child_process");
|
|
587
587
|
const { promisify: promisify5 } = await import("util");
|
|
588
588
|
const { tmpdir } = await import("os");
|
|
589
|
-
const { join:
|
|
589
|
+
const { join: join4 } = await import("path");
|
|
590
590
|
const { readFile: readFile2, unlink } = await import("fs/promises");
|
|
591
591
|
const execAsync5 = promisify5(exec5);
|
|
592
|
-
const tempFile =
|
|
592
|
+
const tempFile = join4(tmpdir(), `cnapse-screen-${Date.now()}.png`);
|
|
593
593
|
try {
|
|
594
594
|
const platform = process.platform;
|
|
595
595
|
if (platform === "win32") {
|
|
@@ -902,29 +902,29 @@ import { promisify as promisify4 } from "util";
|
|
|
902
902
|
// src/tools/filesystem.ts
|
|
903
903
|
import { promises as fs } from "fs";
|
|
904
904
|
import { join, dirname } from "path";
|
|
905
|
-
async function readFile(
|
|
905
|
+
async function readFile(path3) {
|
|
906
906
|
try {
|
|
907
|
-
const content = await fs.readFile(
|
|
907
|
+
const content = await fs.readFile(path3, "utf-8");
|
|
908
908
|
return ok(content);
|
|
909
909
|
} catch (error) {
|
|
910
910
|
return err(`Failed to read file: ${error.message}`);
|
|
911
911
|
}
|
|
912
912
|
}
|
|
913
|
-
async function writeFile(
|
|
913
|
+
async function writeFile(path3, content) {
|
|
914
914
|
try {
|
|
915
|
-
const dir = dirname(
|
|
915
|
+
const dir = dirname(path3);
|
|
916
916
|
await fs.mkdir(dir, { recursive: true });
|
|
917
|
-
await fs.writeFile(
|
|
918
|
-
return ok(`Written ${content.length} bytes to ${
|
|
917
|
+
await fs.writeFile(path3, content, "utf-8");
|
|
918
|
+
return ok(`Written ${content.length} bytes to ${path3}`);
|
|
919
919
|
} catch (error) {
|
|
920
920
|
return err(`Failed to write file: ${error.message}`);
|
|
921
921
|
}
|
|
922
922
|
}
|
|
923
|
-
async function listDir(
|
|
923
|
+
async function listDir(path3, recursive = false) {
|
|
924
924
|
try {
|
|
925
|
-
const stat = await fs.stat(
|
|
925
|
+
const stat = await fs.stat(path3);
|
|
926
926
|
if (!stat.isDirectory()) {
|
|
927
|
-
return err(`Not a directory: ${
|
|
927
|
+
return err(`Not a directory: ${path3}`);
|
|
928
928
|
}
|
|
929
929
|
const entries = [];
|
|
930
930
|
async function walkDir(dir, prefix) {
|
|
@@ -941,7 +941,7 @@ async function listDir(path2, recursive = false) {
|
|
|
941
941
|
}
|
|
942
942
|
}
|
|
943
943
|
}
|
|
944
|
-
await walkDir(
|
|
944
|
+
await walkDir(path3, "");
|
|
945
945
|
entries.sort();
|
|
946
946
|
return ok(entries.join("\n"));
|
|
947
947
|
} catch (error) {
|
|
@@ -1460,7 +1460,9 @@ ${stderr}`
|
|
|
1460
1460
|
|
|
1461
1461
|
// src/services/browser.ts
|
|
1462
1462
|
import { chromium } from "playwright";
|
|
1463
|
-
|
|
1463
|
+
import * as path from "path";
|
|
1464
|
+
import * as os2 from "os";
|
|
1465
|
+
import * as fs2 from "fs";
|
|
1464
1466
|
var context = null;
|
|
1465
1467
|
var activePage = null;
|
|
1466
1468
|
var defaultConfig = {
|
|
@@ -1468,23 +1470,69 @@ var defaultConfig = {
|
|
|
1468
1470
|
// Show browser so user can see what's happening
|
|
1469
1471
|
slowMo: 50,
|
|
1470
1472
|
// Slight delay for visibility
|
|
1471
|
-
viewport: { width: 1280, height: 800 }
|
|
1473
|
+
viewport: { width: 1280, height: 800 },
|
|
1474
|
+
useSystemBrowser: true
|
|
1475
|
+
// Default to using system Chrome
|
|
1472
1476
|
};
|
|
1477
|
+
function findSystemBrowser() {
|
|
1478
|
+
const possiblePaths = [
|
|
1479
|
+
// Chrome paths
|
|
1480
|
+
path.join(process.env["PROGRAMFILES"] || "", "Google", "Chrome", "Application", "chrome.exe"),
|
|
1481
|
+
path.join(process.env["PROGRAMFILES(X86)"] || "", "Google", "Chrome", "Application", "chrome.exe"),
|
|
1482
|
+
path.join(process.env["LOCALAPPDATA"] || "", "Google", "Chrome", "Application", "chrome.exe"),
|
|
1483
|
+
// Edge paths (fallback)
|
|
1484
|
+
path.join(process.env["PROGRAMFILES"] || "", "Microsoft", "Edge", "Application", "msedge.exe"),
|
|
1485
|
+
path.join(process.env["PROGRAMFILES(X86)"] || "", "Microsoft", "Edge", "Application", "msedge.exe")
|
|
1486
|
+
];
|
|
1487
|
+
for (const browserPath of possiblePaths) {
|
|
1488
|
+
if (fs2.existsSync(browserPath)) {
|
|
1489
|
+
return browserPath;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
return null;
|
|
1493
|
+
}
|
|
1494
|
+
function getChromeUserDataDir() {
|
|
1495
|
+
const cnapseProfile = path.join(os2.homedir(), ".cnapse", "chrome-profile");
|
|
1496
|
+
if (!fs2.existsSync(cnapseProfile)) {
|
|
1497
|
+
fs2.mkdirSync(cnapseProfile, { recursive: true });
|
|
1498
|
+
}
|
|
1499
|
+
return cnapseProfile;
|
|
1500
|
+
}
|
|
1473
1501
|
async function initBrowser(config = {}) {
|
|
1474
1502
|
const cfg = { ...defaultConfig, ...config };
|
|
1475
|
-
if (!browser) {
|
|
1476
|
-
browser = await chromium.launch({
|
|
1477
|
-
headless: cfg.headless,
|
|
1478
|
-
slowMo: cfg.slowMo
|
|
1479
|
-
});
|
|
1480
|
-
}
|
|
1481
1503
|
if (!context) {
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1504
|
+
const browserPath = cfg.useSystemBrowser ? findSystemBrowser() : null;
|
|
1505
|
+
const userDataDir = getChromeUserDataDir();
|
|
1506
|
+
if (browserPath && cfg.useSystemBrowser) {
|
|
1507
|
+
context = await chromium.launchPersistentContext(userDataDir, {
|
|
1508
|
+
headless: cfg.headless,
|
|
1509
|
+
slowMo: cfg.slowMo,
|
|
1510
|
+
viewport: cfg.viewport,
|
|
1511
|
+
executablePath: browserPath,
|
|
1512
|
+
channel: void 0,
|
|
1513
|
+
// Don't use channel when specifying executablePath
|
|
1514
|
+
args: [
|
|
1515
|
+
"--disable-blink-features=AutomationControlled",
|
|
1516
|
+
// Less bot detection
|
|
1517
|
+
"--no-first-run",
|
|
1518
|
+
"--no-default-browser-check"
|
|
1519
|
+
]
|
|
1520
|
+
});
|
|
1521
|
+
} else {
|
|
1522
|
+
context = await chromium.launchPersistentContext(userDataDir, {
|
|
1523
|
+
headless: cfg.headless,
|
|
1524
|
+
slowMo: cfg.slowMo,
|
|
1525
|
+
viewport: cfg.viewport,
|
|
1526
|
+
args: [
|
|
1527
|
+
"--disable-blink-features=AutomationControlled"
|
|
1528
|
+
]
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1486
1531
|
}
|
|
1487
|
-
|
|
1532
|
+
const pages = context.pages();
|
|
1533
|
+
if (pages.length > 0) {
|
|
1534
|
+
activePage = pages[0];
|
|
1535
|
+
} else {
|
|
1488
1536
|
activePage = await context.newPage();
|
|
1489
1537
|
}
|
|
1490
1538
|
return activePage;
|
|
@@ -1776,14 +1824,14 @@ async function research(topic, maxSources = 3) {
|
|
|
1776
1824
|
}
|
|
1777
1825
|
|
|
1778
1826
|
// src/lib/tasks.ts
|
|
1779
|
-
import * as
|
|
1780
|
-
import * as
|
|
1781
|
-
import * as
|
|
1782
|
-
var TASK_MEMORY_FILE =
|
|
1827
|
+
import * as fs3 from "fs";
|
|
1828
|
+
import * as path2 from "path";
|
|
1829
|
+
import * as os3 from "os";
|
|
1830
|
+
var TASK_MEMORY_FILE = path2.join(os3.homedir(), ".cnapse", "task-memory.json");
|
|
1783
1831
|
function loadTaskMemory() {
|
|
1784
1832
|
try {
|
|
1785
|
-
if (
|
|
1786
|
-
const data =
|
|
1833
|
+
if (fs3.existsSync(TASK_MEMORY_FILE)) {
|
|
1834
|
+
const data = fs3.readFileSync(TASK_MEMORY_FILE, "utf-8");
|
|
1787
1835
|
return JSON.parse(data);
|
|
1788
1836
|
}
|
|
1789
1837
|
} catch {
|
|
@@ -1809,11 +1857,11 @@ function saveTaskPattern(input, steps) {
|
|
|
1809
1857
|
});
|
|
1810
1858
|
}
|
|
1811
1859
|
memory.patterns = memory.patterns.sort((a, b) => b.successCount - a.successCount).slice(0, 100);
|
|
1812
|
-
const dir =
|
|
1813
|
-
if (!
|
|
1814
|
-
|
|
1860
|
+
const dir = path2.dirname(TASK_MEMORY_FILE);
|
|
1861
|
+
if (!fs3.existsSync(dir)) {
|
|
1862
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
1815
1863
|
}
|
|
1816
|
-
|
|
1864
|
+
fs3.writeFileSync(TASK_MEMORY_FILE, JSON.stringify(memory, null, 2));
|
|
1817
1865
|
} catch {
|
|
1818
1866
|
}
|
|
1819
1867
|
}
|
|
@@ -2654,8 +2702,8 @@ function getTaskMemoryStats() {
|
|
|
2654
2702
|
}
|
|
2655
2703
|
function clearTaskMemory() {
|
|
2656
2704
|
try {
|
|
2657
|
-
if (
|
|
2658
|
-
|
|
2705
|
+
if (fs3.existsSync(TASK_MEMORY_FILE)) {
|
|
2706
|
+
fs3.unlinkSync(TASK_MEMORY_FILE);
|
|
2659
2707
|
}
|
|
2660
2708
|
} catch {
|
|
2661
2709
|
}
|
package/package.json
CHANGED
package/src/services/browser.ts
CHANGED
|
@@ -7,9 +7,14 @@
|
|
|
7
7
|
* - Email (Gmail, Outlook)
|
|
8
8
|
* - Google Sheets/Docs
|
|
9
9
|
* - General web browsing
|
|
10
|
+
*
|
|
11
|
+
* Uses your system Chrome with existing logins and profile!
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
14
|
import { chromium, Browser, Page, BrowserContext } from 'playwright';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import * as os from 'os';
|
|
17
|
+
import * as fs from 'fs';
|
|
13
18
|
|
|
14
19
|
// Singleton browser instance
|
|
15
20
|
let browser: Browser | null = null;
|
|
@@ -21,35 +26,96 @@ interface BrowserConfig {
|
|
|
21
26
|
headless: boolean;
|
|
22
27
|
slowMo: number;
|
|
23
28
|
viewport: { width: number; height: number };
|
|
29
|
+
useSystemBrowser: boolean; // Use system Chrome with your profile
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
const defaultConfig: BrowserConfig = {
|
|
27
33
|
headless: false, // Show browser so user can see what's happening
|
|
28
34
|
slowMo: 50, // Slight delay for visibility
|
|
29
|
-
viewport: { width: 1280, height: 800 }
|
|
35
|
+
viewport: { width: 1280, height: 800 },
|
|
36
|
+
useSystemBrowser: true // Default to using system Chrome
|
|
30
37
|
};
|
|
31
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Find Chrome/Edge executable on Windows
|
|
41
|
+
*/
|
|
42
|
+
function findSystemBrowser(): string | null {
|
|
43
|
+
const possiblePaths = [
|
|
44
|
+
// Chrome paths
|
|
45
|
+
path.join(process.env['PROGRAMFILES'] || '', 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
|
46
|
+
path.join(process.env['PROGRAMFILES(X86)'] || '', 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
|
47
|
+
path.join(process.env['LOCALAPPDATA'] || '', 'Google', 'Chrome', 'Application', 'chrome.exe'),
|
|
48
|
+
// Edge paths (fallback)
|
|
49
|
+
path.join(process.env['PROGRAMFILES'] || '', 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
|
|
50
|
+
path.join(process.env['PROGRAMFILES(X86)'] || '', 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
for (const browserPath of possiblePaths) {
|
|
54
|
+
if (fs.existsSync(browserPath)) {
|
|
55
|
+
return browserPath;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get Chrome user data directory
|
|
63
|
+
*/
|
|
64
|
+
function getChromeUserDataDir(): string {
|
|
65
|
+
// Use a separate profile to avoid conflicts with running Chrome
|
|
66
|
+
const cnapseProfile = path.join(os.homedir(), '.cnapse', 'chrome-profile');
|
|
67
|
+
|
|
68
|
+
// Create if doesn't exist
|
|
69
|
+
if (!fs.existsSync(cnapseProfile)) {
|
|
70
|
+
fs.mkdirSync(cnapseProfile, { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return cnapseProfile;
|
|
74
|
+
}
|
|
75
|
+
|
|
32
76
|
/**
|
|
33
77
|
* Initialize browser if not already running
|
|
78
|
+
* Uses system Chrome with persistent profile (keeps your logins!)
|
|
34
79
|
*/
|
|
35
80
|
export async function initBrowser(config: Partial<BrowserConfig> = {}): Promise<Page> {
|
|
36
81
|
const cfg = { ...defaultConfig, ...config };
|
|
37
82
|
|
|
38
|
-
if (!browser) {
|
|
39
|
-
browser = await chromium.launch({
|
|
40
|
-
headless: cfg.headless,
|
|
41
|
-
slowMo: cfg.slowMo,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
83
|
if (!context) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
84
|
+
const browserPath = cfg.useSystemBrowser ? findSystemBrowser() : null;
|
|
85
|
+
const userDataDir = getChromeUserDataDir();
|
|
86
|
+
|
|
87
|
+
if (browserPath && cfg.useSystemBrowser) {
|
|
88
|
+
// Use persistent context with system Chrome - keeps logins!
|
|
89
|
+
context = await chromium.launchPersistentContext(userDataDir, {
|
|
90
|
+
headless: cfg.headless,
|
|
91
|
+
slowMo: cfg.slowMo,
|
|
92
|
+
viewport: cfg.viewport,
|
|
93
|
+
executablePath: browserPath,
|
|
94
|
+
channel: undefined, // Don't use channel when specifying executablePath
|
|
95
|
+
args: [
|
|
96
|
+
'--disable-blink-features=AutomationControlled', // Less bot detection
|
|
97
|
+
'--no-first-run',
|
|
98
|
+
'--no-default-browser-check',
|
|
99
|
+
]
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
// Fallback to bundled Chromium with persistent context
|
|
103
|
+
context = await chromium.launchPersistentContext(userDataDir, {
|
|
104
|
+
headless: cfg.headless,
|
|
105
|
+
slowMo: cfg.slowMo,
|
|
106
|
+
viewport: cfg.viewport,
|
|
107
|
+
args: [
|
|
108
|
+
'--disable-blink-features=AutomationControlled',
|
|
109
|
+
]
|
|
110
|
+
});
|
|
111
|
+
}
|
|
50
112
|
}
|
|
51
113
|
|
|
52
|
-
|
|
114
|
+
// Get existing page or create new one
|
|
115
|
+
const pages = context.pages();
|
|
116
|
+
if (pages.length > 0) {
|
|
117
|
+
activePage = pages[0];
|
|
118
|
+
} else {
|
|
53
119
|
activePage = await context.newPage();
|
|
54
120
|
}
|
|
55
121
|
|
|
@@ -70,11 +136,14 @@ export async function getPage(): Promise<Page> {
|
|
|
70
136
|
* Close browser
|
|
71
137
|
*/
|
|
72
138
|
export async function closeBrowser(): Promise<void> {
|
|
139
|
+
if (context) {
|
|
140
|
+
await context.close();
|
|
141
|
+
context = null;
|
|
142
|
+
activePage = null;
|
|
143
|
+
}
|
|
73
144
|
if (browser) {
|
|
74
145
|
await browser.close();
|
|
75
146
|
browser = null;
|
|
76
|
-
context = null;
|
|
77
|
-
activePage = null;
|
|
78
147
|
}
|
|
79
148
|
}
|
|
80
149
|
|