machine-bridge-mcp 0.8.2 → 0.10.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 +35 -0
- package/README.md +58 -1
- package/SECURITY.md +22 -0
- package/browser-extension/manifest.json +33 -0
- package/browser-extension/page-automation.js +246 -0
- package/browser-extension/pairing.js +19 -0
- package/browser-extension/service-worker.js +406 -0
- package/docs/AGENT_CONTEXT.md +140 -0
- package/docs/ARCHITECTURE.md +33 -4
- package/docs/CLIENTS.md +8 -0
- package/docs/ENGINEERING.md +5 -1
- package/docs/LOCAL_AUTOMATION.md +111 -0
- package/docs/LOGGING.md +9 -1
- package/docs/OPERATIONS.md +30 -0
- package/docs/PRIVACY.md +2 -1
- package/docs/RELEASING.md +2 -2
- package/docs/TESTING.md +6 -4
- package/package.json +14 -7
- package/scripts/sync-worker-version.mjs +27 -12
- package/src/local/agent-context.mjs +862 -0
- package/src/local/app-automation.mjs +400 -0
- package/src/local/browser-bridge.mjs +757 -0
- package/src/local/cli.mjs +85 -0
- package/src/local/runtime.mjs +146 -4
- package/src/local/state.mjs +1 -1
- package/src/local/stdio.mjs +12 -7
- package/src/shared/server-metadata.json +4 -0
- package/src/shared/tool-catalog.json +1025 -0
- package/src/worker/index.ts +24 -4
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Local application and browser automation
|
|
2
|
+
|
|
3
|
+
Machine Bridge exposes two structured local-automation backends under the canonical `full` profile:
|
|
4
|
+
|
|
5
|
+
1. operating-system application discovery and macOS Accessibility actions;
|
|
6
|
+
2. a Chromium extension connected to a loopback broker for the user's existing browser profile, windows, tabs, cookies, and login state.
|
|
7
|
+
|
|
8
|
+
Neither backend accepts arbitrary caller-supplied AppleScript, JavaScript, or browser-extension code. The MCP surface is a bounded set of typed discovery, inspection, action, form, screenshot, and file-upload operations.
|
|
9
|
+
|
|
10
|
+
## Why the browser backend is an extension
|
|
11
|
+
|
|
12
|
+
Launching a separate automation browser is predictable but loses the user's ordinary profile, active tabs, existing login state, browser extensions, and familiar window. Machine Bridge therefore uses a packaged Manifest V3 extension as the primary backend. It is loaded once into the user's Chromium-family browser and paired with a loopback-only local broker.
|
|
13
|
+
|
|
14
|
+
The extension works with the current profile and supports:
|
|
15
|
+
|
|
16
|
+
- listing existing tabs;
|
|
17
|
+
- reading the current serialized DOM HTML from the main frame or accessible subframes;
|
|
18
|
+
- discovering interactive controls, labels, roles, placeholders, and field metadata across accessible frames and open Shadow DOM roots;
|
|
19
|
+
- structured navigation, click, focus, fill, select, check, uncheck, key press, and form submission;
|
|
20
|
+
- multi-field complex form filling in one operation, bounded to 200 fields and 4 MiB of aggregate text;
|
|
21
|
+
- populating file inputs from registered local resource files;
|
|
22
|
+
- visible-tab screenshots.
|
|
23
|
+
|
|
24
|
+
Current support targets Chrome, Chromium, Microsoft Edge, Brave, Vivaldi, and compatible Chromium browsers. After upgrading Machine Bridge, reload the unpacked extension from the browser extensions page so its packaged scripts match the new runtime. Browser-internal pages, extension stores, some PDF/plugin viewers, cross-origin frames denied to the extension, and pages restricted by enterprise policy may not be scriptable.
|
|
25
|
+
|
|
26
|
+
## One-time browser setup
|
|
27
|
+
|
|
28
|
+
Keep `machine-mcp` running, then use either the MCP tool `pair_browser_extension` or the local CLI:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
machine-mcp browser setup
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The command prints the packaged extension directory and opens the local pairing page. In the browser:
|
|
35
|
+
|
|
36
|
+
1. open the extensions page;
|
|
37
|
+
2. enable Developer mode;
|
|
38
|
+
3. choose **Load unpacked** and select the printed `browser-extension` directory;
|
|
39
|
+
4. return to the local pairing page.
|
|
40
|
+
|
|
41
|
+
The first pairing completes automatically. If the extension is already paired to different local state, the page asks for an explicit user gesture: click the Machine Bridge extension icon while the pairing page is active to confirm replacement. This prevents another localhost page from silently overwriting an established pairing. The extension badge shows `ON` when connected; clicking the icon from another page opens the saved local pairing/status page.
|
|
42
|
+
|
|
43
|
+
`Load unpacked` is the self-hosted/development installation path. A mass-market release should package the same extension source as a signed Chrome Web Store and/or Microsoft Edge Add-ons build, so ordinary users install it through the browser store without enabling Developer mode. This repository change does not publish a store listing.
|
|
44
|
+
|
|
45
|
+
The content script reads pairing material only from the loopback page and stores it in browser-local extension storage. Check status with:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
machine-mcp browser status
|
|
49
|
+
machine-mcp browser path
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The broker is machine-global rather than workspace-global. One local owner listens on the loopback port, and additional Machine Bridge runtimes authenticate to that broker and proxy requests through the same extension connection. This prevents multiple workspaces or stdio clients from repeatedly stealing the browser connection.
|
|
53
|
+
|
|
54
|
+
## Browser tools
|
|
55
|
+
|
|
56
|
+
- `browser_status` reports broker role, extension connection, supported operations, pairing URL, and extension path without returning the pairing token.
|
|
57
|
+
- `pair_browser_extension` opens the local pairing page and returns setup steps.
|
|
58
|
+
- `browser_list_tabs` lists current tabs.
|
|
59
|
+
- `browser_get_source` returns bounded current DOM HTML for selected frames.
|
|
60
|
+
- `browser_inspect_page` returns bounded interactive-element metadata.
|
|
61
|
+
- `browser_action` performs one structured navigation or DOM action. Navigation accepts absolute `http`, `https`, or `file` URLs; script/data schemes are rejected.
|
|
62
|
+
- `browser_fill_form` fills up to 200 fields and can submit once.
|
|
63
|
+
- `browser_upload_files` sets a file input from up to eight registered local resources.
|
|
64
|
+
- `browser_screenshot` returns native MCP image content.
|
|
65
|
+
|
|
66
|
+
Selectors can use CSS, ID, field name, label text, visible text, ARIA/implicit role, placeholder, and a zero-based match index. The fixed page module traverses open Shadow DOM roots; closed shadow roots remain inaccessible. For frame-specific work, inspect all frames first and then pass `frame_id` to an action.
|
|
67
|
+
|
|
68
|
+
## Sensitive form values and files
|
|
69
|
+
|
|
70
|
+
Ordinary `value` arguments travel through the MCP host, the Worker in remote mode, and the local daemon. For passwords, tokens, personal data, or uploaded files, register a local resource from the terminal:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
machine-mcp resource add account-password /path/to/owner-only/password.txt
|
|
74
|
+
machine-mcp resource add application-pdf /path/to/document.pdf
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then use `value_resource` for a text field or `resources` for `browser_upload_files`. The local daemon reads the resource only when executing the operation. Results report the alias and outcome, not the resource content. This reduces model-context and result exposure, but the destination page receives the value by design and can transmit it according to its own behavior.
|
|
78
|
+
|
|
79
|
+
## Application tools
|
|
80
|
+
|
|
81
|
+
- `list_local_applications` discovers installed applications or launchers.
|
|
82
|
+
- `open_local_application` opens an application, URL, or document through the OS launcher.
|
|
83
|
+
- `inspect_local_application` returns a bounded macOS Accessibility tree.
|
|
84
|
+
- `operate_local_application` performs a structured Accessibility action.
|
|
85
|
+
|
|
86
|
+
macOS UI inspection and actions require Accessibility permission for the Node/Machine Bridge process. Application discovery and opening are available on supported desktop platforms; structured UI inspection currently targets macOS. The application backend uses fixed JXA implementation code. The caller selects only an application, structured selector, action, and optional bounded value. `include_values` never returns values for secure-text roles or controls whose metadata indicates passwords, tokens, one-time codes, or payment-card secrets.
|
|
87
|
+
|
|
88
|
+
## Capability discovery and automatic selection
|
|
89
|
+
|
|
90
|
+
`resolve_task_capabilities` rescans instruction files, skills, registered commands, and relevant local automation metadata on every call. It ranks matching skills and commands, optionally loads the best skill, and recommends browser/application tools when the task indicates them.
|
|
91
|
+
|
|
92
|
+
This is the strongest reliable server-side automation boundary available through MCP: discovery, refresh, ranking, and progressive skill loading are automatic. The MCP host still owns the model loop and decides whether a recommended tool is exposed, approved, or invoked. Machine Bridge cannot force ChatGPT web or another host to make a call that the host declines.
|
|
93
|
+
|
|
94
|
+
## Security model
|
|
95
|
+
|
|
96
|
+
The browser extension has broad page access because generic source inspection and complex forms require it. An authorized `full` client can read pages visible to the extension and perform user-visible actions with the user's logged-in authority. A malicious webpage can contain prompt-injection text, deceptive controls, or actions with financial, legal, account, or privacy consequences.
|
|
97
|
+
|
|
98
|
+
The implementation reduces avoidable risk as follows:
|
|
99
|
+
|
|
100
|
+
- all browser/app tools are `full`-only;
|
|
101
|
+
- no arbitrary evaluation or caller-provided script source is accepted;
|
|
102
|
+
- loopback HTTP validates `Host`; extension WebSockets require a random bearer subprotocol and a `chrome-extension://` origin;
|
|
103
|
+
- runtime-to-broker connections require a separate authenticated subprotocol;
|
|
104
|
+
- the pairing token is stored owner-only, embedded only in the non-cacheable local pairing page, and omitted from MCP results and logs;
|
|
105
|
+
- an established extension pairing cannot be silently replaced by another localhost page; replacement requires clicking the extension action on the active pairing page;
|
|
106
|
+
- arguments, form values, page source, screenshots, and results are not operational log data;
|
|
107
|
+
- message, source, form-field, upload, traversal, concurrency, proxy-route, and timeout limits are enforced;
|
|
108
|
+
- MCP cancellation clears local and broker pending state and propagates a cancellation signal to the extension; an action already delivered to a page or application may still have completed;
|
|
109
|
+
- resource-backed text and files are not returned after injection.
|
|
110
|
+
|
|
111
|
+
These controls do not turn the browser into a sandbox and do not validate business intent. Inspect the page and final values before high-impact submissions. Host confirmation and narrow profiles remain independent controls.
|
package/docs/LOGGING.md
CHANGED
|
@@ -78,10 +78,18 @@ The implementation omits:
|
|
|
78
78
|
- file, patch, image, and temporary-file content;
|
|
79
79
|
- OAuth request bodies;
|
|
80
80
|
- connection passwords, daemon secrets, authorization codes, and access tokens;
|
|
81
|
-
- registered resource values and source paths
|
|
81
|
+
- registered resource values and source paths;
|
|
82
|
+
- browser pairing tokens, page URLs/source, DOM metadata, form values, uploaded file bytes, and screenshots;
|
|
83
|
+
- application names, Accessibility trees, selectors, and entered values.
|
|
82
84
|
|
|
83
85
|
Unexpected infrastructure failures are reduced to coarse error classes in normal logs. Client-facing tool errors may contain more detail according to the active path-display policy, but those details are not copied into operational logs.
|
|
84
86
|
|
|
87
|
+
## Local automation events
|
|
88
|
+
|
|
89
|
+
Browser broker ownership, extension connection, and persistent unavailability are infrastructure state. Ordinary tab/source/action/form/upload/screenshot calls remain per-tool debug events and never log their arguments or results. Pairing tokens are excluded from all structured fields.
|
|
90
|
+
|
|
91
|
+
Application discovery and Accessibility operations follow the same rule: permission or runtime failures are classified, while app names, UI trees, selectors, and values are not operational log data.
|
|
92
|
+
|
|
85
93
|
## Bounding and redaction
|
|
86
94
|
|
|
87
95
|
Messages, strings, object depth, object key counts, array item counts, and serialized field payloads are bounded. Control characters and Unicode display controls are neutralized. Fields with secret-like names, path-like keys, and known token formats are recursively redacted.
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -30,6 +30,33 @@ A brief relay interruption is retried automatically and is visible only with `--
|
|
|
30
30
|
|
|
31
31
|
Use `--verbose` only when close codes, close reasons, heartbeat timeouts, and retry delays are needed for diagnosis. A close code of 1006 means the transport ended without a normal close handshake; it does not by itself identify the cause.
|
|
32
32
|
|
|
33
|
+
## Browser extension setup and diagnosis
|
|
34
|
+
|
|
35
|
+
The full-profile daemon starts the loopback browser broker automatically. Install/pair the packaged extension once:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
machine-mcp browser setup
|
|
39
|
+
machine-mcp browser status
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`browser setup` prints the unpacked-extension path and opens the local pairing page. Load that directory from the Chromium extensions page with Developer mode enabled. The pairing page contains the token only in non-cacheable loopback HTML; the CLI and MCP result do not print it. A first pairing is automatic. When local pairing state was reset or moved, click the extension icon on the active pairing page to authorize replacing the extension's previous broker configuration.
|
|
43
|
+
|
|
44
|
+
Diagnosis:
|
|
45
|
+
|
|
46
|
+
| Result | Interpretation |
|
|
47
|
+
|---|---|
|
|
48
|
+
| broker not reachable | no full-profile runtime owns the machine broker, state is stale, or a local security product blocked loopback listening |
|
|
49
|
+
| broker running, extension disconnected | extension not loaded/enabled, pairing not completed, browser profile not running, or MV3 worker reconnect failed |
|
|
50
|
+
| tab list works but page action fails | restricted browser-internal page, inaccessible frame, stale selector, page navigation, enterprise policy, or page script interference |
|
|
51
|
+
| `frame_id` action fails | frame was replaced or is not accessible; inspect frames again |
|
|
52
|
+
| resource-backed upload fails | resource unavailable/too large, input is not `type=file`, page replaced the input, or the site rejects the file |
|
|
53
|
+
|
|
54
|
+
The machine-level broker permits multiple workspace daemons/stdio clients to share one extension. A second runtime becomes an authenticated broker client rather than selecting a new port or replacing the extension. Its owner-only `browser-bridge.json` pairing record is a recognized state-root entry, so validated uninstall can remove the complete Machine Bridge state root without treating the pairing record as unrelated data. Restarting every runtime is not normally required after adding a skill or changing a form page; capability and DOM discovery are live.
|
|
55
|
+
|
|
56
|
+
## macOS application automation
|
|
57
|
+
|
|
58
|
+
Application UI inspection/actions require Accessibility permission for the Node/Machine Bridge process. Grant it in macOS Privacy & Security, then retry `inspect_local_application`. Opening applications does not require the same Accessibility authority. If an app changes UI hierarchy or localizes labels, inspect again and use role/identifier/index selectors instead of assuming a stale title.
|
|
59
|
+
|
|
33
60
|
## Logs
|
|
34
61
|
|
|
35
62
|
Remote autostart definitions prefer a stable PATH alias that resolves to the currently running Node executable and persist a sanitized absolute-only service `PATH` containing the Node/CLI directories, the installer's absolute PATH entries, and platform defaults. This avoids versioned Homebrew-style paths becoming invalid after upgrades and prevents launchd/systemd from falling back to a minimal system-only PATH. Re-run `machine-mcp service install` after changing Node installation families or PATH layout. A service-style `--daemon-only` start that finds the same workspace daemon already running is an idempotent no-op: it exits successfully without repeating warnings or readiness output; explicit policy/secret/change requests still report that changes were not applied. Autostart logs are stored under the state root in `logs/daemon.out.log` and `logs/daemon.err.log`. Files are owner-only where supported and tail-trimmed before daemon startup. On a logging-schema upgrade, bounded prior content is moved into `daemon.out.legacy.log` and `daemon.err.legacy.log`; use the active filenames when diagnosing current behavior.
|
|
@@ -131,6 +158,9 @@ Defense-in-depth limits include:
|
|
|
131
158
|
- managed-job timeout: 1–3,600 seconds per step;
|
|
132
159
|
- managed-job output: 64 KiB per stream and 256 KiB total captured across one job;
|
|
133
160
|
- registered resources: 64, 1 MiB each, 8 MiB referenced per job;
|
|
161
|
+
- browser source: 4 MiB; browser WebSocket messages: 8 MiB; pending browser requests: 32 per runtime;
|
|
162
|
+
- browser forms: 200 fields, 128 KiB per text value, and 4 MiB aggregate text; upload: eight resources and 5 MiB total;
|
|
163
|
+
- application Accessibility inspection: 500 elements and depth 12; action text: 4,000 characters;
|
|
134
164
|
- job-scoped temporary files: 16 files, 512 KiB total content.
|
|
135
165
|
|
|
136
166
|
## Upgrade behavior
|
package/docs/PRIVACY.md
CHANGED
|
@@ -34,7 +34,8 @@ Before committing or publishing:
|
|
|
34
34
|
- inspect the complete staged diff, including tests, snapshots, examples, release notes, and generated metadata;
|
|
35
35
|
- use reserved example domains and neutral aliases;
|
|
36
36
|
- run `npm run privacy:check`, `npm run check`, and `npm pack --dry-run`;
|
|
37
|
-
- treat paths, host aliases, usernames, and
|
|
37
|
+
- treat paths, host aliases, usernames, codenames, real browser URLs/page captures, application names tied to a user, and form data as private metadata even when they are not authentication secrets;
|
|
38
|
+
- keep browser pairing-state files and captured source/screenshots out of fixtures, documentation, support logs, and release assets.
|
|
38
39
|
|
|
39
40
|
The scanner is heuristic. It cannot identify every personal or organizational name, transformed value, image, archive, binary fixture, or data already present in Git history.
|
|
40
41
|
|
package/docs/RELEASING.md
CHANGED
|
@@ -6,7 +6,7 @@ The release invariant is:
|
|
|
6
6
|
- `v<package version>` points to that same commit locally and on GitHub.
|
|
7
7
|
- A final GitHub Release exists for the tag.
|
|
8
8
|
- The GitHub Release contains the npm tarball generated from that commit.
|
|
9
|
-
- `package.json`, `package-lock.json`,
|
|
9
|
+
- `package.json`, `package-lock.json`, the Worker-reported version, and `browser-extension/manifest.json` (`version`/`version_name`) agree.
|
|
10
10
|
- Every release-relevant change since the prior version tag has a higher package version and matching CHANGELOG section.
|
|
11
11
|
- The same reviewed change is present on GitHub and in a new npm version.
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ The release invariant is:
|
|
|
14
14
|
|
|
15
15
|
## Prepare a version
|
|
16
16
|
|
|
17
|
-
1. Set the new version without creating an automatic npm tag:
|
|
17
|
+
1. Set the new version without creating an automatic npm tag. The npm version hook synchronizes the Worker and packaged browser-extension versions:
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
20
|
npm version <version> --no-git-tag-version
|
package/docs/TESTING.md
CHANGED
|
@@ -17,6 +17,8 @@ The suite includes:
|
|
|
17
17
|
- generated Cloudflare Worker types and strict TypeScript checking, including unused-local and unused-parameter rejection;
|
|
18
18
|
- syntax validation for every shipped JavaScript entry point;
|
|
19
19
|
- shared tool-catalog schema, annotation, and profile-inventory checks;
|
|
20
|
+
- global `model_instructions_file` injection in stdio/remote initialization, hierarchical agent-context precedence, live skill rescanning/fingerprints, automatic task ranking/loading, command override/removal, direct argv handling, timeout ceilings, and execution-profile denial;
|
|
21
|
+
- machine-level browser-broker ownership/client proxying, authenticated extension origin/subprotocol, non-cacheable local pairing, pairing-token non-disclosure, resource-backed upload routing, and broker result redaction;
|
|
20
22
|
- canonical path and symbolic-link escape tests;
|
|
21
23
|
- relative-path privacy and error-path redaction tests;
|
|
22
24
|
- atomic create/update, optimistic hash, exact edit, and patch transaction tests;
|
|
@@ -41,8 +43,8 @@ The suite includes:
|
|
|
41
43
|
- deterministic relay connection lifecycle coverage for transport construction/error/deadline, pre-handshake `welcome` validation, authenticated `hello_ack` readiness, identity/version mismatch, retryable Worker handshake errors, fatal protocol errors, autonomous outage-reminder backoff, handshake and heartbeat timeout, brief-outage suppression, sustained-outage escalation, recovery summaries, and supersession;
|
|
42
44
|
- shared no-follow bounded-file reads for normal files, over-limit data, directories, and symbolic links;
|
|
43
45
|
- CLI parsing, policy profiles, and client configuration boundaries;
|
|
44
|
-
- live stdio MCP initialization, discovery, calls, rich content, sessions, cancellation, managed-job acceptance, and a detached job/finally phase that survives stdio shutdown;
|
|
45
|
-
- live local Worker OAuth registration, consent, PKCE, token replay rejection, throttling, CORS, protocol negotiation, dynamic tool advertisement, rich content, daemon replacement, cancellation, malformed daemon JSON/non-object rejection, duplicate hello rejection, and unknown-message closure.
|
|
46
|
+
- live stdio MCP initialization with session instructions, capability resolution, discovery, calls, rich content, sessions, cancellation, managed-job acceptance, and a detached job/finally phase that survives stdio shutdown;
|
|
47
|
+
- live local Worker OAuth registration, consent, PKCE, token replay rejection, throttling, CORS, protocol negotiation, daemon-backed session bootstrap, dynamic tool advertisement, rich content, daemon replacement, cancellation, malformed daemon JSON/non-object rejection, duplicate hello rejection, and unknown-message closure.
|
|
46
48
|
|
|
47
49
|
## Additional release checks
|
|
48
50
|
|
|
@@ -62,7 +64,7 @@ GitHub Actions executes the main suite on Linux, macOS, and Windows using the pi
|
|
|
62
64
|
## Test design rules
|
|
63
65
|
|
|
64
66
|
- Tests should exercise the public boundary rather than only helper functions when practical.
|
|
65
|
-
- Every permission-expanding feature needs a denial test.
|
|
67
|
+
- Every permission-expanding feature needs a denial test. Browser/application features also require a token/value/content non-disclosure assertion.
|
|
66
68
|
- Every bounded resource needs an over-limit test.
|
|
67
69
|
- Every multi-stage mutation needs a no-partial-commit test.
|
|
68
70
|
- Every remote call correlation change needs daemon replacement and cancellation coverage.
|
|
@@ -77,7 +79,7 @@ Run `npm run privacy:check` before committing and before packaging. Developers s
|
|
|
77
79
|
|
|
78
80
|
## Package manifest
|
|
79
81
|
|
|
80
|
-
`npm run package:test` executes a real silent `npm pack --dry-run --json`, requires clean parseable JSON, rejects sensitive local artifacts and credential-like file classes, and verifies that privacy/engineering guidance, the runtime/relay/secure-file modules, contribution discipline, and privacy/release-impact checkers are present in the published package. `npm run install:test` packs the real tarball, installs it into an isolated global prefix with the documented npm 12 options, rejects blocked-script warnings, confirms optional `fsevents` is absent, and runs the installed CLI.
|
|
82
|
+
`npm run package:test` executes a real silent `npm pack --dry-run --json`, requires clean parseable JSON, rejects sensitive local artifacts and credential-like file classes, and verifies that privacy/engineering guidance, the runtime/relay/secure-file/browser/app modules, packaged browser extension, contribution discipline, and privacy/release-impact checkers are present in the published package. `npm run install:test` packs the real tarball, installs it into an isolated global prefix with the documented npm 12 options, rejects blocked-script warnings, confirms optional `fsevents` is absent, and runs the installed CLI.
|
|
81
83
|
|
|
82
84
|
The stdio integration test also sends an oversized line, verifies bounded rejection, and confirms that the next valid request is still processed.
|
|
83
85
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "machine-bridge-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Cross-client MCP bridge for
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Cross-client MCP bridge for local agent context, structured browser and application automation, files, Git, processes, resources, and durable jobs over stdio or OAuth relay.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"engines": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"mbm",
|
|
17
17
|
"mbm.cmd",
|
|
18
18
|
"bin",
|
|
19
|
+
"browser-extension",
|
|
19
20
|
"src/local",
|
|
20
21
|
"src/shared",
|
|
21
22
|
"src/worker/index.ts",
|
|
@@ -38,13 +39,13 @@
|
|
|
38
39
|
"self-test": "node tests/local-self-test.mjs",
|
|
39
40
|
"version:sync": "node scripts/sync-worker-version.mjs",
|
|
40
41
|
"version:check": "node scripts/sync-worker-version.mjs --check",
|
|
41
|
-
"version": "node scripts/sync-worker-version.mjs && git add package.json package-lock.json src/worker/index.ts",
|
|
42
|
+
"version": "node scripts/sync-worker-version.mjs && git add package.json package-lock.json src/worker/index.ts browser-extension/manifest.json",
|
|
42
43
|
"prepack": "npm run version:check && npm run privacy:check && npm run release-impact:check",
|
|
43
44
|
"prepublishOnly": "npm run check && npm run version:check && npm run release:check",
|
|
44
45
|
"worker:types": "wrangler types src/worker/worker-configuration.d.ts",
|
|
45
46
|
"typecheck": "npm run worker:types && tsc -p tsconfig.json --noEmit",
|
|
46
|
-
"syntax": "sh -n mbm && node --check bin/machine-mcp.mjs && node --check src/local/cli.mjs && node --check src/local/runtime.mjs && node --check src/local/relay-connection.mjs && node --check src/local/secure-file.mjs && node --check src/local/patch.mjs && node --check src/local/process-sessions.mjs && node --check src/local/tools.mjs && node --check src/local/stdio.mjs && node --check src/local/state.mjs && node --check src/local/shell.mjs && node --check src/local/service.mjs && node --check src/local/log.mjs && node --check src/local/atomic-fs.mjs && node --check src/local/ssh-key.mjs && node --check src/local/resource-operations.mjs && node --check src/local/full-access-test.mjs && node --check src/local/managed-jobs.mjs && node --check src/local/job-runner.mjs && node --check scripts/sync-worker-version.mjs && node --check scripts/privacy-check.mjs && node --check scripts/release-impact-check.mjs && node --check scripts/network-retry.mjs && node --check scripts/release-state.mjs && node --check scripts/github-release.mjs && node --check tests/worker-integration-test.mjs && node --check tests/stdio-integration-test.mjs && node --check tests/catalog-test.mjs && node --check tests/local-self-test.mjs && node --check tests/runtime-self-test.mjs && node --check tests/managed-jobs-test.mjs && node --check tests/ssh-key-test.mjs && node --check tests/full-access-test.mjs && node --check tests/atomic-fs-test.mjs && node --check tests/package-test.mjs && node --check tests/release-impact-test.mjs && node --check tests/release-state-test.mjs && node --check tests/privacy-test.mjs && node --check tests/network-retry-test.mjs && node --check tests/relay-connection-test.mjs && node --check tests/install-smoke-test.mjs && node --check tests/secure-file-test.mjs && node --check tests/architecture-test.mjs",
|
|
47
|
-
"check": "npm run privacy:check && npm run privacy:test && npm run release-impact:check && npm run release-impact:test && npm run release-state:test && npm run network-retry:test && npm run relay:test && npm run secure-file:test && npm run architecture:test && npm run typecheck && npm run syntax && npm run catalog:test && npm run self-test && npm run atomic-fs:test && npm run package:test && npm run ssh-key:test && npm run full-access:test && npm run managed-jobs:test && npm run stdio:integration-test && npm run worker:integration-test",
|
|
47
|
+
"syntax": "sh -n mbm && node --check bin/machine-mcp.mjs && node --check src/local/cli.mjs && node --check src/local/runtime.mjs && node --check src/local/agent-context.mjs && node --check src/local/browser-bridge.mjs && node --check src/local/app-automation.mjs && node --check src/local/relay-connection.mjs && node --check src/local/secure-file.mjs && node --check src/local/patch.mjs && node --check src/local/process-sessions.mjs && node --check src/local/tools.mjs && node --check src/local/stdio.mjs && node --check src/local/state.mjs && node --check src/local/shell.mjs && node --check src/local/service.mjs && node --check src/local/log.mjs && node --check src/local/atomic-fs.mjs && node --check src/local/ssh-key.mjs && node --check src/local/resource-operations.mjs && node --check src/local/full-access-test.mjs && node --check src/local/managed-jobs.mjs && node --check src/local/job-runner.mjs && node --check scripts/sync-worker-version.mjs && node --check scripts/privacy-check.mjs && node --check scripts/release-impact-check.mjs && node --check scripts/network-retry.mjs && node --check scripts/release-state.mjs && node --check scripts/github-release.mjs && node --check tests/worker-integration-test.mjs && node --check tests/stdio-integration-test.mjs && node --check tests/catalog-test.mjs && node --check tests/agent-context-test.mjs && node --check tests/browser-bridge-test.mjs && node --check tests/app-automation-test.mjs && node --check tests/local-self-test.mjs && node --check tests/runtime-self-test.mjs && node --check tests/managed-jobs-test.mjs && node --check tests/ssh-key-test.mjs && node --check tests/full-access-test.mjs && node --check tests/atomic-fs-test.mjs && node --check tests/package-test.mjs && node --check tests/release-impact-test.mjs && node --check tests/release-state-test.mjs && node --check tests/privacy-test.mjs && node --check tests/network-retry-test.mjs && node --check tests/relay-connection-test.mjs && node --check tests/install-smoke-test.mjs && node --check tests/secure-file-test.mjs && node --check tests/architecture-test.mjs && node --check browser-extension/service-worker.js && node --check browser-extension/pairing.js && node --check browser-extension/page-automation.js",
|
|
48
|
+
"check": "npm run privacy:check && npm run privacy:test && npm run release-impact:check && npm run release-impact:test && npm run release-state:test && npm run network-retry:test && npm run relay:test && npm run secure-file:test && npm run architecture:test && npm run typecheck && npm run syntax && npm run catalog:test && npm run agent-context:test && npm run browser-bridge:test && npm run app-automation:test && npm run self-test && npm run atomic-fs:test && npm run package:test && npm run ssh-key:test && npm run full-access:test && npm run managed-jobs:test && npm run stdio:integration-test && npm run worker:integration-test",
|
|
48
49
|
"worker:dry-run": "wrangler deploy --dry-run",
|
|
49
50
|
"worker:integration-test": "node tests/worker-integration-test.mjs",
|
|
50
51
|
"release:check": "node scripts/github-release.mjs --check",
|
|
@@ -66,7 +67,10 @@
|
|
|
66
67
|
"install:test": "node tests/install-smoke-test.mjs",
|
|
67
68
|
"secure-file:test": "node tests/secure-file-test.mjs",
|
|
68
69
|
"architecture:test": "node tests/architecture-test.mjs",
|
|
69
|
-
"release-state:test": "node tests/release-state-test.mjs"
|
|
70
|
+
"release-state:test": "node tests/release-state-test.mjs",
|
|
71
|
+
"agent-context:test": "node tests/agent-context-test.mjs",
|
|
72
|
+
"browser-bridge:test": "node tests/browser-bridge-test.mjs",
|
|
73
|
+
"app-automation:test": "node tests/app-automation-test.mjs"
|
|
70
74
|
},
|
|
71
75
|
"dependencies": {
|
|
72
76
|
"wrangler": "4.110.0",
|
|
@@ -89,7 +93,10 @@
|
|
|
89
93
|
"cross-platform",
|
|
90
94
|
"ssh",
|
|
91
95
|
"automation",
|
|
92
|
-
"managed-jobs"
|
|
96
|
+
"managed-jobs",
|
|
97
|
+
"browser-automation",
|
|
98
|
+
"desktop-automation",
|
|
99
|
+
"agent-skills"
|
|
93
100
|
],
|
|
94
101
|
"repository": {
|
|
95
102
|
"type": "git",
|
|
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
8
|
const packagePath = path.join(repoRoot, "package.json");
|
|
9
9
|
const workerPath = path.join(repoRoot, "src", "worker", "index.ts");
|
|
10
|
+
const extensionManifestPath = path.join(repoRoot, "browser-extension", "manifest.json");
|
|
10
11
|
const args = new Set(process.argv.slice(2));
|
|
11
12
|
const checkOnly = args.has("--check");
|
|
12
13
|
const log = message => process.stderr.write(`${message}\n`);
|
|
@@ -17,24 +18,38 @@ if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(expected)) {
|
|
|
17
18
|
fail(`package.json version is not a valid release version: ${JSON.stringify(expected)}`);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
if (!
|
|
21
|
+
const workerSource = readFileSync(workerPath, "utf8");
|
|
22
|
+
const workerPattern = /const SERVER_VERSION = "([^"]+)";/;
|
|
23
|
+
const workerMatch = workerSource.match(workerPattern);
|
|
24
|
+
if (!workerMatch) fail("Could not find `const SERVER_VERSION = \"...\";` in src/worker/index.ts");
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
const extension = JSON.parse(readFileSync(extensionManifestPath, "utf8"));
|
|
27
|
+
const extensionVersion = expected.split(/[+-]/, 1)[0];
|
|
28
|
+
const extensionParts = extensionVersion.split(".").map((value) => Number(value));
|
|
29
|
+
if (extensionParts.length < 1 || extensionParts.length > 4 || extensionParts.some((value) => !Number.isInteger(value) || value < 0 || value > 65535)) {
|
|
30
|
+
fail(`package version cannot be represented as a Chromium extension version: ${expected}`);
|
|
29
31
|
}
|
|
32
|
+
const mismatches = [];
|
|
33
|
+
if (workerMatch[1] !== expected) mismatches.push(`Worker=${workerMatch[1]}`);
|
|
34
|
+
if (String(extension.version || "") !== extensionVersion) mismatches.push(`browser-extension.version=${extension.version || "<missing>"}`);
|
|
35
|
+
if (String(extension.version_name || "") !== expected) mismatches.push(`browser-extension.version_name=${extension.version_name || "<missing>"}`);
|
|
30
36
|
|
|
37
|
+
if (!mismatches.length) {
|
|
38
|
+
log(`Runtime versions are in sync: ${expected}`);
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
31
41
|
if (checkOnly) {
|
|
32
|
-
fail(`
|
|
42
|
+
fail(`Version mismatch: package.json=${expected}; ${mismatches.join(", ")}. Run npm run version:sync.`);
|
|
33
43
|
}
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
writeFileSync(workerPath,
|
|
37
|
-
log(`Updated Worker version: ${
|
|
45
|
+
if (workerMatch[1] !== expected) {
|
|
46
|
+
writeFileSync(workerPath, workerSource.replace(workerPattern, `const SERVER_VERSION = "${expected}";`));
|
|
47
|
+
log(`Updated Worker version: ${workerMatch[1]} -> ${expected}`);
|
|
48
|
+
}
|
|
49
|
+
extension.version = extensionVersion;
|
|
50
|
+
extension.version_name = expected;
|
|
51
|
+
writeFileSync(extensionManifestPath, `${JSON.stringify(extension, null, 2)}\n`);
|
|
52
|
+
log(`Updated browser extension version: ${extensionVersion} (${expected})`);
|
|
38
53
|
|
|
39
54
|
function fail(message) {
|
|
40
55
|
console.error(message);
|