btcp-browser-agent 0.1.0
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/CLAUDE.md +230 -0
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/SKILL.md +143 -0
- package/SNAPSHOT_IMPROVEMENTS.md +302 -0
- package/USAGE.md +146 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/docs/browser-cli-design.md +500 -0
- package/examples/chrome-extension/CHANGELOG.md +210 -0
- package/examples/chrome-extension/DEBUG.md +231 -0
- package/examples/chrome-extension/ERROR_FIXED.md +147 -0
- package/examples/chrome-extension/QUICK_TEST.md +189 -0
- package/examples/chrome-extension/README.md +149 -0
- package/examples/chrome-extension/SESSION_ONLY_MODE.md +305 -0
- package/examples/chrome-extension/TEST_WITH_YOUR_TABS.md +97 -0
- package/examples/chrome-extension/build.js +43 -0
- package/examples/chrome-extension/manifest.json +37 -0
- package/examples/chrome-extension/package-lock.json +1063 -0
- package/examples/chrome-extension/package.json +21 -0
- package/examples/chrome-extension/popup.html +195 -0
- package/examples/chrome-extension/src/background.ts +12 -0
- package/examples/chrome-extension/src/content.ts +7 -0
- package/examples/chrome-extension/src/popup.ts +303 -0
- package/examples/chrome-extension/src/scenario-google-github.ts +389 -0
- package/examples/chrome-extension/test-page.html +127 -0
- package/examples/chrome-extension/tests/README.md +206 -0
- package/examples/chrome-extension/tests/scenario-google-to-github-star.ts +380 -0
- package/examples/chrome-extension/tsconfig.json +14 -0
- package/examples/snapshots/README.md +207 -0
- package/examples/snapshots/amazon-com-detail.html +9528 -0
- package/examples/snapshots/amazon-com-detail.snapshot.txt +997 -0
- package/examples/snapshots/convert-snapshots.ts +97 -0
- package/examples/snapshots/edition-cnn-com.html +13292 -0
- package/examples/snapshots/edition-cnn-com.snapshot.txt +562 -0
- package/examples/snapshots/github-com-microsoft-vscode.html +2916 -0
- package/examples/snapshots/github-com-microsoft-vscode.snapshot.txt +455 -0
- package/examples/snapshots/google-search.html +20012 -0
- package/examples/snapshots/google-search.snapshot.txt +195 -0
- package/examples/snapshots/metadata.json +86 -0
- package/examples/snapshots/npr-org-templates.html +2031 -0
- package/examples/snapshots/npr-org-templates.snapshot.txt +224 -0
- package/examples/snapshots/stackoverflow-com.html +5216 -0
- package/examples/snapshots/stackoverflow-com.snapshot.txt +2404 -0
- package/examples/snapshots/test-all-mode.html +46 -0
- package/examples/snapshots/test-all-mode.snapshot.txt +5 -0
- package/examples/snapshots/validate.test.ts +296 -0
- package/package.json +65 -0
- package/packages/cli/package.json +42 -0
- package/packages/cli/src/__tests__/cli.test.ts +434 -0
- package/packages/cli/src/__tests__/errors.test.ts +226 -0
- package/packages/cli/src/__tests__/executor.test.ts +275 -0
- package/packages/cli/src/__tests__/formatter.test.ts +260 -0
- package/packages/cli/src/__tests__/parser.test.ts +288 -0
- package/packages/cli/src/__tests__/suggestions.test.ts +255 -0
- package/packages/cli/src/commands/back.ts +22 -0
- package/packages/cli/src/commands/check.ts +33 -0
- package/packages/cli/src/commands/clear.ts +33 -0
- package/packages/cli/src/commands/click.ts +32 -0
- package/packages/cli/src/commands/closetab.ts +31 -0
- package/packages/cli/src/commands/eval.ts +41 -0
- package/packages/cli/src/commands/fill.ts +30 -0
- package/packages/cli/src/commands/focus.ts +33 -0
- package/packages/cli/src/commands/forward.ts +22 -0
- package/packages/cli/src/commands/goto.ts +34 -0
- package/packages/cli/src/commands/help.ts +162 -0
- package/packages/cli/src/commands/hover.ts +34 -0
- package/packages/cli/src/commands/index.ts +129 -0
- package/packages/cli/src/commands/newtab.ts +35 -0
- package/packages/cli/src/commands/press.ts +40 -0
- package/packages/cli/src/commands/reload.ts +25 -0
- package/packages/cli/src/commands/screenshot.ts +27 -0
- package/packages/cli/src/commands/scroll.ts +64 -0
- package/packages/cli/src/commands/select.ts +35 -0
- package/packages/cli/src/commands/snapshot.ts +21 -0
- package/packages/cli/src/commands/tab.ts +32 -0
- package/packages/cli/src/commands/tabs.ts +26 -0
- package/packages/cli/src/commands/text.ts +27 -0
- package/packages/cli/src/commands/title.ts +17 -0
- package/packages/cli/src/commands/type.ts +38 -0
- package/packages/cli/src/commands/uncheck.ts +33 -0
- package/packages/cli/src/commands/url.ts +17 -0
- package/packages/cli/src/commands/wait.ts +54 -0
- package/packages/cli/src/errors.ts +164 -0
- package/packages/cli/src/executor.ts +68 -0
- package/packages/cli/src/formatter.ts +215 -0
- package/packages/cli/src/index.ts +257 -0
- package/packages/cli/src/parser.ts +195 -0
- package/packages/cli/src/suggestions.ts +207 -0
- package/packages/cli/src/terminal/Terminal.ts +365 -0
- package/packages/cli/src/terminal/index.ts +5 -0
- package/packages/cli/src/types.ts +155 -0
- package/packages/cli/tsconfig.json +20 -0
- package/packages/core/package.json +35 -0
- package/packages/core/src/actions.ts +1210 -0
- package/packages/core/src/errors.ts +296 -0
- package/packages/core/src/index.test.ts +638 -0
- package/packages/core/src/index.ts +220 -0
- package/packages/core/src/ref-map.ts +107 -0
- package/packages/core/src/snapshot.ts +873 -0
- package/packages/core/src/types.ts +536 -0
- package/packages/core/tsconfig.json +23 -0
- package/packages/extension/README.md +129 -0
- package/packages/extension/package.json +43 -0
- package/packages/extension/src/background.ts +888 -0
- package/packages/extension/src/content.ts +172 -0
- package/packages/extension/src/index.ts +579 -0
- package/packages/extension/src/session-manager.ts +385 -0
- package/packages/extension/src/session-types.ts +144 -0
- package/packages/extension/src/types.ts +162 -0
- package/packages/extension/tsconfig.json +28 -0
- package/src/index.ts +64 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +26 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eval command - Execute JavaScript in page context
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { InvalidArgumentsError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
export const evalCommand: CommandHandler = {
|
|
9
|
+
name: 'eval',
|
|
10
|
+
description: 'Execute JavaScript in the page context',
|
|
11
|
+
usage: 'eval <code>',
|
|
12
|
+
examples: [
|
|
13
|
+
'eval document.title',
|
|
14
|
+
'eval "window.scrollTo(0, 0)"',
|
|
15
|
+
'eval "document.querySelectorAll(\'a\').length"',
|
|
16
|
+
],
|
|
17
|
+
|
|
18
|
+
async execute(client, args) {
|
|
19
|
+
if (args.length === 0) {
|
|
20
|
+
throw new InvalidArgumentsError('JavaScript code required', 'eval <code>');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const script = args.join(' ');
|
|
24
|
+
|
|
25
|
+
const response = await client.execute({
|
|
26
|
+
id: `cmd_${Date.now()}`,
|
|
27
|
+
action: 'evaluate',
|
|
28
|
+
script,
|
|
29
|
+
} as any);
|
|
30
|
+
|
|
31
|
+
if (response.success) {
|
|
32
|
+
const result = (response as any).data?.result;
|
|
33
|
+
return {
|
|
34
|
+
success: true,
|
|
35
|
+
data: result !== undefined ? String(result) : '(undefined)',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { success: false, error: response.error };
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fill command - Fill an input field
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { InvalidArgumentsError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
export const fillCommand: CommandHandler = {
|
|
9
|
+
name: 'fill',
|
|
10
|
+
description: 'Fill an input field (sets value directly)',
|
|
11
|
+
usage: 'fill <selector> <value>',
|
|
12
|
+
examples: ['fill @ref:1 "john@example.com"', 'fill #email test@test.com'],
|
|
13
|
+
|
|
14
|
+
async execute(client, args) {
|
|
15
|
+
if (args.length < 2) {
|
|
16
|
+
throw new InvalidArgumentsError('Selector and value required', 'fill <selector> <value>');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const selector = args[0];
|
|
20
|
+
const value = args.slice(1).join(' ');
|
|
21
|
+
|
|
22
|
+
const response = await client.fill(selector, value);
|
|
23
|
+
|
|
24
|
+
if (response.success) {
|
|
25
|
+
return { success: true, message: `Filled: ${selector}` };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return { success: false, error: response.error };
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* focus command - Focus an element
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { InvalidArgumentsError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
export const focusCommand: CommandHandler = {
|
|
9
|
+
name: 'focus',
|
|
10
|
+
description: 'Focus an element',
|
|
11
|
+
usage: 'focus <selector>',
|
|
12
|
+
examples: ['focus @ref:1', 'focus #input'],
|
|
13
|
+
|
|
14
|
+
async execute(client, args) {
|
|
15
|
+
if (args.length === 0) {
|
|
16
|
+
throw new InvalidArgumentsError('Selector required', 'focus <selector>');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const selector = args[0];
|
|
20
|
+
|
|
21
|
+
const response = await client.execute({
|
|
22
|
+
id: `cmd_${Date.now()}`,
|
|
23
|
+
action: 'focus',
|
|
24
|
+
selector,
|
|
25
|
+
} as any);
|
|
26
|
+
|
|
27
|
+
if (response.success) {
|
|
28
|
+
return { success: true, message: `Focused: ${selector}` };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { success: false, error: response.error };
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* forward command - Go forward in browser history
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
|
|
7
|
+
export const forwardCommand: CommandHandler = {
|
|
8
|
+
name: 'forward',
|
|
9
|
+
description: 'Go forward in browser history',
|
|
10
|
+
usage: 'forward',
|
|
11
|
+
examples: ['forward'],
|
|
12
|
+
|
|
13
|
+
async execute(client) {
|
|
14
|
+
const response = await client.forward();
|
|
15
|
+
|
|
16
|
+
if (response.success) {
|
|
17
|
+
return { success: true, message: 'Navigated forward' };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return { success: false, error: response.error };
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* goto command - Navigate to a URL
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { InvalidArgumentsError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
export const gotoCommand: CommandHandler = {
|
|
9
|
+
name: 'goto',
|
|
10
|
+
description: 'Navigate to a URL',
|
|
11
|
+
usage: 'goto <url>',
|
|
12
|
+
examples: ['goto https://example.com', 'goto github.com'],
|
|
13
|
+
|
|
14
|
+
async execute(client, args) {
|
|
15
|
+
if (args.length === 0) {
|
|
16
|
+
throw new InvalidArgumentsError('URL required', 'goto <url>');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let url = args[0];
|
|
20
|
+
|
|
21
|
+
// Add https:// if no protocol specified
|
|
22
|
+
if (!url.match(/^https?:\/\//i)) {
|
|
23
|
+
url = `https://${url}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const response = await client.navigate(url);
|
|
27
|
+
|
|
28
|
+
if (response.success) {
|
|
29
|
+
return { success: true, message: `Navigated to ${url}` };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { success: false, error: response.error };
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* help command - Show help information
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { formatCommandHelp } from '../formatter.js';
|
|
7
|
+
import { commands } from './index.js';
|
|
8
|
+
import { commandCategories, findSimilarCommands } from '../suggestions.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Format categorized help
|
|
12
|
+
*/
|
|
13
|
+
function formatCategorizedHelp(): string {
|
|
14
|
+
let output = 'BTCP Browser CLI - Available Commands\n';
|
|
15
|
+
output += '═'.repeat(40) + '\n\n';
|
|
16
|
+
|
|
17
|
+
for (const [, category] of Object.entries(commandCategories)) {
|
|
18
|
+
output += `▸ ${category.name}\n`;
|
|
19
|
+
output += ` ${category.description}\n\n`;
|
|
20
|
+
|
|
21
|
+
for (const cmdName of category.commands) {
|
|
22
|
+
const cmd = commands[cmdName];
|
|
23
|
+
if (cmd) {
|
|
24
|
+
const padding = ' '.repeat(Math.max(0, 12 - cmdName.length));
|
|
25
|
+
output += ` ${cmdName}${padding}${cmd.description}\n`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
output += '\n';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
output += '─'.repeat(40) + '\n';
|
|
32
|
+
output += 'Tips:\n';
|
|
33
|
+
output += ' • Type "help <command>" for detailed usage\n';
|
|
34
|
+
output += ' • Type "help category <name>" for category commands\n';
|
|
35
|
+
output += ' • Use @ref:N selectors from snapshot output\n';
|
|
36
|
+
output += ' • Comments start with # in multi-line input\n';
|
|
37
|
+
|
|
38
|
+
return output;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Format category-specific help
|
|
43
|
+
*/
|
|
44
|
+
function formatCategoryHelp(categoryKey: string): string | null {
|
|
45
|
+
const category = commandCategories[categoryKey as keyof typeof commandCategories];
|
|
46
|
+
if (!category) return null;
|
|
47
|
+
|
|
48
|
+
let output = `${category.name}\n`;
|
|
49
|
+
output += '─'.repeat(30) + '\n';
|
|
50
|
+
output += `${category.description}\n\n`;
|
|
51
|
+
output += 'Commands:\n\n';
|
|
52
|
+
|
|
53
|
+
for (const cmdName of category.commands) {
|
|
54
|
+
const cmd = commands[cmdName];
|
|
55
|
+
if (cmd) {
|
|
56
|
+
output += ` ${cmd.usage}\n`;
|
|
57
|
+
output += ` ${cmd.description}\n`;
|
|
58
|
+
if (cmd.examples && cmd.examples.length > 0) {
|
|
59
|
+
output += ` Example: ${cmd.examples[0]}\n`;
|
|
60
|
+
}
|
|
61
|
+
output += '\n';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return output;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Format quick reference
|
|
70
|
+
*/
|
|
71
|
+
function formatQuickRef(): string {
|
|
72
|
+
let output = 'Quick Reference\n';
|
|
73
|
+
output += '═'.repeat(40) + '\n\n';
|
|
74
|
+
|
|
75
|
+
output += 'Common Workflow:\n';
|
|
76
|
+
output += ' goto <url> Navigate to page\n';
|
|
77
|
+
output += ' snapshot See page elements\n';
|
|
78
|
+
output += ' click @ref:N Click element\n';
|
|
79
|
+
output += ' type @ref:N "..." Type into input\n';
|
|
80
|
+
output += ' fill @ref:N "..." Fill input field\n\n';
|
|
81
|
+
|
|
82
|
+
output += 'Selectors:\n';
|
|
83
|
+
output += ' @ref:5 Element ref from snapshot\n';
|
|
84
|
+
output += ' #id CSS ID selector\n';
|
|
85
|
+
output += ' .class CSS class selector\n';
|
|
86
|
+
output += ' button Tag name selector\n\n';
|
|
87
|
+
|
|
88
|
+
output += 'Multi-line:\n';
|
|
89
|
+
output += ' Commands can be separated by newlines\n';
|
|
90
|
+
output += ' Lines starting with # are comments\n';
|
|
91
|
+
output += ' Execution stops on first error\n';
|
|
92
|
+
|
|
93
|
+
return output;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const helpCommand: CommandHandler = {
|
|
97
|
+
name: 'help',
|
|
98
|
+
description: 'Show help information',
|
|
99
|
+
usage: 'help [command|category <name>|quick]',
|
|
100
|
+
examples: [
|
|
101
|
+
'help',
|
|
102
|
+
'help goto',
|
|
103
|
+
'help category navigation',
|
|
104
|
+
'help quick',
|
|
105
|
+
],
|
|
106
|
+
|
|
107
|
+
async execute(_client, args) {
|
|
108
|
+
if (args.length === 0) {
|
|
109
|
+
return { success: true, data: formatCategorizedHelp() };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const first = args[0].toLowerCase();
|
|
113
|
+
|
|
114
|
+
// Quick reference
|
|
115
|
+
if (first === 'quick' || first === 'ref') {
|
|
116
|
+
return { success: true, data: formatQuickRef() };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Category help
|
|
120
|
+
if (first === 'category' || first === 'cat') {
|
|
121
|
+
if (args.length < 2) {
|
|
122
|
+
const categories = Object.keys(commandCategories).join(', ');
|
|
123
|
+
return {
|
|
124
|
+
success: true,
|
|
125
|
+
data: `Available categories: ${categories}\n\nUsage: help category <name>`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const categoryHelp = formatCategoryHelp(args[1].toLowerCase());
|
|
129
|
+
if (categoryHelp) {
|
|
130
|
+
return { success: true, data: categoryHelp };
|
|
131
|
+
}
|
|
132
|
+
const categories = Object.keys(commandCategories).join(', ');
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
error: `Unknown category: ${args[1]}\n\nAvailable: ${categories}`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Command help
|
|
140
|
+
const cmdName = first;
|
|
141
|
+
const cmd = commands[cmdName];
|
|
142
|
+
|
|
143
|
+
if (cmd) {
|
|
144
|
+
return { success: true, data: formatCommandHelp(cmd) };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Suggest similar commands
|
|
148
|
+
const similar = findSimilarCommands(cmdName);
|
|
149
|
+
if (similar.length > 0) {
|
|
150
|
+
const suggestions = similar.map((s) => ` ${s}`).join('\n');
|
|
151
|
+
return {
|
|
152
|
+
success: false,
|
|
153
|
+
error: `Unknown command: ${cmdName}\n\nDid you mean:\n${suggestions}\n\nType "help" for all commands`,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
error: `Unknown command: ${cmdName}\n\nType "help" to see available commands`,
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hover command - Hover over an element
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler, CommandClient } from '../types.js';
|
|
6
|
+
import { InvalidArgumentsError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
export const hoverCommand: CommandHandler = {
|
|
9
|
+
name: 'hover',
|
|
10
|
+
description: 'Hover over an element',
|
|
11
|
+
usage: 'hover <selector>',
|
|
12
|
+
examples: ['hover @ref:5', 'hover .menu-item'],
|
|
13
|
+
|
|
14
|
+
async execute(client, args) {
|
|
15
|
+
if (args.length === 0) {
|
|
16
|
+
throw new InvalidArgumentsError('Selector required', 'hover <selector>');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const selector = args[0];
|
|
20
|
+
|
|
21
|
+
// Use execute for hover since it may not be in the simplified client interface
|
|
22
|
+
const response = await client.execute({
|
|
23
|
+
id: `cmd_${Date.now()}`,
|
|
24
|
+
action: 'hover',
|
|
25
|
+
selector,
|
|
26
|
+
} as any);
|
|
27
|
+
|
|
28
|
+
if (response.success) {
|
|
29
|
+
return { success: true, message: `Hovered: ${selector}` };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { success: false, error: response.error };
|
|
33
|
+
},
|
|
34
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command registry - exports all available commands
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
|
|
7
|
+
// Navigation
|
|
8
|
+
import { gotoCommand } from './goto.js';
|
|
9
|
+
import { backCommand } from './back.js';
|
|
10
|
+
import { forwardCommand } from './forward.js';
|
|
11
|
+
import { reloadCommand } from './reload.js';
|
|
12
|
+
import { urlCommand } from './url.js';
|
|
13
|
+
import { titleCommand } from './title.js';
|
|
14
|
+
|
|
15
|
+
// Snapshot & Screenshot
|
|
16
|
+
import { snapshotCommand } from './snapshot.js';
|
|
17
|
+
import { screenshotCommand } from './screenshot.js';
|
|
18
|
+
|
|
19
|
+
// DOM Interaction
|
|
20
|
+
import { clickCommand } from './click.js';
|
|
21
|
+
import { typeCommand } from './type.js';
|
|
22
|
+
import { fillCommand } from './fill.js';
|
|
23
|
+
import { clearCommand } from './clear.js';
|
|
24
|
+
import { hoverCommand } from './hover.js';
|
|
25
|
+
import { scrollCommand } from './scroll.js';
|
|
26
|
+
import { pressCommand } from './press.js';
|
|
27
|
+
import { focusCommand } from './focus.js';
|
|
28
|
+
import { checkCommand } from './check.js';
|
|
29
|
+
import { uncheckCommand } from './uncheck.js';
|
|
30
|
+
import { selectCommand } from './select.js';
|
|
31
|
+
|
|
32
|
+
// Tab Management
|
|
33
|
+
import { tabsCommand } from './tabs.js';
|
|
34
|
+
import { tabCommand } from './tab.js';
|
|
35
|
+
import { newtabCommand } from './newtab.js';
|
|
36
|
+
import { closetabCommand } from './closetab.js';
|
|
37
|
+
|
|
38
|
+
// Utility
|
|
39
|
+
import { waitCommand } from './wait.js';
|
|
40
|
+
import { evalCommand } from './eval.js';
|
|
41
|
+
import { textCommand } from './text.js';
|
|
42
|
+
import { helpCommand } from './help.js';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Command registry - map of command name to handler
|
|
46
|
+
*/
|
|
47
|
+
export const commands: Record<string, CommandHandler> = {
|
|
48
|
+
// Navigation
|
|
49
|
+
goto: gotoCommand,
|
|
50
|
+
back: backCommand,
|
|
51
|
+
forward: forwardCommand,
|
|
52
|
+
reload: reloadCommand,
|
|
53
|
+
url: urlCommand,
|
|
54
|
+
title: titleCommand,
|
|
55
|
+
|
|
56
|
+
// Snapshot & Screenshot
|
|
57
|
+
snapshot: snapshotCommand,
|
|
58
|
+
screenshot: screenshotCommand,
|
|
59
|
+
|
|
60
|
+
// DOM Interaction
|
|
61
|
+
click: clickCommand,
|
|
62
|
+
type: typeCommand,
|
|
63
|
+
fill: fillCommand,
|
|
64
|
+
clear: clearCommand,
|
|
65
|
+
hover: hoverCommand,
|
|
66
|
+
scroll: scrollCommand,
|
|
67
|
+
press: pressCommand,
|
|
68
|
+
focus: focusCommand,
|
|
69
|
+
check: checkCommand,
|
|
70
|
+
uncheck: uncheckCommand,
|
|
71
|
+
select: selectCommand,
|
|
72
|
+
|
|
73
|
+
// Tab Management
|
|
74
|
+
tabs: tabsCommand,
|
|
75
|
+
tab: tabCommand,
|
|
76
|
+
newtab: newtabCommand,
|
|
77
|
+
closetab: closetabCommand,
|
|
78
|
+
|
|
79
|
+
// Utility
|
|
80
|
+
wait: waitCommand,
|
|
81
|
+
eval: evalCommand,
|
|
82
|
+
text: textCommand,
|
|
83
|
+
help: helpCommand,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get a command handler by name
|
|
88
|
+
*/
|
|
89
|
+
export function getCommand(name: string): CommandHandler | undefined {
|
|
90
|
+
return commands[name.toLowerCase()];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get all command handlers
|
|
95
|
+
*/
|
|
96
|
+
export function getAllCommands(): CommandHandler[] {
|
|
97
|
+
return Object.values(commands);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Re-export individual commands for direct imports
|
|
101
|
+
export {
|
|
102
|
+
gotoCommand,
|
|
103
|
+
backCommand,
|
|
104
|
+
forwardCommand,
|
|
105
|
+
reloadCommand,
|
|
106
|
+
urlCommand,
|
|
107
|
+
titleCommand,
|
|
108
|
+
snapshotCommand,
|
|
109
|
+
screenshotCommand,
|
|
110
|
+
clickCommand,
|
|
111
|
+
typeCommand,
|
|
112
|
+
fillCommand,
|
|
113
|
+
clearCommand,
|
|
114
|
+
hoverCommand,
|
|
115
|
+
scrollCommand,
|
|
116
|
+
pressCommand,
|
|
117
|
+
focusCommand,
|
|
118
|
+
checkCommand,
|
|
119
|
+
uncheckCommand,
|
|
120
|
+
selectCommand,
|
|
121
|
+
tabsCommand,
|
|
122
|
+
tabCommand,
|
|
123
|
+
newtabCommand,
|
|
124
|
+
closetabCommand,
|
|
125
|
+
waitCommand,
|
|
126
|
+
evalCommand,
|
|
127
|
+
textCommand,
|
|
128
|
+
helpCommand,
|
|
129
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* newtab command - Open a new tab
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { getFlagBool } from '../parser.js';
|
|
7
|
+
|
|
8
|
+
export const newtabCommand: CommandHandler = {
|
|
9
|
+
name: 'newtab',
|
|
10
|
+
description: 'Open a new tab',
|
|
11
|
+
usage: 'newtab [url] [--background]',
|
|
12
|
+
examples: ['newtab', 'newtab https://example.com', 'newtab github.com --background'],
|
|
13
|
+
|
|
14
|
+
async execute(client, args, flags) {
|
|
15
|
+
let url = args[0];
|
|
16
|
+
|
|
17
|
+
// Add https:// if URL provided without protocol
|
|
18
|
+
if (url && !url.match(/^https?:\/\//i)) {
|
|
19
|
+
url = `https://${url}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const background = getFlagBool(flags, 'background');
|
|
23
|
+
|
|
24
|
+
const result = await client.tabNew({
|
|
25
|
+
url,
|
|
26
|
+
active: !background,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
success: true,
|
|
31
|
+
message: `Opened new tab [${result.tabId}]${url ? `: ${url}` : ''}`,
|
|
32
|
+
data: result,
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* press command - Press a keyboard key
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { InvalidArgumentsError } from '../errors.js';
|
|
7
|
+
|
|
8
|
+
export const pressCommand: CommandHandler = {
|
|
9
|
+
name: 'press',
|
|
10
|
+
description: 'Press a keyboard key',
|
|
11
|
+
usage: 'press <key> [selector]',
|
|
12
|
+
examples: [
|
|
13
|
+
'press Enter',
|
|
14
|
+
'press Tab',
|
|
15
|
+
'press Escape',
|
|
16
|
+
'press Enter @ref:1',
|
|
17
|
+
],
|
|
18
|
+
|
|
19
|
+
async execute(client, args) {
|
|
20
|
+
if (args.length === 0) {
|
|
21
|
+
throw new InvalidArgumentsError('Key required', 'press <key> [selector]');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const key = args[0];
|
|
25
|
+
const selector = args[1];
|
|
26
|
+
|
|
27
|
+
const response = await client.execute({
|
|
28
|
+
id: `cmd_${Date.now()}`,
|
|
29
|
+
action: 'press',
|
|
30
|
+
key,
|
|
31
|
+
selector,
|
|
32
|
+
} as any);
|
|
33
|
+
|
|
34
|
+
if (response.success) {
|
|
35
|
+
return { success: true, message: `Pressed: ${key}` };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { success: false, error: response.error };
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* reload command - Reload the current page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { getFlagBool } from '../parser.js';
|
|
7
|
+
|
|
8
|
+
export const reloadCommand: CommandHandler = {
|
|
9
|
+
name: 'reload',
|
|
10
|
+
description: 'Reload the current page',
|
|
11
|
+
usage: 'reload [--hard]',
|
|
12
|
+
examples: ['reload', 'reload --hard'],
|
|
13
|
+
|
|
14
|
+
async execute(client, _args, flags) {
|
|
15
|
+
const bypassCache = getFlagBool(flags, 'hard');
|
|
16
|
+
const response = await client.reload({ bypassCache });
|
|
17
|
+
|
|
18
|
+
if (response.success) {
|
|
19
|
+
const msg = bypassCache ? 'Hard reloaded page' : 'Reloaded page';
|
|
20
|
+
return { success: true, message: msg };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return { success: false, error: response.error };
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* screenshot command - Capture visible tab
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { getFlagString, getFlagNumber } from '../parser.js';
|
|
7
|
+
import { formatScreenshot } from '../formatter.js';
|
|
8
|
+
|
|
9
|
+
export const screenshotCommand: CommandHandler = {
|
|
10
|
+
name: 'screenshot',
|
|
11
|
+
description: 'Capture screenshot of visible tab',
|
|
12
|
+
usage: 'screenshot [--format png|jpeg] [--quality <0-100>]',
|
|
13
|
+
examples: ['screenshot', 'screenshot --format jpeg --quality 80'],
|
|
14
|
+
|
|
15
|
+
async execute(client, _args, flags) {
|
|
16
|
+
const formatFlag = getFlagString(flags, 'format');
|
|
17
|
+
const format = formatFlag === 'jpeg' ? 'jpeg' : 'png';
|
|
18
|
+
const quality = getFlagNumber(flags, 'quality');
|
|
19
|
+
|
|
20
|
+
const dataUrl = await client.screenshot({ format, quality });
|
|
21
|
+
return {
|
|
22
|
+
success: true,
|
|
23
|
+
message: formatScreenshot(dataUrl),
|
|
24
|
+
data: dataUrl,
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scroll command - Scroll the page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CommandHandler } from '../types.js';
|
|
6
|
+
import { getFlagNumber } from '../parser.js';
|
|
7
|
+
|
|
8
|
+
export const scrollCommand: CommandHandler = {
|
|
9
|
+
name: 'scroll',
|
|
10
|
+
description: 'Scroll the page',
|
|
11
|
+
usage: 'scroll <direction> [amount] | scroll <selector>',
|
|
12
|
+
examples: [
|
|
13
|
+
'scroll down',
|
|
14
|
+
'scroll up 500',
|
|
15
|
+
'scroll down 200',
|
|
16
|
+
'scroll @ref:5',
|
|
17
|
+
],
|
|
18
|
+
|
|
19
|
+
async execute(client, args, flags) {
|
|
20
|
+
if (args.length === 0) {
|
|
21
|
+
// Default to scroll down
|
|
22
|
+
const response = await client.execute({
|
|
23
|
+
id: `cmd_${Date.now()}`,
|
|
24
|
+
action: 'scroll',
|
|
25
|
+
direction: 'down',
|
|
26
|
+
amount: 300,
|
|
27
|
+
} as any);
|
|
28
|
+
return response.success
|
|
29
|
+
? { success: true, message: 'Scrolled down' }
|
|
30
|
+
: { success: false, error: response.error };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const first = args[0].toLowerCase();
|
|
34
|
+
|
|
35
|
+
// Check if it's a direction
|
|
36
|
+
if (['up', 'down', 'left', 'right'].includes(first)) {
|
|
37
|
+
const direction = first as 'up' | 'down' | 'left' | 'right';
|
|
38
|
+
const amount = args[1] ? parseInt(args[1], 10) : 300;
|
|
39
|
+
|
|
40
|
+
const response = await client.execute({
|
|
41
|
+
id: `cmd_${Date.now()}`,
|
|
42
|
+
action: 'scroll',
|
|
43
|
+
direction,
|
|
44
|
+
amount: isNaN(amount) ? 300 : amount,
|
|
45
|
+
} as any);
|
|
46
|
+
|
|
47
|
+
return response.success
|
|
48
|
+
? { success: true, message: `Scrolled ${direction}` }
|
|
49
|
+
: { success: false, error: response.error };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// It's a selector - scroll element into view
|
|
53
|
+
const selector = args[0];
|
|
54
|
+
const response = await client.execute({
|
|
55
|
+
id: `cmd_${Date.now()}`,
|
|
56
|
+
action: 'scrollIntoView',
|
|
57
|
+
selector,
|
|
58
|
+
} as any);
|
|
59
|
+
|
|
60
|
+
return response.success
|
|
61
|
+
? { success: true, message: `Scrolled to: ${selector}` }
|
|
62
|
+
: { success: false, error: response.error };
|
|
63
|
+
},
|
|
64
|
+
};
|