codebot-ai 1.0.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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +247 -0
  3. package/bin/codebot +5 -0
  4. package/dist/agent.d.ts +31 -0
  5. package/dist/agent.js +256 -0
  6. package/dist/banner.d.ts +19 -0
  7. package/dist/banner.js +148 -0
  8. package/dist/browser/cdp.d.ts +29 -0
  9. package/dist/browser/cdp.js +292 -0
  10. package/dist/cli.d.ts +2 -0
  11. package/dist/cli.js +518 -0
  12. package/dist/context/manager.d.ts +27 -0
  13. package/dist/context/manager.js +139 -0
  14. package/dist/context/repo-map.d.ts +5 -0
  15. package/dist/context/repo-map.js +100 -0
  16. package/dist/history.d.ts +27 -0
  17. package/dist/history.js +146 -0
  18. package/dist/index.d.ts +13 -0
  19. package/dist/index.js +42 -0
  20. package/dist/memory.d.ts +39 -0
  21. package/dist/memory.js +168 -0
  22. package/dist/parser.d.ts +8 -0
  23. package/dist/parser.js +79 -0
  24. package/dist/providers/anthropic.d.ts +9 -0
  25. package/dist/providers/anthropic.js +288 -0
  26. package/dist/providers/index.d.ts +5 -0
  27. package/dist/providers/index.js +13 -0
  28. package/dist/providers/openai.d.ts +11 -0
  29. package/dist/providers/openai.js +173 -0
  30. package/dist/providers/registry.d.ts +15 -0
  31. package/dist/providers/registry.js +115 -0
  32. package/dist/setup.d.ts +17 -0
  33. package/dist/setup.js +243 -0
  34. package/dist/tools/browser.d.ts +43 -0
  35. package/dist/tools/browser.js +329 -0
  36. package/dist/tools/edit.d.ts +26 -0
  37. package/dist/tools/edit.js +73 -0
  38. package/dist/tools/execute.d.ts +26 -0
  39. package/dist/tools/execute.js +52 -0
  40. package/dist/tools/glob.d.ts +24 -0
  41. package/dist/tools/glob.js +102 -0
  42. package/dist/tools/grep.d.ts +29 -0
  43. package/dist/tools/grep.js +125 -0
  44. package/dist/tools/index.d.ts +10 -0
  45. package/dist/tools/index.js +49 -0
  46. package/dist/tools/memory.d.ts +36 -0
  47. package/dist/tools/memory.js +114 -0
  48. package/dist/tools/read.d.ts +26 -0
  49. package/dist/tools/read.js +75 -0
  50. package/dist/tools/think.d.ts +18 -0
  51. package/dist/tools/think.js +20 -0
  52. package/dist/tools/web-fetch.d.ts +36 -0
  53. package/dist/tools/web-fetch.js +83 -0
  54. package/dist/tools/write.d.ts +22 -0
  55. package/dist/tools/write.js +65 -0
  56. package/dist/types.d.ts +82 -0
  57. package/dist/types.js +3 -0
  58. package/package.json +57 -0
