tuna-agent 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/browser/actions/download.d.ts +16 -0
  2. package/dist/browser/actions/download.js +39 -0
  3. package/dist/browser/actions/emulation.d.ts +53 -0
  4. package/dist/browser/actions/emulation.js +103 -0
  5. package/dist/browser/actions/evaluate.d.ts +29 -0
  6. package/dist/browser/actions/evaluate.js +92 -0
  7. package/dist/browser/actions/interaction.d.ts +79 -0
  8. package/dist/browser/actions/interaction.js +210 -0
  9. package/dist/browser/actions/keyboard.d.ts +6 -0
  10. package/dist/browser/actions/keyboard.js +9 -0
  11. package/dist/browser/actions/navigation.d.ts +40 -0
  12. package/dist/browser/actions/navigation.js +92 -0
  13. package/dist/browser/actions/wait.d.ts +12 -0
  14. package/dist/browser/actions/wait.js +33 -0
  15. package/dist/browser/browser.d.ts +722 -0
  16. package/dist/browser/browser.js +1066 -0
  17. package/dist/browser/capture/activity.d.ts +22 -0
  18. package/dist/browser/capture/activity.js +39 -0
  19. package/dist/browser/capture/pdf.d.ts +6 -0
  20. package/dist/browser/capture/pdf.js +6 -0
  21. package/dist/browser/capture/response.d.ts +8 -0
  22. package/dist/browser/capture/response.js +28 -0
  23. package/dist/browser/capture/screenshot.d.ts +30 -0
  24. package/dist/browser/capture/screenshot.js +72 -0
  25. package/dist/browser/capture/trace.d.ts +13 -0
  26. package/dist/browser/capture/trace.js +19 -0
  27. package/dist/browser/chrome-launcher.d.ts +8 -0
  28. package/dist/browser/chrome-launcher.js +543 -0
  29. package/dist/browser/connection.d.ts +42 -0
  30. package/dist/browser/connection.js +359 -0
  31. package/dist/browser/index.d.ts +6 -0
  32. package/dist/browser/index.js +3 -0
  33. package/dist/browser/security.d.ts +51 -0
  34. package/dist/browser/security.js +357 -0
  35. package/dist/browser/snapshot/ai-snapshot.d.ts +12 -0
  36. package/dist/browser/snapshot/ai-snapshot.js +47 -0
  37. package/dist/browser/snapshot/aria-snapshot.d.ts +26 -0
  38. package/dist/browser/snapshot/aria-snapshot.js +121 -0
  39. package/dist/browser/snapshot/ref-map.d.ts +31 -0
  40. package/dist/browser/snapshot/ref-map.js +250 -0
  41. package/dist/browser/storage/index.d.ts +36 -0
  42. package/dist/browser/storage/index.js +65 -0
  43. package/dist/browser/types.d.ts +429 -0
  44. package/dist/browser/types.js +2 -0
  45. package/dist/daemon/extension-handlers.d.ts +63 -0
  46. package/dist/daemon/extension-handlers.js +630 -0
  47. package/dist/daemon/index.js +78 -19
  48. package/dist/daemon/ws-client.d.ts +16 -0
  49. package/dist/daemon/ws-client.js +45 -0
  50. package/dist/mcp/browser-server.d.ts +11 -0
  51. package/dist/mcp/browser-server.js +467 -0
  52. package/dist/mcp/knowledge-server.js +43 -18
  53. package/dist/mcp/setup.js +10 -0
  54. package/dist/utils/claude-cli.js +18 -9
  55. package/package.json +2 -1
