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
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
export declare const browserNavigateSchema: {
|
|
10
10
|
page: z.ZodEnum<["home", "library", "image", "video", "upscaler", "blueprints", "flow-state", "models"]>;
|
|
11
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
12
|
+
tabId: z.ZodOptional<z.ZodString>;
|
|
11
13
|
};
|
|
12
14
|
export declare const browserNavigateDescription = "Navigate the browser to a specific Leonardo.ai page. Always use this as the first step before any browser-based generation.";
|
|
13
15
|
export declare function handleBrowserNavigate(args: {
|
|
14
16
|
page: string;
|
|
17
|
+
sessionId?: string;
|
|
18
|
+
tabId?: string;
|
|
15
19
|
}): Promise<{
|
|
16
20
|
content: {
|
|
17
21
|
type: "text";
|
|
@@ -21,11 +25,15 @@ export declare function handleBrowserNavigate(args: {
|
|
|
21
25
|
export declare const browserGenerateSchema: {
|
|
22
26
|
prompt: z.ZodString;
|
|
23
27
|
type: z.ZodOptional<z.ZodEnum<["image", "video"]>>;
|
|
28
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
29
|
+
tabId: z.ZodOptional<z.ZodString>;
|
|
24
30
|
};
|
|
25
31
|
export declare const browserGenerateDescription = "Fill the prompt field and click Generate on the current Leonardo.ai page. Set all other settings (model, style, dimensions, motion controls) BEFORE calling this \u2014 use browser_click, browser_fill, and browser_snapshot to configure the UI first. This tool only handles: navigate to correct page if needed \u2192 clear & fill prompt \u2192 click Generate.";
|
|
26
32
|
export declare function handleBrowserGenerate(args: {
|
|
27
33
|
prompt: string;
|
|
28
34
|
type?: string;
|
|
35
|
+
sessionId?: string;
|
|
36
|
+
tabId?: string;
|
|
29
37
|
}): Promise<{
|
|
30
38
|
content: {
|
|
31
39
|
type: "text";
|
|
@@ -35,11 +43,15 @@ export declare function handleBrowserGenerate(args: {
|
|
|
35
43
|
export declare const browserLibrarySchema: {
|
|
36
44
|
action: z.ZodEnum<["view", "search"]>;
|
|
37
45
|
query: z.ZodOptional<z.ZodString>;
|
|
46
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
47
|
+
tabId: z.ZodOptional<z.ZodString>;
|
|
38
48
|
};
|
|
39
49
|
export declare const browserLibraryDescription = "Open or search Leonardo.ai's library to view past generations. Use browser_snapshot after to see results.";
|
|
40
50
|
export declare function handleBrowserLibrary(args: {
|
|
41
51
|
action: string;
|
|
42
52
|
query?: string;
|
|
53
|
+
sessionId?: string;
|
|
54
|
+
tabId?: string;
|
|
43
55
|
}): Promise<{
|
|
44
56
|
content: {
|
|
45
57
|
type: "text";
|
|
@@ -48,10 +60,14 @@ export declare function handleBrowserLibrary(args: {
|
|
|
48
60
|
}>;
|
|
49
61
|
export declare const browserDownloadSchema: {
|
|
50
62
|
action: z.ZodEnum<["screenshot"]>;
|
|
63
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
64
|
+
tabId: z.ZodOptional<z.ZodString>;
|
|
51
65
|
};
|
|
52
66
|
export declare const browserDownloadDescription = "Capture a screenshot of the current Leonardo.ai page. Use to verify generation results or UI state.";
|
|
53
67
|
export declare function handleBrowserDownload(args: {
|
|
54
68
|
action: string;
|
|
69
|
+
sessionId?: string;
|
|
70
|
+
tabId?: string;
|
|
55
71
|
}): Promise<{
|
|
56
72
|
content: {
|
|
57
73
|
type: "text";
|
|
@@ -62,12 +78,16 @@ export declare const browserReferenceSchema: {
|
|
|
62
78
|
type: z.ZodEnum<["image_ref", "style_ref", "content_ref", "character_ref", "image_to_image", "start_frame", "end_frame"]>;
|
|
63
79
|
filePaths: z.ZodArray<z.ZodString, "many">;
|
|
64
80
|
autoClear: z.ZodOptional<z.ZodBoolean>;
|
|
81
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
82
|
+
tabId: z.ZodOptional<z.ZodString>;
|
|
65
83
|
};
|
|
66
84
|
export declare const browserReferenceDescription: string;
|
|
67
85
|
export declare function handleBrowserReference(args: {
|
|
68
86
|
type: string;
|
|
69
87
|
filePaths: string[];
|
|
70
88
|
autoClear?: boolean;
|
|
89
|
+
sessionId?: string;
|
|
90
|
+
tabId?: string;
|
|
71
91
|
}): Promise<{
|
|
72
92
|
content: {
|
|
73
93
|
type: "text";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"leonardo-browser-tools.d.ts","sourceRoot":"","sources":["../../src/tools/leonardo-browser-tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"leonardo-browser-tools.d.ts","sourceRoot":"","sources":["../../src/tools/leonardo-browser-tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,eAAO,MAAM,qBAAqB;;;;CAOjC,CAAC;AAEF,eAAO,MAAM,0BAA0B,gIACwF,CAAC;AAEhI,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;GAuBrG;AAID,eAAO,MAAM,qBAAqB;;;;;CAKjC,CAAC;AAEF,eAAO,MAAM,0BAA0B,0WACmT,CAAC;AAE3V,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;GA4BA;AAID,eAAO,MAAM,oBAAoB;;;;;CAKhC,CAAC;AAEF,eAAO,MAAM,yBAAyB,8GACwE,CAAC;AAE/G,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;GAiBtH;AAID,eAAO,MAAM,qBAAqB;;;;CAIjC,CAAC;AAEF,eAAO,MAAM,0BAA0B,wGACgE,CAAC;AAExG,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;GAWvG;AAID,eAAO,MAAM,sBAAsB;;;;;;CAgBlC,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAI+C,CAAC;AAExF,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;GA+FA"}
|
|
@@ -8,14 +8,9 @@
|
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import * as ni from '../services/neural-interface.js';
|
|
10
10
|
import { text } from './response.js';
|
|
11
|
+
const tabIdField = z.string().optional().describe('Target a specific tab within the session. Auto-resolved from environment if omitted.');
|
|
11
12
|
const LEO_BASE = 'https://app.leonardo.ai';
|
|
12
|
-
// ── Helper
|
|
13
|
-
async function getSession() {
|
|
14
|
-
const resolved = await ni.resolveSession(undefined, { url: LEO_BASE });
|
|
15
|
-
if ('error' in resolved)
|
|
16
|
-
return null;
|
|
17
|
-
return resolved.sessionId;
|
|
18
|
-
}
|
|
13
|
+
// ── Helper ────────────────────────────────────────────────────
|
|
19
14
|
async function wait(ms) {
|
|
20
15
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
21
16
|
}
|
|
@@ -25,11 +20,13 @@ export const browserNavigateSchema = {
|
|
|
25
20
|
'home', 'library', 'image', 'video', 'upscaler',
|
|
26
21
|
'blueprints', 'flow-state', 'models',
|
|
27
22
|
]).describe('Leonardo.ai page to navigate to.'),
|
|
23
|
+
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
24
|
+
tabId: tabIdField,
|
|
28
25
|
};
|
|
29
26
|
export const browserNavigateDescription = 'Navigate the browser to a specific Leonardo.ai page. Always use this as the first step before any browser-based generation.';
|
|
30
27
|
export async function handleBrowserNavigate(args) {
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
28
|
+
const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
|
|
29
|
+
if ('error' in resolved)
|
|
33
30
|
return text('No browser session available. Open Leonardo.ai in the browser first using browser_navigate or the Apps menu.');
|
|
34
31
|
const paths = {
|
|
35
32
|
'home': '/',
|
|
@@ -42,7 +39,7 @@ export async function handleBrowserNavigate(args) {
|
|
|
42
39
|
'models': '/models-training',
|
|
43
40
|
};
|
|
44
41
|
const path = paths[args.page] || '/';
|
|
45
|
-
const res = await ni.navigate(sessionId, `${LEO_BASE}${path}
|
|
42
|
+
const res = await ni.navigate(resolved.sessionId, `${LEO_BASE}${path}`, resolved.tabId);
|
|
46
43
|
if (res.error)
|
|
47
44
|
return text(`Navigation failed: ${res.error}`);
|
|
48
45
|
// Wait for page to settle
|
|
@@ -53,30 +50,32 @@ export async function handleBrowserNavigate(args) {
|
|
|
53
50
|
export const browserGenerateSchema = {
|
|
54
51
|
prompt: z.string().describe('The generation prompt to type into Leonardo.ai.'),
|
|
55
52
|
type: z.enum(['image', 'video']).optional().describe('Generation type. Default: image.'),
|
|
53
|
+
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
54
|
+
tabId: tabIdField,
|
|
56
55
|
};
|
|
57
56
|
export const browserGenerateDescription = 'Fill the prompt field and click Generate on the current Leonardo.ai page. Set all other settings (model, style, dimensions, motion controls) BEFORE calling this — use browser_click, browser_fill, and browser_snapshot to configure the UI first. This tool only handles: navigate to correct page if needed → clear & fill prompt → click Generate.';
|
|
58
57
|
export async function handleBrowserGenerate(args) {
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
58
|
+
const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
|
|
59
|
+
if ('error' in resolved)
|
|
61
60
|
return text('No browser session available. Open Leonardo.ai first.');
|
|
62
61
|
const genType = args.type || 'image';
|
|
63
62
|
// Check current URL — navigate only if not already on the right page
|
|
64
|
-
const snapshot = await ni.snapshot(sessionId);
|
|
63
|
+
const snapshot = await ni.snapshot(resolved.sessionId, resolved.tabId);
|
|
65
64
|
const currentUrl = snapshot?.url || '';
|
|
66
65
|
const targetPath = genType === 'video' ? '/image-generation/video' : '/image-generation';
|
|
67
66
|
if (!currentUrl.includes(targetPath)) {
|
|
68
|
-
const navRes = await ni.navigate(sessionId, `${LEO_BASE}${targetPath}
|
|
67
|
+
const navRes = await ni.navigate(resolved.sessionId, `${LEO_BASE}${targetPath}`, resolved.tabId);
|
|
69
68
|
if (navRes.error)
|
|
70
69
|
return text(`Navigation failed: ${navRes.error}`);
|
|
71
70
|
await wait(3000);
|
|
72
71
|
}
|
|
73
72
|
// Clear and fill the prompt textbox
|
|
74
|
-
const fillRes = await ni.fill(sessionId, '[aria-label="Prompt"], textbox[name="Prompt"], textarea', args.prompt);
|
|
73
|
+
const fillRes = await ni.fill(resolved.sessionId, '[aria-label="Prompt"], textbox[name="Prompt"], textarea', args.prompt, undefined, resolved.tabId);
|
|
75
74
|
if (fillRes.error)
|
|
76
75
|
return text(`Failed to fill prompt: ${fillRes.error}`);
|
|
77
76
|
await wait(500);
|
|
78
77
|
// Click the Generate button
|
|
79
|
-
const clickRes = await ni.click(sessionId, 'button[aria-label="Generate"], button:has-text("Generate")');
|
|
78
|
+
const clickRes = await ni.click(resolved.sessionId, 'button[aria-label="Generate"], button:has-text("Generate")', undefined, resolved.tabId);
|
|
80
79
|
if (clickRes.error)
|
|
81
80
|
return text(`Failed to click Generate: ${clickRes.error}`);
|
|
82
81
|
return text(`Generation started (${genType}).\n\nPrompt: "${args.prompt.substring(0, 120)}${args.prompt.length > 120 ? '...' : ''}"\n\nThe generation is processing in Leonardo.ai. Use browser_snapshot or browser_screenshot to check progress and see results. Results also appear in the library.`);
|
|
@@ -85,21 +84,23 @@ export async function handleBrowserGenerate(args) {
|
|
|
85
84
|
export const browserLibrarySchema = {
|
|
86
85
|
action: z.enum(['view', 'search']).describe('Action: view (open library) or search (search for a term).'),
|
|
87
86
|
query: z.string().optional().describe('Search query (for search action).'),
|
|
87
|
+
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
88
|
+
tabId: tabIdField,
|
|
88
89
|
};
|
|
89
90
|
export const browserLibraryDescription = 'Open or search Leonardo.ai\'s library to view past generations. Use browser_snapshot after to see results.';
|
|
90
91
|
export async function handleBrowserLibrary(args) {
|
|
91
|
-
const
|
|
92
|
-
if (
|
|
92
|
+
const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
|
|
93
|
+
if ('error' in resolved)
|
|
93
94
|
return text('No browser session available.');
|
|
94
|
-
const navRes = await ni.navigate(sessionId, `${LEO_BASE}/library
|
|
95
|
+
const navRes = await ni.navigate(resolved.sessionId, `${LEO_BASE}/library`, resolved.tabId);
|
|
95
96
|
if (navRes.error)
|
|
96
97
|
return text(`Navigation failed: ${navRes.error}`);
|
|
97
98
|
await wait(2000);
|
|
98
99
|
if (args.action === 'search' && args.query) {
|
|
99
|
-
const fillRes = await ni.fill(sessionId, 'input[placeholder*="Search" i]', args.query);
|
|
100
|
+
const fillRes = await ni.fill(resolved.sessionId, 'input[placeholder*="Search" i]', args.query, undefined, resolved.tabId);
|
|
100
101
|
if (fillRes.error)
|
|
101
102
|
return text(`Failed to search: ${fillRes.error}`);
|
|
102
|
-
await ni.pressKey(sessionId, 'Enter');
|
|
103
|
+
await ni.pressKey(resolved.sessionId, 'Enter', resolved.tabId);
|
|
103
104
|
return text(`Searched Leonardo.ai library for: "${args.query}". Use browser_snapshot to see results.`);
|
|
104
105
|
}
|
|
105
106
|
return text('Leonardo.ai library opened. Use browser_snapshot to see recent generations.');
|
|
@@ -107,14 +108,16 @@ export async function handleBrowserLibrary(args) {
|
|
|
107
108
|
// ── Browser Download / Screenshot ─────────────────────────────
|
|
108
109
|
export const browserDownloadSchema = {
|
|
109
110
|
action: z.enum(['screenshot']).describe('Action: screenshot (capture current Leonardo.ai page state).'),
|
|
111
|
+
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
112
|
+
tabId: tabIdField,
|
|
110
113
|
};
|
|
111
114
|
export const browserDownloadDescription = 'Capture a screenshot of the current Leonardo.ai page. Use to verify generation results or UI state.';
|
|
112
115
|
export async function handleBrowserDownload(args) {
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
116
|
+
const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
|
|
117
|
+
if ('error' in resolved)
|
|
115
118
|
return text('No browser session available.');
|
|
116
119
|
if (args.action === 'screenshot') {
|
|
117
|
-
const res = await ni.screenshot(sessionId);
|
|
120
|
+
const res = await ni.screenshot(resolved.sessionId, resolved.tabId);
|
|
118
121
|
if (res.error)
|
|
119
122
|
return text(`Screenshot failed: ${res.error}`);
|
|
120
123
|
return text('Screenshot captured of the current Leonardo.ai page.');
|
|
@@ -130,14 +133,16 @@ export const browserReferenceSchema = {
|
|
|
130
133
|
'Video generation: start_frame, end_frame. Availability depends on the selected model — check the model advisor.'),
|
|
131
134
|
filePaths: z.array(z.string()).describe('Absolute file paths of images to upload as reference. Get paths from image_staged tool or from user-attached images.'),
|
|
132
135
|
autoClear: z.boolean().optional().describe('Auto-delete the source images from data/images/ after successful upload. Default: true.'),
|
|
136
|
+
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
137
|
+
tabId: tabIdField,
|
|
133
138
|
};
|
|
134
139
|
export const browserReferenceDescription = 'Upload reference image(s) to Leonardo.ai for guided generation. Supports image references, style/content/character references, ' +
|
|
135
140
|
'image-to-image, and video start/end frames. Must be called AFTER selecting a model (reference type availability is model-dependent) ' +
|
|
136
141
|
'and BEFORE calling leonardo_browser_generate. Uses browser automation to click the reference panel, upload the file(s), and confirm. ' +
|
|
137
142
|
'By default, auto-clears uploaded images from the SynaBun image store after success.';
|
|
138
143
|
export async function handleBrowserReference(args) {
|
|
139
|
-
const
|
|
140
|
-
if (
|
|
144
|
+
const resolved = await ni.resolveSession(args.sessionId, { url: LEO_BASE }, args.tabId);
|
|
145
|
+
if ('error' in resolved)
|
|
141
146
|
return text('No browser session available. Open Leonardo.ai first.');
|
|
142
147
|
const { type, filePaths, autoClear = true } = args;
|
|
143
148
|
const isVideo = type === 'start_frame' || type === 'end_frame';
|
|
@@ -145,7 +150,7 @@ export async function handleBrowserReference(args) {
|
|
|
145
150
|
let openResult;
|
|
146
151
|
if (isVideo) {
|
|
147
152
|
// Video: click "Add Image Guidance to generation" button
|
|
148
|
-
openResult = await ni.evaluate(sessionId, `(() => {
|
|
153
|
+
openResult = await ni.evaluate(resolved.sessionId, `(() => {
|
|
149
154
|
const btns = document.querySelectorAll('button');
|
|
150
155
|
for (const b of btns) {
|
|
151
156
|
if (b.textContent.includes('Image Guidance') || b.textContent.includes('Add Image')) {
|
|
@@ -153,11 +158,11 @@ export async function handleBrowserReference(args) {
|
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
160
|
return 'not found';
|
|
156
|
-
})()
|
|
161
|
+
})()`, resolved.tabId);
|
|
157
162
|
}
|
|
158
163
|
else {
|
|
159
164
|
// Image: click "Add elements" button
|
|
160
|
-
openResult = await ni.evaluate(sessionId, `(() => {
|
|
165
|
+
openResult = await ni.evaluate(resolved.sessionId, `(() => {
|
|
161
166
|
const btns = document.querySelectorAll('button');
|
|
162
167
|
for (const b of btns) {
|
|
163
168
|
if (b.textContent.includes('Add elements') || b.textContent.includes('Add Elements')) {
|
|
@@ -165,7 +170,7 @@ export async function handleBrowserReference(args) {
|
|
|
165
170
|
}
|
|
166
171
|
}
|
|
167
172
|
return 'not found';
|
|
168
|
-
})()
|
|
173
|
+
})()`, resolved.tabId);
|
|
169
174
|
}
|
|
170
175
|
if (openResult.error)
|
|
171
176
|
return text(`Failed to open reference panel: ${openResult.error}`);
|
|
@@ -181,22 +186,22 @@ export async function handleBrowserReference(args) {
|
|
|
181
186
|
};
|
|
182
187
|
const labels = typeLabels[type] || [type];
|
|
183
188
|
const labelSearch = labels.map(l => `b.textContent.includes('${l}')`).join(' || ');
|
|
184
|
-
const selectResult = await ni.evaluate(sessionId, `(() => {
|
|
189
|
+
const selectResult = await ni.evaluate(resolved.sessionId, `(() => {
|
|
185
190
|
const btns = document.querySelectorAll('button, [role="tab"], [role="menuitem"]');
|
|
186
191
|
for (const b of btns) {
|
|
187
192
|
if (${labelSearch}) { b.click(); return 'selected'; }
|
|
188
193
|
}
|
|
189
194
|
return 'not found — use browser_snapshot to see available options';
|
|
190
|
-
})()
|
|
195
|
+
})()`, resolved.tabId);
|
|
191
196
|
if (selectResult.error)
|
|
192
197
|
return text(`Failed to select reference type: ${selectResult.error}. Use browser_snapshot to see the panel.`);
|
|
193
198
|
await wait(1000);
|
|
194
199
|
}
|
|
195
200
|
// Step 3: Find file input and upload
|
|
196
|
-
const uploadResult = await ni.upload(sessionId, 'input[type="file"]', filePaths);
|
|
201
|
+
const uploadResult = await ni.upload(resolved.sessionId, 'input[type="file"]', filePaths, undefined, resolved.tabId);
|
|
197
202
|
if (uploadResult.error) {
|
|
198
203
|
// Fallback: try broader selector
|
|
199
|
-
const fallback = await ni.upload(sessionId, 'input[accept*="image"]', filePaths);
|
|
204
|
+
const fallback = await ni.upload(resolved.sessionId, 'input[accept*="image"]', filePaths, undefined, resolved.tabId);
|
|
200
205
|
if (fallback.error) {
|
|
201
206
|
return text(`Failed to upload reference image(s): ${uploadResult.error}. ` +
|
|
202
207
|
`The file input may not be visible yet — try browser_snapshot to inspect the panel, ` +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"leonardo-browser-tools.js","sourceRoot":"","sources":["../../src/tools/leonardo-browser-tools.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,iCAAiC,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,MAAM,
|
|
1
|
+
{"version":3,"file":"leonardo-browser-tools.js","sourceRoot":"","sources":["../../src/tools/leonardo-browser-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@ const BASE_URL = process.env.NEURAL_INTERFACE_URL
|
|
|
8
8
|
|| `http://localhost:${process.env.NEURAL_PORT || '3344'}`;
|
|
9
9
|
const DEFAULT_TIMEOUT = 10_000;
|
|
10
10
|
const LONG_TIMEOUT = 30_000;
|
|
11
|
+
const SESSION_CREATE_TIMEOUT = 70_000;
|
|
11
12
|
|
|
12
13
|
export interface BrowserSessionInfo {
|
|
13
14
|
id: string;
|
|
@@ -67,8 +68,12 @@ async function request(
|
|
|
67
68
|
*/
|
|
68
69
|
export async function resolveSession(
|
|
69
70
|
sessionId?: string,
|
|
70
|
-
autoCreate?: { url?: string }
|
|
71
|
-
|
|
71
|
+
autoCreate?: { url?: string },
|
|
72
|
+
tabId?: string
|
|
73
|
+
): Promise<{ sessionId: string; tabId?: string } | { error: string }> {
|
|
74
|
+
// Resolve tab ID from explicit param or environment variable
|
|
75
|
+
const resolvedTabId = tabId || process.env.SYNABUN_BROWSER_TAB || undefined;
|
|
76
|
+
|
|
72
77
|
// Agent-scoped browser session — set by the agent orchestrator to pin
|
|
73
78
|
// this MCP instance to a specific browser session (multi-agent isolation).
|
|
74
79
|
// Verify the pinned session still exists; fall through to auto-select if gone.
|
|
@@ -76,14 +81,14 @@ export async function resolveSession(
|
|
|
76
81
|
if (pinnedSession && !sessionId) {
|
|
77
82
|
const check = await request('GET', '/api/browser/sessions');
|
|
78
83
|
const active = ((check.sessions || []) as BrowserSessionInfo[]).find(s => s.id === pinnedSession);
|
|
79
|
-
if (active) return { sessionId: pinnedSession };
|
|
80
|
-
|
|
84
|
+
if (active) return { sessionId: pinnedSession, tabId: resolvedTabId };
|
|
85
|
+
return { error: `Pinned browser session ${pinnedSession} is no longer available. Launch the automation again so SynaBun can open the selected browser profile.` };
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
if (sessionId) {
|
|
84
89
|
// Trust the server — it will 404 if the session doesn't exist.
|
|
85
90
|
// Skipping the extra GET /api/browser/sessions verification call saves a full round-trip.
|
|
86
|
-
return { sessionId };
|
|
91
|
+
return { sessionId, tabId: resolvedTabId };
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
// List sessions
|
|
@@ -92,16 +97,16 @@ export async function resolveSession(
|
|
|
92
97
|
const sessions = (data.sessions || []) as BrowserSessionInfo[];
|
|
93
98
|
|
|
94
99
|
if (sessions.length === 1) {
|
|
95
|
-
return { sessionId: sessions[0].id };
|
|
100
|
+
return { sessionId: sessions[0].id, tabId: resolvedTabId };
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
if (sessions.length === 0) {
|
|
99
104
|
if (autoCreate) {
|
|
100
105
|
const created = await request('POST', '/api/browser/sessions', {
|
|
101
106
|
url: autoCreate.url || 'about:blank',
|
|
102
|
-
},
|
|
107
|
+
}, SESSION_CREATE_TIMEOUT);
|
|
103
108
|
if (created.error) return { error: `Failed to auto-create session: ${created.error}` };
|
|
104
|
-
return { sessionId: created.sessionId as string };
|
|
109
|
+
return { sessionId: created.sessionId as string, tabId: resolvedTabId };
|
|
105
110
|
}
|
|
106
111
|
return { error: 'No browser sessions open. Use browser_session to create one first, or use browser_navigate with a URL to auto-create.' };
|
|
107
112
|
}
|
|
@@ -130,7 +135,7 @@ export async function listSessions(): Promise<NiResponse> {
|
|
|
130
135
|
export async function createSession(url?: string): Promise<NiResponse> {
|
|
131
136
|
return request('POST', '/api/browser/sessions', {
|
|
132
137
|
url: url || 'about:blank',
|
|
133
|
-
},
|
|
138
|
+
}, SESSION_CREATE_TIMEOUT);
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
export async function closeSession(sessionId: string): Promise<NiResponse> {
|
|
@@ -139,98 +144,105 @@ export async function closeSession(sessionId: string): Promise<NiResponse> {
|
|
|
139
144
|
|
|
140
145
|
// ── Navigation ──
|
|
141
146
|
|
|
142
|
-
export async function navigate(sessionId: string, url: string): Promise<NiResponse> {
|
|
143
|
-
return request('POST', `/api/browser/sessions/${sessionId}/navigate`, { url }, LONG_TIMEOUT);
|
|
147
|
+
export async function navigate(sessionId: string, url: string, tabId?: string): Promise<NiResponse> {
|
|
148
|
+
return request('POST', `/api/browser/sessions/${sessionId}/navigate`, { url, ...(tabId && { tabId }) }, LONG_TIMEOUT);
|
|
144
149
|
}
|
|
145
150
|
|
|
146
|
-
export async function goBack(sessionId: string): Promise<NiResponse> {
|
|
147
|
-
return request('POST', `/api/browser/sessions/${sessionId}/back
|
|
151
|
+
export async function goBack(sessionId: string, tabId?: string): Promise<NiResponse> {
|
|
152
|
+
return request('POST', `/api/browser/sessions/${sessionId}/back`, { ...(tabId && { tabId }) });
|
|
148
153
|
}
|
|
149
154
|
|
|
150
|
-
export async function goForward(sessionId: string): Promise<NiResponse> {
|
|
151
|
-
return request('POST', `/api/browser/sessions/${sessionId}/forward
|
|
155
|
+
export async function goForward(sessionId: string, tabId?: string): Promise<NiResponse> {
|
|
156
|
+
return request('POST', `/api/browser/sessions/${sessionId}/forward`, { ...(tabId && { tabId }) });
|
|
152
157
|
}
|
|
153
158
|
|
|
154
|
-
export async function reload(sessionId: string): Promise<NiResponse> {
|
|
155
|
-
return request('POST', `/api/browser/sessions/${sessionId}/reload`,
|
|
159
|
+
export async function reload(sessionId: string, tabId?: string): Promise<NiResponse> {
|
|
160
|
+
return request('POST', `/api/browser/sessions/${sessionId}/reload`, { ...(tabId && { tabId }) }, LONG_TIMEOUT);
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
// ── Interaction (selector-based) ──
|
|
159
164
|
|
|
160
|
-
export async function click(sessionId: string, selector: string, nthMatch?: number): Promise<NiResponse> {
|
|
161
|
-
return request('POST', `/api/browser/sessions/${sessionId}/click`, { selector, ...(nthMatch !== undefined && { nthMatch }) });
|
|
165
|
+
export async function click(sessionId: string, selector: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
|
|
166
|
+
return request('POST', `/api/browser/sessions/${sessionId}/click`, { selector, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
|
|
162
167
|
}
|
|
163
168
|
|
|
164
|
-
export async function fill(sessionId: string, selector: string, value: string, nthMatch?: number): Promise<NiResponse> {
|
|
165
|
-
return request('POST', `/api/browser/sessions/${sessionId}/fill`, { selector, value, ...(nthMatch !== undefined && { nthMatch }) });
|
|
169
|
+
export async function fill(sessionId: string, selector: string, value: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
|
|
170
|
+
return request('POST', `/api/browser/sessions/${sessionId}/fill`, { selector, value, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
|
|
166
171
|
}
|
|
167
172
|
|
|
168
|
-
export async function type(sessionId: string, selector: string | null, text: string, nthMatch?: number): Promise<NiResponse> {
|
|
169
|
-
return request('POST', `/api/browser/sessions/${sessionId}/type`, { selector, text, ...(nthMatch !== undefined && { nthMatch }) });
|
|
173
|
+
export async function type(sessionId: string, selector: string | null, text: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
|
|
174
|
+
return request('POST', `/api/browser/sessions/${sessionId}/type`, { selector, text, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
|
|
170
175
|
}
|
|
171
176
|
|
|
172
|
-
export async function hover(sessionId: string, selector: string, nthMatch?: number): Promise<NiResponse> {
|
|
173
|
-
return request('POST', `/api/browser/sessions/${sessionId}/hover`, { selector, ...(nthMatch !== undefined && { nthMatch }) });
|
|
177
|
+
export async function hover(sessionId: string, selector: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
|
|
178
|
+
return request('POST', `/api/browser/sessions/${sessionId}/hover`, { selector, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
|
|
174
179
|
}
|
|
175
180
|
|
|
176
|
-
export async function selectOption(sessionId: string, selector: string, value: string, nthMatch?: number): Promise<NiResponse> {
|
|
177
|
-
return request('POST', `/api/browser/sessions/${sessionId}/select`, { selector, value, ...(nthMatch !== undefined && { nthMatch }) });
|
|
181
|
+
export async function selectOption(sessionId: string, selector: string, value: string, nthMatch?: number, tabId?: string): Promise<NiResponse> {
|
|
182
|
+
return request('POST', `/api/browser/sessions/${sessionId}/select`, { selector, value, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) });
|
|
178
183
|
}
|
|
179
184
|
|
|
180
|
-
export async function pressKey(sessionId: string, key: string): Promise<NiResponse> {
|
|
181
|
-
return request('POST', `/api/browser/sessions/${sessionId}/press`, { key });
|
|
185
|
+
export async function pressKey(sessionId: string, key: string, tabId?: string): Promise<NiResponse> {
|
|
186
|
+
return request('POST', `/api/browser/sessions/${sessionId}/press`, { key, ...(tabId && { tabId }) });
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
export async function scroll(
|
|
185
190
|
sessionId: string,
|
|
186
|
-
opts: { direction: string; distance?: number; selector?: string }
|
|
191
|
+
opts: { direction: string; distance?: number; selector?: string },
|
|
192
|
+
tabId?: string
|
|
187
193
|
): Promise<NiResponse> {
|
|
188
|
-
return request('POST', `/api/browser/sessions/${sessionId}/scroll`, opts as Record<string, unknown>);
|
|
194
|
+
return request('POST', `/api/browser/sessions/${sessionId}/scroll`, { ...opts, ...(tabId && { tabId }) } as Record<string, unknown>);
|
|
189
195
|
}
|
|
190
196
|
|
|
191
197
|
export async function upload(
|
|
192
198
|
sessionId: string,
|
|
193
199
|
selector: string,
|
|
194
200
|
filePaths: string[],
|
|
195
|
-
nthMatch?: number
|
|
201
|
+
nthMatch?: number,
|
|
202
|
+
tabId?: string
|
|
196
203
|
): Promise<NiResponse> {
|
|
197
|
-
return request('POST', `/api/browser/sessions/${sessionId}/upload`, { selector, filePaths, ...(nthMatch !== undefined && { nthMatch }) }, LONG_TIMEOUT);
|
|
204
|
+
return request('POST', `/api/browser/sessions/${sessionId}/upload`, { selector, filePaths, ...(nthMatch !== undefined && { nthMatch }), ...(tabId && { tabId }) }, LONG_TIMEOUT);
|
|
198
205
|
}
|
|
199
206
|
|
|
200
207
|
// ── Observation ──
|
|
201
208
|
|
|
202
|
-
export async function snapshot(sessionId: string, selector?: string): Promise<NiResponse> {
|
|
203
|
-
if (selector) return request('POST', `/api/browser/sessions/${sessionId}/snapshot`, { selector });
|
|
204
|
-
|
|
209
|
+
export async function snapshot(sessionId: string, selector?: string, tabId?: string): Promise<NiResponse> {
|
|
210
|
+
if (selector) return request('POST', `/api/browser/sessions/${sessionId}/snapshot`, { selector, ...(tabId && { tabId }) });
|
|
211
|
+
const qs = tabId ? `?tabId=${tabId}` : '';
|
|
212
|
+
return request('GET', `/api/browser/sessions/${sessionId}/snapshot${qs}`);
|
|
205
213
|
}
|
|
206
214
|
|
|
207
|
-
export async function getContent(sessionId: string): Promise<NiResponse> {
|
|
208
|
-
|
|
215
|
+
export async function getContent(sessionId: string, tabId?: string): Promise<NiResponse> {
|
|
216
|
+
const qs = tabId ? `?tabId=${tabId}` : '';
|
|
217
|
+
return request('GET', `/api/browser/sessions/${sessionId}/content${qs}`);
|
|
209
218
|
}
|
|
210
219
|
|
|
211
|
-
export async function getMarkdown(sessionId: string): Promise<NiResponse> {
|
|
212
|
-
|
|
220
|
+
export async function getMarkdown(sessionId: string, tabId?: string): Promise<NiResponse> {
|
|
221
|
+
const qs = tabId ? `?tabId=${tabId}` : '';
|
|
222
|
+
return request('GET', `/api/browser/sessions/${sessionId}/markdown${qs}`, undefined, LONG_TIMEOUT);
|
|
213
223
|
}
|
|
214
224
|
|
|
215
225
|
export async function fetchMarkdown(url: string, timeout?: number): Promise<NiResponse> {
|
|
216
226
|
return request('POST', '/api/fetch-markdown', { url, timeout }, LONG_TIMEOUT);
|
|
217
227
|
}
|
|
218
228
|
|
|
219
|
-
export async function screenshot(sessionId: string): Promise<NiResponse> {
|
|
220
|
-
|
|
229
|
+
export async function screenshot(sessionId: string, tabId?: string): Promise<NiResponse> {
|
|
230
|
+
const qs = tabId ? `?tabId=${tabId}` : '';
|
|
231
|
+
return request('GET', `/api/browser/sessions/${sessionId}/screenshot-base64${qs}`);
|
|
221
232
|
}
|
|
222
233
|
|
|
223
234
|
// ── Advanced ──
|
|
224
235
|
|
|
225
|
-
export async function evaluate(sessionId: string, script: string): Promise<NiResponse> {
|
|
226
|
-
return request('POST', `/api/browser/sessions/${sessionId}/evaluate`, { script }, LONG_TIMEOUT);
|
|
236
|
+
export async function evaluate(sessionId: string, script: string, tabId?: string): Promise<NiResponse> {
|
|
237
|
+
return request('POST', `/api/browser/sessions/${sessionId}/evaluate`, { script, ...(tabId && { tabId }) }, LONG_TIMEOUT);
|
|
227
238
|
}
|
|
228
239
|
|
|
229
240
|
export async function waitFor(
|
|
230
241
|
sessionId: string,
|
|
231
|
-
opts: { selector?: string; state?: string; loadState?: string; timeout?: number }
|
|
242
|
+
opts: { selector?: string; state?: string; loadState?: string; timeout?: number },
|
|
243
|
+
tabId?: string
|
|
232
244
|
): Promise<NiResponse> {
|
|
233
|
-
return request('POST', `/api/browser/sessions/${sessionId}/wait`, opts, LONG_TIMEOUT);
|
|
245
|
+
return request('POST', `/api/browser/sessions/${sessionId}/wait`, { ...opts, ...(tabId && { tabId }) }, LONG_TIMEOUT);
|
|
234
246
|
}
|
|
235
247
|
|
|
236
248
|
// ── Whiteboard ──
|
|
@@ -2,25 +2,28 @@ import { z } from 'zod';
|
|
|
2
2
|
import * as ni from '../services/neural-interface.js';
|
|
3
3
|
import { text } 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_evaluate ──
|
|
6
8
|
|
|
7
9
|
export const browserEvaluateSchema = {
|
|
8
10
|
script: z.string().optional().describe('JavaScript code to execute in the browser page context. The return value will be serialized to JSON.'),
|
|
9
11
|
expression: z.string().optional().describe('Alias for "script" — accepted for compatibility with Playwright conventions.'),
|
|
10
12
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
13
|
+
tabId: tabIdField,
|
|
11
14
|
};
|
|
12
15
|
|
|
13
16
|
export const browserEvaluateDescription =
|
|
14
17
|
'Execute JavaScript in the browser page context and return the result. Useful for reading DOM state, extracting data, or performing actions not covered by other tools.';
|
|
15
18
|
|
|
16
|
-
export async function handleBrowserEvaluate(args: { script?: string; expression?: string; sessionId?: string }) {
|
|
19
|
+
export async function handleBrowserEvaluate(args: { script?: string; expression?: string; sessionId?: string; tabId?: string }) {
|
|
17
20
|
const code = args.script || args.expression;
|
|
18
21
|
if (!code) return text('Either "script" or "expression" is required.');
|
|
19
22
|
|
|
20
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
23
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
21
24
|
if ('error' in resolved) return text(resolved.error);
|
|
22
25
|
|
|
23
|
-
const result = await ni.evaluate(resolved.sessionId, code);
|
|
26
|
+
const result = await ni.evaluate(resolved.sessionId, code, resolved.tabId);
|
|
24
27
|
if (result.error) return text(`Evaluate failed: ${result.error}`);
|
|
25
28
|
|
|
26
29
|
let msg: string;
|
|
@@ -45,6 +48,7 @@ export const browserWaitSchema = {
|
|
|
45
48
|
),
|
|
46
49
|
timeout: z.coerce.number().optional().describe('Timeout in ms (default: 10000 for element waits, 15000 for loadState).'),
|
|
47
50
|
sessionId: z.string().optional().describe('Browser session ID. If omitted, auto-selects the only open session.'),
|
|
51
|
+
tabId: tabIdField,
|
|
48
52
|
};
|
|
49
53
|
|
|
50
54
|
export const browserWaitDescription =
|
|
@@ -56,8 +60,9 @@ export async function handleBrowserWait(args: {
|
|
|
56
60
|
loadState?: string;
|
|
57
61
|
timeout?: number;
|
|
58
62
|
sessionId?: string;
|
|
63
|
+
tabId?: string;
|
|
59
64
|
}) {
|
|
60
|
-
const resolved = await ni.resolveSession(args.sessionId);
|
|
65
|
+
const resolved = await ni.resolveSession(args.sessionId, undefined, args.tabId);
|
|
61
66
|
if ('error' in resolved) return text(resolved.error);
|
|
62
67
|
|
|
63
68
|
const result = await ni.waitFor(resolved.sessionId, {
|
|
@@ -65,7 +70,7 @@ export async function handleBrowserWait(args: {
|
|
|
65
70
|
state: args.state,
|
|
66
71
|
loadState: args.loadState,
|
|
67
72
|
timeout: args.timeout,
|
|
68
|
-
});
|
|
73
|
+
}, resolved.tabId);
|
|
69
74
|
if (result.error) return text(`Wait failed: ${result.error}`);
|
|
70
75
|
|
|
71
76
|
if (args.loadState) {
|