machine-bridge-mcp 0.13.0 → 0.14.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 +19 -0
- package/README.md +38 -9
- package/SECURITY.md +3 -3
- package/browser-extension/devtools-input.js +136 -0
- package/browser-extension/manifest.json +3 -2
- package/browser-extension/page-automation.js +309 -29
- package/browser-extension/service-worker.js +108 -5
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/LOCAL_AUTOMATION.md +13 -9
- package/docs/OPERATIONS.md +12 -5
- package/docs/TESTING.md +4 -4
- package/package.json +8 -4
- package/src/local/agent-context.mjs +1 -1
- package/src/local/browser-bridge.mjs +186 -88
- package/src/local/browser-command.mjs +126 -0
- package/src/local/cli.mjs +21 -3
- package/src/local/runtime.mjs +2 -0
- package/src/shared/tool-catalog.json +208 -2
- package/src/worker/index.ts +14 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.14.0 - 2026-07-13
|
|
4
|
+
|
|
5
|
+
### Windows installation and OAuth callback reliability
|
|
6
|
+
|
|
7
|
+
- Bootstrap installation through pinned npm 12.0.1 from an empty temporary directory before using `--allow-scripts`, declare the npm 12 requirement in package `engines`, and make `machine-mcp doctor` verify the active npm version. This removes the unsupported old-npm path shown by Windows and gives accurate diagnostics for `Unknown cli config "--allow-scripts"` and legacy `devEngines.node` errors without guessing which package supplied the invalid metadata.
|
|
8
|
+
- Construct OAuth callback destinations with the URL API and return `303 See Other` after consent instead of manually concatenating a `Location` string. Add an end-to-end ChatGPT callback regression with reserved state characters and exact origin/path checks.
|
|
9
|
+
- Extend the isolated install smoke test to require npm 12, install from an empty directory, verify the packaged npm engine requirement, and run the test on Linux, macOS, and Windows CI.
|
|
10
|
+
|
|
11
|
+
### Existing-profile browser automation
|
|
12
|
+
|
|
13
|
+
- Keep the authenticated Manifest V3 extension as the primary browser backend so automation operates the user's ordinary Chromium profile, tabs, extensions, cookies, and login state instead of launching a separate profile. Add tab creation/activation/closure and explicit combined waits for URL, document readiness, page text, and element state.
|
|
14
|
+
- Replace fragile inspect-then-selector flows with bounded semantic snapshots containing stable per-document/frame element references, visibility/enabled/editable state, and viewport geometry. Page actions now wait for attachment, visibility, enabled/editable state, geometric stability, and unobscured pointer hit targets; ambiguous selectors and stale references fail explicitly.
|
|
15
|
+
- Add double-click, hover, append-text, and scroll-into-view actions. Top-frame click, double-click, hover, key press, and text input can use fixed short-lived Chromium DevTools Input commands through explicit `auto`, `trusted`, or `dom` modes. The extension exposes no caller-selected CDP method or arbitrary JavaScript and detaches the debugger in `finally`.
|
|
16
|
+
|
|
17
|
+
### Architecture, security, tests, and documentation
|
|
18
|
+
|
|
19
|
+
- Extract browser command normalization into a focused local module and isolate trusted input in a fixed extension module. Add a versioned extension capability handshake, stale-build reload guidance, explicit keepalive handling, and replacement validation that preserves the current compatible connection until the candidate is accepted; accepted replacements reject in-flight direct and proxied requests with retry guidance instead of leaving them to time out. Preserve resource-backed secrets/files, strict action/value validation, bounded broker messages, cancellation, source limits, and the existing owner-only pairing model.
|
|
20
|
+
- Add behavior-level tests for command contracts, trusted-input command sequences and cleanup, semantic-ref stability, deterministic scrolling, obscured-target waiting, stale refs, broker routing, catalog parity, and architecture invariants. Update browser setup, permission, security, architecture, testing, and tool documentation.
|
|
21
|
+
|
|
3
22
|
## 0.13.0 - 2026-07-13
|
|
4
23
|
|
|
5
24
|
### Automatic capability routing and observability
|
package/README.md
CHANGED
|
@@ -47,10 +47,38 @@ This default prioritizes usability, not least privilege. `run_process` and proce
|
|
|
47
47
|
|
|
48
48
|
Node.js 26 or newer and npm 12 or newer are required. The repository pins the active development versions in `.node-version`, `.nvmrc`, and `packageManager`; project installs fail on older Node runtimes.
|
|
49
49
|
|
|
50
|
+
Use a new temporary directory for the bootstrap. The existing `npm`/`npx` front-end may inspect nearby project metadata before it launches the requested npm version; starting from an empty directory prevents an unrelated legacy `devEngines` declaration from blocking the bootstrap. The actual installation is then executed by the pinned npm 12 CLI rather than whichever npm happens to be first on `PATH`.
|
|
51
|
+
|
|
52
|
+
macOS/Linux:
|
|
53
|
+
|
|
50
54
|
```sh
|
|
51
|
-
|
|
55
|
+
install_dir="$(mktemp -d)"
|
|
56
|
+
(
|
|
57
|
+
cd "$install_dir"
|
|
58
|
+
npx --yes npm@12.0.1 install --global npm@12.0.1
|
|
59
|
+
npx --yes npm@12.0.1 install --global --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
|
|
60
|
+
)
|
|
61
|
+
rm -rf "$install_dir"
|
|
62
|
+
npm --version
|
|
63
|
+
machine-mcp doctor
|
|
52
64
|
```
|
|
53
65
|
|
|
66
|
+
Windows Command Prompt (`cmd.exe`):
|
|
67
|
+
|
|
68
|
+
```bat
|
|
69
|
+
set "MBM_INSTALL_DIR=%TEMP%\machine-bridge-mcp-install-%RANDOM%-%RANDOM%"
|
|
70
|
+
mkdir "%MBM_INSTALL_DIR%"
|
|
71
|
+
pushd "%MBM_INSTALL_DIR%"
|
|
72
|
+
npx --yes npm@12.0.1 install --global npm@12.0.1
|
|
73
|
+
npx --yes npm@12.0.1 install --global --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
|
|
74
|
+
popd
|
|
75
|
+
rmdir /s /q "%MBM_INSTALL_DIR%"
|
|
76
|
+
npm --version
|
|
77
|
+
machine-mcp doctor
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`Unknown cli config "--allow-scripts"` proves the Machine Bridge install was executed by npm 11 or older rather than the required npm 12. `Invalid property "node"` or `Invalid property "devEngines.node"` means that npm parsed a legacy `devEngines` object; the npm debug log is required to identify which package supplied it. The empty-directory, pinned-npm procedure avoids relying on that old parser. If `npm --version` still reports an older version after the bootstrap, reopen the terminal and rerun `machine-mcp doctor`.
|
|
81
|
+
|
|
54
82
|
Recent npm releases may otherwise warn that Wrangler's native dependencies (`esbuild`, `workerd`, and `sharp`) have install scripts awaiting approval. The scoped command approves the reviewed native script names that npm 12 evaluates during global resolution while `--omit=optional` keeps optional `fsevents` out of the installed runtime. `fsevents` is used for development-time filesystem watching rather than Machine Bridge runtime or deployment. Omitting `--omit=optional` can therefore produce a harmless blocked-script warning for `fsevents@2.3.3`; use the documented command rather than changing global npm policy. `machine-mcp doctor` remains the authoritative runtime check.
|
|
55
83
|
|
|
56
84
|
From a source checkout, the checked-in exact-version `allowScripts` policy approves the reviewed native dependencies:
|
|
@@ -81,14 +109,13 @@ On first remote start, the CLI:
|
|
|
81
109
|
|
|
82
110
|
A normal `machine-mcp` invocation is a foreground start: it remains attached to the terminal and prints `info` logs. Startup and other state-changing operations use an owner-token/process-identity lock and wait up to 30 seconds for an ordinary concurrent startup to finish instead of failing on a brief launchd/systemd race. After a global package upgrade, the CLI unloads the platform service and also checks the workspace lock for a detached/orphan `--daemon-only` process that the service manager no longer tracks. It terminates only a process whose PID, process start time, entrypoint, canonical workspace, canonical state root, and daemon-only arguments all match, waits up to 15 seconds for the PID and lock to disappear, and then starts the installed version in the foreground. A genuine foreground or unverifiable conflict is left untouched and reported with actionable guidance. To run only in the background, use `machine-mcp service start`; inspect its owner-only logs under `~/.local/state/machine-bridge-mcp/logs/`.
|
|
83
111
|
|
|
84
|
-
Recommended upgrade sequence:
|
|
112
|
+
Recommended upgrade sequence: repeat the package-free temporary-directory installation above, then run:
|
|
85
113
|
|
|
86
114
|
```sh
|
|
87
|
-
npm install -g --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
|
|
88
115
|
machine-mcp --verbose
|
|
89
116
|
```
|
|
90
117
|
|
|
91
|
-
The global install replaces files on disk but cannot hot-reload an already running Node process; the
|
|
118
|
+
The global install replaces files on disk but cannot hot-reload an already running Node process; the foreground command performs the bounded service and orphan-daemon takeover. Keep `--omit=optional` in the installation step to prevent the development-only optional `fsevents` package from producing a blocked-install-script warning. If takeover fails, run `machine-mcp service status` first; it reports the platform service and workspace daemon separately. Then run `machine-mcp service stop` and retry.
|
|
92
119
|
|
|
93
120
|
Use the printed values in the MCP client:
|
|
94
121
|
|
|
@@ -97,7 +124,7 @@ MCP Server URL: https://<worker>.<account>.workers.dev/mcp
|
|
|
97
124
|
MCP connection password: mcp_password_...
|
|
98
125
|
```
|
|
99
126
|
|
|
100
|
-
The remote authorization flow uses an authorization code, PKCE S256, exact redirect/resource binding, expiring access tokens stored as hashes, and a token-version value for bulk revocation.
|
|
127
|
+
The remote authorization flow uses an authorization code, PKCE S256, exact redirect/resource binding, expiring access tokens stored as hashes, and a token-version value for bulk revocation. After password approval, the Worker constructs the registered callback with the URL API and returns `303 See Other`, so OAuth response parameters are encoded and the browser performs a GET to the callback.
|
|
101
128
|
|
|
102
129
|
## Optional local stdio MCP
|
|
103
130
|
|
|
@@ -159,7 +186,7 @@ Under canonical `full`, Machine Bridge also exposes structured local automation:
|
|
|
159
186
|
|
|
160
187
|
- installed application discovery/opening and macOS Accessibility inspection/actions;
|
|
161
188
|
- a packaged Chromium extension that controls the user's existing daily browser profile, active tabs, login state, and windows;
|
|
162
|
-
- current DOM source and frame/open-Shadow-DOM inspection,
|
|
189
|
+
- current DOM source and frame/open-Shadow-DOM inspection, stable semantic element references, actionability waits, fixed trusted mouse/keyboard input, explicit browser waits, tab management, complex multi-field forms, resource-backed secret fields, resource-backed file uploads, and screenshots.
|
|
163
190
|
|
|
164
191
|
One-time browser setup:
|
|
165
192
|
|
|
@@ -168,7 +195,7 @@ machine-mcp browser setup
|
|
|
168
195
|
machine-mcp browser status
|
|
169
196
|
```
|
|
170
197
|
|
|
171
|
-
Load the printed unpacked-extension directory once in Chrome, Edge, Brave, Vivaldi, or another compatible Chromium browser. The extension badge reports connection state, and clicking it opens the saved local pairing page. The local pairing token remains in owner-only state and the loopback pairing page; it is not returned through MCP. For a mass-market release, distribute the same extension as a signed browser-store build rather than asking end users to enable Developer mode. See [Local application and browser automation](docs/LOCAL_AUTOMATION.md).
|
|
198
|
+
Load the printed unpacked-extension directory once in Chrome, Edge, Brave, Vivaldi, or another compatible Chromium browser. After an upgrade that adds permissions, reload the unpacked extension and accept the browser prompt; trusted input uses the Chromium `debugger` permission only for fixed, short-lived DevTools Input commands. The extension badge reports connection state, and clicking it opens the saved local pairing page. The local pairing token remains in owner-only state and the loopback pairing page; it is not returned through MCP. For a mass-market release, distribute the same extension as a signed browser-store build rather than asking end users to enable Developer mode. See [Local application and browser automation](docs/LOCAL_AUTOMATION.md).
|
|
172
199
|
|
|
173
200
|
Machine Bridge can discover, refresh, rank, and load capabilities automatically. The ChatGPT/MCP host still owns tool selection and approval, so the server cannot force a host to expose or invoke a recommended skill, command, app, or browser operation. Check `server_info.observability.capability_routing` or `project_overview.capabilityRouting` to distinguish “the host never called the resolver” from “the resolver ran but found no relevant capability.”
|
|
174
201
|
|
|
@@ -319,9 +346,11 @@ The exact `tools/list` response reflects the active local policy. Definitions co
|
|
|
319
346
|
- `browser_status`
|
|
320
347
|
- `pair_browser_extension`
|
|
321
348
|
- `browser_list_tabs`
|
|
349
|
+
- `browser_manage_tabs` — create, activate, or close tabs
|
|
322
350
|
- `browser_get_source` — bounded current DOM HTML, including selected frames
|
|
323
|
-
- `browser_inspect_page`
|
|
324
|
-
- `
|
|
351
|
+
- `browser_inspect_page` — semantic controls, stable per-document refs, state, and geometry
|
|
352
|
+
- `browser_wait` — bounded URL/load/text/element-state waits
|
|
353
|
+
- `browser_action` — actionability checks plus `auto`, `trusted`, or `dom` input mode
|
|
325
354
|
- `browser_fill_form`
|
|
326
355
|
- `browser_upload_files` — registered local resources to file inputs
|
|
327
356
|
- `browser_screenshot`
|
package/SECURITY.md
CHANGED
|
@@ -71,11 +71,11 @@ The global `model_instructions_file` is an explicit user trust decision. It is r
|
|
|
71
71
|
|
|
72
72
|
## Browser and application automation
|
|
73
73
|
|
|
74
|
-
Canonical `full` exposes structured browser and desktop UI authority. The browser extension operates the user's existing Chromium profile, including active login state, and has broad host access so it can inspect arbitrary pages
|
|
74
|
+
Canonical `full` exposes structured browser and desktop UI authority. The browser extension operates the user's existing Chromium profile, including active login state, and has broad host access plus Chromium `debugger` permission so it can inspect arbitrary pages, fill complex forms, and issue trusted input. An authorized client may therefore read page content, click controls, submit forms, upload registered files, and cause transactions with the user's browser identity. macOS Accessibility actions similarly operate applications with the local user's UI authority.
|
|
75
75
|
|
|
76
76
|
The browser loopback broker validates loopback `Host`, requires a random owner-only bearer subprotocol, accepts extension sockets only with a `chrome-extension://` origin, and authenticates additional local runtimes separately. Initial extension pairing is trust-on-first-use; after pairing, a different localhost page cannot replace the stored broker unless the user clicks the extension action while the genuine pairing page is active. The pairing token is never returned by MCP or written to operational logs. These controls defend against casual cross-origin and DNS-rebinding access; they do not protect against malicious code already running as the same OS user or a compromised browser extension profile.
|
|
77
77
|
|
|
78
|
-
Caller-supplied JavaScript, AppleScript,
|
|
78
|
+
Caller-supplied JavaScript, AppleScript, JXA source, and arbitrary DevTools methods are deliberately unsupported. Actions use fixed implementation code and structured selectors. The debugger adapter is restricted to fixed `Input` commands, attaches only around one trusted action, and detaches in `finally`; `input_mode` makes fallback behavior explicit. The broker validates a versioned extension capability hello before activating a connection and does not let an invalid replacement candidate displace an already compatible extension. This removes an avoidable arbitrary-evaluation surface but does not make DOM or Accessibility actions safe. Pages can contain prompt injection, misleading labels, hidden consequences, cross-origin frames unavailable to inspection, or state that changes between inspection and submission. High-impact account, financial, legal, medical, publishing, deletion, and purchase actions require contextual review and any host/user confirmation the client provides.
|
|
79
79
|
|
|
80
80
|
Text and file resources can be injected locally so their contents do not appear in MCP arguments or results. The destination page/application still receives the value, and remote tool metadata/results still traverse the Worker and host. Page source and screenshots can themselves contain secrets. Browser/app tool arguments, source, screenshots, field values, and results are intentionally omitted from operational logs.
|
|
81
81
|
|
|
@@ -145,7 +145,7 @@ Runner diagnostic logs are owner-only and do not receive child stdout/stderr. St
|
|
|
145
145
|
|
|
146
146
|
## OAuth and public endpoints
|
|
147
147
|
|
|
148
|
-
Remote mode uses authorization code flow with PKCE S256, exact redirect/resource/client binding, expiring authorization codes and access tokens, hashed token storage, token-version revocation, and bounded dynamic client registration.
|
|
148
|
+
Remote mode uses authorization code flow with PKCE S256, exact redirect/resource/client binding, expiring authorization codes and access tokens, hashed token storage, token-version revocation, and bounded dynamic client registration. Successful consent constructs the registered callback through the URL API and returns `303 See Other`; response parameters are encoded rather than concatenated into an unchecked header string.
|
|
149
149
|
|
|
150
150
|
The authorization page displays the validated client name and redirect URI. Enter the connection password only after initiating the connection and recognizing both values.
|
|
151
151
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
if (globalThis.__machineBridgeDevtoolsInput) return;
|
|
3
|
+
|
|
4
|
+
const MODIFIERS = Object.freeze({ Alt: 1, Control: 2, Meta: 4, Shift: 8 });
|
|
5
|
+
const tabQueues = new Map();
|
|
6
|
+
const KEY_DATA = Object.freeze({
|
|
7
|
+
Enter: { code: "Enter", virtualKeyCode: 13 },
|
|
8
|
+
Tab: { code: "Tab", virtualKeyCode: 9 },
|
|
9
|
+
Escape: { code: "Escape", virtualKeyCode: 27 },
|
|
10
|
+
Backspace: { code: "Backspace", virtualKeyCode: 8 },
|
|
11
|
+
Delete: { code: "Delete", virtualKeyCode: 46 },
|
|
12
|
+
ArrowLeft: { code: "ArrowLeft", virtualKeyCode: 37 },
|
|
13
|
+
ArrowUp: { code: "ArrowUp", virtualKeyCode: 38 },
|
|
14
|
+
ArrowRight: { code: "ArrowRight", virtualKeyCode: 39 },
|
|
15
|
+
ArrowDown: { code: "ArrowDown", virtualKeyCode: 40 },
|
|
16
|
+
Home: { code: "Home", virtualKeyCode: 36 },
|
|
17
|
+
End: { code: "End", virtualKeyCode: 35 },
|
|
18
|
+
PageUp: { code: "PageUp", virtualKeyCode: 33 },
|
|
19
|
+
PageDown: { code: "PageDown", virtualKeyCode: 34 },
|
|
20
|
+
Space: { code: "Space", virtualKeyCode: 32, key: " " },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
async function perform(tabId, action, details = {}) {
|
|
24
|
+
if (!Number.isInteger(tabId) || tabId < 1) throw new Error("trusted input requires a valid tab");
|
|
25
|
+
const previous = tabQueues.get(tabId) || Promise.resolve();
|
|
26
|
+
const current = previous.catch(() => {}).then(() => withDebugger(tabId, async (send) => {
|
|
27
|
+
if (action === "click") return mouseClick(send, details.point, 1);
|
|
28
|
+
if (action === "double_click") return mouseClick(send, details.point, 2);
|
|
29
|
+
if (action === "hover") return mouseMove(send, details.point);
|
|
30
|
+
if (action === "press") return pressKey(send, details.key || "Enter");
|
|
31
|
+
if (action === "type_text") return send("Input.insertText", { text: String(details.text ?? "") });
|
|
32
|
+
throw new Error(`trusted input does not support '${action}'`);
|
|
33
|
+
}));
|
|
34
|
+
tabQueues.set(tabId, current);
|
|
35
|
+
try { return await current; }
|
|
36
|
+
finally { if (tabQueues.get(tabId) === current) tabQueues.delete(tabId); }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function withDebugger(tabId, operation) {
|
|
40
|
+
const target = { tabId };
|
|
41
|
+
let attached = false;
|
|
42
|
+
try {
|
|
43
|
+
await chrome.debugger.attach(target, "1.3");
|
|
44
|
+
attached = true;
|
|
45
|
+
const send = (method, params = {}) => chrome.debugger.sendCommand(target, method, params);
|
|
46
|
+
return await operation(send);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error(`trusted browser input unavailable: ${cleanError(error)}`);
|
|
49
|
+
} finally {
|
|
50
|
+
if (attached) {
|
|
51
|
+
try { await chrome.debugger.detach(target); } catch {}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function mouseMove(send, point) {
|
|
57
|
+
const { x, y } = normalizePoint(point);
|
|
58
|
+
await send("Input.dispatchMouseEvent", { type: "mouseMoved", x, y, button: "none" });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function mouseClick(send, point, clickCount) {
|
|
62
|
+
const { x, y } = normalizePoint(point);
|
|
63
|
+
await mouseMove(send, { x, y });
|
|
64
|
+
for (let count = 1; count <= clickCount; count += 1) {
|
|
65
|
+
await send("Input.dispatchMouseEvent", { type: "mousePressed", x, y, button: "left", buttons: 1, clickCount: count });
|
|
66
|
+
await send("Input.dispatchMouseEvent", { type: "mouseReleased", x, y, button: "left", buttons: 0, clickCount: count });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function pressKey(send, shortcut) {
|
|
71
|
+
const parsed = parseShortcut(shortcut);
|
|
72
|
+
const down = {
|
|
73
|
+
type: parsed.text ? "keyDown" : "rawKeyDown",
|
|
74
|
+
key: parsed.key,
|
|
75
|
+
code: parsed.code,
|
|
76
|
+
modifiers: parsed.modifiers,
|
|
77
|
+
windowsVirtualKeyCode: parsed.virtualKeyCode,
|
|
78
|
+
nativeVirtualKeyCode: parsed.virtualKeyCode,
|
|
79
|
+
...(parsed.text ? { text: parsed.text, unmodifiedText: parsed.text } : {}),
|
|
80
|
+
};
|
|
81
|
+
await send("Input.dispatchKeyEvent", down);
|
|
82
|
+
await send("Input.dispatchKeyEvent", {
|
|
83
|
+
type: "keyUp",
|
|
84
|
+
key: parsed.key,
|
|
85
|
+
code: parsed.code,
|
|
86
|
+
modifiers: parsed.modifiers,
|
|
87
|
+
windowsVirtualKeyCode: parsed.virtualKeyCode,
|
|
88
|
+
nativeVirtualKeyCode: parsed.virtualKeyCode,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseShortcut(value) {
|
|
93
|
+
const parts = String(value || "Enter").split("+").map((part) => part.trim()).filter(Boolean);
|
|
94
|
+
const keyName = parts.pop() || "Enter";
|
|
95
|
+
let modifiers = 0;
|
|
96
|
+
for (const raw of parts) {
|
|
97
|
+
const name = raw === "Ctrl" ? "Control" : raw === "Cmd" || raw === "Command" ? "Meta" : raw;
|
|
98
|
+
if (!(name in MODIFIERS)) throw new Error(`unsupported key modifier: ${raw}`);
|
|
99
|
+
modifiers |= MODIFIERS[name];
|
|
100
|
+
}
|
|
101
|
+
const known = KEY_DATA[keyName];
|
|
102
|
+
if (known) return {
|
|
103
|
+
key: known.key || keyName,
|
|
104
|
+
code: known.code,
|
|
105
|
+
virtualKeyCode: known.virtualKeyCode,
|
|
106
|
+
modifiers,
|
|
107
|
+
text: "",
|
|
108
|
+
};
|
|
109
|
+
if ([...keyName].length !== 1) throw new Error(`unsupported key: ${keyName}`);
|
|
110
|
+
const character = keyName;
|
|
111
|
+
const upper = character.toUpperCase();
|
|
112
|
+
return {
|
|
113
|
+
key: character,
|
|
114
|
+
code: /[A-Za-z]/.test(character) ? `Key${upper}` : "",
|
|
115
|
+
virtualKeyCode: upper.codePointAt(0),
|
|
116
|
+
modifiers,
|
|
117
|
+
text: modifiers & (MODIFIERS.Control | MODIFIERS.Meta | MODIFIERS.Alt) ? "" : character,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalizePoint(point) {
|
|
122
|
+
const x = Number(point?.x);
|
|
123
|
+
const y = Number(point?.y);
|
|
124
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || x < 0 || y < 0) throw new Error("trusted input target has no usable viewport point");
|
|
125
|
+
return { x, y };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function cleanError(error) {
|
|
129
|
+
return String(error?.message || error || "unknown error").replace(/\s+/g, " ").slice(0, 500);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Object.defineProperty(globalThis, "__machineBridgeDevtoolsInput", {
|
|
133
|
+
value: Object.freeze({ perform }),
|
|
134
|
+
configurable: false,
|
|
135
|
+
});
|
|
136
|
+
})();
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Machine Bridge Browser",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.14.0",
|
|
5
5
|
"description": "Connects the current Chromium browser profile to the local Machine Bridge runtime for user-authorized automation.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"tabs",
|
|
8
|
+
"debugger",
|
|
8
9
|
"scripting",
|
|
9
10
|
"storage",
|
|
10
11
|
"alarms"
|
|
@@ -29,5 +30,5 @@
|
|
|
29
30
|
"action": {
|
|
30
31
|
"default_title": "Machine Bridge Browser"
|
|
31
32
|
},
|
|
32
|
-
"version_name": "0.
|
|
33
|
+
"version_name": "0.14.0"
|
|
33
34
|
}
|