@bsbofmusic/agent-browser-mcp-opencode 0.1.0 → 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.
- package/CHANGELOG.md +29 -0
- package/README.md +246 -0
- package/THIRD_PARTY_NOTICES.md +291 -0
- package/index.js +186 -0
- package/package.json +33 -18
- package/src/detect.js +286 -0
- package/src/doctor.js +387 -0
- package/src/ensure.js +425 -0
- package/src/tools/actions/click.js +77 -0
- package/src/tools/actions/close.js +45 -0
- package/src/tools/actions/fill.js +77 -0
- package/src/tools/actions/find.js +107 -0
- package/src/tools/actions/getText.js +70 -0
- package/src/tools/actions/open.js +96 -0
- package/src/tools/actions/screenshot.js +81 -0
- package/src/tools/actions/snapshot.js +94 -0
- package/src/tools/actions/tab.js +91 -0
- package/src/tools/actions/wait.js +85 -0
- package/src/tools/doctor.js +42 -0
- package/src/tools/ensure.js +46 -0
- package/src/tools/exec.js +120 -0
- package/src/tools/help.js +76 -0
- package/src/tools/index.js +50 -0
- package/src/tools/version.js +83 -0
- package/dist/bootstrap.js +0 -72
- package/dist/index.js +0 -121
- package/src/bootstrap.ts +0 -75
- package/src/index.ts +0 -162
- package/tsconfig.json +0 -14
package/index.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
import { tools, listTools } from './src/tools/index.js';
|
|
6
|
+
import { execToolHandler } from './src/tools/exec.js';
|
|
7
|
+
import { ensureToolHandler } from './src/tools/ensure.js';
|
|
8
|
+
import { doctorToolHandler } from './src/tools/doctor.js';
|
|
9
|
+
import { helpToolHandler } from './src/tools/help.js';
|
|
10
|
+
import { versionToolHandler } from './src/tools/version.js';
|
|
11
|
+
import { openToolHandler } from './src/tools/actions/open.js';
|
|
12
|
+
import { snapshotToolHandler } from './src/tools/actions/snapshot.js';
|
|
13
|
+
import { clickToolHandler } from './src/tools/actions/click.js';
|
|
14
|
+
import { fillToolHandler } from './src/tools/actions/fill.js';
|
|
15
|
+
import { screenshotToolHandler } from './src/tools/actions/screenshot.js';
|
|
16
|
+
import { closeToolHandler } from './src/tools/actions/close.js';
|
|
17
|
+
import { getTextToolHandler } from './src/tools/actions/getText.js';
|
|
18
|
+
import { findToolHandler } from './src/tools/actions/find.js';
|
|
19
|
+
import { waitToolHandler } from './src/tools/actions/wait.js';
|
|
20
|
+
import { tabToolHandler } from './src/tools/actions/tab.js';
|
|
21
|
+
import { bootstrapEnsure, ensureLatest } from './src/ensure.js';
|
|
22
|
+
|
|
23
|
+
const LOG_LEVEL = process.env.LOG_LEVEL || 'info';
|
|
24
|
+
|
|
25
|
+
function log(level, message, data = null) {
|
|
26
|
+
const levels = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
27
|
+
if (levels[level] <= levels[LOG_LEVEL]) {
|
|
28
|
+
const timestamp = new Date().toISOString();
|
|
29
|
+
console.error(`[${timestamp}] [${level.toUpperCase()}] ${message}`, data ? JSON.stringify(data) : '');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const MCP_VERSION = '1.0.0';
|
|
34
|
+
let isInitialized = false;
|
|
35
|
+
let stdinBuffer = '';
|
|
36
|
+
|
|
37
|
+
const handlerMap = {
|
|
38
|
+
'browser_ensure': ensureToolHandler,
|
|
39
|
+
'browser_doctor': doctorToolHandler,
|
|
40
|
+
'browser_help': helpToolHandler,
|
|
41
|
+
'browser_version': versionToolHandler,
|
|
42
|
+
'browser_exec': execToolHandler,
|
|
43
|
+
'browser_open': openToolHandler,
|
|
44
|
+
'browser_snapshot': snapshotToolHandler,
|
|
45
|
+
'browser_click': clickToolHandler,
|
|
46
|
+
'browser_fill': fillToolHandler,
|
|
47
|
+
'browser_screenshot': screenshotToolHandler,
|
|
48
|
+
'browser_close': closeToolHandler,
|
|
49
|
+
'browser_get_text': getTextToolHandler,
|
|
50
|
+
'browser_find': findToolHandler,
|
|
51
|
+
'browser_wait': waitToolHandler,
|
|
52
|
+
'browser_tab': tabToolHandler,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
async function handleMessage(message) {
|
|
56
|
+
const { jsonrpc, id, method, params } = message;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
if (method === 'initialize') {
|
|
60
|
+
const result = {
|
|
61
|
+
protocolVersion: '2024-11-05',
|
|
62
|
+
capabilities: { tools: {} },
|
|
63
|
+
serverInfo: { name: 'agent-browser-mcp', version: MCP_VERSION },
|
|
64
|
+
};
|
|
65
|
+
sendResponse(id, result);
|
|
66
|
+
} else if (method === 'tools/list') {
|
|
67
|
+
const result = { tools: listTools() };
|
|
68
|
+
sendResponse(id, result);
|
|
69
|
+
} else if (method === 'tools/call') {
|
|
70
|
+
const { name, arguments: args } = params;
|
|
71
|
+
|
|
72
|
+
log('info', `Tool called: ${name}`, args);
|
|
73
|
+
|
|
74
|
+
const ALWAYS_LATEST = process.env.ALWAYS_LATEST !== '0';
|
|
75
|
+
|
|
76
|
+
if (ALWAYS_LATEST && isInitialized) {
|
|
77
|
+
try {
|
|
78
|
+
const updateResult = await ensureLatest();
|
|
79
|
+
if (!updateResult.skipped && updateResult.ok) {
|
|
80
|
+
log('info', 'Updated to latest version', { version: updateResult.latestVersion });
|
|
81
|
+
}
|
|
82
|
+
} catch (e) {
|
|
83
|
+
log('warn', 'Failed to check for updates', { error: e.message });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const handler = handlerMap[name];
|
|
88
|
+
|
|
89
|
+
if (!handler) {
|
|
90
|
+
sendResponse(id, {
|
|
91
|
+
content: [{
|
|
92
|
+
type: 'text',
|
|
93
|
+
text: JSON.stringify({
|
|
94
|
+
ok: false,
|
|
95
|
+
error: `Unknown tool: ${name}`,
|
|
96
|
+
logs: [`Tool ${name} not found`],
|
|
97
|
+
nextSteps: ['Use browser_help to see available tools'],
|
|
98
|
+
}),
|
|
99
|
+
}],
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const result = await handler(args);
|
|
105
|
+
|
|
106
|
+
sendResponse(id, {
|
|
107
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
sendResponse(id, null);
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
log('error', 'Error handling message', { error: error.message });
|
|
114
|
+
sendResponse(id, null, error.message);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function sendResponse(id, result, error = null) {
|
|
119
|
+
const response = {
|
|
120
|
+
jsonrpc: '2.0',
|
|
121
|
+
id,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (error) {
|
|
125
|
+
response.error = { code: -32603, message: error };
|
|
126
|
+
} else {
|
|
127
|
+
response.result = result;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
process.stdout.write(JSON.stringify(response) + '\n');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function sendNotification(method, params) {
|
|
134
|
+
const notification = { jsonrpc: '2.0', method, params };
|
|
135
|
+
process.stdout.write(JSON.stringify(notification) + '\n');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function initialize() {
|
|
139
|
+
if (isInitialized) return;
|
|
140
|
+
|
|
141
|
+
log('info', 'Initializing MCP server...');
|
|
142
|
+
|
|
143
|
+
const result = await bootstrapEnsure();
|
|
144
|
+
|
|
145
|
+
if (!result.ok) {
|
|
146
|
+
log('warn', 'Bootstrap failed, MCP may not be fully functional', { error: result.error });
|
|
147
|
+
} else {
|
|
148
|
+
log('info', 'Bootstrap completed successfully');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
isInitialized = true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
process.stdin.setEncoding('utf8');
|
|
155
|
+
|
|
156
|
+
process.stdin.on('data', (chunk) => {
|
|
157
|
+
stdinBuffer += chunk;
|
|
158
|
+
|
|
159
|
+
let newlineIndex;
|
|
160
|
+
while ((newlineIndex = stdinBuffer.indexOf('\n')) !== -1) {
|
|
161
|
+
const line = stdinBuffer.slice(0, newlineIndex);
|
|
162
|
+
stdinBuffer = stdinBuffer.slice(newlineIndex + 1);
|
|
163
|
+
|
|
164
|
+
if (line.trim()) {
|
|
165
|
+
try {
|
|
166
|
+
const message = JSON.parse(line);
|
|
167
|
+
|
|
168
|
+
if (message.method === 'initialize') {
|
|
169
|
+
initialize().then(() => {
|
|
170
|
+
handleMessage(message);
|
|
171
|
+
sendNotification('initialized', {});
|
|
172
|
+
}).catch((error) => {
|
|
173
|
+
log('error', 'Failed to initialize', { error: error.message });
|
|
174
|
+
process.exit(1);
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
handleMessage(message);
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
log('error', 'Failed to parse message', { error: e.message, line });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
log('info', 'MCP server started, waiting for initialization...');
|
package/package.json
CHANGED
|
@@ -1,27 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsbofmusic/agent-browser-mcp-opencode",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Vercel agent-browser - browser automation CLI for AI agents",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "tsc -p tsconfig.json",
|
|
8
|
-
"start": "node dist/bootstrap.js",
|
|
9
|
-
"prepublishOnly": "npm run build"
|
|
10
|
-
},
|
|
11
|
-
"keywords": [],
|
|
12
|
-
"author": "",
|
|
13
|
-
"license": "ISC",
|
|
14
6
|
"type": "module",
|
|
15
|
-
"private": false,
|
|
16
7
|
"bin": {
|
|
17
|
-
"agent-browser-mcp
|
|
8
|
+
"agent-browser-mcp": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node index.js",
|
|
12
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
13
|
+
"doctor": "node index.js doctor"
|
|
18
14
|
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"model-context-protocol",
|
|
18
|
+
"agent-browser",
|
|
19
|
+
"browser-automation",
|
|
20
|
+
"ai-agent",
|
|
21
|
+
"headless-browser",
|
|
22
|
+
"vercel",
|
|
23
|
+
"opencode"
|
|
24
|
+
],
|
|
25
|
+
"author": "bsbofmusic",
|
|
26
|
+
"license": "Apache-2.0",
|
|
19
27
|
"dependencies": {
|
|
20
|
-
"
|
|
21
|
-
"
|
|
28
|
+
"execa": "^9.5.2",
|
|
29
|
+
"semver": "^7.7.1"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/vercel-labs/agent-browser"
|
|
22
37
|
},
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
"
|
|
38
|
+
"homepage": "https://github.com/vercel-labs/agent-browser",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/vercel-labs/agent-browser/issues"
|
|
26
41
|
}
|
|
27
|
-
}
|
|
42
|
+
}
|
package/src/detect.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { execSync, exec } from 'child_process';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
10
|
+
|
|
11
|
+
export const DETECT_LOG_LEVEL = process.env.LOG_LEVEL || 'info';
|
|
12
|
+
|
|
13
|
+
function log(level, message, data = null) {
|
|
14
|
+
const levels = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
15
|
+
if (levels[level] <= levels[DETECT_LOG_LEVEL]) {
|
|
16
|
+
const timestamp = new Date().toISOString();
|
|
17
|
+
console.error(`[${timestamp}] [${level.toUpperCase()}] ${message}`, data ? JSON.stringify(data) : '');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function detectOS() {
|
|
22
|
+
const platform = os.platform();
|
|
23
|
+
const arch = os.arch();
|
|
24
|
+
const release = os.release();
|
|
25
|
+
|
|
26
|
+
let isAdmin = false;
|
|
27
|
+
try {
|
|
28
|
+
if (platform === 'win32') {
|
|
29
|
+
execSync('net session', { stdio: 'ignore' });
|
|
30
|
+
isAdmin = true;
|
|
31
|
+
} else {
|
|
32
|
+
execSync('id -u', { stdio: 'ignore' });
|
|
33
|
+
isAdmin = process.getuid?.() === 0 || execSync('id -u').toString().trim() === '0';
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
isAdmin = false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
platform,
|
|
41
|
+
arch,
|
|
42
|
+
release,
|
|
43
|
+
isAdmin,
|
|
44
|
+
cpus: os.cpus().length,
|
|
45
|
+
totalMemory: os.totalmem(),
|
|
46
|
+
freeMemory: os.freemem(),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function detectRuntime() {
|
|
51
|
+
const results = {
|
|
52
|
+
node: { available: false, version: null },
|
|
53
|
+
npm: { available: false, version: null },
|
|
54
|
+
pnpm: { available: false, version: null },
|
|
55
|
+
yarn: { available: false, version: null },
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const checkCommand = (cmd) => {
|
|
59
|
+
try {
|
|
60
|
+
const version = execSync(`${cmd} --version`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
61
|
+
return version;
|
|
62
|
+
} catch (e) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
results.node = { available: true, version: process.version };
|
|
68
|
+
results.npm = { available: false, version: checkCommand('npm') };
|
|
69
|
+
results.pnpm = { available: false, version: checkCommand('pnpm') };
|
|
70
|
+
results.yarn = { available: false, version: checkCommand('yarn') };
|
|
71
|
+
|
|
72
|
+
if (results.npm.version) results.npm.available = true;
|
|
73
|
+
if (results.pnpm.version) results.pnpm.available = true;
|
|
74
|
+
if (results.yarn.version) results.yarn.available = true;
|
|
75
|
+
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function detectPackageManager() {
|
|
80
|
+
const managers = [];
|
|
81
|
+
|
|
82
|
+
const checkCmd = (cmd) => {
|
|
83
|
+
try {
|
|
84
|
+
execSync(`${cmd} --version`, { stdio: 'ignore' });
|
|
85
|
+
return true;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (checkCmd('npm')) managers.push('npm');
|
|
92
|
+
if (checkCmd('pnpm')) managers.push('pnpm');
|
|
93
|
+
if (checkCmd('yarn')) managers.push('yarn');
|
|
94
|
+
|
|
95
|
+
return managers;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function detectDocker() {
|
|
99
|
+
let available = false;
|
|
100
|
+
let version = null;
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
version = execSync('docker --version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
104
|
+
available = true;
|
|
105
|
+
} catch (e) {
|
|
106
|
+
try {
|
|
107
|
+
version = execSync('podman --version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
108
|
+
available = true;
|
|
109
|
+
} catch (e2) {
|
|
110
|
+
available = false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { available, version };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function detectNetwork() {
|
|
118
|
+
const checks = {
|
|
119
|
+
npmRegistry: false,
|
|
120
|
+
github: false,
|
|
121
|
+
google: false,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const checkUrl = (url) => {
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
const cmd = process.platform === 'win32'
|
|
127
|
+
? `ping -n 1 -w 1000 ${url} > nul 2>&1 && echo OK`
|
|
128
|
+
: `ping -c 1 -W 1 ${url} > /dev/null 2>&1 && echo OK`;
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
execSync(cmd, { encoding: 'utf8', timeout: 3000 });
|
|
132
|
+
resolve(true);
|
|
133
|
+
} catch {
|
|
134
|
+
resolve(false);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
checks.npmRegistry = await checkUrl('registry.npmjs.org');
|
|
140
|
+
checks.github = await checkUrl('github.com');
|
|
141
|
+
checks.google = await checkUrl('google.com');
|
|
142
|
+
|
|
143
|
+
return checks;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function detectDisk() {
|
|
147
|
+
try {
|
|
148
|
+
const checkDir = process.platform === 'win32' ? 'C:' : '/';
|
|
149
|
+
const stats = require('child_process').execSync(
|
|
150
|
+
process.platform === 'win32'
|
|
151
|
+
? `wmic logicaldisk where "DeviceID='${checkDir}'" get Size,FreeSpace /value`
|
|
152
|
+
: `df -k "${checkDir}"`,
|
|
153
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
let freeSpace = 0;
|
|
157
|
+
let totalSpace = 0;
|
|
158
|
+
|
|
159
|
+
if (process.platform === 'win32') {
|
|
160
|
+
const freeMatch = checkDir === 'C:' ? checkDir.match(/FreeSpace=(\d+)/) : null;
|
|
161
|
+
const sizeMatch = checkDir === 'C:' ? checkDir.match(/Size=(\d+)/) : null;
|
|
162
|
+
if (freeMatch) freeSpace = parseInt(freeMatch[1], 10);
|
|
163
|
+
if (sizeMatch) totalSpace = parseInt(sizeMatch[1], 10);
|
|
164
|
+
} else {
|
|
165
|
+
const parts = checkDir.trim().split(/\s+/);
|
|
166
|
+
if (parts.length >= 4) {
|
|
167
|
+
freeSpace = parseInt(parts[3], 10) * 1024;
|
|
168
|
+
totalSpace = parseInt(parts[1], 10) * 1024;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
freeSpace,
|
|
174
|
+
totalSpace,
|
|
175
|
+
hasEnoughSpace: freeSpace > 1024 * 1024 * 1024,
|
|
176
|
+
};
|
|
177
|
+
} catch (e) {
|
|
178
|
+
return { freeSpace: 0, totalSpace: 0, hasEnoughSpace: false };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export async function detectAgentBrowser() {
|
|
183
|
+
let installed = false;
|
|
184
|
+
let version = null;
|
|
185
|
+
let chromiumInstalled = false;
|
|
186
|
+
let path_ = null;
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
version = execSync('agent-browser --version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
190
|
+
installed = true;
|
|
191
|
+
} catch (e) {
|
|
192
|
+
try {
|
|
193
|
+
version = execSync('npx agent-browser --version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
194
|
+
installed = true;
|
|
195
|
+
} catch (e2) {
|
|
196
|
+
installed = false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (installed) {
|
|
201
|
+
try {
|
|
202
|
+
execSync('agent-browser --help', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
203
|
+
chromiumInstalled = true;
|
|
204
|
+
} catch (e) {
|
|
205
|
+
chromiumInstalled = false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const homeDir = os.homedir();
|
|
209
|
+
path_ = path.join(homeDir, '.agent-browser');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
installed,
|
|
214
|
+
version,
|
|
215
|
+
chromiumInstalled,
|
|
216
|
+
path: path_,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export async function detectPermissions() {
|
|
221
|
+
const checks = {
|
|
222
|
+
npmGlobalWritable: false,
|
|
223
|
+
tempWritable: false,
|
|
224
|
+
homeWritable: false,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
execSync('npm config get prefix', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
229
|
+
checks.npmGlobalWritable = true;
|
|
230
|
+
} catch (e) {
|
|
231
|
+
checks.npmGlobalWritable = false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
const tempDir = os.tmpdir();
|
|
236
|
+
const testFile = path.join(tempDir, `agent-browser-test-${Date.now()}`);
|
|
237
|
+
fs.writeFileSync(testFile, 'test');
|
|
238
|
+
fs.unlinkSync(testFile);
|
|
239
|
+
checks.tempWritable = true;
|
|
240
|
+
} catch (e) {
|
|
241
|
+
checks.tempWritable = false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const homeDir = os.homedir();
|
|
246
|
+
const testFile = path.join(homeDir, `.agent-browser-test-${Date.now()}`);
|
|
247
|
+
fs.writeFileSync(testFile, 'test');
|
|
248
|
+
fs.unlinkSync(testFile);
|
|
249
|
+
checks.homeWritable = true;
|
|
250
|
+
} catch (e) {
|
|
251
|
+
checks.homeWritable = false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return checks;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export async function runFullDetection() {
|
|
258
|
+
log('info', 'Starting full environment detection...');
|
|
259
|
+
|
|
260
|
+
const detection = {
|
|
261
|
+
timestamp: new Date().toISOString(),
|
|
262
|
+
os: await detectOS(),
|
|
263
|
+
runtime: await detectRuntime(),
|
|
264
|
+
packageManagers: await detectPackageManager(),
|
|
265
|
+
docker: await detectDocker(),
|
|
266
|
+
network: await detectNetwork(),
|
|
267
|
+
disk: await detectDisk(),
|
|
268
|
+
agentBrowser: await detectAgentBrowser(),
|
|
269
|
+
permissions: await detectPermissions(),
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
log('info', 'Environment detection completed', detection);
|
|
273
|
+
return detection;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export default {
|
|
277
|
+
detectOS,
|
|
278
|
+
detectRuntime,
|
|
279
|
+
detectPackageManager,
|
|
280
|
+
detectDocker,
|
|
281
|
+
detectNetwork,
|
|
282
|
+
detectDisk,
|
|
283
|
+
detectAgentBrowser,
|
|
284
|
+
detectPermissions,
|
|
285
|
+
runFullDetection,
|
|
286
|
+
};
|