synabun 2026.3.31007 → 2026.4.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.
- package/CHANGELOG.md +51 -52
- package/hooks/claude-code/prompt-submit.mjs +6 -6
- package/mcp-server/dist/services/neural-interface.d.ts +20 -19
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +52 -45
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts +4 -0
- package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.js +7 -4
- package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts +16 -0
- package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-interact.js +25 -16
- package/mcp-server/dist/tools/browser-interact.js.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts +8 -0
- package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.js +13 -8
- package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts +48 -0
- package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-observe.js +74 -49
- package/mcp-server/dist/tools/browser-observe.js.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +20 -0
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +39 -34
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
- package/mcp-server/src/services/neural-interface.ts +58 -46
- package/mcp-server/src/tools/browser-advanced.ts +10 -5
- package/mcp-server/src/tools/browser-interact.ts +34 -24
- package/mcp-server/src/tools/browser-navigate.ts +83 -77
- package/mcp-server/src/tools/browser-observe.ts +99 -73
- package/mcp-server/src/tools/leonardo-browser-tools.ts +47 -37
- package/neural-interface/public/shared/styles.css +49 -0
- package/neural-interface/public/shared/ui-automation-studio.js +71 -3
- package/neural-interface/public/shared/ui-claude-panel.js +29 -23
- package/neural-interface/public/shared/ui-settings.js +10 -10
- package/neural-interface/public/shared/ui-terminal.js +9 -7
- package/neural-interface/server.js +732 -386
- package/package.json +1 -1
|
@@ -2,6 +2,8 @@ import { z } from 'zod';
|
|
|
2
2
|
import * as ni from '../services/neural-interface.js';
|
|
3
3
|
import { text, image } from './response.js';
|
|
4
4
|
|
|
5
|
+
const tabIdField = z.string().optional().describe('Target a specific tab within the session. Auto-resolved from environment if omitted.');
|
|
6
|
+
|
|
5
7
|
// ── browser_snapshot ──
|
|
6
8
|
|
|
7
9
|
export const browserSnapshotSchema = {
|
|
@@ -9,6 +11,7 @@ export const browserSnapshotSchema = {
|
|
|
9
11
|
'Scope snapshot to a specific element\'s subtree. Dramatically reduces output on complex pages. Twitter: [data-testid="primaryColumn"] for main feed, [data-testid="tweet"] for a single tweet card.'
|
|
10
12
|
),
|
|
11
13
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
14
|
+
tabId: tabIdField,
|
|
12
15
|
};
|
|
13
16
|
|
|
14
17
|
export const browserSnapshotDescription =
|
|
@@ -59,11 +62,11 @@ function formatSnapshotNode(node: Record<string, unknown>, indent = 0): string {
|
|
|
59
62
|
return line;
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
export async function handleBrowserSnapshot(args: { selector?: string; sessionId?: string }) {
|
|
63
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
65
|
+
export async function handleBrowserSnapshot(args: { selector?: string; sessionId?: string; tabId?: string }) {
|
|
66
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
64
67
|
if ('error' in resolved) return text(resolved.error);
|
|
65
68
|
|
|
66
|
-
const result = await ni.snapshot(resolved.sessionId, args.selector);
|
|
69
|
+
const result = await ni.snapshot(resolved.sessionId, args.selector, resolved.tabId);
|
|
67
70
|
if (result.error) return text(`Snapshot failed: ${result.error}`);
|
|
68
71
|
|
|
69
72
|
const tree = result.snapshot as Record<string, unknown> | null;
|
|
@@ -88,17 +91,18 @@ export const browserContentSchema = {
|
|
|
88
91
|
'Output format. "text" returns raw innerText (fast, no structure). "markdown" returns clean markdown with headings, links, and lists preserved — strips nav/header/footer/ads automatically. Markdown is better for LLM consumption.'
|
|
89
92
|
),
|
|
90
93
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
94
|
+
tabId: tabIdField,
|
|
91
95
|
};
|
|
92
96
|
|
|
93
97
|
export const browserContentDescription =
|
|
94
98
|
'Get the content of the current page. Set format="markdown" for clean markdown with structure preserved (headings, links, lists) — nav/header/footer/ads stripped automatically. Default format="text" returns raw visible text. Markdown is preferred for LLM consumption — up to 80% more token-efficient than raw HTML.';
|
|
95
99
|
|
|
96
|
-
export async function handleBrowserContent(args: { format?: string; sessionId?: string }) {
|
|
97
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
100
|
+
export async function handleBrowserContent(args: { format?: string; sessionId?: string; tabId?: string }) {
|
|
101
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
98
102
|
if ('error' in resolved) return text(resolved.error);
|
|
99
103
|
|
|
100
104
|
if (args.format === 'markdown') {
|
|
101
|
-
const result = await ni.getMarkdown(resolved.sessionId);
|
|
105
|
+
const result = await ni.getMarkdown(resolved.sessionId, resolved.tabId);
|
|
102
106
|
if (result.error) return text(`Markdown extraction failed: ${result.error}`);
|
|
103
107
|
|
|
104
108
|
let msg = `URL: ${result.url}\nTitle: "${result.title}"\nTokens: ~${result.tokens}\n\n`;
|
|
@@ -107,7 +111,7 @@ export async function handleBrowserContent(args: { format?: string; sessionId?:
|
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
// Default: plain text
|
|
110
|
-
const result = await ni.getContent(resolved.sessionId);
|
|
114
|
+
const result = await ni.getContent(resolved.sessionId, resolved.tabId);
|
|
111
115
|
if (result.error) return text(`Content failed: ${result.error}`);
|
|
112
116
|
|
|
113
117
|
let msg = `URL: ${result.url}\nTitle: "${result.title}"\n\n`;
|
|
@@ -120,16 +124,17 @@ export async function handleBrowserContent(args: { format?: string; sessionId?:
|
|
|
120
124
|
|
|
121
125
|
export const browserScreenshotSchema = {
|
|
122
126
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
127
|
+
tabId: tabIdField,
|
|
123
128
|
};
|
|
124
129
|
|
|
125
130
|
export const browserScreenshotDescription =
|
|
126
131
|
'Take a screenshot of the current page. Returns a base64-encoded JPEG image. Use sparingly — prefer browser_snapshot for most tasks as it is far more token-efficient.';
|
|
127
132
|
|
|
128
|
-
export async function handleBrowserScreenshot(args: { sessionId?: string }) {
|
|
129
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
133
|
+
export async function handleBrowserScreenshot(args: { sessionId?: string; tabId?: string }) {
|
|
134
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
130
135
|
if ('error' in resolved) return text(resolved.error);
|
|
131
136
|
|
|
132
|
-
const result = await ni.screenshot(resolved.sessionId);
|
|
137
|
+
const result = await ni.screenshot(resolved.sessionId, resolved.tabId);
|
|
133
138
|
if (result.error) return text(`Screenshot failed: ${result.error}`);
|
|
134
139
|
|
|
135
140
|
return {
|
|
@@ -163,16 +168,17 @@ Array.from(document.querySelectorAll('[data-testid="tweet"]')).map(el => {
|
|
|
163
168
|
|
|
164
169
|
export const browserExtractTweetsSchema = {
|
|
165
170
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
171
|
+
tabId: tabIdField,
|
|
166
172
|
};
|
|
167
173
|
|
|
168
174
|
export const browserExtractTweetsDescription =
|
|
169
175
|
'Extract all currently visible tweets as structured JSON (author, handle, text, time, url, replies, reposts, likes, views). Much faster than browser_snapshot for data harvesting — use this in scraping/loop flows instead of reading the ARIA tree. Navigate to x.com/search?q=%23hashtag&f=live first for latest-first hashtag results.';
|
|
170
176
|
|
|
171
|
-
export async function handleBrowserExtractTweets(args: { sessionId?: string }) {
|
|
172
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
177
|
+
export async function handleBrowserExtractTweets(args: { sessionId?: string; tabId?: string }) {
|
|
178
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
173
179
|
if ('error' in resolved) return text(resolved.error);
|
|
174
180
|
|
|
175
|
-
const result = await ni.evaluate(resolved.sessionId, TWEET_EXTRACTOR_SCRIPT);
|
|
181
|
+
const result = await ni.evaluate(resolved.sessionId, TWEET_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
176
182
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
177
183
|
|
|
178
184
|
const tweets = result.result as Array<Record<string, unknown>>;
|
|
@@ -206,6 +212,7 @@ Array.from(document.querySelectorAll('[role="article"]')).map(el => {
|
|
|
206
212
|
|
|
207
213
|
export const browserExtractFbPostsSchema = {
|
|
208
214
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
215
|
+
tabId: tabIdField,
|
|
209
216
|
};
|
|
210
217
|
|
|
211
218
|
export const browserExtractFbPostsDescription =
|
|
@@ -213,11 +220,11 @@ export const browserExtractFbPostsDescription =
|
|
|
213
220
|
'Works on group feeds and Pages. Scroll down first with browser_scroll to load more posts. ' +
|
|
214
221
|
'Much faster than browser_snapshot for data harvesting from Facebook.';
|
|
215
222
|
|
|
216
|
-
export async function handleBrowserExtractFbPosts(args: { sessionId?: string }) {
|
|
217
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
223
|
+
export async function handleBrowserExtractFbPosts(args: { sessionId?: string; tabId?: string }) {
|
|
224
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
218
225
|
if ('error' in resolved) return text(resolved.error);
|
|
219
226
|
|
|
220
|
-
const result = await ni.evaluate(resolved.sessionId, FB_POST_EXTRACTOR_SCRIPT);
|
|
227
|
+
const result = await ni.evaluate(resolved.sessionId, FB_POST_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
221
228
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
222
229
|
|
|
223
230
|
const posts = result.result as Array<Record<string, unknown>>;
|
|
@@ -258,6 +265,7 @@ const TIKTOK_FEED_EXTRACTOR_SCRIPT = `
|
|
|
258
265
|
|
|
259
266
|
export const browserExtractTiktokVideosSchema = {
|
|
260
267
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
268
|
+
tabId: tabIdField,
|
|
261
269
|
};
|
|
262
270
|
|
|
263
271
|
export const browserExtractTiktokVideosDescription =
|
|
@@ -266,11 +274,11 @@ export const browserExtractTiktokVideosDescription =
|
|
|
266
274
|
'Navigate to tiktok.com/ or tiktok.com/following first. Scroll with browser_scroll to load more videos. ' +
|
|
267
275
|
'Much faster than browser_snapshot for data harvesting from TikTok feeds.';
|
|
268
276
|
|
|
269
|
-
export async function handleBrowserExtractTiktokVideos(args: { sessionId?: string }) {
|
|
270
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
277
|
+
export async function handleBrowserExtractTiktokVideos(args: { sessionId?: string; tabId?: string }) {
|
|
278
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
271
279
|
if ('error' in resolved) return text(resolved.error);
|
|
272
280
|
|
|
273
|
-
const result = await ni.evaluate(resolved.sessionId, TIKTOK_FEED_EXTRACTOR_SCRIPT);
|
|
281
|
+
const result = await ni.evaluate(resolved.sessionId, TIKTOK_FEED_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
274
282
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
275
283
|
|
|
276
284
|
const videos = result.result as Array<Record<string, unknown>>;
|
|
@@ -302,6 +310,7 @@ Array.from(document.querySelectorAll('[data-e2e="search_top-item"]')).map(el =>
|
|
|
302
310
|
|
|
303
311
|
export const browserExtractTiktokSearchSchema = {
|
|
304
312
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
313
|
+
tabId: tabIdField,
|
|
305
314
|
};
|
|
306
315
|
|
|
307
316
|
export const browserExtractTiktokSearchDescription =
|
|
@@ -310,11 +319,11 @@ export const browserExtractTiktokSearchDescription =
|
|
|
310
319
|
'Navigate to tiktok.com/search?q=<query> first. Scroll to load more results. ' +
|
|
311
320
|
'Much faster than browser_snapshot for harvesting TikTok search results.';
|
|
312
321
|
|
|
313
|
-
export async function handleBrowserExtractTiktokSearch(args: { sessionId?: string }) {
|
|
314
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
322
|
+
export async function handleBrowserExtractTiktokSearch(args: { sessionId?: string; tabId?: string }) {
|
|
323
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
315
324
|
if ('error' in resolved) return text(resolved.error);
|
|
316
325
|
|
|
317
|
-
const result = await ni.evaluate(resolved.sessionId, TIKTOK_SEARCH_EXTRACTOR_SCRIPT);
|
|
326
|
+
const result = await ni.evaluate(resolved.sessionId, TIKTOK_SEARCH_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
318
327
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
319
328
|
|
|
320
329
|
const videos = result.result as Array<Record<string, unknown>>;
|
|
@@ -348,6 +357,7 @@ Array.from(document.querySelectorAll('[data-tt="components_PostInfoCell_a"]')).m
|
|
|
348
357
|
|
|
349
358
|
export const browserExtractTiktokStudioSchema = {
|
|
350
359
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
360
|
+
tabId: tabIdField,
|
|
351
361
|
};
|
|
352
362
|
|
|
353
363
|
export const browserExtractTiktokStudioDescription =
|
|
@@ -356,11 +366,11 @@ export const browserExtractTiktokStudioDescription =
|
|
|
356
366
|
'Navigate to tiktok.com/tiktokstudio/content first. Scroll to load more posts. ' +
|
|
357
367
|
'Use this to audit, manage, or bulk-read your published TikTok content.';
|
|
358
368
|
|
|
359
|
-
export async function handleBrowserExtractTiktokStudio(args: { sessionId?: string }) {
|
|
360
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
369
|
+
export async function handleBrowserExtractTiktokStudio(args: { sessionId?: string; tabId?: string }) {
|
|
370
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
361
371
|
if ('error' in resolved) return text(resolved.error);
|
|
362
372
|
|
|
363
|
-
const result = await ni.evaluate(resolved.sessionId, TIKTOK_STUDIO_EXTRACTOR_SCRIPT);
|
|
373
|
+
const result = await ni.evaluate(resolved.sessionId, TIKTOK_STUDIO_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
364
374
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
365
375
|
|
|
366
376
|
const posts = result.result as Array<Record<string, unknown>>;
|
|
@@ -395,6 +405,7 @@ const TIKTOK_PROFILE_EXTRACTOR_SCRIPT = `
|
|
|
395
405
|
|
|
396
406
|
export const browserExtractTiktokProfileSchema = {
|
|
397
407
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
408
|
+
tabId: tabIdField,
|
|
398
409
|
};
|
|
399
410
|
|
|
400
411
|
export const browserExtractTiktokProfileDescription =
|
|
@@ -403,11 +414,11 @@ export const browserExtractTiktokProfileDescription =
|
|
|
403
414
|
'Navigate to tiktok.com/@username first. Scroll down to load more videos in the grid. ' +
|
|
404
415
|
'Use this to audit a creator profile or collect video URLs for further processing.';
|
|
405
416
|
|
|
406
|
-
export async function handleBrowserExtractTiktokProfile(args: { sessionId?: string }) {
|
|
407
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
417
|
+
export async function handleBrowserExtractTiktokProfile(args: { sessionId?: string; tabId?: string }) {
|
|
418
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
408
419
|
if ('error' in resolved) return text(resolved.error);
|
|
409
420
|
|
|
410
|
-
const result = await ni.evaluate(resolved.sessionId, TIKTOK_PROFILE_EXTRACTOR_SCRIPT);
|
|
421
|
+
const result = await ni.evaluate(resolved.sessionId, TIKTOK_PROFILE_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
411
422
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
412
423
|
|
|
413
424
|
const profile = result.result as Record<string, unknown>;
|
|
@@ -443,6 +454,7 @@ Array.from(document.querySelectorAll('[aria-label="Lista de conversas"] [role="r
|
|
|
443
454
|
|
|
444
455
|
export const browserExtractWaChatsSchema = {
|
|
445
456
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
457
|
+
tabId: tabIdField,
|
|
446
458
|
};
|
|
447
459
|
|
|
448
460
|
export const browserExtractWaChatsDescription =
|
|
@@ -451,11 +463,11 @@ export const browserExtractWaChatsDescription =
|
|
|
451
463
|
'Must be on web.whatsapp.com with the chat list visible. Scroll the sidebar with browser_scroll to load more chats. ' +
|
|
452
464
|
'Use browser_click on span[title="Chat Name"] to open a specific chat.';
|
|
453
465
|
|
|
454
|
-
export async function handleBrowserExtractWaChats(args: { sessionId?: string }) {
|
|
455
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
466
|
+
export async function handleBrowserExtractWaChats(args: { sessionId?: string; tabId?: string }) {
|
|
467
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
456
468
|
if ('error' in resolved) return text(resolved.error);
|
|
457
469
|
|
|
458
|
-
const result = await ni.evaluate(resolved.sessionId, WA_CHATS_EXTRACTOR_SCRIPT);
|
|
470
|
+
const result = await ni.evaluate(resolved.sessionId, WA_CHATS_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
459
471
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
460
472
|
|
|
461
473
|
const chats = result.result as Array<Record<string, unknown>>;
|
|
@@ -489,6 +501,7 @@ Array.from(document.querySelectorAll('div.copyable-text[data-pre-plain-text]')).
|
|
|
489
501
|
|
|
490
502
|
export const browserExtractWaMessagesSchema = {
|
|
491
503
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
504
|
+
tabId: tabIdField,
|
|
492
505
|
};
|
|
493
506
|
|
|
494
507
|
export const browserExtractWaMessagesDescription =
|
|
@@ -497,11 +510,11 @@ export const browserExtractWaMessagesDescription =
|
|
|
497
510
|
'Open a chat first by clicking span[title="Chat Name"]. Scroll up with browser_scroll to load older messages. ' +
|
|
498
511
|
'direction is "in" for received and "out" for sent messages.';
|
|
499
512
|
|
|
500
|
-
export async function handleBrowserExtractWaMessages(args: { sessionId?: string }) {
|
|
501
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
513
|
+
export async function handleBrowserExtractWaMessages(args: { sessionId?: string; tabId?: string }) {
|
|
514
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
502
515
|
if ('error' in resolved) return text(resolved.error);
|
|
503
516
|
|
|
504
|
-
const result = await ni.evaluate(resolved.sessionId, WA_MESSAGES_EXTRACTOR_SCRIPT);
|
|
517
|
+
const result = await ni.evaluate(resolved.sessionId, WA_MESSAGES_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
505
518
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
506
519
|
|
|
507
520
|
const messages = result.result as Array<Record<string, unknown>>;
|
|
@@ -537,6 +550,7 @@ Array.from(document.querySelectorAll('article')).map(el => {
|
|
|
537
550
|
|
|
538
551
|
export const browserExtractIgFeedSchema = {
|
|
539
552
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
553
|
+
tabId: tabIdField,
|
|
540
554
|
};
|
|
541
555
|
|
|
542
556
|
export const browserExtractIgFeedDescription =
|
|
@@ -544,11 +558,11 @@ export const browserExtractIgFeedDescription =
|
|
|
544
558
|
'(username, profileUrl, postUrl, caption, likes, comments, time, datetime, isSponsored, hasFollow). ' +
|
|
545
559
|
'Navigate to instagram.com/ first. Scroll down with browser_scroll to load more posts.';
|
|
546
560
|
|
|
547
|
-
export async function handleBrowserExtractIgFeed(args: { sessionId?: string }) {
|
|
548
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
561
|
+
export async function handleBrowserExtractIgFeed(args: { sessionId?: string; tabId?: string }) {
|
|
562
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
549
563
|
if ('error' in resolved) return text(resolved.error);
|
|
550
564
|
|
|
551
|
-
const result = await ni.evaluate(resolved.sessionId, IG_FEED_EXTRACTOR_SCRIPT);
|
|
565
|
+
const result = await ni.evaluate(resolved.sessionId, IG_FEED_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
552
566
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
553
567
|
|
|
554
568
|
const posts = result.result as Array<Record<string, unknown>>;
|
|
@@ -599,6 +613,7 @@ const IG_PROFILE_EXTRACTOR_SCRIPT = `
|
|
|
599
613
|
|
|
600
614
|
export const browserExtractIgProfileSchema = {
|
|
601
615
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
616
|
+
tabId: tabIdField,
|
|
602
617
|
};
|
|
603
618
|
|
|
604
619
|
export const browserExtractIgProfileDescription =
|
|
@@ -606,11 +621,11 @@ export const browserExtractIgProfileDescription =
|
|
|
606
621
|
'(username, displayName, bio, posts, followers, followerExact, following, isVerified, website, gridPosts, highlights). ' +
|
|
607
622
|
'Navigate to instagram.com/username/ first. Returns bio, stats, post grid (up to 12), and story highlights.';
|
|
608
623
|
|
|
609
|
-
export async function handleBrowserExtractIgProfile(args: { sessionId?: string }) {
|
|
610
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
624
|
+
export async function handleBrowserExtractIgProfile(args: { sessionId?: string; tabId?: string }) {
|
|
625
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
611
626
|
if ('error' in resolved) return text(resolved.error);
|
|
612
627
|
|
|
613
|
-
const result = await ni.evaluate(resolved.sessionId, IG_PROFILE_EXTRACTOR_SCRIPT);
|
|
628
|
+
const result = await ni.evaluate(resolved.sessionId, IG_PROFILE_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
614
629
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
615
630
|
|
|
616
631
|
const profile = result.result as Record<string, unknown>;
|
|
@@ -674,6 +689,7 @@ const IG_POST_EXTRACTOR_SCRIPT = `
|
|
|
674
689
|
|
|
675
690
|
export const browserExtractIgPostSchema = {
|
|
676
691
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
692
|
+
tabId: tabIdField,
|
|
677
693
|
};
|
|
678
694
|
|
|
679
695
|
export const browserExtractIgPostDescription =
|
|
@@ -682,11 +698,11 @@ export const browserExtractIgPostDescription =
|
|
|
682
698
|
'Navigate to instagram.com/p/POST_ID/ or instagram.com/reel/REEL_ID/ first. ' +
|
|
683
699
|
'Scroll the comment area to load more comments before extracting.';
|
|
684
700
|
|
|
685
|
-
export async function handleBrowserExtractIgPost(args: { sessionId?: string }) {
|
|
686
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
701
|
+
export async function handleBrowserExtractIgPost(args: { sessionId?: string; tabId?: string }) {
|
|
702
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
687
703
|
if ('error' in resolved) return text(resolved.error);
|
|
688
704
|
|
|
689
|
-
const result = await ni.evaluate(resolved.sessionId, IG_POST_EXTRACTOR_SCRIPT);
|
|
705
|
+
const result = await ni.evaluate(resolved.sessionId, IG_POST_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
690
706
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
691
707
|
|
|
692
708
|
const post = result.result as Record<string, unknown>;
|
|
@@ -740,6 +756,7 @@ const IG_REELS_EXTRACTOR_SCRIPT = `
|
|
|
740
756
|
|
|
741
757
|
export const browserExtractIgReelsSchema = {
|
|
742
758
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
759
|
+
tabId: tabIdField,
|
|
743
760
|
};
|
|
744
761
|
|
|
745
762
|
export const browserExtractIgReelsDescription =
|
|
@@ -747,11 +764,11 @@ export const browserExtractIgReelsDescription =
|
|
|
747
764
|
'(username, profileUrl, caption, likes, comments, audioName, audioUrl, hasFollow). ' +
|
|
748
765
|
'Navigate to instagram.com/reels/ first. Scroll down to load more reels.';
|
|
749
766
|
|
|
750
|
-
export async function handleBrowserExtractIgReels(args: { sessionId?: string }) {
|
|
751
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
767
|
+
export async function handleBrowserExtractIgReels(args: { sessionId?: string; tabId?: string }) {
|
|
768
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
752
769
|
if ('error' in resolved) return text(resolved.error);
|
|
753
770
|
|
|
754
|
-
const result = await ni.evaluate(resolved.sessionId, IG_REELS_EXTRACTOR_SCRIPT);
|
|
771
|
+
const result = await ni.evaluate(resolved.sessionId, IG_REELS_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
755
772
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
756
773
|
|
|
757
774
|
const reels = result.result as Array<Record<string, unknown>>;
|
|
@@ -774,6 +791,7 @@ Array.from(document.querySelectorAll('main a[href*="/p/"], main a[href*="/reel/"
|
|
|
774
791
|
|
|
775
792
|
export const browserExtractIgSearchSchema = {
|
|
776
793
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
794
|
+
tabId: tabIdField,
|
|
777
795
|
};
|
|
778
796
|
|
|
779
797
|
export const browserExtractIgSearchDescription =
|
|
@@ -781,11 +799,11 @@ export const browserExtractIgSearchDescription =
|
|
|
781
799
|
'Navigate to instagram.com/explore/ first. Scroll to load more content. ' +
|
|
782
800
|
'For hashtag search, navigate to instagram.com/explore/tags/HASHTAG/.';
|
|
783
801
|
|
|
784
|
-
export async function handleBrowserExtractIgSearch(args: { sessionId?: string }) {
|
|
785
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
802
|
+
export async function handleBrowserExtractIgSearch(args: { sessionId?: string; tabId?: string }) {
|
|
803
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
786
804
|
if ('error' in resolved) return text(resolved.error);
|
|
787
805
|
|
|
788
|
-
const result = await ni.evaluate(resolved.sessionId, IG_SEARCH_EXTRACTOR_SCRIPT);
|
|
806
|
+
const result = await ni.evaluate(resolved.sessionId, IG_SEARCH_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
789
807
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
790
808
|
|
|
791
809
|
const posts = result.result as Array<Record<string, unknown>>;
|
|
@@ -835,6 +853,7 @@ Array.from(document.querySelectorAll('.feed-shared-update-v2[data-urn*="urn:li:a
|
|
|
835
853
|
|
|
836
854
|
export const browserExtractLiFeedSchema = {
|
|
837
855
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
856
|
+
tabId: tabIdField,
|
|
838
857
|
};
|
|
839
858
|
|
|
840
859
|
export const browserExtractLiFeedDescription =
|
|
@@ -843,11 +862,11 @@ export const browserExtractLiFeedDescription =
|
|
|
843
862
|
'Navigate to linkedin.com/feed/ first. Scroll down with browser_scroll to load more posts. ' +
|
|
844
863
|
'Much faster than browser_snapshot for data harvesting from the LinkedIn feed.';
|
|
845
864
|
|
|
846
|
-
export async function handleBrowserExtractLiFeed(args: { sessionId?: string }) {
|
|
847
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
865
|
+
export async function handleBrowserExtractLiFeed(args: { sessionId?: string; tabId?: string }) {
|
|
866
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
848
867
|
if ('error' in resolved) return text(resolved.error);
|
|
849
868
|
|
|
850
|
-
const result = await ni.evaluate(resolved.sessionId, LI_FEED_EXTRACTOR_SCRIPT);
|
|
869
|
+
const result = await ni.evaluate(resolved.sessionId, LI_FEED_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
851
870
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
852
871
|
|
|
853
872
|
const posts = result.result as Array<Record<string, unknown>>;
|
|
@@ -890,6 +909,7 @@ const LI_PROFILE_EXTRACTOR_SCRIPT = `
|
|
|
890
909
|
|
|
891
910
|
export const browserExtractLiProfileSchema = {
|
|
892
911
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
912
|
+
tabId: tabIdField,
|
|
893
913
|
};
|
|
894
914
|
|
|
895
915
|
export const browserExtractLiProfileDescription =
|
|
@@ -898,11 +918,11 @@ export const browserExtractLiProfileDescription =
|
|
|
898
918
|
'Navigate to linkedin.com/in/USERNAME/ first. Sections include Experience, Education, Skills, etc. ' +
|
|
899
919
|
'Use linkedin.com/in/me/ for the logged-in user\'s own profile.';
|
|
900
920
|
|
|
901
|
-
export async function handleBrowserExtractLiProfile(args: { sessionId?: string }) {
|
|
902
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
921
|
+
export async function handleBrowserExtractLiProfile(args: { sessionId?: string; tabId?: string }) {
|
|
922
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
903
923
|
if ('error' in resolved) return text(resolved.error);
|
|
904
924
|
|
|
905
|
-
const result = await ni.evaluate(resolved.sessionId, LI_PROFILE_EXTRACTOR_SCRIPT);
|
|
925
|
+
const result = await ni.evaluate(resolved.sessionId, LI_PROFILE_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
906
926
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
907
927
|
|
|
908
928
|
const profile = result.result as Record<string, unknown>;
|
|
@@ -946,6 +966,7 @@ const LI_POST_EXTRACTOR_SCRIPT = `
|
|
|
946
966
|
|
|
947
967
|
export const browserExtractLiPostSchema = {
|
|
948
968
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
969
|
+
tabId: tabIdField,
|
|
949
970
|
};
|
|
950
971
|
|
|
951
972
|
export const browserExtractLiPostDescription =
|
|
@@ -954,11 +975,11 @@ export const browserExtractLiPostDescription =
|
|
|
954
975
|
'Navigate to linkedin.com/feed/update/urn:li:activity:ID/ first. ' +
|
|
955
976
|
'Click "Comentar" button to expand comment section, then scroll to load more comments before extracting.';
|
|
956
977
|
|
|
957
|
-
export async function handleBrowserExtractLiPost(args: { sessionId?: string }) {
|
|
958
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
978
|
+
export async function handleBrowserExtractLiPost(args: { sessionId?: string; tabId?: string }) {
|
|
979
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
959
980
|
if ('error' in resolved) return text(resolved.error);
|
|
960
981
|
|
|
961
|
-
const result = await ni.evaluate(resolved.sessionId, LI_POST_EXTRACTOR_SCRIPT);
|
|
982
|
+
const result = await ni.evaluate(resolved.sessionId, LI_POST_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
962
983
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
963
984
|
|
|
964
985
|
const post = result.result as Record<string, unknown>;
|
|
@@ -987,6 +1008,7 @@ Array.from(document.querySelectorAll('article.nt-card')).map(card => {
|
|
|
987
1008
|
|
|
988
1009
|
export const browserExtractLiNotificationsSchema = {
|
|
989
1010
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
1011
|
+
tabId: tabIdField,
|
|
990
1012
|
};
|
|
991
1013
|
|
|
992
1014
|
export const browserExtractLiNotificationsDescription =
|
|
@@ -994,11 +1016,11 @@ export const browserExtractLiNotificationsDescription =
|
|
|
994
1016
|
'Navigate to linkedin.com/notifications/ first. Scroll down to load older notifications. ' +
|
|
995
1017
|
'Useful for monitoring engagement, connection requests, and mentions.';
|
|
996
1018
|
|
|
997
|
-
export async function handleBrowserExtractLiNotifications(args: { sessionId?: string }) {
|
|
998
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
1019
|
+
export async function handleBrowserExtractLiNotifications(args: { sessionId?: string; tabId?: string }) {
|
|
1020
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
999
1021
|
if ('error' in resolved) return text(resolved.error);
|
|
1000
1022
|
|
|
1001
|
-
const result = await ni.evaluate(resolved.sessionId, LI_NOTIFICATIONS_EXTRACTOR_SCRIPT);
|
|
1023
|
+
const result = await ni.evaluate(resolved.sessionId, LI_NOTIFICATIONS_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
1002
1024
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
1003
1025
|
|
|
1004
1026
|
const notifs = result.result as Array<Record<string, unknown>>;
|
|
@@ -1035,6 +1057,7 @@ const LI_MESSAGES_EXTRACTOR_SCRIPT = `
|
|
|
1035
1057
|
|
|
1036
1058
|
export const browserExtractLiMessagesSchema = {
|
|
1037
1059
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
1060
|
+
tabId: tabIdField,
|
|
1038
1061
|
};
|
|
1039
1062
|
|
|
1040
1063
|
export const browserExtractLiMessagesDescription =
|
|
@@ -1044,11 +1067,11 @@ export const browserExtractLiMessagesDescription =
|
|
|
1044
1067
|
'Navigate to linkedin.com/messaging/ first. Click a conversation to load its messages. ' +
|
|
1045
1068
|
'To send a message: browser_fill on .msg-form__contenteditable then browser_click on .msg-form__send-button.';
|
|
1046
1069
|
|
|
1047
|
-
export async function handleBrowserExtractLiMessages(args: { sessionId?: string }) {
|
|
1048
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
1070
|
+
export async function handleBrowserExtractLiMessages(args: { sessionId?: string; tabId?: string }) {
|
|
1071
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
1049
1072
|
if ('error' in resolved) return text(resolved.error);
|
|
1050
1073
|
|
|
1051
|
-
const result = await ni.evaluate(resolved.sessionId, LI_MESSAGES_EXTRACTOR_SCRIPT);
|
|
1074
|
+
const result = await ni.evaluate(resolved.sessionId, LI_MESSAGES_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
1052
1075
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
1053
1076
|
|
|
1054
1077
|
const data = result.result as Record<string, unknown>;
|
|
@@ -1104,6 +1127,7 @@ const LI_SEARCH_PEOPLE_EXTRACTOR_SCRIPT = `
|
|
|
1104
1127
|
|
|
1105
1128
|
export const browserExtractLiSearchPeopleSchema = {
|
|
1106
1129
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
1130
|
+
tabId: tabIdField,
|
|
1107
1131
|
};
|
|
1108
1132
|
|
|
1109
1133
|
export const browserExtractLiSearchPeopleDescription =
|
|
@@ -1112,11 +1136,11 @@ export const browserExtractLiSearchPeopleDescription =
|
|
|
1112
1136
|
'Navigate to linkedin.com/search/results/people/?keywords=QUERY first. ' +
|
|
1113
1137
|
'Scroll down to load more results. Also works on linkedin.com/search/results/all/.';
|
|
1114
1138
|
|
|
1115
|
-
export async function handleBrowserExtractLiSearchPeople(args: { sessionId?: string }) {
|
|
1116
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
1139
|
+
export async function handleBrowserExtractLiSearchPeople(args: { sessionId?: string; tabId?: string }) {
|
|
1140
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
1117
1141
|
if ('error' in resolved) return text(resolved.error);
|
|
1118
1142
|
|
|
1119
|
-
const result = await ni.evaluate(resolved.sessionId, LI_SEARCH_PEOPLE_EXTRACTOR_SCRIPT);
|
|
1143
|
+
const result = await ni.evaluate(resolved.sessionId, LI_SEARCH_PEOPLE_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
1120
1144
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
1121
1145
|
|
|
1122
1146
|
const people = result.result as Array<Record<string, unknown>>;
|
|
@@ -1175,6 +1199,7 @@ const LI_NETWORK_EXTRACTOR_SCRIPT = `
|
|
|
1175
1199
|
|
|
1176
1200
|
export const browserExtractLiNetworkSchema = {
|
|
1177
1201
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
1202
|
+
tabId: tabIdField,
|
|
1178
1203
|
};
|
|
1179
1204
|
|
|
1180
1205
|
export const browserExtractLiNetworkDescription =
|
|
@@ -1185,11 +1210,11 @@ export const browserExtractLiNetworkDescription =
|
|
|
1185
1210
|
'To accept an invitation: browser_click on button with the acceptLabel. ' +
|
|
1186
1211
|
'To connect: browser_click on the respective Conectar/Connect button.';
|
|
1187
1212
|
|
|
1188
|
-
export async function handleBrowserExtractLiNetwork(args: { sessionId?: string }) {
|
|
1189
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
1213
|
+
export async function handleBrowserExtractLiNetwork(args: { sessionId?: string; tabId?: string }) {
|
|
1214
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
1190
1215
|
if ('error' in resolved) return text(resolved.error);
|
|
1191
1216
|
|
|
1192
|
-
const result = await ni.evaluate(resolved.sessionId, LI_NETWORK_EXTRACTOR_SCRIPT);
|
|
1217
|
+
const result = await ni.evaluate(resolved.sessionId, LI_NETWORK_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
1193
1218
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
1194
1219
|
|
|
1195
1220
|
const data = result.result as Record<string, unknown>;
|
|
@@ -1304,6 +1329,7 @@ const LI_JOBS_EXTRACTOR_SCRIPT = `
|
|
|
1304
1329
|
|
|
1305
1330
|
export const browserExtractLiJobsSchema = {
|
|
1306
1331
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
1332
|
+
tabId: tabIdField,
|
|
1307
1333
|
};
|
|
1308
1334
|
|
|
1309
1335
|
export const browserExtractLiJobsDescription =
|
|
@@ -1314,11 +1340,11 @@ export const browserExtractLiJobsDescription =
|
|
|
1314
1340
|
'For search: use linkedin.com/jobs/search/?keywords=QUERY&location=LOCATION. ' +
|
|
1315
1341
|
'Returns an array of job objects.';
|
|
1316
1342
|
|
|
1317
|
-
export async function handleBrowserExtractLiJobs(args: { sessionId?: string }) {
|
|
1318
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
1343
|
+
export async function handleBrowserExtractLiJobs(args: { sessionId?: string; tabId?: string }) {
|
|
1344
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
1319
1345
|
if ('error' in resolved) return text(resolved.error);
|
|
1320
1346
|
|
|
1321
|
-
const result = await ni.evaluate(resolved.sessionId, LI_JOBS_EXTRACTOR_SCRIPT);
|
|
1347
|
+
const result = await ni.evaluate(resolved.sessionId, LI_JOBS_EXTRACTOR_SCRIPT, resolved.tabId);
|
|
1322
1348
|
if (result.error) return text(`Extract failed: ${result.error}`);
|
|
1323
1349
|
|
|
1324
1350
|
const jobs = result.result as Array<Record<string, unknown>>;
|