@@ -0,0 +1,250 @@
1
+ export const INTERACTIVE_ROLES = new Set([
2
+ 'button', 'link', 'textbox', 'checkbox', 'radio', 'combobox', 'listbox',
3
+ 'menuitem', 'menuitemcheckbox', 'menuitemradio', 'option', 'searchbox',
4
+ 'slider', 'spinbutton', 'switch', 'tab', 'treeitem',
5
+ ]);
6
+ export const CONTENT_ROLES = new Set([
7
+ 'heading', 'cell', 'gridcell', 'columnheader', 'rowheader',
8
+ 'listitem', 'article', 'region', 'main', 'navigation',
9
+ ]);
10
+ export const STRUCTURAL_ROLES = new Set([
11
+ 'generic', 'group', 'list', 'table', 'row', 'rowgroup', 'grid', 'treegrid',
12
+ 'menu', 'menubar', 'toolbar', 'tablist', 'tree', 'directory', 'document',
13
+ 'application', 'presentation', 'none',
14
+ ]);
15
+ function getIndentLevel(line) {
16
+ const match = line.match(/^(\s*)/);
17
+ return match ? Math.floor(match[1].length / 2) : 0;
18
+ }
19
+ function matchInteractiveSnapshotLine(line, options) {
20
+ const depth = getIndentLevel(line);
21
+ if (options.maxDepth !== undefined && depth > options.maxDepth) {
22
+ return null;
23
+ }
24
+ const match = line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);
25
+ if (!match) {
26
+ return null;
27
+ }
28
+ const [, , roleRaw, name, suffix] = match;
29
+ if (roleRaw.startsWith('/')) {
30
+ return null;
31
+ }
32
+ const role = roleRaw.toLowerCase();
33
+ return {
34
+ roleRaw: roleRaw,
35
+ role,
36
+ ...(name ? { name } : {}),
37
+ suffix: suffix,
38
+ };
39
+ }
40
+ function createRoleNameTracker() {
41
+ const counts = new Map();
42
+ const refsByKey = new Map();
43
+ return {
44
+ counts,
45
+ refsByKey,
46
+ getKey(role, name) {
47
+ return `${role}:${name ?? ''}`;
48
+ },
49
+ getNextIndex(role, name) {
50
+ const key = this.getKey(role, name);
51
+ const current = counts.get(key) ?? 0;
52
+ counts.set(key, current + 1);
53
+ return current;
54
+ },
55
+ trackRef(role, name, ref) {
56
+ const key = this.getKey(role, name);
57
+ const list = refsByKey.get(key) ?? [];
58
+ list.push(ref);
59
+ refsByKey.set(key, list);
60
+ },
61
+ getDuplicateKeys() {
62
+ const out = new Set();
63
+ for (const [key, refs] of refsByKey)
64
+ if (refs.length > 1)
65
+ out.add(key);
66
+ return out;
67
+ },
68
+ };
69
+ }
70
+ function removeNthFromNonDuplicates(refs, tracker) {
71
+ const duplicates = tracker.getDuplicateKeys();
72
+ for (const [ref, data] of Object.entries(refs)) {
73
+ const key = tracker.getKey(data.role, data.name);
74
+ if (!duplicates.has(key))
75
+ delete refs[ref]?.nth;
76
+ }
77
+ }
78
+ function compactTree(tree) {
79
+ const lines = tree.split('\n');
80
+ const result = [];
81
+ for (let i = 0; i < lines.length; i++) {
82
+ const line = lines[i];
83
+ if (line.includes('[ref=')) {
84
+ result.push(line);
85
+ continue;
86
+ }
87
+ if (line.includes(':') && !line.trimEnd().endsWith(':')) {
88
+ result.push(line);
89
+ continue;
90
+ }
91
+ const currentIndent = getIndentLevel(line);
92
+ let hasRelevantChildren = false;
93
+ for (let j = i + 1; j < lines.length; j++) {
94
+ if (getIndentLevel(lines[j]) <= currentIndent)
95
+ break;
96
+ if (lines[j]?.includes('[ref=')) {
97
+ hasRelevantChildren = true;
98
+ break;
99
+ }
100
+ }
101
+ if (hasRelevantChildren)
102
+ result.push(line);
103
+ }
104
+ return result.join('\n');
105
+ }
106
+ /**
107
+ * Build a role snapshot from Playwright's ariaSnapshot() output.
108
+ * Assigns ref IDs (e1, e2, ...) to interactive/content elements.
109
+ */
110
+ export function buildRoleSnapshotFromAriaSnapshot(ariaSnapshot, options = {}) {
111
+ const lines = ariaSnapshot.split('\n');
112
+ const refs = {};
113
+ const tracker = createRoleNameTracker();
114
+ let counter = 0;
115
+ const nextRef = () => { counter++; return `e${counter}`; };
116
+ if (options.interactive) {
117
+ const result = [];
118
+ for (const line of lines) {
119
+ const parsed = matchInteractiveSnapshotLine(line, options);
120
+ if (!parsed)
121
+ continue;
122
+ const { roleRaw, role, name, suffix } = parsed;
123
+ if (!INTERACTIVE_ROLES.has(role))
124
+ continue;
125
+ const prefix = line.match(/^(\s*-\s*)/)?.[1] ?? '';
126
+ const ref = nextRef();
127
+ const nth = tracker.getNextIndex(role, name);
128
+ tracker.trackRef(role, name, ref);
129
+ refs[ref] = { role, name, nth };
130
+ let enhanced = `${prefix}${roleRaw}`;
131
+ if (name)
132
+ enhanced += ` "${name}"`;
133
+ enhanced += ` [ref=${ref}]`;
134
+ if (nth > 0)
135
+ enhanced += ` [nth=${nth}]`;
136
+ if (suffix.includes('['))
137
+ enhanced += suffix;
138
+ result.push(enhanced);
139
+ }
140
+ removeNthFromNonDuplicates(refs, tracker);
141
+ return { snapshot: result.join('\n') || '(no interactive elements)', refs };
142
+ }
143
+ const result = [];
144
+ for (const line of lines) {
145
+ const depth = getIndentLevel(line);
146
+ if (options.maxDepth !== undefined && depth > options.maxDepth)
147
+ continue;
148
+ const match = line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);
149
+ if (!match) {
150
+ result.push(line);
151
+ continue;
152
+ }
153
+ const [, prefix, roleRaw, name, suffix] = match;
154
+ if (roleRaw.startsWith('/')) {
155
+ result.push(line);
156
+ continue;
157
+ }
158
+ const role = roleRaw.toLowerCase();
159
+ const isInteractive = INTERACTIVE_ROLES.has(role);
160
+ const isContent = CONTENT_ROLES.has(role);
161
+ const isStructural = STRUCTURAL_ROLES.has(role);
162
+ if (options.compact && isStructural && !name)
163
+ continue;
164
+ if (!(isInteractive || (isContent && name))) {
165
+ result.push(line);
166
+ continue;
167
+ }
168
+ const ref = nextRef();
169
+ const nth = tracker.getNextIndex(role, name);
170
+ tracker.trackRef(role, name, ref);
171
+ refs[ref] = { role, name, nth };
172
+ let enhanced = `${prefix}${roleRaw}`;
173
+ if (name)
174
+ enhanced += ` "${name}"`;
175
+ enhanced += ` [ref=${ref}]`;
176
+ if (nth > 0)
177
+ enhanced += ` [nth=${nth}]`;
178
+ if (suffix)
179
+ enhanced += suffix;
180
+ result.push(enhanced);
181
+ }
182
+ removeNthFromNonDuplicates(refs, tracker);
183
+ const tree = result.join('\n') || '(empty)';
184
+ return { snapshot: options.compact ? compactTree(tree) : tree, refs };
185
+ }
186
+ /**
187
+ * Build a role snapshot from Playwright's AI snapshot output.
188
+ * Preserves Playwright's own aria-ref ids (e.g. ref=e13).
189
+ */
190
+ export function buildRoleSnapshotFromAiSnapshot(aiSnapshot, options = {}) {
191
+ const lines = String(aiSnapshot ?? '').split('\n');
192
+ const refs = {};
193
+ function parseAiSnapshotRef(suffix) {
194
+ const match = suffix.match(/\[ref=(e\d+)\]/i);
195
+ return match ? match[1] : null;
196
+ }
197
+ if (options.interactive) {
198
+ const out = [];
199
+ for (const line of lines) {
200
+ const parsed = matchInteractiveSnapshotLine(line, options);
201
+ if (!parsed)
202
+ continue;
203
+ const { roleRaw, role, name, suffix } = parsed;
204
+ if (!INTERACTIVE_ROLES.has(role))
205
+ continue;
206
+ const ref = parseAiSnapshotRef(suffix);
207
+ if (!ref)
208
+ continue;
209
+ const prefix = line.match(/^(\s*-\s*)/)?.[1] ?? '';
210
+ refs[ref] = { role, ...(name ? { name } : {}) };
211
+ out.push(`${prefix}${roleRaw}${name ? ` "${name}"` : ''}${suffix}`);
212
+ }
213
+ return { snapshot: out.join('\n') || '(no interactive elements)', refs };
214
+ }
215
+ const out = [];
216
+ for (const line of lines) {
217
+ const depth = getIndentLevel(line);
218
+ if (options.maxDepth !== undefined && depth > options.maxDepth)
219
+ continue;
220
+ const match = line.match(/^(\s*-\s*)(\w+)(?:\s+"([^"]*)")?(.*)$/);
221
+ if (!match) {
222
+ out.push(line);
223
+ continue;
224
+ }
225
+ const [, , roleRaw, name, suffix] = match;
226
+ if (roleRaw.startsWith('/')) {
227
+ out.push(line);
228
+ continue;
229
+ }
230
+ const role = roleRaw.toLowerCase();
231
+ const isStructural = STRUCTURAL_ROLES.has(role);
232
+ if (options.compact && isStructural && !name)
233
+ continue;
234
+ const ref = parseAiSnapshotRef(suffix);
235
+ if (ref)
236
+ refs[ref] = { role, ...(name ? { name } : {}) };
237
+ out.push(line);
238
+ }
239
+ const tree = out.join('\n') || '(empty)';
240
+ return { snapshot: options.compact ? compactTree(tree) : tree, refs };
241
+ }
242
+ export function getRoleSnapshotStats(snapshot, refs) {
243
+ const interactive = Object.values(refs).filter(r => INTERACTIVE_ROLES.has(r.role)).length;
244
+ return {
245
+ lines: snapshot.split('\n').length,
246
+ chars: snapshot.length,
247
+ refs: Object.keys(refs).length,
248
+ interactive,
249
+ };
250
+ }
@@ -0,0 +1,36 @@
1
+ import type { CookieData, StorageKind } from '../types.js';
2
+ export declare function cookiesGetViaPlaywright(opts: {
3
+ cdpUrl: string;
4
+ targetId?: string;
5
+ }): Promise<{
6
+ cookies: Awaited<ReturnType<import('playwright-core').BrowserContext['cookies']>>;
7
+ }>;
8
+ export declare function cookiesSetViaPlaywright(opts: {
9
+ cdpUrl: string;
10
+ targetId?: string;
11
+ cookie: CookieData;
12
+ }): Promise<void>;
13
+ export declare function cookiesClearViaPlaywright(opts: {
14
+ cdpUrl: string;
15
+ targetId?: string;
16
+ }): Promise<void>;
17
+ export declare function storageGetViaPlaywright(opts: {
18
+ cdpUrl: string;
19
+ targetId?: string;
20
+ kind: StorageKind;
21
+ key?: string;
22
+ }): Promise<{
23
+ values: Record<string, string>;
24
+ }>;
25
+ export declare function storageSetViaPlaywright(opts: {
26
+ cdpUrl: string;
27
+ targetId?: string;
28
+ kind: StorageKind;
29
+ key: string;
30
+ value: string;
31
+ }): Promise<void>;
32
+ export declare function storageClearViaPlaywright(opts: {
33
+ cdpUrl: string;
34
+ targetId?: string;
35
+ kind: StorageKind;
36
+ }): Promise<void>;
@@ -0,0 +1,65 @@
1
+ import { getPageForTargetId, ensurePageState } from '../connection.js';
2
+ // ── Cookies ──
3
+ export async function cookiesGetViaPlaywright(opts) {
4
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
5
+ ensurePageState(page);
6
+ return { cookies: await page.context().cookies() };
7
+ }
8
+ export async function cookiesSetViaPlaywright(opts) {
9
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
10
+ ensurePageState(page);
11
+ const cookie = opts.cookie;
12
+ if (!cookie.name || cookie.value === undefined)
13
+ throw new Error('cookie name and value are required');
14
+ const hasUrl = typeof cookie.url === 'string' && cookie.url.trim();
15
+ const hasDomain = typeof cookie.domain === 'string' && cookie.domain.trim();
16
+ if (!hasUrl && !hasDomain)
17
+ throw new Error('cookie requires url or domain');
18
+ await page.context().addCookies([cookie]);
19
+ }
20
+ export async function cookiesClearViaPlaywright(opts) {
21
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
22
+ ensurePageState(page);
23
+ await page.context().clearCookies();
24
+ }
25
+ // ── localStorage / sessionStorage ──
26
+ export async function storageGetViaPlaywright(opts) {
27
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
28
+ ensurePageState(page);
29
+ return {
30
+ values: await page.evaluate(({ kind, key }) => {
31
+ const store = kind === 'session' ? window.sessionStorage : window.localStorage;
32
+ if (key) {
33
+ const value = store.getItem(key);
34
+ return value === null ? {} : { [key]: value };
35
+ }
36
+ const out = {};
37
+ for (let i = 0; i < store.length; i++) {
38
+ const k = store.key(i);
39
+ if (!k)
40
+ continue;
41
+ const v = store.getItem(k);
42
+ if (v !== null)
43
+ out[k] = v;
44
+ }
45
+ return out;
46
+ }, { kind: opts.kind, key: opts.key }) ?? {},
47
+ };
48
+ }
49
+ export async function storageSetViaPlaywright(opts) {
50
+ const key = String(opts.key ?? '');
51
+ if (!key)
52
+ throw new Error('key is required');
53
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
54
+ ensurePageState(page);
55
+ await page.evaluate(({ kind, key: k, value }) => {
56
+ (kind === 'session' ? window.sessionStorage : window.localStorage).setItem(k, value);
57
+ }, { kind: opts.kind, key, value: String(opts.value ?? '') });
58
+ }
59
+ export async function storageClearViaPlaywright(opts) {
60
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
61
+ ensurePageState(page);
62
+ await page.evaluate(({ kind }) => {
63
+ (kind === 'session' ? window.sessionStorage : window.localStorage).clear();
64
+ }, { kind: opts.kind });
65
+ }