arena-bridge 0.5.2 → 0.6.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 +9 -0
- package/README.md +40 -4
- package/package.json +1 -1
- package/src/browser.mjs +100 -5
- package/src/cli.mjs +86 -0
- package/src/direct.mjs +1 -1
- package/src/hub.mjs +1 -1
- package/src/mcp.mjs +1 -1
- package/src/prompt.mjs +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
The format follows [Keep a Changelog](https://keepachangelog.com/) and the project uses
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 0.6.0
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Named browser profiles for the agent: `chrome_launch {"profile":"work"}` keeps its logins between
|
|
11
|
+
runs, `chrome_profiles` lists them, and `arena-bridge browser list|open|close` manages them from
|
|
12
|
+
the command line so the user can log in once.
|
|
13
|
+
- A visible agent window can be sized and placed (`width`, `height`, `x`, `y`) so it stays out of
|
|
14
|
+
the way on the user's screen.
|
|
15
|
+
|
|
7
16
|
## 0.5.2
|
|
8
17
|
|
|
9
18
|
### Fixed
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="license">
|
|
13
13
|
<img src="https://img.shields.io/badge/node-%3E%3D18-green.svg?style=flat-square" alt="node">
|
|
14
14
|
<img src="https://img.shields.io/badge/dependencies-0-brightgreen.svg?style=flat-square" alt="dependencies">
|
|
15
|
-
<img src="https://img.shields.io/badge/tests-
|
|
15
|
+
<img src="https://img.shields.io/badge/tests-96%20passed-brightgreen.svg?style=flat-square" alt="tests">
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
<p align="center">
|
|
@@ -79,7 +79,7 @@ arena-bridge setup --root ~/projects
|
|
|
79
79
|
| Файлы | `list_dir` `read_file` `write_file` `edit_file` `delete_path` `search_files` `file_get` `file_put` | работа с файлами в разрешённых папках |
|
|
80
80
|
| Шелл | `bash` `sysinfo` | команды и сведения о машине |
|
|
81
81
|
| Процессы | `start_process` `read_process_output` `write_process` `kill_process` | фоновые дев-серверы |
|
|
82
|
-
| Chrome | `chrome_launch` `chrome_attach` `chrome_open` `chrome_navigate` `chrome_snapshot` `chrome_click` `chrome_type` `chrome_press` `chrome_screenshot` `chrome_evaluate` `chrome_tabs` `chrome_stop` | браузер через CDP |
|
|
82
|
+
| Chrome | `chrome_launch` `chrome_profiles` `chrome_attach` `chrome_open` `chrome_navigate` `chrome_snapshot` `chrome_click` `chrome_type` `chrome_press` `chrome_screenshot` `chrome_evaluate` `chrome_tabs` `chrome_stop` | браузер через CDP |
|
|
83
83
|
| Десктоп | `mac_see` `mac_click` `mac_type` `mac_key` `mac_scroll` `mac_move` `mac_drag` `mac_apps` `mac_windows` `mac_app_launch` `mac_app_focus` `mac_app_quit` `mac_permissions` | macOS через Peekaboo |
|
|
84
84
|
|
|
85
85
|
Полный каталог с JSON-схемами: `GET /tools` или `arena-bridge tools`.
|
|
@@ -171,6 +171,41 @@ arena-bridge trust --off
|
|
|
171
171
|
|
|
172
172
|
---
|
|
173
173
|
|
|
174
|
+
## Отдельные браузерные «кабинеты» агента
|
|
175
|
+
|
|
176
|
+
Агенту можно выдать несколько независимых профилей браузера — например «рабочий», «клиентский»,
|
|
177
|
+
«тестовый». В каждом свои входы, и они переживают перезапуск моста.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
arena-bridge browser open work # открыть Chrome с профилем «work»
|
|
181
|
+
# вы логинитесь в нужные сайты и закрываете окно — входы сохраняются
|
|
182
|
+
arena-bridge browser list # какие профили есть
|
|
183
|
+
arena-bridge browser close work # закрыть окно этого профиля
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Агент открывает тот же профиль через `chrome_launch {"profile":"work"}` — уже с вашими входами.
|
|
187
|
+
Каталог профилей агент тоже видит: `chrome_profiles`.
|
|
188
|
+
|
|
189
|
+
Ваш собственный Chrome (на порту 9222, с вашими сессиями) доступен отдельно — через
|
|
190
|
+
`chrome_attach`; любые действия в нём требуют подтверждения.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Как это не мешает вам
|
|
195
|
+
|
|
196
|
+
| Что беспокоит | Как решается |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| окно агента занимает экран | по умолчанию агент работает в фоне; видимое окно можно сделать маленьким и в углу: `chrome_launch {"headless":false,"width":900,"height":700,"x":-920,"y":40}` |
|
|
199
|
+
| агент перехватывает фокус и курсор | действия в нативных приложениях идут «в фон» — в нужное окно, без переключения на вас; курсор двигается только по вашей явной просьбе |
|
|
200
|
+
| окно агента на вашем рабочем столе | macOS умеет несколько рабочих столов: окно агента живёт на отдельном столе, вы — на своём |
|
|
201
|
+
| нужен отдельный «второй монитор» | в macOS виртуальный монитор штатно не создаётся; сторонние утилиты (BetterDisplay, DisplayLink) умеют — тогда окно агента всегда там, где вы не работаете |
|
|
202
|
+
| нужен полностью независимый курсор и стол | только отдельная виртуальная машина; на 16 ГБ и Intel 2019 года она заметно тормозит и съедает 30–60 ГБ — как правило, не нужно |
|
|
203
|
+
|
|
204
|
+
Рекомендация для ноутбука с 16 ГБ и Intel 2019: отдельные профили браузера + фоновый режим +
|
|
205
|
+
при необходимости второй рабочий стол macOS. Полноценная виртуальная машина того не стоит.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
174
209
|
## Безопасность
|
|
175
210
|
|
|
176
211
|
| Токен | Кто знает | Что позволяет |
|
|
@@ -215,6 +250,7 @@ arena-bridge audit -n 50 # что происходило
|
|
|
215
250
|
| `version` | установленная и последняя версии |
|
|
216
251
|
| `update` | обновить пакет с npm (`--check` — только проверить) |
|
|
217
252
|
| `service` | macOS-фоновый сервис: `install` / `uninstall` / `status` |
|
|
253
|
+
| `browser` | профили браузера агента: `list` / `open <name>` / `close <name>` |
|
|
218
254
|
| `rotate` | сменить агентский или девайсный токен |
|
|
219
255
|
|
|
220
256
|
---
|
|
@@ -246,8 +282,8 @@ curl -sS -X POST "https://<ваш-url>/call" \
|
|
|
246
282
|
|
|
247
283
|
```bash
|
|
248
284
|
npm install # зависимостей нет — только dev-тесты
|
|
249
|
-
npm test #
|
|
250
|
-
npm run test:python #
|
|
285
|
+
npm test # 52 теста Node: direct, approval, trust, MCP, hub, туннель, Chrome, десктоп
|
|
286
|
+
npm run test:python # 44 теста Python: то же самое
|
|
251
287
|
```
|
|
252
288
|
|
|
253
289
|
Тесты не требуют ни Chrome, ни Peekaboo: используются фейковые CDP- и Peekaboo-серверы
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arena-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Give a cloud AI agent (Arena.ai Agent Mode, Claude, Cursor, ...) controlled access to your own machine over HTTP/MCP: files, shell, Chrome and the macOS desktop, with an approval gate, trust windows and an audit log.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/browser.mjs
CHANGED
|
@@ -33,12 +33,72 @@ const KEYS = {
|
|
|
33
33
|
|
|
34
34
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
35
35
|
|
|
36
|
-
function findChrome() {
|
|
36
|
+
export function findChrome() {
|
|
37
37
|
if (process.env.CHROME_PATH && fs.existsSync(process.env.CHROME_PATH)) return process.env.CHROME_PATH;
|
|
38
38
|
for (const c of CHROME_CANDIDATES) if (fs.existsSync(c)) return c;
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/** Where the named agent profiles live (follows ARENA_BRIDGE_HOME when set). */
|
|
43
|
+
export function profileDir() {
|
|
44
|
+
const base = process.env.ARENA_BRIDGE_HOME || path.join(os.homedir(), '.arena-bridge');
|
|
45
|
+
return path.join(base, 'chrome-profiles');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const sanitizeProfile = (name) =>
|
|
49
|
+
String(name)
|
|
50
|
+
.trim()
|
|
51
|
+
.replace(/[^a-zA-Z0-9._-]+/g, '-')
|
|
52
|
+
.replace(/^-+|-+$/g, '')
|
|
53
|
+
.slice(0, 60) || 'default';
|
|
54
|
+
|
|
55
|
+
/** A named profile keeps its logins between agent runs; a path is used as given. */
|
|
56
|
+
export function resolveProfile(raw, port = 9223) {
|
|
57
|
+
if (!raw) return path.join(os.homedir(), '.arena-bridge', `chrome-profile-${port}`);
|
|
58
|
+
const value = String(raw);
|
|
59
|
+
if (value.startsWith('/') || value.startsWith('~')) {
|
|
60
|
+
return path.resolve(value.replace(/^~/, os.homedir()));
|
|
61
|
+
}
|
|
62
|
+
return path.join(profileDir(), sanitizeProfile(value));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function profileName(raw) {
|
|
66
|
+
if (!raw) return null;
|
|
67
|
+
const value = String(raw);
|
|
68
|
+
if (value.startsWith('/') || value.startsWith('~')) return null;
|
|
69
|
+
return sanitizeProfile(value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function listProfiles() {
|
|
73
|
+
const out = [];
|
|
74
|
+
let names = [];
|
|
75
|
+
try {
|
|
76
|
+
names = fs.readdirSync(profileDir());
|
|
77
|
+
} catch {
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
for (const name of names) {
|
|
81
|
+
const dir = path.join(profileDir(), name);
|
|
82
|
+
let stat = null;
|
|
83
|
+
try {
|
|
84
|
+
stat = fs.statSync(dir);
|
|
85
|
+
} catch {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (!stat.isDirectory()) continue;
|
|
89
|
+
let size = 0;
|
|
90
|
+
try {
|
|
91
|
+
size = fs
|
|
92
|
+
.readdirSync(dir)
|
|
93
|
+
.reduce((acc, f) => acc + (fs.statSync(path.join(dir, f)).size || 0), 0);
|
|
94
|
+
} catch {
|
|
95
|
+
/* ignore */
|
|
96
|
+
}
|
|
97
|
+
out.push({ name, path: dir, mtime: stat.mtimeMs, files: fs.readdirSync(dir).length, approx_bytes: size });
|
|
98
|
+
}
|
|
99
|
+
return out.sort((a, b) => b.mtime - a.mtime);
|
|
100
|
+
}
|
|
101
|
+
|
|
42
102
|
const S = (props, required) => ({
|
|
43
103
|
type: 'object',
|
|
44
104
|
properties: props || {},
|
|
@@ -140,9 +200,7 @@ export class BrowserManager {
|
|
|
140
200
|
}
|
|
141
201
|
const headless = args.headless !== false;
|
|
142
202
|
const port = Number(args.port || 9223);
|
|
143
|
-
const profile = args.profile
|
|
144
|
-
? path.resolve(String(args.profile).replace(/^~/, os.homedir()))
|
|
145
|
-
: path.join(os.homedir(), '.arena-bridge', `chrome-profile-${port}`);
|
|
203
|
+
const profile = resolveProfile(args.profile, port);
|
|
146
204
|
|
|
147
205
|
fs.mkdirSync(profile, { recursive: true });
|
|
148
206
|
|
|
@@ -157,6 +215,14 @@ export class BrowserManager {
|
|
|
157
215
|
'--remote-allow-origins=*',
|
|
158
216
|
];
|
|
159
217
|
if (headless) chromeArgs.push(process.env.ARENA_HEADLESS_FLAG || '--headless=new');
|
|
218
|
+
if (!headless) {
|
|
219
|
+
const width = Number(args.width || 0);
|
|
220
|
+
const height = Number(args.height || 0);
|
|
221
|
+
if (width && height) chromeArgs.push(`--window-size=${width},${height}`);
|
|
222
|
+
if (Number.isFinite(Number(args.x)) && Number.isFinite(Number(args.y))) {
|
|
223
|
+
chromeArgs.push(`--window-position=${Number(args.x)},${Number(args.y)}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
160
226
|
if (args.url) chromeArgs.push(String(args.url));
|
|
161
227
|
|
|
162
228
|
const proc = spawn(bin, chromeArgs, { detached: true, stdio: 'ignore' });
|
|
@@ -193,6 +259,7 @@ export class BrowserManager {
|
|
|
193
259
|
headless,
|
|
194
260
|
port,
|
|
195
261
|
profile,
|
|
262
|
+
profile_name: profileName(args.profile),
|
|
196
263
|
ws_url: wsUrl,
|
|
197
264
|
tabs: [...session.tabs.values()].map((t) => ({ tab_id: t.targetId, title: t.title, url: t.url })),
|
|
198
265
|
note: 'This browser belongs to the agent — no local approval needed for actions on it.',
|
|
@@ -307,13 +374,41 @@ export class BrowserManager {
|
|
|
307
374
|
headless: { type: 'boolean', description: 'true (default) = no window, false = visible window' },
|
|
308
375
|
port: { type: 'number', description: 'CDP port (default 9223)' },
|
|
309
376
|
url: { type: 'string', description: 'URL to open on start' },
|
|
310
|
-
profile: {
|
|
377
|
+
profile: {
|
|
378
|
+
type: 'string',
|
|
379
|
+
description:
|
|
380
|
+
'named profile (keeps its logins between runs) or a directory path. ' +
|
|
381
|
+
'Default: a throwaway profile for this port.',
|
|
382
|
+
},
|
|
383
|
+
width: { type: 'number', description: 'window width in pixels (visible window only)' },
|
|
384
|
+
height: { type: 'number', description: 'window height in pixels (visible window only)' },
|
|
385
|
+
x: { type: 'number', description: 'window left edge, e.g. -900 puts it on the left of a wide screen' },
|
|
386
|
+
y: { type: 'number', description: 'window top edge' },
|
|
311
387
|
},
|
|
312
388
|
[],
|
|
313
389
|
),
|
|
314
390
|
required: [],
|
|
315
391
|
handler: (args, ctx) => ctx.browser.launch(args, ctx),
|
|
316
392
|
},
|
|
393
|
+
{
|
|
394
|
+
name: 'chrome_profiles',
|
|
395
|
+
description:
|
|
396
|
+
'List the agent\'s own named browser profiles (each keeps its own logins between runs). ' +
|
|
397
|
+
'Launch one with chrome_launch {"profile":"<name>"}; the user logs into it once with ' +
|
|
398
|
+
'"arena-bridge browser open <name>" on their machine.',
|
|
399
|
+
approval: false,
|
|
400
|
+
schema: S({}, []),
|
|
401
|
+
required: [],
|
|
402
|
+
handler: async () => {
|
|
403
|
+
const profiles = listProfiles();
|
|
404
|
+
return {
|
|
405
|
+
ok: true,
|
|
406
|
+
count: profiles.length,
|
|
407
|
+
profiles,
|
|
408
|
+
hint: 'Ask the user to log in: arena-bridge browser open <name> — then use chrome_launch {"profile":"<name>"}',
|
|
409
|
+
};
|
|
410
|
+
},
|
|
411
|
+
},
|
|
317
412
|
{
|
|
318
413
|
name: 'chrome_attach',
|
|
319
414
|
description:
|
package/src/cli.mjs
CHANGED
|
@@ -11,8 +11,10 @@ import { buildPrompt } from './prompt.mjs';
|
|
|
11
11
|
import { listApprovals, decide } from './approvals.mjs';
|
|
12
12
|
import { tailAudit } from './audit.mjs';
|
|
13
13
|
import { startTunnel, isPortOpen } from './tunnel.mjs';
|
|
14
|
+
import { spawn, execSync } from 'node:child_process';
|
|
14
15
|
import { parseDuration, trustState, saveTrust, clearTrust } from './trust.mjs';
|
|
15
16
|
import { findPeekaboo } from './mac.mjs';
|
|
17
|
+
import { findChrome, listProfiles, resolveProfile, sanitizeProfile } from './browser.mjs';
|
|
16
18
|
import { packageInfo, latestVersion, compareVersions, selfUpdate } from './selfupdate.mjs';
|
|
17
19
|
import { serviceStatus, serviceInstall, serviceUninstall } from './service.mjs';
|
|
18
20
|
|
|
@@ -78,6 +80,8 @@ Commands:
|
|
|
78
80
|
update Update this package from npm (--check to only look)
|
|
79
81
|
service Run the bridge as a macOS background service
|
|
80
82
|
install | uninstall | status
|
|
83
|
+
browser The agent's own browser profiles
|
|
84
|
+
list | open <name> | close <name>
|
|
81
85
|
|
|
82
86
|
Common options:
|
|
83
87
|
--mode direct|hub mode for "init"
|
|
@@ -437,6 +441,86 @@ function cmdService(args) {
|
|
|
437
441
|
if (!st.installed) console.log(' install it with: arena-bridge service install');
|
|
438
442
|
}
|
|
439
443
|
|
|
444
|
+
function cmdBrowser(args) {
|
|
445
|
+
const what = args._[1] || 'list';
|
|
446
|
+
if (what === 'list') {
|
|
447
|
+
const profiles = listProfiles();
|
|
448
|
+
if (!profiles.length) {
|
|
449
|
+
console.log('no agent browser profiles yet — create one: arena-bridge browser open <name>');
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
for (const p of profiles) {
|
|
453
|
+
console.log(`${p.name.padEnd(20)} ${String(p.files).padStart(3)} files ${new Date(p.mtime).toLocaleString()}`);
|
|
454
|
+
console.log(` ${p.path}`);
|
|
455
|
+
}
|
|
456
|
+
console.log('\nThe agent opens a profile with: chrome_launch {"profile":"<name>"}');
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (what === 'open') {
|
|
460
|
+
const name = args._[2];
|
|
461
|
+
if (!name) {
|
|
462
|
+
console.error('usage: arena-bridge browser open <name>');
|
|
463
|
+
process.exit(1);
|
|
464
|
+
}
|
|
465
|
+
const bin = findChrome();
|
|
466
|
+
if (!bin) {
|
|
467
|
+
console.error('✗ Chrome not found in /Applications');
|
|
468
|
+
process.exit(1);
|
|
469
|
+
}
|
|
470
|
+
const profile = resolveProfile(name);
|
|
471
|
+
fs.mkdirSync(profile, { recursive: true });
|
|
472
|
+
const chromeArgs = [
|
|
473
|
+
`--user-data-dir=${profile}`,
|
|
474
|
+
'--no-first-run',
|
|
475
|
+
'--no-default-browser-check',
|
|
476
|
+
'--remote-debugging-port=0',
|
|
477
|
+
];
|
|
478
|
+
const width = Number(args.flags.width || 0);
|
|
479
|
+
const height = Number(args.flags.height || 0);
|
|
480
|
+
if (width && height) chromeArgs.push(`--window-size=${width},${height}`);
|
|
481
|
+
if (args.flags.x != null && args.flags.y != null) {
|
|
482
|
+
chromeArgs.push(`--window-position=${Number(args.flags.x)},${Number(args.flags.y)}`);
|
|
483
|
+
}
|
|
484
|
+
const proc = spawn(bin, chromeArgs, { detached: true, stdio: 'ignore' });
|
|
485
|
+
proc.unref();
|
|
486
|
+
console.log(`✓ Chrome opened with the profile "${sanitizeProfile(name)}"`);
|
|
487
|
+
console.log(` profile: ${profile}`);
|
|
488
|
+
console.log(' log in to the sites the agent needs and close this window — the logins stay.');
|
|
489
|
+
console.log(` the agent opens the same profile with: chrome_launch {"profile":"${sanitizeProfile(name)}"}`);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (what === 'close') {
|
|
493
|
+
const name = args._[2];
|
|
494
|
+
if (!name) {
|
|
495
|
+
console.error('usage: arena-bridge browser close <name>');
|
|
496
|
+
process.exit(1);
|
|
497
|
+
}
|
|
498
|
+
const profile = resolveProfile(name);
|
|
499
|
+
let out = '';
|
|
500
|
+
try {
|
|
501
|
+
out = execSync('ps -Ao pid,command', { encoding: 'utf8' });
|
|
502
|
+
} catch {
|
|
503
|
+
/* ignore */
|
|
504
|
+
}
|
|
505
|
+
let killed = 0;
|
|
506
|
+
for (const line of out.split('\n')) {
|
|
507
|
+
if (!line.includes(`--user-data-dir=${profile}`)) continue;
|
|
508
|
+
const pid = Number(line.trim().split(/\s+/)[0]);
|
|
509
|
+
if (!pid) continue;
|
|
510
|
+
try {
|
|
511
|
+
process.kill(pid);
|
|
512
|
+
killed++;
|
|
513
|
+
} catch {
|
|
514
|
+
/* ignore */
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
console.log(`✓ closed ${killed} Chrome window(s) for "${sanitizeProfile(name)}"`);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
console.error(`unknown browser action: ${what}\nuse: list | open <name> | close <name>`);
|
|
521
|
+
process.exitCode = 1;
|
|
522
|
+
}
|
|
523
|
+
|
|
440
524
|
function cmdRotate(args) {
|
|
441
525
|
const cfg = loadConfig();
|
|
442
526
|
const what = args.flags.device ? 'device' : 'agent';
|
|
@@ -521,6 +605,8 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
521
605
|
return cmdUpdate(args);
|
|
522
606
|
case 'service':
|
|
523
607
|
return cmdService(args);
|
|
608
|
+
case 'browser':
|
|
609
|
+
return cmdBrowser(args);
|
|
524
610
|
case undefined:
|
|
525
611
|
case 'help':
|
|
526
612
|
case '--help':
|
package/src/direct.mjs
CHANGED
package/src/hub.mjs
CHANGED
package/src/mcp.mjs
CHANGED
|
@@ -48,7 +48,7 @@ export async function handleMcp(req, res, ctx) {
|
|
|
48
48
|
mcpResponse(id, {
|
|
49
49
|
protocolVersion: PROTOCOL_VERSION,
|
|
50
50
|
capabilities: { tools: { listChanged: false } },
|
|
51
|
-
serverInfo: { name: 'arena-bridge', version: '0.
|
|
51
|
+
serverInfo: { name: 'arena-bridge', version: '0.6.0' },
|
|
52
52
|
instructions:
|
|
53
53
|
'Tools operate the user\'s own machine. Dangerous operations may return ' +
|
|
54
54
|
'PROTECTED and require a local human approval.',
|
package/src/prompt.mjs
CHANGED
|
@@ -69,6 +69,10 @@ ${tools.join('\n')}
|
|
|
69
69
|
3. \`chrome_attach\` connects to the user's own Chrome (default port 9222, with their logged-in sessions).
|
|
70
70
|
Actions on an attached browser require a local approval — ask for it instead of retrying.
|
|
71
71
|
4. Screenshots: pass \`{"path": "shot.png"}\` to save the PNG inside the authorized roots, then read it with \`file_get\`.
|
|
72
|
+
5. Named profiles keep their logins between runs: \`chrome_launch {"profile":"work"}\` opens
|
|
73
|
+
\`~/.arena-bridge/chrome-profiles/work\`. \`chrome_profiles\` lists them. The user logs in once with
|
|
74
|
+
\`arena-bridge browser open work\` on their machine — ask for that instead of trying to log in yourself.
|
|
75
|
+
6. A visible window can be kept small and out of the way: \`chrome_launch {"headless":false,"width":900,"height":700,"x":-920,"y":40}\`.
|
|
72
76
|
|
|
73
77
|
## Desktop control (macOS, through Peekaboo)
|
|
74
78
|
1. \`mac_see\` is the entry point: it takes a screenshot of the screen (or of one app window with
|
|
@@ -78,6 +82,8 @@ ${tools.join('\n')}
|
|
|
78
82
|
4. If a capture or a click fails, run \`mac_permissions\` — macOS needs Screen Recording and
|
|
79
83
|
Accessibility permissions for the terminal that runs the bridge.
|
|
80
84
|
5. Screenshots are saved as PNG inside the authorized roots; read them back with \`file_get\`.
|
|
85
|
+
6. Stay out of the user's way: do not pass \`foreground\`, do not focus windows and do not move the
|
|
86
|
+
pointer unless the user asks for it. Background actions are delivered to the target window only.
|
|
81
87
|
|
|
82
88
|
## Approvals
|
|
83
89
|
${trustLine}
|