package/dist/setup.js ADDED
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadConfig = loadConfig;
37
+ exports.saveConfig = saveConfig;
38
+ exports.isFirstRun = isFirstRun;
39
+ exports.runSetup = runSetup;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const os = __importStar(require("os"));
43
+ const readline = __importStar(require("readline"));
44
+ const registry_1 = require("./providers/registry");
45
+ const CONFIG_DIR = path.join(os.homedir(), '.codebot');
46
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
47
+ /** Load saved config from ~/.codebot/config.json */
48
+ function loadConfig() {
49
+ try {
50
+ if (fs.existsSync(CONFIG_FILE)) {
51
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
52
+ }
53
+ }
54
+ catch {
55
+ // corrupt config, ignore
56
+ }
57
+ return {};
58
+ }
59
+ /** Save config to ~/.codebot/config.json */
60
+ function saveConfig(config) {
61
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
62
+ // Never persist API keys to disk — use env vars
63
+ const safe = { ...config };
64
+ delete safe.apiKey;
65
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(safe, null, 2) + '\n');
66
+ }
67
+ /** Check if this is the first run (no config, no env keys) */
68
+ function isFirstRun() {
69
+ if (fs.existsSync(CONFIG_FILE))
70
+ return false;
71
+ // Check if any provider API keys are set
72
+ const envKeys = [
73
+ 'ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GEMINI_API_KEY',
74
+ 'DEEPSEEK_API_KEY', 'GROQ_API_KEY', 'MISTRAL_API_KEY',
75
+ 'XAI_API_KEY', 'CODEBOT_API_KEY',
76
+ ];
77
+ for (const key of envKeys) {
78
+ if (process.env[key])
79
+ return false;
80
+ }
81
+ return true;
82
+ }
83
+ /** Detect what local LLM servers are running */
84
+ async function detectLocalServers() {
85
+ const servers = [];
86
+ const candidates = [
87
+ { url: 'http://localhost:11434', name: 'Ollama' },
88
+ { url: 'http://localhost:1234', name: 'LM Studio' },
89
+ { url: 'http://localhost:8000', name: 'vLLM' },
90
+ ];
91
+ for (const { url, name } of candidates) {
92
+ try {
93
+ const res = await fetch(`${url}/v1/models`, {
94
+ signal: AbortSignal.timeout(2000),
95
+ });
96
+ if (res.ok) {
97
+ const data = await res.json();
98
+ const models = (data.data || []).map(m => m.id);
99
+ servers.push({ name, url, models });
100
+ }
101
+ }
102
+ catch {
103
+ // not running
104
+ }
105
+ }
106
+ return servers;
107
+ }
108
+ /** Detect which cloud API keys are available */
109
+ function detectApiKeys() {
110
+ return Object.entries(registry_1.PROVIDER_DEFAULTS).map(([provider, defaults]) => ({
111
+ provider,
112
+ envVar: defaults.envKey,
113
+ set: !!process.env[defaults.envKey],
114
+ }));
115
+ }
116
+ const C = {
117
+ reset: '\x1b[0m',
118
+ bold: '\x1b[1m',
119
+ dim: '\x1b[2m',
120
+ red: '\x1b[31m',
121
+ green: '\x1b[32m',
122
+ yellow: '\x1b[33m',
123
+ cyan: '\x1b[36m',
124
+ };
125
+ function fmt(text, style) {
126
+ return `${C[style]}${text}${C.reset}`;
127
+ }
128
+ function ask(rl, question) {
129
+ return new Promise(resolve => {
130
+ rl.question(question, answer => resolve(answer.trim()));
131
+ });
132
+ }
133
+ /** Interactive setup wizard */
134
+ async function runSetup() {
135
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
136
+ console.log(fmt('\n⚡ CodeBot AI — Setup', 'bold'));
137
+ console.log(fmt(' Let\'s get you configured.\n', 'dim'));
138
+ // Step 1: Detect local servers
139
+ console.log(fmt('Scanning for local LLM servers...', 'dim'));
140
+ const localServers = await detectLocalServers();
141
+ // Step 2: Detect API keys
142
+ const apiKeys = detectApiKeys();
143
+ const availableKeys = apiKeys.filter(k => k.set);
144
+ // Show what was found
145
+ if (localServers.length > 0) {
146
+ for (const server of localServers) {
147
+ console.log(fmt(` ✓ ${server.name} detected (${server.models.length} models)`, 'green'));
148
+ }
149
+ }
150
+ else {
151
+ console.log(fmt(' No local LLM servers detected.', 'dim'));
152
+ }
153
+ if (availableKeys.length > 0) {
154
+ for (const key of availableKeys) {
155
+ console.log(fmt(` ✓ ${key.provider} API key found (${key.envVar})`, 'green'));
156
+ }
157
+ }
158
+ const missingKeys = apiKeys.filter(k => !k.set);
159
+ if (missingKeys.length > 0 && localServers.length === 0) {
160
+ console.log(fmt('\n No API keys found. Set one to use cloud models:', 'yellow'));
161
+ for (const key of missingKeys) {
162
+ console.log(fmt(` export ${key.envVar}="your-key-here"`, 'dim'));
163
+ }
164
+ }
165
+ // Step 3: Choose provider
166
+ console.log(fmt('\nChoose your setup:', 'bold'));
167
+ const options = [];
168
+ let idx = 1;
169
+ // Local options first
170
+ for (const server of localServers) {
171
+ const defaultModel = server.models[0] || 'qwen2.5-coder:32b';
172
+ options.push({ label: `${server.name} (local, free)`, provider: 'openai', model: defaultModel, baseUrl: server.url });
173
+ console.log(` ${fmt(`${idx}`, 'cyan')} ${server.name} — ${defaultModel} ${fmt('(local, free, private)', 'green')}`);
174
+ idx++;
175
+ }
176
+ // Cloud options
177
+ for (const key of availableKeys) {
178
+ const models = Object.entries(registry_1.MODEL_REGISTRY)
179
+ .filter(([, info]) => info.provider === key.provider)
180
+ .map(([name]) => name);
181
+ const defaultModel = models[0] || key.provider;
182
+ const defaults = registry_1.PROVIDER_DEFAULTS[key.provider];
183
+ options.push({ label: key.provider, provider: key.provider, model: defaultModel, baseUrl: defaults.baseUrl });
184
+ console.log(` ${fmt(`${idx}`, 'cyan')} ${key.provider} — ${defaultModel} ${fmt('(cloud)', 'dim')}`);
185
+ idx++;
186
+ }
187
+ if (options.length === 0) {
188
+ console.log(fmt('\n No providers available. Either:', 'yellow'));
189
+ console.log(fmt(' 1. Install Ollama: https://ollama.ai', 'dim'));
190
+ console.log(fmt(' 2. Set an API key: export ANTHROPIC_API_KEY="..."', 'dim'));
191
+ rl.close();
192
+ return {};
193
+ }
194
+ const choice = await ask(rl, fmt(`\nSelect [1-${options.length}]: `, 'cyan'));
195
+ const selected = options[parseInt(choice, 10) - 1] || options[0];
196
+ // Step 4: Show available models for chosen provider
197
+ const providerModels = Object.entries(registry_1.MODEL_REGISTRY)
198
+ .filter(([, info]) => {
199
+ if (selected.provider === 'openai' && !info.provider)
200
+ return true; // local models
201
+ return info.provider === selected.provider;
202
+ })
203
+ .map(([name]) => name);
204
+ if (providerModels.length > 1) {
205
+ console.log(fmt(`\nAvailable models for ${selected.label}:`, 'bold'));
206
+ providerModels.slice(0, 10).forEach((m, i) => {
207
+ const marker = m === selected.model ? fmt(' (default)', 'green') : '';
208
+ console.log(` ${fmt(`${i + 1}`, 'cyan')} ${m}${marker}`);
209
+ });
210
+ const modelChoice = await ask(rl, fmt(`\nModel [Enter for ${selected.model}]: `, 'cyan'));
211
+ if (modelChoice) {
212
+ const modelIdx = parseInt(modelChoice, 10) - 1;
213
+ if (providerModels[modelIdx]) {
214
+ selected.model = providerModels[modelIdx];
215
+ }
216
+ else if (modelChoice.length > 2) {
217
+ // Treat as model name
218
+ selected.model = modelChoice;
219
+ }
220
+ }
221
+ }
222
+ // Step 5: Auto mode?
223
+ const autoChoice = await ask(rl, fmt('\nEnable autonomous mode? (skip permission prompts) [y/N]: ', 'cyan'));
224
+ const autoApprove = autoChoice.toLowerCase().startsWith('y');
225
+ rl.close();
226
+ // Save config
227
+ const config = {
228
+ model: selected.model,
229
+ provider: selected.provider,
230
+ baseUrl: selected.baseUrl,
231
+ autoApprove,
232
+ };
233
+ saveConfig(config);
234
+ console.log(fmt('\n✓ Config saved to ~/.codebot/config.json', 'green'));
235
+ console.log(fmt(` Model: ${config.model}`, 'dim'));
236
+ console.log(fmt(` Provider: ${config.provider}`, 'dim'));
237
+ if (autoApprove) {
238
+ console.log(fmt(` Mode: AUTONOMOUS`, 'yellow'));
239
+ }
240
+ console.log(fmt(`\nRun ${fmt('codebot', 'bold')} to start. Run ${fmt('codebot --setup', 'bold')} to reconfigure.\n`, 'dim'));
241
+ return config;
242
+ }
243
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1,43 @@
1
+ import { Tool } from '../types';
2
+ export declare class BrowserTool implements Tool {
3
+ name: string;
4
+ description: string;
5
+ permission: Tool['permission'];
6
+ parameters: {
7
+ type: string;
8
+ properties: {
9
+ action: {
10
+ type: string;
11
+ description: string;
12
+ enum: string[];
13
+ };
14
+ url: {
15
+ type: string;
16
+ description: string;
17
+ };
18
+ selector: {
19
+ type: string;
20
+ description: string;
21
+ };
22
+ text: {
23
+ type: string;
24
+ description: string;
25
+ };
26
+ expression: {
27
+ type: string;
28
+ description: string;
29
+ };
30
+ };
31
+ required: string[];
32
+ };
33
+ execute(args: Record<string, unknown>): Promise<string>;
34
+ private navigate;
35
+ private getContent;
36
+ private screenshot;
37
+ private click;
38
+ private typeText;
39
+ private evaluate;
40
+ private listTabs;
41
+ private closeBrowser;
42
+ }
43
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1,329 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BrowserTool = void 0;
4
+ const cdp_1 = require("../browser/cdp");
5
+ const child_process_1 = require("child_process");
6
+ // Shared browser instance across tool calls
7
+ let client = null;
8
+ let debugPort = 9222;
9
+ async function ensureConnected() {
10
+ if (client?.isConnected())
11
+ return client;
12
+ // Try connecting to existing Chrome
13
+ try {
14
+ const wsUrl = await (0, cdp_1.getDebuggerUrl)(debugPort);
15
+ client = new cdp_1.CDPClient();
16
+ await client.connect(wsUrl);
17
+ // Get or create a page target
18
+ const targets = await (0, cdp_1.getTargets)(debugPort);
19
+ const page = targets.find(t => t.url !== 'about:blank' && !t.url.startsWith('devtools://'))
20
+ || targets[0];
21
+ if (page?.webSocketDebuggerUrl) {
22
+ client.close();
23
+ client = new cdp_1.CDPClient();
24
+ await client.connect(page.webSocketDebuggerUrl);
25
+ }
26
+ await client.send('Page.enable');
27
+ await client.send('Runtime.enable');
28
+ return client;
29
+ }
30
+ catch {
31
+ // Chrome not running with debugging — try to launch it
32
+ }
33
+ // Launch Chrome with debugging
34
+ const chromePaths = process.platform === 'win32'
35
+ ? [
36
+ 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
37
+ 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
38
+ 'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe',
39
+ ]
40
+ : process.platform === 'linux'
41
+ ? [
42
+ 'google-chrome',
43
+ 'google-chrome-stable',
44
+ 'chromium',
45
+ 'chromium-browser',
46
+ '/usr/bin/google-chrome',
47
+ '/usr/bin/chromium',
48
+ '/snap/bin/chromium',
49
+ ]
50
+ : [
51
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
52
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
53
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
54
+ 'google-chrome',
55
+ 'chromium',
56
+ ];
57
+ let launched = false;
58
+ for (const chromePath of chromePaths) {
59
+ try {
60
+ (0, child_process_1.execSync)(`"${chromePath}" --remote-debugging-port=${debugPort} --no-first-run --no-default-browser-check about:blank &`, { stdio: 'ignore', timeout: 5000 });
61
+ launched = true;
62
+ break;
63
+ }
64
+ catch {
65
+ continue;
66
+ }
67
+ }
68
+ if (!launched) {
69
+ throw new Error('Could not launch Chrome. Start Chrome manually with:\n' +
70
+ ` chrome --remote-debugging-port=${debugPort}\n` +
71
+ 'Or on macOS:\n' +
72
+ ` /Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --remote-debugging-port=${debugPort}`);
73
+ }
74
+ // Wait for Chrome to start
75
+ for (let i = 0; i < 10; i++) {
76
+ try {
77
+ await new Promise(r => setTimeout(r, 500));
78
+ const wsUrl = await (0, cdp_1.getDebuggerUrl)(debugPort);
79
+ client = new cdp_1.CDPClient();
80
+ await client.connect(wsUrl);
81
+ const targets = await (0, cdp_1.getTargets)(debugPort);
82
+ const page = targets[0];
83
+ if (page?.webSocketDebuggerUrl) {
84
+ client.close();
85
+ client = new cdp_1.CDPClient();
86
+ await client.connect(page.webSocketDebuggerUrl);
87
+ }
88
+ await client.send('Page.enable');
89
+ await client.send('Runtime.enable');
90
+ return client;
91
+ }
92
+ catch {
93
+ continue;
94
+ }
95
+ }
96
+ throw new Error('Chrome launched but could not connect via CDP');
97
+ }
98
+ class BrowserTool {
99
+ name = 'browser';
100
+ description = 'Control a web browser. Navigate to URLs, read page content, click elements, type text, run JavaScript, take screenshots. Use for web browsing, social media, testing, and automation.';
101
+ permission = 'prompt';
102
+ parameters = {
103
+ type: 'object',
104
+ properties: {
105
+ action: {
106
+ type: 'string',
107
+ description: 'Action to perform',
108
+ enum: ['navigate', 'content', 'screenshot', 'click', 'type', 'evaluate', 'tabs', 'close'],
109
+ },
110
+ url: { type: 'string', description: 'URL to navigate to (for navigate action)' },
111
+ selector: { type: 'string', description: 'CSS selector for element (for click/type)' },
112
+ text: { type: 'string', description: 'Text to type (for type action)' },
113
+ expression: { type: 'string', description: 'JavaScript to evaluate (for evaluate action)' },
114
+ },
115
+ required: ['action'],
116
+ };
117
+ async execute(args) {
118
+ const action = args.action;
119
+ try {
120
+ switch (action) {
121
+ case 'navigate':
122
+ return await this.navigate(args.url);
123
+ case 'content':
124
+ return await this.getContent();
125
+ case 'screenshot':
126
+ return await this.screenshot();
127
+ case 'click':
128
+ return await this.click(args.selector);
129
+ case 'type':
130
+ return await this.typeText(args.selector, args.text);
131
+ case 'evaluate':
132
+ return await this.evaluate(args.expression);
133
+ case 'tabs':
134
+ return await this.listTabs();
135
+ case 'close':
136
+ return this.closeBrowser();
137
+ default:
138
+ return `Unknown action: ${action}. Use: navigate, content, screenshot, click, type, evaluate, tabs, close`;
139
+ }
140
+ }
141
+ catch (err) {
142
+ const msg = err instanceof Error ? err.message : String(err);
143
+ return `Browser error: ${msg}`;
144
+ }
145
+ }
146
+ async navigate(url) {
147
+ if (!url)
148
+ return 'Error: url is required';
149
+ const cdp = await ensureConnected();
150
+ // Auto-add protocol
151
+ if (!url.startsWith('http://') && !url.startsWith('https://')) {
152
+ url = 'https://' + url;
153
+ }
154
+ await cdp.send('Page.navigate', { url });
155
+ // Wait for page load
156
+ await new Promise(r => setTimeout(r, 2000));
157
+ // Get page title
158
+ const result = await cdp.send('Runtime.evaluate', {
159
+ expression: 'document.title',
160
+ returnByValue: true,
161
+ });
162
+ const title = result.result?.value || 'untitled';
163
+ return `Navigated to: ${url}\nTitle: ${title}`;
164
+ }
165
+ async getContent() {
166
+ const cdp = await ensureConnected();
167
+ const result = await cdp.send('Runtime.evaluate', {
168
+ expression: `
169
+ (function() {
170
+ // Remove scripts and styles
171
+ const clone = document.body.cloneNode(true);
172
+ clone.querySelectorAll('script, style, noscript, svg, iframe').forEach(el => el.remove());
173
+
174
+ // Get text content, preserving structure
175
+ function getText(node, depth) {
176
+ if (depth > 10) return '';
177
+ let text = '';
178
+ for (const child of node.childNodes) {
179
+ if (child.nodeType === 3) {
180
+ text += child.textContent.trim() + ' ';
181
+ } else if (child.nodeType === 1) {
182
+ const tag = child.tagName.toLowerCase();
183
+ if (['div', 'p', 'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'tr', 'section', 'article'].includes(tag)) {
184
+ text += '\\n';
185
+ }
186
+ // Include link hrefs
187
+ if (tag === 'a' && child.href) {
188
+ text += getText(child, depth + 1) + ' [' + child.href + '] ';
189
+ } else if (tag === 'img' && child.alt) {
190
+ text += '[image: ' + child.alt + '] ';
191
+ } else {
192
+ text += getText(child, depth + 1);
193
+ }
194
+ }
195
+ }
196
+ return text;
197
+ }
198
+
199
+ let content = 'URL: ' + window.location.href + '\\n';
200
+ content += 'Title: ' + document.title + '\\n\\n';
201
+ content += getText(clone, 0);
202
+ return content.replace(/\\n\\s*\\n\\s*\\n/g, '\\n\\n').substring(0, 30000);
203
+ })()
204
+ `,
205
+ returnByValue: true,
206
+ });
207
+ return result.result?.value || 'No content';
208
+ }
209
+ async screenshot() {
210
+ const cdp = await ensureConnected();
211
+ const result = await cdp.send('Page.captureScreenshot', { format: 'png' });
212
+ const data = result.data;
213
+ if (!data)
214
+ return 'Failed to capture screenshot';
215
+ // Save to temp file
216
+ const fs = require('fs');
217
+ const path = require('path');
218
+ const os = require('os');
219
+ const filePath = path.join(os.tmpdir(), `codebot-screenshot-${Date.now()}.png`);
220
+ fs.writeFileSync(filePath, Buffer.from(data, 'base64'));
221
+ return `Screenshot saved: ${filePath} (${Math.round(data.length * 0.75 / 1024)}KB)`;
222
+ }
223
+ async click(selector) {
224
+ if (!selector)
225
+ return 'Error: selector is required';
226
+ const cdp = await ensureConnected();
227
+ const result = await cdp.send('Runtime.evaluate', {
228
+ expression: `
229
+ (function() {
230
+ const el = document.querySelector(${JSON.stringify(selector)});
231
+ if (!el) return 'Element not found: ${selector}';
232
+ el.click();
233
+ return 'Clicked: ' + (el.tagName || '') + ' ' + (el.textContent || '').substring(0, 50).trim();
234
+ })()
235
+ `,
236
+ returnByValue: true,
237
+ });
238
+ return result.result?.value || 'Click executed';
239
+ }
240
+ async typeText(selector, text) {
241
+ if (!selector)
242
+ return 'Error: selector is required';
243
+ if (!text)
244
+ return 'Error: text is required';
245
+ const cdp = await ensureConnected();
246
+ // Focus the element
247
+ await cdp.send('Runtime.evaluate', {
248
+ expression: `
249
+ (function() {
250
+ const el = document.querySelector(${JSON.stringify(selector)});
251
+ if (el) { el.focus(); el.value = ''; }
252
+ })()
253
+ `,
254
+ });
255
+ // Type character by character using Input.dispatchKeyEvent
256
+ for (const char of text) {
257
+ await cdp.send('Input.dispatchKeyEvent', {
258
+ type: 'keyDown',
259
+ text: char,
260
+ key: char,
261
+ code: `Key${char.toUpperCase()}`,
262
+ });
263
+ await cdp.send('Input.dispatchKeyEvent', {
264
+ type: 'keyUp',
265
+ key: char,
266
+ code: `Key${char.toUpperCase()}`,
267
+ });
268
+ }
269
+ // Also set value directly as fallback
270
+ await cdp.send('Runtime.evaluate', {
271
+ expression: `
272
+ (function() {
273
+ const el = document.querySelector(${JSON.stringify(selector)});
274
+ if (el) {
275
+ el.value = ${JSON.stringify(text)};
276
+ el.dispatchEvent(new Event('input', { bubbles: true }));
277
+ el.dispatchEvent(new Event('change', { bubbles: true }));
278
+ }
279
+ })()
280
+ `,
281
+ });
282
+ return `Typed "${text.length > 50 ? text.substring(0, 50) + '...' : text}" into ${selector}`;
283
+ }
284
+ async evaluate(expression) {
285
+ if (!expression)
286
+ return 'Error: expression is required';
287
+ const cdp = await ensureConnected();
288
+ const result = await cdp.send('Runtime.evaluate', {
289
+ expression,
290
+ returnByValue: true,
291
+ awaitPromise: true,
292
+ });
293
+ const val = result.result;
294
+ if (val?.type === 'undefined')
295
+ return 'undefined';
296
+ if (val?.value !== undefined) {
297
+ return typeof val.value === 'string' ? val.value : JSON.stringify(val.value, null, 2);
298
+ }
299
+ const exception = result.exceptionDetails;
300
+ if (exception) {
301
+ const ex = exception.exception;
302
+ return `Error: ${ex?.description || ex?.value || 'Unknown error'}`;
303
+ }
304
+ return JSON.stringify(val, null, 2);
305
+ }
306
+ async listTabs() {
307
+ try {
308
+ const targets = await (0, cdp_1.getTargets)(debugPort);
309
+ if (targets.length === 0)
310
+ return 'No tabs open.';
311
+ return targets
312
+ .filter(t => t.url && !t.url.startsWith('devtools://'))
313
+ .map((t, i) => `${i + 1}. ${t.title || '(no title)'}\n ${t.url}`)
314
+ .join('\n');
315
+ }
316
+ catch {
317
+ return 'Browser not connected. Use navigate first.';
318
+ }
319
+ }
320
+ closeBrowser() {
321
+ if (client) {
322
+ client.close();
323
+ client = null;
324
+ }
325
+ return 'Browser connection closed.';
326
+ }
327
+ }
328
+ exports.BrowserTool = BrowserTool;
329
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1,26 @@
1
+ import { Tool } from '../types';
2
+ export declare class EditFileTool implements Tool {
3
+ name: string;
4
+ description: string;
5
+ permission: Tool['permission'];
6
+ parameters: {
7
+ type: string;
8
+ properties: {
9
+ path: {
10
+ type: string;
11
+ description: string;
12
+ };
13
+ old_string: {
14
+ type: string;
15
+ description: string;
16
+ };
17
+ new_string: {
18
+ type: string;
19
+ description: string;
20
+ };
21
+ };
22
+ required: string[];
23
+ };
24
+ execute(args: Record<string, unknown>): Promise<string>;
25
+ }
26
+ //# sourceMappingURL=edit.d.ts.map