@aryacode/dsh-plugin-arya-computer-use 1.0.3 → 1.0.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/client.js +8 -5
- package/index.js +1 -0
- package/mcp/browser-launcher.mjs +69 -2
- package/mcp/settings.mjs +7 -4
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -14,8 +14,8 @@ window.__ModuleLoader__.load({
|
|
|
14
14
|
remoteIndicatorVisible: true, indicatorText: 'Arya is controlling your computer', pointerLabel: 'Arya', indicatorPosition: 'top',
|
|
15
15
|
indicatorIdleMs: 800, stopShortcut: '', resumeToken: 0, previewToken: 0,
|
|
16
16
|
headedDefault: false, overlayDefault: true, driverLockEnabled: true,
|
|
17
|
-
toolPreset: 'lite',
|
|
18
|
-
browserTools: 'core', browserProfile: '', browserSession: '', browserStateFile: '', browserCdp: '', browserHeaded: false,
|
|
17
|
+
toolPreset: 'lite',
|
|
18
|
+
browserTools: 'core', browserProfile: '', browserSession: '', browserStateFile: '', browserCdp: '', browserHeaded: false, browserToolFlags: 'strip',
|
|
19
19
|
};
|
|
20
20
|
const equal = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
21
21
|
const dictionaries = {
|
|
@@ -36,11 +36,12 @@ window.__ModuleLoader__.load({
|
|
|
36
36
|
progressOpacity: '背景不透明度(%)', progressWidth: '宽度(像素)', progressHeight: '高度(像素)',
|
|
37
37
|
progressPosition: '浮窗位置', progress_topRight: '右上', progress_topLeft: '左上', progress_bottomRight: '右下', progress_bottomLeft: '左下', progress_custom: '自定义',
|
|
38
38
|
progressX: 'X 坐标', progressY: 'Y 坐标', progressShortcut: '浮窗解锁快捷键', progressShortcutHint: '按下后临时解锁浮窗以便拖动。留空表示不设置。',
|
|
39
|
-
toolSurfaceTitle: '工具范围', toolPreset: '工具预设', toolPresetHint: 'lite 直接列出全部 16 个工具;lazy 只暴露 tools / snapshot / click,其余按需加载。修改后需重启 MCP 生效。', toolPresetLite: 'lite · 全部 16 个', toolPresetLazy: 'lazy · 按需加载',
|
|
39
|
+
toolSurfaceTitle: '工具范围', toolPreset: '工具预设', toolPresetHint: 'lite 直接列出全部 16 个工具;lazy 只暴露 tools / snapshot / click,其余按需加载。修改后需重启 MCP 生效。', toolPresetLite: 'lite · 全部 16 个', toolPresetLazy: 'lazy · 按需加载',
|
|
40
40
|
behaviorTitle: '操作方式', headedDefault: 'Real mouse and keyboard', headedDefaultHint: 'Off by default: input goes through background accessibility patterns and never moves your pointer or steals focus. Turn on to use real keyboard/mouse input, which briefly focuses the target window.',
|
|
41
41
|
overlayDefault: '引擎会话叠加层', overlayDefaultHint: '启动桌面会话时启用引擎自带的光标叠加层。Arya 的虚拟光标不依赖它。',
|
|
42
42
|
driverLock: '单会话驱动', driverLockHint: '同一时间只允许一个对话操作桌面;其他对话调用桌面工具会收到 BUSY 提示。关闭后最近使用的对话自动接管。',
|
|
43
43
|
browserTitle: '网页通道', browserTools: '工具范围', browserToolsHint: 'agent-browser 向 MCP 暴露的工具集。修改后需重启 MCP 生效。',
|
|
44
|
+
browserFlags: '精简工具定义', browserFlagsHint: '插件启动网页通道时已经设好域名、证书、会话等全局参数,每个工具里重复的这些参数可以删掉:strip 全部删除(约省 1.8 万 token),lean 保留会话与状态恢复相关项,off 保持 agent-browser 原样。修改后需重启 MCP 生效。', browserFlagsStrip: 'strip · 全部删除', browserFlagsLean: 'lean · 保留会话项', browserFlagsOff: 'off · 保持原样',
|
|
44
45
|
browserProfile: 'Chrome 配置', browserProfileHint: '配置名称(如 Default)或目录路径,让网页通道复用你的登录状态。',
|
|
45
46
|
browserSession: '会话名', browserSessionHint: '隔离的会话键;不同会话名的 Cookie 与标签页彼此独立。',
|
|
46
47
|
browserCdp: '连接我的浏览器', browserCdpHint: 'CDP 地址,例如 http://127.0.0.1:9222。用 --remote-debugging-port=9222 启动 Chrome 即可共享已登录的窗口。',
|
|
@@ -87,11 +88,12 @@ window.__ModuleLoader__.load({
|
|
|
87
88
|
progressOpacity: 'Background opacity (%)', progressWidth: 'Width (px)', progressHeight: 'Height (px)',
|
|
88
89
|
progressPosition: 'Overlay position', progress_topRight: 'Top right', progress_topLeft: 'Top left', progress_bottomRight: 'Bottom right', progress_bottomLeft: 'Bottom left', progress_custom: 'Custom',
|
|
89
90
|
progressX: 'X coordinate', progressY: 'Y coordinate', progressShortcut: 'Overlay unlock shortcut', progressShortcutHint: 'Press to temporarily unlock the overlay so you can drag it. Empty means none.',
|
|
90
|
-
toolSurfaceTitle: 'Tool surface', toolPreset: 'Tool preset', toolPresetHint: 'lite lists all 16 tools up front; lazy exposes tools / snapshot / click and loads the rest on demand. Changes apply after the next MCP restart.', toolPresetLite: 'lite · all 16', toolPresetLazy: 'lazy · load on demand',
|
|
91
|
+
toolSurfaceTitle: 'Tool surface', toolPreset: 'Tool preset', toolPresetHint: 'lite lists all 16 tools up front; lazy exposes tools / snapshot / click and loads the rest on demand. Changes apply after the next MCP restart.', toolPresetLite: 'lite · all 16', toolPresetLazy: 'lazy · load on demand',
|
|
91
92
|
behaviorTitle: 'Control style', headedDefault: 'Real mouse and keyboard', headedDefaultHint: 'Off by default: input goes through background accessibility patterns and never moves your pointer or steals focus. Turn on to use real keyboard/mouse input, which briefly focuses the target window.',
|
|
92
93
|
overlayDefault: 'Engine session overlay', overlayDefaultHint: "Starts the desktop session with the engine's own cursor overlay. Arya's ghost cursor does not depend on it; leave it off unless you want the engine marker too.",
|
|
93
94
|
driverLock: 'One conversation drives at a time', driverLockHint: 'While one conversation is driving, desktop calls from other conversations receive a BUSY notice. Turn off to let the most recent conversation take over automatically.',
|
|
94
95
|
browserTitle: 'Web lane', browserTools: 'Tool surface', browserToolsHint: 'Which agent-browser tool profiles the MCP server exposes. Changing this applies on the next MCP restart.',
|
|
96
|
+
browserFlags: 'Trim tool schemas', browserFlagsHint: 'The plugin already sets domains, certificates, session and other host-wide flags when it starts the web lane, so the copies repeated inside every tool can go: strip removes them all (about 18k tokens saved), lean keeps the session and state-restore ones, off leaves the agent-browser schemas untouched. Changes apply after the next MCP restart.', browserFlagsStrip: 'strip · remove all', browserFlagsLean: 'lean · keep session flags', browserFlagsOff: 'off · keep as shipped',
|
|
95
97
|
browserProfile: 'Chrome profile', browserProfileHint: 'A profile name such as Default, or a directory path, so the web lane reuses your sign-ins.',
|
|
96
98
|
browserSession: 'Session', browserSessionHint: 'An isolated session key; cookies and tabs stay separate per name.',
|
|
97
99
|
browserCdp: 'Connect to my browser', browserCdpHint: 'A CDP endpoint such as http://127.0.0.1:9222. Start Chrome with --remote-debugging-port=9222 to share a signed-in window.',
|
|
@@ -350,12 +352,13 @@ window.__ModuleLoader__.load({
|
|
|
350
352
|
h('div', { className: 'arya-grid' },
|
|
351
353
|
switchItem('headedDefault', 'headedDefault', 'headedDefaultHint', arts.keyboard(draft.headedDefault)),
|
|
352
354
|
switchItem('overlayDefault', 'overlayDefault', 'overlayDefaultHint', arts.cursor(draft.overlayDefault)),
|
|
353
|
-
switchItem('driverLockEnabled', 'driverLock', 'driverLockHint', arts.shield(draft.driverLockEnabled)),
|
|
355
|
+
switchItem('driverLockEnabled', 'driverLock', 'driverLockHint', arts.shield(draft.driverLockEnabled)),
|
|
354
356
|
field(title(t('toolPreset'), 'toolPresetHint', { htmlFor: idPrefix + '-toolPreset' }), h('select', { id: idPrefix + '-toolPreset', value: draft.toolPreset, disabled, onChange: (e) => change('toolPreset', e.target.value) }, h('option', { value: 'lite' }, t('toolPresetLite')), h('option', { value: 'lazy' }, t('toolPresetLazy')))))),
|
|
355
357
|
h('section', { className: 'arya-section', 'aria-labelledby': idPrefix + '-browser-title' },
|
|
356
358
|
head(idPrefix + '-browser-title', 'browserTitle'),
|
|
357
359
|
h('div', { className: 'arya-grid' },
|
|
358
360
|
field(title(t('browserTools'), 'browserToolsHint', { htmlFor: idPrefix + '-browserTools' }), h('select', { id: idPrefix + '-browserTools', value: draft.browserTools, disabled, onChange: (e) => change('browserTools', e.target.value) }, ...['core', 'network', 'state', 'debug', 'tabs', 'react', 'mobile', 'all'].map((key) => h('option', { key, value: key }, key)))),
|
|
361
|
+
field(title(t('browserFlags'), 'browserFlagsHint', { htmlFor: idPrefix + '-browserToolFlags' }), h('select', { id: idPrefix + '-browserToolFlags', value: draft.browserToolFlags, disabled, onChange: (e) => change('browserToolFlags', e.target.value) }, ...[['strip', 'browserFlagsStrip'], ['lean', 'browserFlagsLean'], ['off', 'browserFlagsOff']].map(([value, label]) => h('option', { key: value, value }, t(label))))),
|
|
359
362
|
switchItem('browserHeaded', 'browserHeaded', 'browserHeadedHint', arts.monitor && arts.monitor(draft.browserHeaded) || arts.cursor(draft.browserHeaded))),
|
|
360
363
|
h('div', { className: 'arya-fields' },
|
|
361
364
|
field(title(t('browserProfile'), 'browserProfileHint', { htmlFor: idPrefix + '-browserProfile' }), h('input', { id: idPrefix + '-browserProfile', type: 'text', maxLength: 200, placeholder: 'Default', value: draft.browserProfile, disabled, onChange: (e) => change('browserProfile', e.target.value) })),
|
package/index.js
CHANGED
|
@@ -223,6 +223,7 @@ const fields = {
|
|
|
223
223
|
driverLockEnabled: z.boolean().default(true).description('One conversation drives the desktop at a time'),
|
|
224
224
|
toolPreset: z.union(['lite', 'lazy'].map(value => z.const(value))).default('lite').description('Tool surface size: lite lists all 16 tools; lazy starts with tools/snapshot/click and reveals the rest on demand'),
|
|
225
225
|
browserTools: z.union(['core', 'network', 'state', 'debug', 'tabs', 'react', 'mobile', 'all'].map(value => z.const(value))).default('core').description('agent-browser tool profiles exposed on the MCP row'),
|
|
226
|
+
browserToolFlags: z.union(['strip', 'lean', 'off'].map(value => z.const(value))).default('strip').description('Web lane schema size: strip drops the host/launch flags the plugin sets at launch from every tool, lean keeps session/restore/timeout, off keeps the agent-browser schemas unchanged'),
|
|
226
227
|
browserProfile: z.string().default('').description('Chrome profile name or directory the web lane reuses for sign-ins'),
|
|
227
228
|
browserSession: z.string().default('').description('Isolated browser session key'),
|
|
228
229
|
browserStateFile: z.string().default('').description('Saved sign-in state file for the web lane'),
|
package/mcp/browser-launcher.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
19
19
|
import { createRequire } from 'node:module';
|
|
20
20
|
import { homedir, platform, arch } from 'node:os';
|
|
21
21
|
import { dirname, join } from 'node:path';
|
|
22
|
+
import { Transform } from 'node:stream';
|
|
22
23
|
|
|
23
24
|
const require = createRequire(import.meta.url);
|
|
24
25
|
const SHIM = require.resolve('agent-browser/bin/agent-browser.js');
|
|
@@ -57,6 +58,69 @@ function readSettings() {
|
|
|
57
58
|
|
|
58
59
|
const text = (value) => (typeof value === 'string' ? value.trim() : '');
|
|
59
60
|
|
|
61
|
+
// Every agent-browser tool repeats the same host/launch flags in its schema, and
|
|
62
|
+
// the plugin already sets them on the server command line below. They are worth
|
|
63
|
+
// about 630 tokens per tool, so the relay rewrites tools/list responses to drop
|
|
64
|
+
// them. agent-browser rejects undeclared argument keys with an invalid-params
|
|
65
|
+
// error naming the key, so a caller that passes a dropped flag is told, not
|
|
66
|
+
// silently mis-ruled.
|
|
67
|
+
export const FLAG_KEYS_HOST = ['extraArgs', 'caCert', 'clearCaCert', 'useSystemCa', 'allowedDomains', 'namespace', 'idleTimeout'];
|
|
68
|
+
export const FLAG_KEYS_REST = ['restore', 'restoreSave', 'restoreCheckFn', 'restoreCheckText', 'restoreCheckUrl', 'session', 'timeoutMs'];
|
|
69
|
+
|
|
70
|
+
export function flagKeys(mode) {
|
|
71
|
+
if (mode === 'lean') return FLAG_KEYS_HOST;
|
|
72
|
+
if (mode === 'off') return [];
|
|
73
|
+
return [...FLAG_KEYS_HOST, ...FLAG_KEYS_REST];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function stripToolFlags(tools, keys) {
|
|
77
|
+
const drop = new Set(keys);
|
|
78
|
+
let removed = 0;
|
|
79
|
+
for (const tool of Array.isArray(tools) ? tools : []) {
|
|
80
|
+
const schema = tool?.inputSchema;
|
|
81
|
+
if (schema?.properties) {
|
|
82
|
+
for (const key of drop) if (key in schema.properties) { delete schema.properties[key]; removed++; }
|
|
83
|
+
}
|
|
84
|
+
if (Array.isArray(schema?.required)) {
|
|
85
|
+
const kept = schema.required.filter((key) => !drop.has(key));
|
|
86
|
+
removed += schema.required.length - kept.length;
|
|
87
|
+
schema.required = kept;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return removed;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// MCP stdio is one JSON message per line, so the relay transforms line by line.
|
|
94
|
+
// Anything that is not a tools/list response is forwarded byte-for-byte.
|
|
95
|
+
export function transformLine(line, drop) {
|
|
96
|
+
if (!line.includes('"tools"')) return line;
|
|
97
|
+
let message;
|
|
98
|
+
try { message = JSON.parse(line); } catch { return line; }
|
|
99
|
+
if (!Array.isArray(message?.result?.tools)) return line;
|
|
100
|
+
return stripToolFlags(message.result.tools, drop) ? JSON.stringify(message) : line;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function createFlagStripTransform(keys) {
|
|
104
|
+
const drop = new Set(keys);
|
|
105
|
+
let pending = '';
|
|
106
|
+
return new Transform({
|
|
107
|
+
transform(chunk, encoding, callback) {
|
|
108
|
+
pending += chunk.toString('utf8');
|
|
109
|
+
let cut = pending.indexOf('\n');
|
|
110
|
+
while (cut >= 0) {
|
|
111
|
+
this.push(`${transformLine(pending.slice(0, cut), drop)}\n`);
|
|
112
|
+
pending = pending.slice(cut + 1);
|
|
113
|
+
cut = pending.indexOf('\n');
|
|
114
|
+
}
|
|
115
|
+
callback();
|
|
116
|
+
},
|
|
117
|
+
flush(callback) {
|
|
118
|
+
if (pending) this.push(transformLine(pending, drop));
|
|
119
|
+
callback();
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
60
124
|
export function browserArgs(settings = {}) {
|
|
61
125
|
const args = [SHIM, 'mcp'];
|
|
62
126
|
const tools = text(settings.browserTools);
|
|
@@ -74,7 +138,9 @@ export function browserArgs(settings = {}) {
|
|
|
74
138
|
}
|
|
75
139
|
|
|
76
140
|
function main() {
|
|
77
|
-
const
|
|
141
|
+
const settings = readSettings();
|
|
142
|
+
const args = browserArgs(settings);
|
|
143
|
+
const flagKeys_ = flagKeys(text(settings.browserToolFlags));
|
|
78
144
|
const native = nativeBin();
|
|
79
145
|
const child = native
|
|
80
146
|
? spawn(native, args.slice(1), { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true })
|
|
@@ -85,7 +151,8 @@ function main() {
|
|
|
85
151
|
});
|
|
86
152
|
child.on('exit', (code, signal) => process.exit(code ?? (signal ? 1 : 0)));
|
|
87
153
|
process.stdin.pipe(child.stdin);
|
|
88
|
-
child.stdout.pipe(process.stdout);
|
|
154
|
+
if (flagKeys_.length) child.stdout.pipe(createFlagStripTransform(flagKeys_)).pipe(process.stdout);
|
|
155
|
+
else child.stdout.pipe(process.stdout);
|
|
89
156
|
child.stderr.pipe(process.stderr);
|
|
90
157
|
child.stdin.on('error', () => { /* child gone mid-write; the exit handler ends us */ });
|
|
91
158
|
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
|
package/mcp/settings.mjs
CHANGED
|
@@ -10,6 +10,7 @@ export const DEFAULT_SETTINGS = Object.freeze({
|
|
|
10
10
|
resumeToken: 0, previewToken: 0,
|
|
11
11
|
headedDefault: false, overlayDefault: true, driverLockEnabled: true, toolPreset: 'lite',
|
|
12
12
|
browserTools: 'core', browserProfile: '', browserSession: '', browserStateFile: '', browserCdp: '', browserHeaded: false,
|
|
13
|
+
browserToolFlags: 'strip',
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
export const STOP_SHORTCUT_PATTERN = /^(?:(?:Ctrl\+)?(?:Alt\+)?(?:Shift\+)?(?:Win\+)?(?:[A-Z0-9]|F(?:[1-9]|1[0-9]|2[0-4])|Space|Enter|Tab|Backspace|Delete|Insert|Home|End|PageUp|PageDown|Up|Down|Left|Right|Pause|CapsLock|NumLock|ScrollLock))?$/;
|
|
@@ -37,6 +38,7 @@ const SETTING_SWITCHES = ['progressOverlayEnabled', 'indicatorEnabled', 'cursorI
|
|
|
37
38
|
SETTING_CHOICES.progressOverlayPosition = ['top-right','top-left','bottom-right','bottom-left','custom'];
|
|
38
39
|
SETTING_CHOICES.progressOverlayMode = ['source','summary'];
|
|
39
40
|
SETTING_CHOICES.browserTools = ['core', 'network', 'state', 'debug', 'tabs', 'react', 'mobile', 'all'];
|
|
41
|
+
SETTING_CHOICES.browserToolFlags = ['strip', 'lean', 'off'];
|
|
40
42
|
SETTING_CHOICES.toolPreset = ['lite', 'lazy'];
|
|
41
43
|
export function repairStaleSettings(input) {
|
|
42
44
|
if (!input || typeof input !== 'object' || Array.isArray(input)) return { value: input, repaired: [] };
|
|
@@ -56,10 +58,10 @@ export function normalizeSettings(input = {}) {
|
|
|
56
58
|
if (typeof value.indicatorText !== 'string' || !value.indicatorText.trim() || value.indicatorText.length > 120 || /[\r\n\x00-\x1f]/.test(value.indicatorText)) {
|
|
57
59
|
throw new Error('indicatorText must be one line of 1–120 characters');
|
|
58
60
|
}
|
|
59
|
-
if (typeof value.pointerLabel !== 'string' || !value.pointerLabel.trim() || value.pointerLabel.trim().length > 16 || /[\r\n\x00-\x1f]/.test(value.pointerLabel)) {
|
|
60
|
-
throw new Error('pointerLabel must be one line of 1–16 characters');
|
|
61
|
-
}
|
|
62
|
-
value.pointerLabel = value.pointerLabel.trim();
|
|
61
|
+
if (typeof value.pointerLabel !== 'string' || !value.pointerLabel.trim() || value.pointerLabel.trim().length > 16 || /[\r\n\x00-\x1f]/.test(value.pointerLabel)) {
|
|
62
|
+
throw new Error('pointerLabel must be one line of 1–16 characters');
|
|
63
|
+
}
|
|
64
|
+
value.pointerLabel = value.pointerLabel.trim();
|
|
63
65
|
if (!['top', 'bottom'].includes(value.indicatorPosition)) throw new Error('indicatorPosition must be top or bottom');
|
|
64
66
|
if (!Number.isInteger(value.indicatorIdleMs) || value.indicatorIdleMs < 200 || value.indicatorIdleMs > 3000) throw new Error('indicatorIdleMs must be 200–3000');
|
|
65
67
|
value.stopShortcut = normalizeStopShortcut(value.stopShortcut);
|
|
@@ -72,6 +74,7 @@ export function normalizeSettings(input = {}) {
|
|
|
72
74
|
if (!SETTING_CHOICES.progressOverlayMode.includes(value.progressOverlayMode)) throw new Error('Invalid progressOverlayMode');
|
|
73
75
|
if (!SETTING_CHOICES.toolPreset.includes(value.toolPreset)) throw new Error('toolPreset must be one of ' + SETTING_CHOICES.toolPreset.join(', '));
|
|
74
76
|
if (!SETTING_CHOICES.browserTools.includes(value.browserTools)) throw new Error('browserTools must be one of ' + SETTING_CHOICES.browserTools.join(', '));
|
|
77
|
+
if (!SETTING_CHOICES.browserToolFlags.includes(value.browserToolFlags)) throw new Error('browserToolFlags must be one of ' + SETTING_CHOICES.browserToolFlags.join(', '));
|
|
75
78
|
for (const key of ['browserProfile', 'browserSession', 'browserStateFile', 'browserCdp']) {
|
|
76
79
|
if (typeof value[key] !== 'string') throw new Error(`${key} must be a string`);
|
|
77
80
|
if (/[\r\n\x00-\x1f]/.test(value[key])) throw new Error(`${key} must be one line`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aryacode/dsh-plugin-arya-computer-use",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Computer use for DSH/AryaCode: agent-desktop UIA engine, agent-browser web lane, takeover UI with a ghost cursor",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|