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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -0
  3. package/bin/stealth.js +50 -0
  4. package/package.json +65 -0
  5. package/skills/SKILL.md +244 -0
  6. package/src/browser.js +341 -0
  7. package/src/client.js +115 -0
  8. package/src/commands/batch.js +180 -0
  9. package/src/commands/browse.js +101 -0
  10. package/src/commands/config.js +85 -0
  11. package/src/commands/crawl.js +169 -0
  12. package/src/commands/daemon.js +143 -0
  13. package/src/commands/extract.js +153 -0
  14. package/src/commands/fingerprint.js +306 -0
  15. package/src/commands/interactive.js +284 -0
  16. package/src/commands/mcp.js +68 -0
  17. package/src/commands/monitor.js +160 -0
  18. package/src/commands/pdf.js +109 -0
  19. package/src/commands/profile.js +112 -0
  20. package/src/commands/proxy.js +116 -0
  21. package/src/commands/screenshot.js +96 -0
  22. package/src/commands/search.js +162 -0
  23. package/src/commands/serve.js +240 -0
  24. package/src/config.js +123 -0
  25. package/src/cookies.js +67 -0
  26. package/src/daemon-entry.js +19 -0
  27. package/src/daemon.js +294 -0
  28. package/src/errors.js +136 -0
  29. package/src/extractors/base.js +59 -0
  30. package/src/extractors/bing.js +47 -0
  31. package/src/extractors/duckduckgo.js +91 -0
  32. package/src/extractors/github.js +103 -0
  33. package/src/extractors/google.js +173 -0
  34. package/src/extractors/index.js +55 -0
  35. package/src/extractors/youtube.js +87 -0
  36. package/src/humanize.js +210 -0
  37. package/src/index.js +32 -0
  38. package/src/macros.js +36 -0
  39. package/src/mcp-server.js +341 -0
  40. package/src/output.js +65 -0
  41. package/src/profiles.js +308 -0
  42. package/src/proxy-pool.js +256 -0
  43. package/src/retry.js +112 -0
  44. 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
+ }