stealth-cli 0.5.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/LICENSE +21 -0
- package/README.md +295 -0
- package/bin/stealth.js +50 -0
- package/package.json +65 -0
- package/skills/SKILL.md +244 -0
- package/src/browser.js +341 -0
- package/src/client.js +115 -0
- package/src/commands/batch.js +180 -0
- package/src/commands/browse.js +101 -0
- package/src/commands/config.js +85 -0
- package/src/commands/crawl.js +169 -0
- package/src/commands/daemon.js +143 -0
- package/src/commands/extract.js +153 -0
- package/src/commands/fingerprint.js +306 -0
- package/src/commands/interactive.js +284 -0
- package/src/commands/mcp.js +68 -0
- package/src/commands/monitor.js +160 -0
- package/src/commands/pdf.js +109 -0
- package/src/commands/profile.js +112 -0
- package/src/commands/proxy.js +116 -0
- package/src/commands/screenshot.js +96 -0
- package/src/commands/search.js +162 -0
- package/src/commands/serve.js +240 -0
- package/src/config.js +123 -0
- package/src/cookies.js +67 -0
- package/src/daemon-entry.js +19 -0
- package/src/daemon.js +294 -0
- package/src/errors.js +136 -0
- package/src/extractors/base.js +59 -0
- package/src/extractors/bing.js +47 -0
- package/src/extractors/duckduckgo.js +91 -0
- package/src/extractors/github.js +103 -0
- package/src/extractors/google.js +173 -0
- package/src/extractors/index.js +55 -0
- package/src/extractors/youtube.js +87 -0
- package/src/humanize.js +210 -0
- package/src/index.js +32 -0
- package/src/macros.js +36 -0
- package/src/mcp-server.js +341 -0
- package/src/output.js +65 -0
- package/src/profiles.js +308 -0
- package/src/proxy-pool.js +256 -0
- package/src/retry.js +112 -0
- package/src/session.js +159 -0
package/src/session.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session management — persist and restore browsing sessions
|
|
3
|
+
*
|
|
4
|
+
* A session saves:
|
|
5
|
+
* - Cookies
|
|
6
|
+
* - Browsing history (visited URLs)
|
|
7
|
+
* - Last active URL
|
|
8
|
+
* - Profile reference
|
|
9
|
+
*
|
|
10
|
+
* Storage: ~/.stealth/sessions/<name>.json
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import os from 'os';
|
|
16
|
+
|
|
17
|
+
const SESSIONS_DIR = path.join(os.homedir(), '.stealth', 'sessions');
|
|
18
|
+
|
|
19
|
+
function ensureDir() {
|
|
20
|
+
fs.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function sessionPath(name) {
|
|
24
|
+
const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
25
|
+
return path.join(SESSIONS_DIR, `${safeName}.json`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create or load a session
|
|
30
|
+
*/
|
|
31
|
+
export function getSession(name) {
|
|
32
|
+
ensureDir();
|
|
33
|
+
const filePath = sessionPath(name);
|
|
34
|
+
|
|
35
|
+
if (fs.existsSync(filePath)) {
|
|
36
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
name,
|
|
41
|
+
profile: null,
|
|
42
|
+
cookies: [],
|
|
43
|
+
history: [],
|
|
44
|
+
lastUrl: null,
|
|
45
|
+
createdAt: new Date().toISOString(),
|
|
46
|
+
lastAccess: null,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Save session state
|
|
52
|
+
*/
|
|
53
|
+
export function saveSession(name, session) {
|
|
54
|
+
ensureDir();
|
|
55
|
+
const filePath = sessionPath(name);
|
|
56
|
+
session.lastAccess = new Date().toISOString();
|
|
57
|
+
fs.writeFileSync(filePath, JSON.stringify(session, null, 2));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Save current browser state to session
|
|
62
|
+
*/
|
|
63
|
+
export async function captureSession(name, context, page, opts = {}) {
|
|
64
|
+
const session = getSession(name);
|
|
65
|
+
|
|
66
|
+
// Save cookies
|
|
67
|
+
try {
|
|
68
|
+
session.cookies = await context.cookies();
|
|
69
|
+
} catch {}
|
|
70
|
+
|
|
71
|
+
// Save current URL
|
|
72
|
+
try {
|
|
73
|
+
session.lastUrl = page.url();
|
|
74
|
+
} catch {}
|
|
75
|
+
|
|
76
|
+
// Append to history
|
|
77
|
+
if (session.lastUrl && session.lastUrl !== 'about:blank') {
|
|
78
|
+
if (!session.history.includes(session.lastUrl)) {
|
|
79
|
+
session.history.push(session.lastUrl);
|
|
80
|
+
}
|
|
81
|
+
// Keep history manageable
|
|
82
|
+
if (session.history.length > 100) {
|
|
83
|
+
session.history = session.history.slice(-100);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Link profile if provided
|
|
88
|
+
if (opts.profile) {
|
|
89
|
+
session.profile = opts.profile;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
saveSession(name, session);
|
|
93
|
+
return session;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Restore session into browser context
|
|
98
|
+
* Returns the last URL to navigate to
|
|
99
|
+
*/
|
|
100
|
+
export async function restoreSession(name, context) {
|
|
101
|
+
const session = getSession(name);
|
|
102
|
+
|
|
103
|
+
// Restore cookies
|
|
104
|
+
if (session.cookies && session.cookies.length > 0) {
|
|
105
|
+
try {
|
|
106
|
+
// Filter expired cookies
|
|
107
|
+
const now = Date.now() / 1000;
|
|
108
|
+
const validCookies = session.cookies.filter((c) => {
|
|
109
|
+
if (c.expires && c.expires > 0 && c.expires < now) return false;
|
|
110
|
+
return true;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (validCookies.length > 0) {
|
|
114
|
+
await context.addCookies(validCookies);
|
|
115
|
+
}
|
|
116
|
+
} catch {}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
lastUrl: session.lastUrl,
|
|
121
|
+
history: session.history,
|
|
122
|
+
cookiesRestored: session.cookies?.length || 0,
|
|
123
|
+
profile: session.profile,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* List all sessions
|
|
129
|
+
*/
|
|
130
|
+
export function listSessions() {
|
|
131
|
+
ensureDir();
|
|
132
|
+
const files = fs.readdirSync(SESSIONS_DIR).filter((f) => f.endsWith('.json'));
|
|
133
|
+
|
|
134
|
+
return files.map((f) => {
|
|
135
|
+
try {
|
|
136
|
+
const session = JSON.parse(fs.readFileSync(path.join(SESSIONS_DIR, f), 'utf-8'));
|
|
137
|
+
return {
|
|
138
|
+
name: session.name,
|
|
139
|
+
lastUrl: session.lastUrl || '-',
|
|
140
|
+
cookies: session.cookies?.length || 0,
|
|
141
|
+
history: session.history?.length || 0,
|
|
142
|
+
profile: session.profile || '-',
|
|
143
|
+
lastAccess: session.lastAccess || 'never',
|
|
144
|
+
};
|
|
145
|
+
} catch {
|
|
146
|
+
return { name: f.replace('.json', ''), error: 'corrupted' };
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Delete a session
|
|
153
|
+
*/
|
|
154
|
+
export function deleteSession(name) {
|
|
155
|
+
const filePath = sessionPath(name);
|
|
156
|
+
if (fs.existsSync(filePath)) {
|
|
157
|
+
fs.unlinkSync(filePath);
|
|
158
|
+
}
|
|
159
|
+
}
|