aether-mcp-server 2.1.0 → 2.1.1

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.
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * Browser navigation and tab management functions extracted from cdp-bridge.ts.
4
+ *
5
+ * Each function takes a CdpClient, params, and optional dependencies (snapshotCache,
6
+ * logger) and returns a result. This module is designed to be consumed by the bridge
7
+ * layer and can also be used directly by other parts of the server.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.connect = connect;
11
+ exports.navigate = navigate;
12
+ exports.getTabs = getTabs;
13
+ exports.newTab = newTab;
14
+ exports.switchTab = switchTab;
15
+ exports.closeTab = closeTab;
16
+ exports.smartNavigate = smartNavigate;
17
+ const eval_scripts_1 = require("../eval-scripts");
18
+ // ─── Connect ────────────────────────────────────────────────────────────
19
+ /**
20
+ * Connect to an existing Chrome instance on the given debugging port.
21
+ */
22
+ async function connect(client, params) {
23
+ const port = params.port ?? 9222;
24
+ await client.connect(port);
25
+ return 'Connected to browser';
26
+ }
27
+ // ─── Navigate ────────────────────────────────────────────────────────────
28
+ /**
29
+ * Navigate the active tab to a URL and wait for the page to settle.
30
+ * Invalidates the snapshot cache after navigation.
31
+ */
32
+ async function navigate(client, params, snapshotCache) {
33
+ await client.navigateAndWait(params.url, params.timeout ?? 10000);
34
+ snapshotCache.invalidate('navigate');
35
+ return 'Navigated';
36
+ }
37
+ // ─── Tab Management ─────────────────────────────────────────────────────
38
+ /**
39
+ * Get all open browser tabs (targets).
40
+ */
41
+ async function getTabs(client, _params) {
42
+ const result = await client.sendCommand('Target.getTargets', {});
43
+ return result.targetInfos ?? [];
44
+ }
45
+ /**
46
+ * Open a new tab with an optional URL.
47
+ */
48
+ async function newTab(client, params) {
49
+ const result = await client.sendCommand('Target.createTarget', {
50
+ url: params.url ?? 'about:blank',
51
+ });
52
+ return result.targetId
53
+ ? `Created new tab: ${result.targetId}`
54
+ : 'Created new tab';
55
+ }
56
+ /**
57
+ * Switch the active debugging session to a different tab.
58
+ * Requires the targetId of the tab to switch to.
59
+ */
60
+ async function switchTab(client, params) {
61
+ if (!params.targetId) {
62
+ throw new Error('targetId required to switch tabs');
63
+ }
64
+ await client.sendCommand('Target.activateTarget', {
65
+ targetId: params.targetId,
66
+ });
67
+ await client.switchToTarget(params.targetId, params.port ?? 9222);
68
+ return `Switched to tab ${params.targetId}`;
69
+ }
70
+ /**
71
+ * Close a tab by its targetId.
72
+ */
73
+ async function closeTab(client, params) {
74
+ await client.sendCommand('Target.closeTarget', {
75
+ targetId: params.targetId,
76
+ });
77
+ return 'Closed tab';
78
+ }
79
+ /**
80
+ * Navigate to a URL with optional popup dismissal, condition waiting, and
81
+ * screenshot capture. This is a higher-level wrapper around navigate().
82
+ */
83
+ async function smartNavigate(client, params, snapshotCache, logger) {
84
+ const { url, waitFor, dismissPopups, screenshot, timeout } = params;
85
+ const timeoutMs = timeout ?? 30000;
86
+ try {
87
+ logger.info(`smartNavigate: Navigating to ${url}`, { timeout: timeoutMs });
88
+ await client.navigateAndWait(url, timeoutMs);
89
+ snapshotCache.invalidate('smartNavigate');
90
+ // Dismiss popups if requested (default: true)
91
+ if (dismissPopups !== false) {
92
+ logger.debug('smartNavigate: Dismissing popups');
93
+ await client.evaluate(eval_scripts_1.DISMISS_POPUPS_SCRIPT).catch((err) => {
94
+ logger.warn('smartNavigate: Popup dismissal failed', {
95
+ error: err.message,
96
+ });
97
+ });
98
+ }
99
+ // Wait for a specific condition after navigation
100
+ if (waitFor) {
101
+ if (waitFor.type === 'network_idle') {
102
+ logger.debug('smartNavigate: Waiting for network idle', {
103
+ timeout: waitFor.timeout ?? 3000,
104
+ });
105
+ await client
106
+ .waitForNetworkIdle(500, waitFor.timeout ?? 3000)
107
+ .catch(() => { });
108
+ }
109
+ else if (waitFor.type === 'element' && waitFor.selector) {
110
+ logger.debug('smartNavigate: Waiting for selector', {
111
+ selector: waitFor.selector,
112
+ timeout: waitFor.timeout ?? 5000,
113
+ });
114
+ await client
115
+ .waitForSelector(waitFor.selector, waitFor.timeout ?? 5000)
116
+ .catch(() => { });
117
+ }
118
+ }
119
+ // Collect current page info
120
+ const currentUrl = await client
121
+ .evaluate('window.location.href')
122
+ .catch(() => url);
123
+ const title = await client
124
+ .evaluate('document.title')
125
+ .catch(() => 'Unknown');
126
+ // Optionally take a screenshot
127
+ const screenshotData = screenshot === true
128
+ ? await client.screenshot('jpeg', 70).catch(() => null)
129
+ : null;
130
+ logger.info('smartNavigate: Complete', {
131
+ finalUrl: currentUrl,
132
+ title,
133
+ hasScreenshot: screenshotData !== null,
134
+ });
135
+ return {
136
+ success: true,
137
+ url: currentUrl,
138
+ title,
139
+ screenshot: screenshotData,
140
+ };
141
+ }
142
+ catch (e) {
143
+ logger.error('smartNavigate: Failed', { error: e.message, url });
144
+ return { success: false, url, title: 'Unknown', error: e.message };
145
+ }
146
+ }
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ /**
3
+ * Session, auth, and browser management functions extracted from cdp-bridge.ts.
4
+ *
5
+ * Each function takes a CdpClient (and optional dependencies like PageSnapshotCache)
6
+ * and returns a result. This module is designed to be consumed by the bridge layer
7
+ * and can also be used directly by other parts of the server.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.defaultAuthStatePath = defaultAuthStatePath;
44
+ exports.toCookieParam = toCookieParam;
45
+ exports.saveAuthState = saveAuthState;
46
+ exports.loadAuthState = loadAuthState;
47
+ exports.launchBrowser = launchBrowser;
48
+ exports.killBrowser = killBrowser;
49
+ exports.listBrowsers = listBrowsers;
50
+ exports.listBrowserProfiles = listBrowserProfiles;
51
+ exports.getCookies = getCookies;
52
+ exports.setCookie = setCookie;
53
+ exports.clearCache = clearCache;
54
+ const eval_scripts_1 = require("../eval-scripts");
55
+ const fs = __importStar(require("fs/promises"));
56
+ const path = __importStar(require("path"));
57
+ // ─── Helpers ───────────────────────────────────────────────────────────
58
+ /**
59
+ * Resolve the default path for the auth-state JSON file.
60
+ * Defaults to <cwd>/.aether/auth-state.json unless a custom path is provided.
61
+ */
62
+ function defaultAuthStatePath(custom) {
63
+ if (custom)
64
+ return path.resolve(String(custom));
65
+ return path.resolve(process.cwd(), ".aether", "auth-state.json");
66
+ }
67
+ /**
68
+ * Strip read-only fields from CDP Cookie objects so they can be passed
69
+ * to Network.setCookies as CookieParam objects.
70
+ *
71
+ * CDP Network.getAllCookies returns Cookie objects with extra fields
72
+ * (size, session, etc.) that Network.setCookies rejects.
73
+ * Keep only the writable CookieParam fields.
74
+ */
75
+ function toCookieParam(c) {
76
+ const param = {
77
+ name: c.name,
78
+ value: c.value,
79
+ domain: c.domain,
80
+ path: c.path,
81
+ secure: c.secure,
82
+ httpOnly: c.httpOnly,
83
+ };
84
+ if (typeof c.expires === "number" && c.expires > 0)
85
+ param.expires = c.expires;
86
+ if (c.sameSite)
87
+ param.sameSite = c.sameSite;
88
+ if (c.priority)
89
+ param.priority = c.priority;
90
+ if (c.sourceScheme)
91
+ param.sourceScheme = c.sourceScheme;
92
+ if (typeof c.sourcePort === "number")
93
+ param.sourcePort = c.sourcePort;
94
+ if (c.partitionKey)
95
+ param.partitionKey = c.partitionKey;
96
+ return param;
97
+ }
98
+ /**
99
+ * Export the current session (cookies + localStorage + sessionStorage of the
100
+ * active origin) to a JSON file so a logged-in state can be reused later.
101
+ */
102
+ async function saveAuthState(client, params, snapshotCache) {
103
+ const filePath = defaultAuthStatePath(params.path);
104
+ // Collect cookies (try Network API first, fall back to Storage API)
105
+ const cookiesRes = await client
106
+ .sendCommand("Network.getAllCookies", {})
107
+ .catch(() => client.sendCommand("Storage.getCookies", {}).catch(() => ({ cookies: [] })));
108
+ const cookies = (cookiesRes?.cookies || []).map((c) => toCookieParam(c));
109
+ // Collect localStorage and sessionStorage for the active origin
110
+ const storage = await client.evaluate(eval_scripts_1.EXPORT_STORAGE_SCRIPT).catch(() => null);
111
+ const state = {
112
+ version: 1,
113
+ savedAt: new Date().toISOString(),
114
+ cookies,
115
+ origins: storage ? [storage] : [],
116
+ };
117
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
118
+ await fs.writeFile(filePath, JSON.stringify(state, null, 2), "utf8");
119
+ return {
120
+ success: true,
121
+ path: filePath,
122
+ cookies: cookies.length,
123
+ origins: state.origins.length,
124
+ storageKeys: storage
125
+ ? Object.keys(storage.localStorage).length +
126
+ Object.keys(storage.sessionStorage).length
127
+ : 0,
128
+ };
129
+ }
130
+ /**
131
+ * Restore a session saved by saveAuthState. Cookies are set globally; storage
132
+ * is restored for the active origin (navigate to the site first), then the tab
133
+ * is reloaded so the session takes effect.
134
+ */
135
+ async function loadAuthState(client, params, snapshotCache) {
136
+ const filePath = defaultAuthStatePath(params.path);
137
+ let raw;
138
+ try {
139
+ raw = await fs.readFile(filePath, "utf8");
140
+ }
141
+ catch (e) {
142
+ return {
143
+ success: false,
144
+ path: filePath,
145
+ message: `Could not read auth state: ${e?.message}`,
146
+ };
147
+ }
148
+ let state;
149
+ try {
150
+ state = JSON.parse(raw);
151
+ }
152
+ catch (e) {
153
+ return {
154
+ success: false,
155
+ path: filePath,
156
+ message: `Invalid auth state JSON: ${e?.message}`,
157
+ };
158
+ }
159
+ let cookiesSet = 0;
160
+ if (Array.isArray(state.cookies) && state.cookies.length) {
161
+ const cookieParams = state.cookies.map((c) => toCookieParam(c));
162
+ await client.setCookies(cookieParams).catch((err) => {
163
+ console.error("[Aether] setCookies failed during loadAuthState:", err?.message);
164
+ });
165
+ cookiesSet = cookieParams.length;
166
+ }
167
+ let storageRestored = 0;
168
+ let storageSkipped = 0;
169
+ const currentOrigin = await client
170
+ .evaluate("location.origin")
171
+ .catch(() => "");
172
+ for (const entry of state.origins || []) {
173
+ if (entry.origin && currentOrigin && entry.origin !== currentOrigin) {
174
+ storageSkipped++;
175
+ continue;
176
+ }
177
+ const data = JSON.stringify({
178
+ localStorage: entry.localStorage || {},
179
+ sessionStorage: entry.sessionStorage || {},
180
+ });
181
+ const ok = await client
182
+ .evaluate(`
183
+ (function() {
184
+ try {
185
+ const data = ${data};
186
+ for (const k in data.localStorage) localStorage.setItem(k, data.localStorage[k]);
187
+ for (const k in data.sessionStorage) sessionStorage.setItem(k, data.sessionStorage[k]);
188
+ return true;
189
+ } catch (e) { return false; }
190
+ })()
191
+ `)
192
+ .catch(() => false);
193
+ if (ok)
194
+ storageRestored++;
195
+ }
196
+ if (params.reload !== false) {
197
+ await client.reload(false).catch(() => { });
198
+ }
199
+ snapshotCache.invalidate("load_auth_state");
200
+ return {
201
+ success: true,
202
+ path: filePath,
203
+ cookiesSet,
204
+ storageRestored,
205
+ storageSkipped,
206
+ note: storageSkipped > 0
207
+ ? "Some storage origins were skipped; navigate to that origin before loading to restore them."
208
+ : undefined,
209
+ };
210
+ }
211
+ /**
212
+ * Launch a new browser instance using automatic detection.
213
+ * If no browser is specified, the client will auto-detect available browsers.
214
+ */
215
+ async function launchBrowser(client, options) {
216
+ await client.launchAuto({
217
+ browser: options?.browser,
218
+ headless: options?.headless,
219
+ port: options?.port,
220
+ profile: options?.profile,
221
+ profileDirectory: options?.profileDirectory,
222
+ userDataDir: options?.userDataDir,
223
+ });
224
+ const profileLabel = options?.profile || options?.profileDirectory;
225
+ return profileLabel
226
+ ? `Browser launched successfully with profile "${profileLabel}"`
227
+ : "Browser launched successfully";
228
+ }
229
+ /**
230
+ * Kill the currently managed browser process.
231
+ */
232
+ async function killBrowser(client) {
233
+ await client.killBrowser();
234
+ return "Browser killed";
235
+ }
236
+ /**
237
+ * List all available (installed) browsers on the system.
238
+ */
239
+ async function listBrowsers(client) {
240
+ return await client.listAvailableBrowsers();
241
+ }
242
+ /**
243
+ * List browser profiles for a given browser (defaults to Brave).
244
+ */
245
+ async function listBrowserProfiles(client, browser) {
246
+ return await client.listBrowserProfiles(browser || "brave");
247
+ }
248
+ // ─── Cookie & Cache Management ─────────────────────────────────────────
249
+ /**
250
+ * Get all cookies for the current page URL.
251
+ */
252
+ async function getCookies(client) {
253
+ const result = await client.sendCommand("Network.getCookies", {
254
+ urls: [
255
+ (await client.evaluate("window.location.href").catch(() => "*")) ||
256
+ "*",
257
+ ],
258
+ });
259
+ return result.cookies || [];
260
+ }
261
+ /**
262
+ * Set a single cookie on the current page.
263
+ */
264
+ async function setCookie(client, params) {
265
+ const cookies = [
266
+ {
267
+ name: params.cookieName || params.name,
268
+ value: params.cookieValue || params.value,
269
+ url: params.url ||
270
+ (await client.evaluate("window.location.href").catch(() => undefined)),
271
+ domain: params.domain,
272
+ path: params.path || "/",
273
+ secure: params.secure || false,
274
+ httpOnly: params.httpOnly || false,
275
+ },
276
+ ];
277
+ await client.sendCommand("Network.setCookies", { cookies });
278
+ return "Cookie set";
279
+ }
280
+ /**
281
+ * Clear the browser cache and all cookies.
282
+ */
283
+ async function clearCache(client) {
284
+ await client.sendCommand("Network.clearBrowserCache", {});
285
+ await client.sendCommand("Network.clearBrowserCookies", {});
286
+ return "Cache cleared";
287
+ }