arena-bridge 0.6.3 → 0.6.4
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 +10 -0
- package/README.md +2 -1
- package/package.json +1 -1
- package/src/direct.mjs +1 -1
- package/src/hub.mjs +1 -1
- package/src/mac.mjs +68 -31
- package/src/mcp.mjs +1 -1
- package/src/prompt.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ 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.4
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- The accessibility element map is read from Peekaboo's real payload (`data.ui_elements`), so
|
|
11
|
+
`mac_see` and `mac_element` now return the controls of a window instead of an empty list.
|
|
12
|
+
Elements carry their label, description, identifier and value-settable flag.
|
|
13
|
+
- Commands follow the Peekaboo v4 grammar: `set-value "<value>" --on <id>`, `menu click --item|--path`
|
|
14
|
+
with an explicit app, `space switch --to <n> --foreground`, and the tree-only read
|
|
15
|
+
`see --tree --no-screenshot --json` (no screenshot artifact for pure lookups).
|
|
16
|
+
|
|
7
17
|
## 0.6.3
|
|
8
18
|
|
|
9
19
|
### Added
|
package/README.md
CHANGED
|
@@ -150,7 +150,7 @@ cookies и localStorage нет.
|
|
|
150
150
|
{"tool": "mac_set", "args": {"on": "B2", "value": "hello"}} // вписать значение напрямую
|
|
151
151
|
{"tool": "mac_type", "args": {"text": "hello", "submit": true}}
|
|
152
152
|
{"tool": "mac_key", "args": {"key": "cmd+s"}}
|
|
153
|
-
{"tool": "mac_menu_click", "args": {"
|
|
153
|
+
{"tool": "mac_menu_click", "args": {"app": "Safari", "item": "Save"}} // пункт меню по названию
|
|
154
154
|
{"tool": "mac_wait", "args": {"name": "Saved", "timeout": 20}} // дождаться результата
|
|
155
155
|
{"tool": "mac_apps", "args": {}} // что запущено
|
|
156
156
|
{"tool": "mac_permissions", "args": {}} // чего не хватает в macOS
|
|
@@ -172,6 +172,7 @@ macOS отдаёт приложениям два разных слоя: карт
|
|
|
172
172
|
| как вписать текст | свайп, фокус, клавиатура | `mac_set {"on":"<id>","value":"..."}` — без фокуса и без клавиш |
|
|
173
173
|
| как открыть «Файл → Экспорт» | перемещать курсор по меню | `mac_menu_click {"item":"Экспорт"}` — по названию |
|
|
174
174
|
| как убедиться, что получилось | смотреть скриншот | `mac_wait {"name":"Готово"}` — ждать появления |
|
|
175
|
+
| как заполнить поле | свайп + клавиатура + фокус | `mac_set {"on":"<id>","value":"..."}` — значение пишется в элемент |
|
|
175
176
|
| чужой ввод и фокус | мешает вам | действия уходят в фоновое окно |
|
|
176
177
|
|
|
177
178
|
Практический вывод: с выданным Accessibility агент работает заметно точнее и тише. Разрешение
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arena-bridge",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
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/direct.mjs
CHANGED
package/src/hub.mjs
CHANGED
package/src/mac.mjs
CHANGED
|
@@ -100,21 +100,31 @@ function parseJson(text) {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
function collectElements(node, out = [], depth = 0) {
|
|
103
|
-
if (!node || depth >
|
|
103
|
+
if (!node || depth > 8) return out;
|
|
104
104
|
if (Array.isArray(node)) {
|
|
105
105
|
for (const n of node) collectElements(n, out, depth + 1);
|
|
106
106
|
return out;
|
|
107
107
|
}
|
|
108
108
|
if (typeof node !== 'object') return out;
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
for (const key of ['ui_elements', 'elements']) {
|
|
110
|
+
const list = node[key];
|
|
111
|
+
if (!Array.isArray(list) || !list.length) continue;
|
|
112
|
+
for (const e of list) {
|
|
113
|
+
if (!e || typeof e !== 'object') continue;
|
|
114
|
+
const value = e.value;
|
|
111
115
|
out.push({
|
|
112
|
-
id: e.id ?? e.
|
|
113
|
-
role: e.role ?? e.elementRole ?? e.type ?? null,
|
|
116
|
+
id: e.id ?? e.element_id ?? e.elementId ?? null,
|
|
117
|
+
role: e.role ?? e.ax_role ?? e.elementRole ?? e.type ?? null,
|
|
114
118
|
title: e.title ?? e.label ?? e.name ?? e.placeholder ?? null,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
label: e.label ?? null,
|
|
120
|
+
description: e.description ?? null,
|
|
121
|
+
identifier: e.identifier ?? null,
|
|
122
|
+
value: typeof value === 'string' ? value.slice(0, 200) : (value ?? null),
|
|
123
|
+
bounds: e.bounds ?? e.frame ?? e.rect ?? null,
|
|
124
|
+
enabled: e.is_enabled ?? e.enabled ?? null,
|
|
125
|
+
actionable: e.is_actionable ?? null,
|
|
126
|
+
settable: e.is_value_settable ?? null,
|
|
127
|
+
shortcut: e.keyboard_shortcut ?? null,
|
|
118
128
|
});
|
|
119
129
|
}
|
|
120
130
|
return out;
|
|
@@ -178,10 +188,16 @@ export function macTools(cfg = {}) {
|
|
|
178
188
|
const seeVariants = (args, outPath) => {
|
|
179
189
|
const mode = args.mode === 'window' ? 'window' : 'screen';
|
|
180
190
|
const base = ['see', '--mode', mode, ...targetFlags(args)];
|
|
191
|
+
if (!outPath) {
|
|
192
|
+
return [
|
|
193
|
+
['see', '--tree', '--no-screenshot', '--json', ...targetFlags(args)],
|
|
194
|
+
['see', '--tree', '--json', ...targetFlags(args)],
|
|
195
|
+
['see', '--json', ...targetFlags(args)],
|
|
196
|
+
];
|
|
197
|
+
}
|
|
181
198
|
return [
|
|
182
|
-
[...base, '--json', '--path', outPath, '--snapshot'],
|
|
183
|
-
[...base, '--json', '--path', outPath, '--tree'],
|
|
184
199
|
[...base, '--json', '--path', outPath],
|
|
200
|
+
[...base, '--path', outPath, '--json'],
|
|
185
201
|
['image', '--mode', mode, ...targetFlags(args), '--json', '--path', outPath],
|
|
186
202
|
];
|
|
187
203
|
};
|
|
@@ -202,7 +218,10 @@ export function macTools(cfg = {}) {
|
|
|
202
218
|
|
|
203
219
|
const matchElement = (e, args) => {
|
|
204
220
|
const q = (v) => String(v ?? '').toLowerCase();
|
|
205
|
-
|
|
221
|
+
const text = [e.title, e.label, e.description, e.identifier, e.value, e.id]
|
|
222
|
+
.map(q)
|
|
223
|
+
.join('\n');
|
|
224
|
+
if (args.name && !text.includes(q(args.name))) return false;
|
|
206
225
|
if (args.role && !q(e.role).includes(q(args.role))) return false;
|
|
207
226
|
if (args.value && !q(e.value).includes(q(args.value))) return false;
|
|
208
227
|
return true;
|
|
@@ -210,11 +229,8 @@ export function macTools(cfg = {}) {
|
|
|
210
229
|
|
|
211
230
|
const searchPath = () => path.join(os.tmpdir(), 'arena-bridge-see', 'see.png');
|
|
212
231
|
|
|
213
|
-
const searchSee =
|
|
214
|
-
|
|
215
|
-
ensureDir(path.dirname(out));
|
|
216
|
-
return collectSee({ app: args.app, window_title: args.window_title, mode: args.mode || 'screen' }, out);
|
|
217
|
-
};
|
|
232
|
+
const searchSee = (args) =>
|
|
233
|
+
collectSee({ app: args.app, window_title: args.window_title, mode: args.mode || 'screen' }, null);
|
|
218
234
|
|
|
219
235
|
return [
|
|
220
236
|
{
|
|
@@ -607,9 +623,10 @@ export function macTools(cfg = {}) {
|
|
|
607
623
|
const res = await runVariants(
|
|
608
624
|
p.bin,
|
|
609
625
|
[
|
|
626
|
+
['set-value', String(args.value), '--on', String(args.on), ...flags],
|
|
627
|
+
['set-value', String(args.value), '--on', String(args.on)],
|
|
610
628
|
['set-value', '--on', String(args.on), String(args.value), ...flags],
|
|
611
629
|
['set-value', '--on', String(args.on), '--value', String(args.value), ...flags],
|
|
612
|
-
['set-value', String(args.on), String(args.value), ...flags],
|
|
613
630
|
],
|
|
614
631
|
{ timeout: 30 },
|
|
615
632
|
);
|
|
@@ -645,22 +662,42 @@ export function macTools(cfg = {}) {
|
|
|
645
662
|
},
|
|
646
663
|
{
|
|
647
664
|
name: 'mac_menu_click',
|
|
648
|
-
description:
|
|
665
|
+
description:
|
|
666
|
+
'Trigger a menu item by name (e.g. "Save", "Copy Link") or a nested path ("File > Export") — no pointer, no coordinates.',
|
|
649
667
|
approval: true,
|
|
650
|
-
schema: S(
|
|
651
|
-
|
|
668
|
+
schema: S(
|
|
669
|
+
{
|
|
670
|
+
item: { type: 'string', description: 'name of the menu item' },
|
|
671
|
+
path: { type: 'string', description: 'nested path, e.g. "File > Export"' },
|
|
672
|
+
app: { type: 'string', description: 'required: the background menu click needs a target app' },
|
|
673
|
+
},
|
|
674
|
+
[],
|
|
675
|
+
),
|
|
676
|
+
required: [],
|
|
652
677
|
handler: async (args) => {
|
|
653
678
|
const p = checkPeek(peek());
|
|
654
679
|
if (!p.ok) return p;
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
680
|
+
if (!args.item && !args.path) {
|
|
681
|
+
return { ok: false, error: 'missing_item', hint: 'Pass item ("Save") or path ("File > Export").' };
|
|
682
|
+
}
|
|
683
|
+
if (!args.app) {
|
|
684
|
+
return {
|
|
685
|
+
ok: false,
|
|
686
|
+
error: 'missing_app',
|
|
687
|
+
hint: 'A background menu click needs an explicit app: mac_menu_click {"app":"Safari","item":"Save"}.',
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
const flags = ['--app', String(args.app)];
|
|
691
|
+
const variants = args.path
|
|
692
|
+
? [
|
|
693
|
+
['menu', 'click', '--path', String(args.path), ...flags],
|
|
694
|
+
['menu', 'click', String(args.path), ...flags],
|
|
695
|
+
]
|
|
696
|
+
: [
|
|
697
|
+
['menu', 'click', '--item', String(args.item), ...flags],
|
|
698
|
+
['menu', 'click', String(args.item), ...flags],
|
|
699
|
+
];
|
|
700
|
+
const res = await runVariants(p.bin, variants, { timeout: 30 });
|
|
664
701
|
return finish(res, 'menu-click');
|
|
665
702
|
},
|
|
666
703
|
},
|
|
@@ -703,8 +740,8 @@ export function macTools(cfg = {}) {
|
|
|
703
740
|
const res = await runVariants(
|
|
704
741
|
p.bin,
|
|
705
742
|
[
|
|
706
|
-
['space', 'switch', '--to', String(args.to)],
|
|
707
|
-
['space', 'switch', String(args.to)],
|
|
743
|
+
['space', 'switch', '--to', String(args.to), '--foreground'],
|
|
744
|
+
['space', 'switch', String(args.to), '--foreground'],
|
|
708
745
|
],
|
|
709
746
|
{ timeout: 30 },
|
|
710
747
|
);
|
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.6.
|
|
51
|
+
serverInfo: { name: 'arena-bridge', version: '0.6.4' },
|
|
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
|
@@ -92,7 +92,7 @@ ${tools.join('\n')}
|
|
|
92
92
|
6. Stay out of the user's way: do not pass \`foreground\`, do not focus windows and do not move the
|
|
93
93
|
pointer unless the user asks for it. Background actions are delivered to the target window only.
|
|
94
94
|
7. \`mac_set\` writes a value straight into a control (no keystrokes, no focus change);
|
|
95
|
-
\`mac_menu_click\` triggers a menu item by its name — both are more reliable than typing.
|
|
95
|
+
\`mac_menu_click\` triggers a menu item by its name (it needs the \`app\` name) — both are more reliable than typing.
|
|
96
96
|
8. \`mac_wait\` waits for a control to appear or disappear instead of sleeping and guessing.
|
|
97
97
|
9. \`mac_spaces\` lists the macOS virtual desktops; \`mac_space_switch\` moves the agent to another
|
|
98
98
|
Space so the user keeps the screen they are looking at.
|