tuna-agent 0.1.0 → 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 (68) hide show
  1. package/dist/agents/claude-code-adapter.d.ts +3 -1
  2. package/dist/agents/claude-code-adapter.js +28 -4
  3. package/dist/agents/factory.d.ts +2 -1
  4. package/dist/agents/factory.js +2 -2
  5. package/dist/browser/actions/download.d.ts +16 -0
  6. package/dist/browser/actions/download.js +39 -0
  7. package/dist/browser/actions/emulation.d.ts +53 -0
  8. package/dist/browser/actions/emulation.js +103 -0
  9. package/dist/browser/actions/evaluate.d.ts +29 -0
  10. package/dist/browser/actions/evaluate.js +92 -0
  11. package/dist/browser/actions/interaction.d.ts +79 -0
  12. package/dist/browser/actions/interaction.js +210 -0
  13. package/dist/browser/actions/keyboard.d.ts +6 -0
  14. package/dist/browser/actions/keyboard.js +9 -0
  15. package/dist/browser/actions/navigation.d.ts +40 -0
  16. package/dist/browser/actions/navigation.js +92 -0
  17. package/dist/browser/actions/wait.d.ts +12 -0
  18. package/dist/browser/actions/wait.js +33 -0
  19. package/dist/browser/browser.d.ts +722 -0
  20. package/dist/browser/browser.js +1066 -0
  21. package/dist/browser/capture/activity.d.ts +22 -0
  22. package/dist/browser/capture/activity.js +39 -0
  23. package/dist/browser/capture/pdf.d.ts +6 -0
  24. package/dist/browser/capture/pdf.js +6 -0
  25. package/dist/browser/capture/response.d.ts +8 -0
  26. package/dist/browser/capture/response.js +28 -0
  27. package/dist/browser/capture/screenshot.d.ts +30 -0
  28. package/dist/browser/capture/screenshot.js +72 -0
  29. package/dist/browser/capture/trace.d.ts +13 -0
  30. package/dist/browser/capture/trace.js +19 -0
  31. package/dist/browser/chrome-launcher.d.ts +8 -0
  32. package/dist/browser/chrome-launcher.js +543 -0
  33. package/dist/browser/connection.d.ts +42 -0
  34. package/dist/browser/connection.js +359 -0
  35. package/dist/browser/index.d.ts +6 -0
  36. package/dist/browser/index.js +3 -0
  37. package/dist/browser/security.d.ts +51 -0
  38. package/dist/browser/security.js +357 -0
  39. package/dist/browser/snapshot/ai-snapshot.d.ts +12 -0
  40. package/dist/browser/snapshot/ai-snapshot.js +47 -0
  41. package/dist/browser/snapshot/aria-snapshot.d.ts +26 -0
  42. package/dist/browser/snapshot/aria-snapshot.js +121 -0
  43. package/dist/browser/snapshot/ref-map.d.ts +31 -0
  44. package/dist/browser/snapshot/ref-map.js +250 -0
  45. package/dist/browser/storage/index.d.ts +36 -0
  46. package/dist/browser/storage/index.js +65 -0
  47. package/dist/browser/types.d.ts +429 -0
  48. package/dist/browser/types.js +2 -0
  49. package/dist/cli/commands/extension.d.ts +10 -0
  50. package/dist/cli/commands/extension.js +86 -0
  51. package/dist/cli/index.js +12 -0
  52. package/dist/daemon/extension-handlers.d.ts +63 -0
  53. package/dist/daemon/extension-handlers.js +630 -0
  54. package/dist/daemon/index.js +173 -44
  55. package/dist/daemon/ws-client.d.ts +28 -8
  56. package/dist/daemon/ws-client.js +68 -62
  57. package/dist/mcp/browser-server.d.ts +11 -0
  58. package/dist/mcp/browser-server.js +467 -0
  59. package/dist/mcp/knowledge-server.d.ts +11 -0
  60. package/dist/mcp/knowledge-server.js +263 -0
  61. package/dist/mcp/setup.d.ts +20 -0
  62. package/dist/mcp/setup.js +94 -0
  63. package/dist/types/index.d.ts +2 -0
  64. package/dist/utils/claude-cli.d.ts +2 -0
  65. package/dist/utils/claude-cli.js +29 -9
  66. package/dist/utils/message-schemas.d.ts +4 -1
  67. package/dist/utils/message-schemas.js +6 -1
  68. package/package.json +2 -1
@@ -0,0 +1,210 @@
1
+ import { getPageForTargetId, ensurePageState, restoreRoleRefsForTarget, refLocator, toAIFriendlyError, normalizeTimeoutMs, } from '../connection.js';
2
+ import { assertSafeUploadPaths } from '../security.js';
3
+ export async function clickViaPlaywright(opts) {
4
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
5
+ ensurePageState(page);
6
+ restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
7
+ const locator = refLocator(page, opts.ref);
8
+ const timeout = normalizeTimeoutMs(opts.timeoutMs, 8000, 60000);
9
+ try {
10
+ if (opts.doubleClick) {
11
+ await locator.dblclick({ timeout, button: opts.button, modifiers: opts.modifiers });
12
+ }
13
+ else {
14
+ await locator.click({ timeout, button: opts.button, modifiers: opts.modifiers });
15
+ }
16
+ }
17
+ catch (err) {
18
+ throw toAIFriendlyError(err, opts.ref);
19
+ }
20
+ }
21
+ export async function hoverViaPlaywright(opts) {
22
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
23
+ ensurePageState(page);
24
+ restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
25
+ try {
26
+ await refLocator(page, opts.ref).hover({
27
+ timeout: normalizeTimeoutMs(opts.timeoutMs, 8000, 60000),
28
+ });
29
+ }
30
+ catch (err) {
31
+ throw toAIFriendlyError(err, opts.ref);
32
+ }
33
+ }
34
+ export async function typeViaPlaywright(opts) {
35
+ const text = String(opts.text ?? '');
36
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
37
+ ensurePageState(page);
38
+ restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
39
+ const locator = refLocator(page, opts.ref);
40
+ const timeout = normalizeTimeoutMs(opts.timeoutMs, 8000, 60000);
41
+ try {
42
+ if (opts.slowly) {
43
+ await locator.click({ timeout });
44
+ await locator.pressSequentially(text, { timeout, delay: 75 });
45
+ }
46
+ else {
47
+ await locator.fill(text, { timeout });
48
+ }
49
+ if (opts.submit)
50
+ await locator.press('Enter', { timeout });
51
+ }
52
+ catch (err) {
53
+ throw toAIFriendlyError(err, opts.ref);
54
+ }
55
+ }
56
+ export async function selectOptionViaPlaywright(opts) {
57
+ if (!opts.values?.length)
58
+ throw new Error('values are required');
59
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
60
+ ensurePageState(page);
61
+ restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
62
+ try {
63
+ await refLocator(page, opts.ref).selectOption(opts.values, {
64
+ timeout: normalizeTimeoutMs(opts.timeoutMs, 8000, 60000),
65
+ });
66
+ }
67
+ catch (err) {
68
+ throw toAIFriendlyError(err, opts.ref);
69
+ }
70
+ }
71
+ export async function dragViaPlaywright(opts) {
72
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
73
+ ensurePageState(page);
74
+ restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
75
+ try {
76
+ await refLocator(page, opts.startRef).dragTo(refLocator(page, opts.endRef), {
77
+ timeout: normalizeTimeoutMs(opts.timeoutMs, 8000, 60000),
78
+ });
79
+ }
80
+ catch (err) {
81
+ throw toAIFriendlyError(err, `${opts.startRef} -> ${opts.endRef}`);
82
+ }
83
+ }
84
+ export async function fillFormViaPlaywright(opts) {
85
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
86
+ ensurePageState(page);
87
+ restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
88
+ const timeout = normalizeTimeoutMs(opts.timeoutMs, 8000, 60000);
89
+ for (let i = 0; i < opts.fields.length; i++) {
90
+ const field = opts.fields[i];
91
+ const ref = field.ref.trim();
92
+ const type = (typeof field.type === 'string' ? field.type.trim() : '') || 'text';
93
+ const rawValue = field.value;
94
+ const value = rawValue == null ? '' : String(rawValue);
95
+ if (!ref)
96
+ throw new Error(`fill(): field at index ${i} has empty ref`);
97
+ const locator = refLocator(page, ref);
98
+ if (type === 'checkbox' || type === 'radio') {
99
+ const checked = rawValue === true || rawValue === 1 || rawValue === '1' || rawValue === 'true';
100
+ try {
101
+ await locator.setChecked(checked, { timeout });
102
+ }
103
+ catch (err) {
104
+ throw toAIFriendlyError(err, ref);
105
+ }
106
+ continue;
107
+ }
108
+ try {
109
+ await locator.fill(value, { timeout });
110
+ }
111
+ catch (err) {
112
+ throw toAIFriendlyError(err, ref);
113
+ }
114
+ }
115
+ }
116
+ export async function scrollIntoViewViaPlaywright(opts) {
117
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
118
+ ensurePageState(page);
119
+ restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
120
+ try {
121
+ await refLocator(page, opts.ref).scrollIntoViewIfNeeded({
122
+ timeout: normalizeTimeoutMs(opts.timeoutMs, 20000),
123
+ });
124
+ }
125
+ catch (err) {
126
+ throw toAIFriendlyError(err, opts.ref);
127
+ }
128
+ }
129
+ export async function highlightViaPlaywright(opts) {
130
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
131
+ ensurePageState(page);
132
+ restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
133
+ try {
134
+ await refLocator(page, opts.ref).highlight();
135
+ }
136
+ catch (err) {
137
+ throw toAIFriendlyError(err, opts.ref);
138
+ }
139
+ }
140
+ export async function setInputFilesViaPlaywright(opts) {
141
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
142
+ ensurePageState(page);
143
+ restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
144
+ const locator = opts.ref
145
+ ? refLocator(page, opts.ref)
146
+ : opts.element
147
+ ? page.locator(opts.element).first()
148
+ : null;
149
+ if (!locator)
150
+ throw new Error('Either ref or element is required for setInputFiles');
151
+ await assertSafeUploadPaths(opts.paths);
152
+ try {
153
+ await locator.setInputFiles(opts.paths);
154
+ }
155
+ catch (err) {
156
+ throw toAIFriendlyError(err, opts.ref ?? opts.element ?? 'unknown');
157
+ }
158
+ }
159
+ export async function armDialogViaPlaywright(opts) {
160
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
161
+ ensurePageState(page);
162
+ const timeout = normalizeTimeoutMs(opts.timeoutMs, 30000, 120000);
163
+ return new Promise((resolve, reject) => {
164
+ const timer = setTimeout(() => {
165
+ page.removeListener('dialog', handler);
166
+ reject(new Error(`No dialog appeared within ${timeout}ms`));
167
+ }, timeout);
168
+ const handler = async (dialog) => {
169
+ clearTimeout(timer);
170
+ try {
171
+ if (opts.accept) {
172
+ await dialog.accept(opts.promptText);
173
+ }
174
+ else {
175
+ await dialog.dismiss();
176
+ }
177
+ resolve();
178
+ }
179
+ catch (err) {
180
+ reject(err);
181
+ }
182
+ };
183
+ page.once('dialog', handler);
184
+ });
185
+ }
186
+ export async function armFileUploadViaPlaywright(opts) {
187
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
188
+ ensurePageState(page);
189
+ const timeout = normalizeTimeoutMs(opts.timeoutMs, 30000, 120000);
190
+ return new Promise((resolve, reject) => {
191
+ const timer = setTimeout(() => {
192
+ page.removeListener('filechooser', handler);
193
+ reject(new Error(`No file chooser appeared within ${timeout}ms`));
194
+ }, timeout);
195
+ const handler = async (fc) => {
196
+ clearTimeout(timer);
197
+ try {
198
+ const paths = opts.paths ?? [];
199
+ if (paths.length > 0)
200
+ await assertSafeUploadPaths(paths);
201
+ await fc.setFiles(paths);
202
+ resolve();
203
+ }
204
+ catch (err) {
205
+ reject(err);
206
+ }
207
+ };
208
+ page.once('filechooser', handler);
209
+ });
210
+ }
@@ -0,0 +1,6 @@
1
+ export declare function pressKeyViaPlaywright(opts: {
2
+ cdpUrl: string;
3
+ targetId?: string;
4
+ key: string;
5
+ delayMs?: number;
6
+ }): Promise<void>;
@@ -0,0 +1,9 @@
1
+ import { getPageForTargetId, ensurePageState } from '../connection.js';
2
+ export async function pressKeyViaPlaywright(opts) {
3
+ const key = String(opts.key ?? '').trim();
4
+ if (!key)
5
+ throw new Error('key is required');
6
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
7
+ ensurePageState(page);
8
+ await page.keyboard.press(key, { delay: Math.max(0, Math.floor(opts.delayMs ?? 0)) });
9
+ }
@@ -0,0 +1,40 @@
1
+ import type { BrowserTab, SsrfPolicy } from '../types.js';
2
+ export declare function navigateViaPlaywright(opts: {
3
+ cdpUrl: string;
4
+ targetId?: string;
5
+ url: string;
6
+ timeoutMs?: number;
7
+ ssrfPolicy?: SsrfPolicy;
8
+ /** @deprecated Use ssrfPolicy: { dangerouslyAllowPrivateNetwork: true } instead */
9
+ allowInternal?: boolean;
10
+ }): Promise<{
11
+ url: string;
12
+ }>;
13
+ export declare function listPagesViaPlaywright(opts: {
14
+ cdpUrl: string;
15
+ }): Promise<BrowserTab[]>;
16
+ export declare function createPageViaPlaywright(opts: {
17
+ cdpUrl: string;
18
+ url?: string;
19
+ ssrfPolicy?: SsrfPolicy;
20
+ /** @deprecated Use ssrfPolicy: { dangerouslyAllowPrivateNetwork: true } instead */
21
+ allowInternal?: boolean;
22
+ }): Promise<BrowserTab>;
23
+ export declare function closePageViaPlaywright(opts: {
24
+ cdpUrl: string;
25
+ targetId?: string;
26
+ }): Promise<void>;
27
+ export declare function closePageByTargetIdViaPlaywright(opts: {
28
+ cdpUrl: string;
29
+ targetId: string;
30
+ }): Promise<void>;
31
+ export declare function focusPageByTargetIdViaPlaywright(opts: {
32
+ cdpUrl: string;
33
+ targetId: string;
34
+ }): Promise<void>;
35
+ export declare function resizeViewportViaPlaywright(opts: {
36
+ cdpUrl: string;
37
+ targetId?: string;
38
+ width: number;
39
+ height: number;
40
+ }): Promise<void>;
@@ -0,0 +1,92 @@
1
+ import { connectBrowser, getPageForTargetId, ensurePageState, pageTargetId, findPageByTargetId, getAllPages, normalizeTimeoutMs } from '../connection.js';
2
+ import { assertBrowserNavigationAllowed } from '../security.js';
3
+ export async function navigateViaPlaywright(opts) {
4
+ const url = String(opts.url ?? '').trim();
5
+ if (!url)
6
+ throw new Error('url is required');
7
+ const policy = opts.allowInternal ? { ...opts.ssrfPolicy, dangerouslyAllowPrivateNetwork: true } : opts.ssrfPolicy;
8
+ await assertBrowserNavigationAllowed({ url, ssrfPolicy: policy });
9
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
10
+ ensurePageState(page);
11
+ await page.goto(url, { timeout: normalizeTimeoutMs(opts.timeoutMs, 30000), waitUntil: 'domcontentloaded' });
12
+ return { url: page.url() };
13
+ }
14
+ export async function listPagesViaPlaywright(opts) {
15
+ const { browser } = await connectBrowser(opts.cdpUrl);
16
+ const pages = await getAllPages(browser);
17
+ const results = [];
18
+ for (const page of pages) {
19
+ const tid = await pageTargetId(page).catch(() => null);
20
+ if (tid)
21
+ results.push({
22
+ targetId: tid,
23
+ title: await page.title().catch(() => ''),
24
+ url: page.url(),
25
+ type: 'page',
26
+ });
27
+ }
28
+ return results;
29
+ }
30
+ export async function createPageViaPlaywright(opts) {
31
+ const targetUrl = (opts.url ?? '').trim() || 'about:blank';
32
+ if (targetUrl !== 'about:blank') {
33
+ const policy = opts.allowInternal ? { ...opts.ssrfPolicy, dangerouslyAllowPrivateNetwork: true } : opts.ssrfPolicy;
34
+ await assertBrowserNavigationAllowed({ url: targetUrl, ssrfPolicy: policy });
35
+ }
36
+ const { browser } = await connectBrowser(opts.cdpUrl);
37
+ const context = browser.contexts()[0] ?? await browser.newContext();
38
+ const page = await context.newPage();
39
+ ensurePageState(page);
40
+ if (targetUrl !== 'about:blank') {
41
+ await page.goto(targetUrl, { timeout: normalizeTimeoutMs(undefined, 30000), waitUntil: 'domcontentloaded' });
42
+ }
43
+ const tid = await pageTargetId(page).catch(() => null);
44
+ if (!tid)
45
+ throw new Error('Failed to get targetId for new page');
46
+ return {
47
+ targetId: tid,
48
+ title: await page.title().catch(() => ''),
49
+ url: page.url(),
50
+ type: 'page',
51
+ };
52
+ }
53
+ export async function closePageViaPlaywright(opts) {
54
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
55
+ await page.close();
56
+ }
57
+ export async function closePageByTargetIdViaPlaywright(opts) {
58
+ const { browser } = await connectBrowser(opts.cdpUrl);
59
+ const page = await findPageByTargetId(browser, opts.targetId, opts.cdpUrl);
60
+ if (!page)
61
+ throw new Error(`Tab not found (targetId: ${opts.targetId}). Use browser.tabs() to list open tabs.`);
62
+ await page.close();
63
+ }
64
+ export async function focusPageByTargetIdViaPlaywright(opts) {
65
+ const { browser } = await connectBrowser(opts.cdpUrl);
66
+ const page = await findPageByTargetId(browser, opts.targetId, opts.cdpUrl);
67
+ if (!page)
68
+ throw new Error(`Tab not found (targetId: ${opts.targetId}). Use browser.tabs() to list open tabs.`);
69
+ try {
70
+ await page.bringToFront();
71
+ }
72
+ catch (err) {
73
+ const session = await page.context().newCDPSession(page);
74
+ try {
75
+ await session.send('Page.bringToFront');
76
+ }
77
+ catch {
78
+ throw err;
79
+ }
80
+ finally {
81
+ await session.detach().catch(() => { });
82
+ }
83
+ }
84
+ }
85
+ export async function resizeViewportViaPlaywright(opts) {
86
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
87
+ ensurePageState(page);
88
+ await page.setViewportSize({
89
+ width: Math.max(1, Math.floor(opts.width)),
90
+ height: Math.max(1, Math.floor(opts.height)),
91
+ });
92
+ }
@@ -0,0 +1,12 @@
1
+ export declare function waitForViaPlaywright(opts: {
2
+ cdpUrl: string;
3
+ targetId?: string;
4
+ timeMs?: number;
5
+ text?: string;
6
+ textGone?: string;
7
+ selector?: string;
8
+ url?: string;
9
+ loadState?: 'load' | 'domcontentloaded' | 'networkidle';
10
+ fn?: string;
11
+ timeoutMs?: number;
12
+ }): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import { getPageForTargetId, ensurePageState, normalizeTimeoutMs } from '../connection.js';
2
+ export async function waitForViaPlaywright(opts) {
3
+ const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
4
+ ensurePageState(page);
5
+ const timeout = normalizeTimeoutMs(opts.timeoutMs, 20000);
6
+ if (typeof opts.timeMs === 'number' && Number.isFinite(opts.timeMs)) {
7
+ await page.waitForTimeout(Math.max(0, opts.timeMs));
8
+ }
9
+ if (opts.text) {
10
+ await page.getByText(opts.text).first().waitFor({ state: 'visible', timeout });
11
+ }
12
+ if (opts.textGone) {
13
+ await page.getByText(opts.textGone).first().waitFor({ state: 'hidden', timeout });
14
+ }
15
+ if (opts.selector) {
16
+ const selector = String(opts.selector).trim();
17
+ if (selector)
18
+ await page.locator(selector).first().waitFor({ state: 'visible', timeout });
19
+ }
20
+ if (opts.url) {
21
+ const url = String(opts.url).trim();
22
+ if (url)
23
+ await page.waitForURL(url, { timeout });
24
+ }
25
+ if (opts.loadState) {
26
+ await page.waitForLoadState(opts.loadState, { timeout });
27
+ }
28
+ if (opts.fn) {
29
+ const fn = String(opts.fn).trim();
30
+ if (fn)
31
+ await page.waitForFunction(fn, undefined, { timeout });
32
+ }
33
+ }