machine-bridge-mcp 0.6.0 → 0.6.2
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 +41 -0
- package/README.md +21 -4
- package/SECURITY.md +10 -4
- package/docs/ARCHITECTURE.md +10 -2
- package/docs/CLIENTS.md +11 -1
- package/docs/LOGGING.md +9 -18
- package/docs/MANAGED_JOBS.md +8 -0
- package/docs/OPERATIONS.md +24 -6
- package/docs/TESTING.md +5 -1
- package/package.json +12 -6
- package/src/local/atomic-fs.mjs +37 -0
- package/src/local/cli.mjs +63 -3
- package/src/local/daemon.mjs +67 -5
- package/src/local/full-access-test.mjs +206 -0
- package/src/local/job-runner.mjs +3 -2
- package/src/local/managed-jobs.mjs +3 -2
- package/src/local/resource-operations.mjs +66 -0
- package/src/local/ssh-key.mjs +125 -0
- package/src/local/state.mjs +5 -4
- package/src/local/stdio.mjs +2 -4
- package/src/local/tools.mjs +37 -3
- package/src/shared/server-metadata.json +3 -1
- package/src/shared/tool-catalog.json +33 -0
- package/src/worker/index.ts +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.2 - 2026-07-10
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Retry atomic JSON file replacement with bounded backoff for transient Windows sharing failures (`EPERM`, `EACCES`, `EBUSY`, and `ENOTEMPTY`). State, managed-job manager, and detached runner commits now use one shared implementation.
|
|
8
|
+
- Prevent an intermittent Windows `runner_failed` result caused by a transient status-file replacement failure after the same commit had passed the protected pull-request checks.
|
|
9
|
+
- Preserve immediate failure for non-transient filesystem errors and keep temporary-file cleanup behavior unchanged.
|
|
10
|
+
- Restore corrupt-state backup creation through the shared replacement primitive, add collision-resistant backup names, and verify that recovery preserves the original corrupt bytes.
|
|
11
|
+
|
|
12
|
+
### Tests and diagnostics
|
|
13
|
+
|
|
14
|
+
- Add deterministic injected transient/non-transient atomic replacement tests.
|
|
15
|
+
- Run the full real-machine sandbox three consecutive times on Windows, exercising repeated detached runner, status, result, and finally-step commits.
|
|
16
|
+
- Include managed-job error and cleanup classifications in `full-test` output when a lifecycle check fails.
|
|
17
|
+
|
|
18
|
+
## 0.6.1 - 2026-07-10
|
|
19
|
+
|
|
20
|
+
### Full-profile contract and operator workflows
|
|
21
|
+
|
|
22
|
+
- Make named profiles canonical capability contracts. A stored `full` label is repaired to writes, shell execution, unrestricted paths, full parent environment, absolute path output, and the complete tool catalog; deliberate individual overrides remain `custom`. Advance the policy revision to 3 and expose the contract in `server_info` and `doctor`.
|
|
23
|
+
- Add `machine-mcp full-test`, a real local-machine acceptance suite using disposable directories. It verifies outside-workspace I/O, direct and shell execution, parent-environment inheritance, SSH key generation, sandbox `authorized_keys`, SSH client parsing, Google OS Login command availability, a non-mutating sudo probe, and detached finally cleanup without changing cloud or remote state.
|
|
24
|
+
- Add `machine-mcp resource generate-ssh-key NAME [PATH]` and the canonical-full-only MCP tool `generate_ssh_key_resource`. Both generate or reuse an Ed25519 pair locally, register the private file as a resource, and return only paths, modes, key type, and public fingerprint.
|
|
25
|
+
|
|
26
|
+
### Logging
|
|
27
|
+
|
|
28
|
+
- Remove every ordinary per-tool event from default `info`/`warn` logs. Starts, successes, failures, cancellations, durations, slow calls, tool names, coarse outcome classes, and call correlation are now debug-only in both remote daemon and stdio transports.
|
|
29
|
+
- Keep default logs focused on deployment, connection, protocol, relay, service, and infrastructure health. Tool arguments, command text, inputs, outputs, and results remain omitted at all levels.
|
|
30
|
+
|
|
31
|
+
### Security, correctness, and portability
|
|
32
|
+
|
|
33
|
+
- Verify that an existing SSH public key matches its private key and that the private key is usable non-interactively before reuse. Reject incomplete pairs and symbolic links, enforce owner-only private modes where supported, and never return private bytes.
|
|
34
|
+
- Fix the previously untested RSA key-generation argument order and add real RSA coverage.
|
|
35
|
+
- Centralize CLI and MCP key generation/registration in one locked state transaction. Roll back a newly created pair if state persistence fails, handle canonical path aliases, and reapply permissions after cross-filesystem installation.
|
|
36
|
+
- Validate the resource state layout before deriving its root and restrict the MCP key generator to the complete canonical `full` profile.
|
|
37
|
+
- Avoid reading user SSH configuration during `full-test`, canonicalize temporary path aliases, and report sudo/cloud prerequisites separately from core Machine Bridge capability.
|
|
38
|
+
|
|
39
|
+
### Tests and documentation
|
|
40
|
+
|
|
41
|
+
- Add real Ed25519/RSA generation, reuse, mismatch, incomplete-pair, symlink, mode, private-content, CLI, stdio, Worker-policy, and real-machine full-profile regression coverage.
|
|
42
|
+
- Update logging, architecture, operations, client, managed-job, security, and testing documentation to match the canonical-full and debug-only per-tool event contracts.
|
|
43
|
+
|
|
3
44
|
## 0.6.0 - 2026-07-10
|
|
4
45
|
|
|
5
46
|
### Added
|
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ A newly selected workspace starts with the maximum-permission `full` profile for
|
|
|
32
32
|
- tool results may return absolute paths;
|
|
33
33
|
- child processes inherit the complete parent environment.
|
|
34
34
|
|
|
35
|
-
Policy state records whether it came from the default, an explicit named profile, or custom overrides.
|
|
35
|
+
Policy state records whether it came from the default, an explicit named profile, or custom overrides. Named profiles are canonical contracts: a stored `full` profile is repaired on load to enable writes, shell execution, unrestricted paths, full parent environment, absolute path output, and the complete tool catalog. Any explicit per-capability narrowing is stored as `custom`. Policy revision 3 refreshes default/migrated state and preserves explicit restrictive/custom profiles.
|
|
36
36
|
|
|
37
37
|
| Profile | File edits | Direct argv processes | Shell commands | Filesystem scope | Process environment |
|
|
38
38
|
|---|---:|---:|---:|---|---|
|
|
@@ -152,6 +152,14 @@ machine-mcp doctor
|
|
|
152
152
|
|
|
153
153
|
A successful `diagnose_runtime` response proves that request reached the local daemon. It then reports fixed probes for Machine Bridge policy, private filesystem access, direct process spawning, shell execution, managed-job storage, and registered resources. If the host blocks the tool call before any structured response, the server cannot diagnose that request because it never received it.
|
|
154
154
|
|
|
155
|
+
Run a real local acceptance test for the canonical `full` contract:
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
machine-mcp full-test --workspace /path/to/project
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The test uses a temporary sandbox to perform an outside-workspace read/write, direct process, shell command, parent-environment inheritance, Ed25519 generation, temporary `authorized_keys` write, SSH client check, Google Cloud OS Login command availability check, non-mutating `sudo -n true` probe, and detached managed-job/finally lifecycle. It does not add a cloud key, contact a remote maintenance host, modify a user account, or retain the generated key.
|
|
162
|
+
|
|
155
163
|
## Managed jobs and local resources
|
|
156
164
|
|
|
157
165
|
Long, remote, multi-step, or cleanup-sensitive work should not depend on a sequence of later MCP calls remaining available. `start_job` durably accepts ordered argv steps plus `finally_steps`, then launches an independent local runner. It continues after MCP disconnects or later host-side tool refusals.
|
|
@@ -166,6 +174,14 @@ machine-mcp resource add racknerd-key ~/.ssh/racknerd_ed25519
|
|
|
166
174
|
machine-mcp resource list
|
|
167
175
|
```
|
|
168
176
|
|
|
177
|
+
Generate an Ed25519 key and register its private file in one operation:
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
machine-mcp resource generate-ssh-key racknerd-key ~/.ssh/ai-racknerd-maint-ed25519
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Under canonical `full`, an authorized MCP host can invoke `generate_ssh_key_resource` with the same semantics. The tool is idempotent, verifies that existing public/private files match, enforces local file modes where supported, and returns only paths, public fingerprint, key type, and registration status—not private key bytes.
|
|
184
|
+
|
|
169
185
|
A job refers to the alias rather than the value:
|
|
170
186
|
|
|
171
187
|
```json
|
|
@@ -242,6 +258,7 @@ Repository-configured external diff, text conversion, and filesystem-monitor hel
|
|
|
242
258
|
|
|
243
259
|
- `diagnose_runtime` — fixed layered probes; no user-controlled command input
|
|
244
260
|
- `list_local_resources` — aliases and validation status without paths or values
|
|
261
|
+
- `generate_ssh_key_resource` — canonical-full-only Ed25519 generation and private-file resource registration without returning private content
|
|
245
262
|
- `stage_job` — persist a validated plan for later local approval without executing it
|
|
246
263
|
- `start_job` — detached ordered argv steps, private temporary files, and finally steps
|
|
247
264
|
- `list_jobs`
|
|
@@ -296,7 +313,7 @@ Remote mode supports:
|
|
|
296
313
|
- Linux `systemd --user`, with best-effort lingering;
|
|
297
314
|
- Windows Scheduled Task at logon.
|
|
298
315
|
|
|
299
|
-
The service definition contains neither credentials nor a duplicate policy. It loads the selected policy from owner-only local state and uses the documented `full` default if policy state is absent. Background services run at log level `warn`:
|
|
316
|
+
The service definition contains neither credentials nor a duplicate policy. It loads the selected policy from owner-only local state and uses the documented `full` default if policy state is absent. Background services run at log level `warn`: relay, protocol, and service problems are retained, while all per-tool success/failure/cancellation/timing events remain debug-only. Logs are owner-only where supported and bounded by tail trimming.
|
|
300
317
|
|
|
301
318
|
## Secret rotation
|
|
302
319
|
|
|
@@ -317,7 +334,7 @@ Default state roots:
|
|
|
317
334
|
|
|
318
335
|
State/config writes use owner-only temporary files, flushes, and atomic rename. Malformed state is retained as a bounded corrupt backup before reconstruction. Resource source paths are redacted from `status` output. Active managed-job plans are owner-only and are deleted after a terminal result; bounded redacted results are retained temporarily. Uninstall validates markers, canonical paths, active locks, workspace/source exclusions, and known contents before recursive deletion.
|
|
319
336
|
|
|
320
|
-
Default foreground logs show startup/deployment transitions, relay connectivity, warnings/errors
|
|
337
|
+
Default foreground logs show startup/deployment transitions, relay connectivity, and infrastructure warnings/errors. Every per-tool event—including success, failure, cancellation, and slow-call timing—appears only at `--log-level debug` or `--verbose`. Background services use `warn`, so ordinary tool outcomes never fill daemon logs. Log messages and structured fields are bounded, secret-like keys and known token formats are redacted, and tool arguments/results are not written. See [docs/LOGGING.md](docs/LOGGING.md) and [docs/OPERATIONS.md](docs/OPERATIONS.md).
|
|
321
338
|
|
|
322
339
|
## Development and verification
|
|
323
340
|
|
|
@@ -330,7 +347,7 @@ npm audit --omit=dev --audit-level=high
|
|
|
330
347
|
npm pack --dry-run
|
|
331
348
|
```
|
|
332
349
|
|
|
333
|
-
`npm run check` covers generated Worker types, TypeScript, JavaScript syntax, the shared tool catalog, local path/write/process/state/log/service invariants, managed-job detachment/resource redaction/finally/cancellation/recovery, a live stdio MCP flow, and a live local OAuth/Worker/WebSocket/MCP flow. GitHub Actions runs the suite on Linux, macOS, and Windows with supported Node versions.
|
|
350
|
+
`npm run check` covers generated Worker types, TypeScript, JavaScript syntax, the shared tool catalog, local path/write/process/state/log/service invariants, Ed25519/RSA generation and key-pair validation, real-machine `full` sandbox acceptance, managed-job detachment/resource redaction/finally/cancellation/recovery, a live stdio MCP flow, and a live local OAuth/Worker/WebSocket/MCP flow. GitHub Actions runs the suite on Linux, macOS, and Windows with supported Node versions.
|
|
334
351
|
|
|
335
352
|
See [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md), [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), and [SECURITY.md](SECURITY.md).
|
|
336
353
|
|
package/SECURITY.md
CHANGED
|
@@ -32,9 +32,9 @@ The Worker is a remote authentication and relay boundary. The local runtime is t
|
|
|
32
32
|
|
|
33
33
|
## Profiles are capability sets, not sandboxes
|
|
34
34
|
|
|
35
|
-
The default for newly selected workspaces is `full`, which prioritizes ease of use over least privilege.
|
|
35
|
+
The default for newly selected workspaces is `full`, which prioritizes ease of use over least privilege. Named profiles are canonical capability contracts. A stored `full` label is repaired on load to the complete maximum-permission field set and tool catalog; a deliberate per-capability override is represented as `custom`, not as a partially restricted `full`.
|
|
36
36
|
|
|
37
|
-
- `full` exposes all tools, unrestricted direct filesystem paths, absolute path output, and the complete parent process environment.
|
|
37
|
+
- `full` exposes all tools, unrestricted direct filesystem paths, absolute path output, shell execution, and the complete parent process environment.
|
|
38
38
|
- `agent` exposes file mutation, direct argv execution, and process sessions while keeping direct filesystem tools workspace-confined and the process environment isolated.
|
|
39
39
|
- `edit` exposes read and mutation tools without process execution.
|
|
40
40
|
- `review` exposes read-only workspace/Git/image tools.
|
|
@@ -57,6 +57,12 @@ Maximum local policy does not bypass Unix permissions, Windows ACLs, macOS TCC/S
|
|
|
57
57
|
|
|
58
58
|
Processes are not confined by the filesystem-tool resolver. They can access paths, networks, processes, credential stores, and devices available to the local user.
|
|
59
59
|
|
|
60
|
+
## Full-profile verification
|
|
61
|
+
|
|
62
|
+
`machine-mcp full-test` performs real local operations inside disposable temporary directories: unrestricted file access, direct and shell processes, inherited environment, SSH key generation/matching, a sandbox `authorized_keys` write, SSH client parsing, Google Cloud OS Login command discovery, a non-mutating sudo availability probe, and detached job cleanup. It makes no cloud or remote-server change and cannot prove that an MCP host will deliver a future request. The command verifies the Machine Bridge and local-machine portion of the authority chain only.
|
|
63
|
+
|
|
64
|
+
A canonical `full` profile guarantees that Machine Bridge itself will not reject a catalogued tool because of its local profile, path scope, environment mode, or shell mode. It cannot override the MCP host/connector, operating-system access controls, endpoint-security policy, remote authentication, cloud IAM, `sudo`, or service-side authorization.
|
|
65
|
+
|
|
60
66
|
## Mutation integrity
|
|
61
67
|
|
|
62
68
|
Writes are bounded, reject symbolic-link/non-regular destinations, use same-directory staging, and commit atomically per file. Create-only commit fails if a concurrent destination appears. Expected hashes and exact edits reduce accidental stale overwrites.
|
|
@@ -75,7 +81,7 @@ The default `full` profile passes the complete parent environment. Narrower prof
|
|
|
75
81
|
|
|
76
82
|
## Local resources and managed jobs
|
|
77
83
|
|
|
78
|
-
Local resources are registered
|
|
84
|
+
Local resources are registered through the operator-controlled CLI or, only under canonical `full`, through `generate_ssh_key_resource`. State stores canonical paths and metadata, not file contents. The SSH generator creates or reuses an unencrypted automation key, verifies that the public and private files match, rejects symbolic links/incomplete pairs, applies `0600`/`0644` modes where supported, and returns no private bytes. Unix-like registration rejects group/other-readable private resource files unless explicitly overridden. Portable mode checks do not fully describe Windows ACLs or extended Unix ACLs; the operator remains responsible for platform permissions.
|
|
79
85
|
|
|
80
86
|
At job acceptance, referenced resources are bounded and hashed. The detached runner reopens and verifies each hash before copying it to a private `0600` runtime file. Resource copies are removed after the finally phase. A changed or unavailable resource fails closed.
|
|
81
87
|
|
|
@@ -131,7 +137,7 @@ Process sessions are interactive and intentionally die with runtime disconnect/r
|
|
|
131
137
|
|
|
132
138
|
## Logs and privacy
|
|
133
139
|
|
|
134
|
-
Default operational logs record startup/
|
|
140
|
+
Default operational logs record startup/deployment, relay, protocol, service, and infrastructure transitions. Every ordinary per-tool event—start, success, failure, cancellation, and duration—is debug-only. Tool arguments, command text, stdin, file/patch contents, and outputs are omitted. Messages and fields are bounded; unexpected daemon and Worker infrastructure exceptions are reduced to error classes. Git author email is omitted from `git_log` unless explicitly requested.
|
|
135
141
|
|
|
136
142
|
No logging policy can prevent data from being returned to a client that explicitly invokes an enabled tool. The Worker necessarily relays remote tool arguments and results; this is not end-to-end encryption against the user's Cloudflare execution environment. Managed-job result files may contain remote command output and are owner-only local data, not operational logs.
|
|
137
143
|
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -58,6 +58,14 @@ Running managed jobs do not belong to an MCP socket or daemon call ID and snapsh
|
|
|
58
58
|
|
|
59
59
|
Local resource registrations remain in owner-only state and are reloaded for every new job. MCP-visible resource inventory includes aliases and validation status but never source paths, hashes, or contents.
|
|
60
60
|
|
|
61
|
+
### Canonical policy contracts
|
|
62
|
+
|
|
63
|
+
Named profiles are normalized to their complete capability sets. In particular, `full` always means write enabled, shell execution, unrestricted paths, complete parent environment, absolute-path display, and every catalog tool. CLI flags that alter an individual capability deliberately change the profile identity to `custom`. Policy revision 3 repairs stale or manually edited named-profile fields rather than allowing a misleading partially restricted `full` label.
|
|
64
|
+
|
|
65
|
+
The full-only `generate_ssh_key_resource` operation is implemented locally. The Worker only filters and relays its shared catalog definition. Local generation uses `ssh-keygen`, verifies public/private correspondence, registers the private file through the same owner-only state transaction as the CLI, and rolls back a newly created pair if state persistence fails.
|
|
66
|
+
|
|
67
|
+
`full-test` constructs a local runtime with canonical full policy and executes real operations in disposable directories. It is an acceptance test for the local implementation, not a probe that changes remote systems or bypasses host policy.
|
|
68
|
+
|
|
61
69
|
### Stdio MCP server
|
|
62
70
|
|
|
63
71
|
The stdio server implements newline-delimited JSON-RPC over stdin/stdout. It negotiates supported MCP versions, advertises policy-filtered tools, returns text plus structured content, supports native image content, maps cancellation notifications to runtime call IDs, and sends level-filtered logs only to stderr.
|
|
@@ -175,7 +183,7 @@ Pending calls retain their originating socket reference. A stale socket cannot c
|
|
|
175
183
|
|
|
176
184
|
## Persistence
|
|
177
185
|
|
|
178
|
-
Local state and global config are owner-only, versioned, size-bounded, and written through temporary files, flushes, and atomic rename. Reads reject symbolic links and use no-follow descriptors where supported. Malformed or oversized state becomes a bounded-count `.corrupt-*` backup. Resource paths are omitted from redacted status output. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
|
|
186
|
+
Local state and global config are owner-only, versioned, size-bounded, and written through temporary files, flushes, and atomic rename. State, managed-job manager, and detached runner commits share a bounded retrying atomic-replace primitive for transient Windows `EPERM`, `EACCES`, `EBUSY`, and `ENOTEMPTY` sharing failures. Reads reject symbolic links and use no-follow descriptors where supported. Malformed or oversized state becomes a bounded-count `.corrupt-*` backup. Resource paths are omitted from redacted status output. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
|
|
179
187
|
|
|
180
188
|
Active managed jobs persist an owner-only plan, status, runner PID, and bounded runner diagnostics. Terminal jobs delete the full plan and retain only bounded status/redacted results for up to seven days. This balances crash cleanup with minimization of scripts, stdin, argv, environment overrides, and resource source paths.
|
|
181
189
|
|
|
@@ -187,7 +195,7 @@ OAuth metadata is pruned on access. Expired codes/tokens, old throttling records
|
|
|
187
195
|
|
|
188
196
|
Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status, policy origin/revision, managed-job counts, and resource alias names without paths or values. `diagnose_runtime` runs fixed local probes and explicitly reports that its own request reached the daemon.
|
|
189
197
|
|
|
190
|
-
Foreground logging defaults to `info`; autostart uses `warn`.
|
|
198
|
+
Foreground logging defaults to `info`; autostart uses `warn`. All per-tool starts, successes, failures, cancellations, durations, and shortened random correlation IDs are debug-only. Normal logs retain deployment, connection, protocol, service, and infrastructure events rather than duplicating MCP tool outcomes. Unexpected local and Worker infrastructure errors are reduced to classes. Messages, strings, arrays, object depth/key counts, and serialized fields are bounded.
|
|
191
199
|
|
|
192
200
|
Cloudflare sampling is size control rather than an audit log. The project intentionally does not claim complete forensic logging. See [LOGGING.md](LOGGING.md).
|
|
193
201
|
|
package/docs/CLIENTS.md
CHANGED
|
@@ -118,13 +118,23 @@ Enter the printed `/mcp` URL in the remote MCP connector. During OAuth authoriza
|
|
|
118
118
|
|
|
119
119
|
## Profile guidance
|
|
120
120
|
|
|
121
|
-
- `full` is the default and prioritizes immediate usability. It
|
|
121
|
+
- `full` is the default and prioritizes immediate usability. It is a canonical contract exposing every catalog tool, shell execution, unrestricted direct filesystem paths, absolute path output, and the full parent environment. Any individual narrowing is represented as `custom`.
|
|
122
122
|
- `agent` retains file mutation and direct process execution but removes shell parsing, confines direct filesystem tools to the workspace, and isolates the process environment.
|
|
123
123
|
- `edit` permits deterministic file mutation without process execution.
|
|
124
124
|
- `review` is read-only and workspace-confined.
|
|
125
125
|
|
|
126
126
|
A client configuration is an authorization decision. Anyone who controls an authorized host can invoke every tool exposed by the selected profile.
|
|
127
127
|
|
|
128
|
+
Before connecting a host, verify the local implementation directly:
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
machine-mcp full-test --workspace /path/to/project
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
A passing result proves that Machine Bridge and the local OS allowed its temporary file/process/shell/key/job probes at that time. It does not prove that a hosted MCP connector will deliver a later request or that a cloud/remote account will authorize it.
|
|
135
|
+
|
|
136
|
+
For SSH automation, prefer `generate_ssh_key_resource` under canonical full, or `machine-mcp resource generate-ssh-key` from the terminal. The private key remains a local resource; only paths and the public fingerprint are returned. Installation of the public key into Google OS Login or a remote `authorized_keys` file remains an explicit external action.
|
|
137
|
+
|
|
128
138
|
## Host-side safety rules
|
|
129
139
|
|
|
130
140
|
The local `full` profile controls Machine Bridge's own tool catalog, path resolver, path display, process environment, and shell availability. It does not control the MCP host's model policy, approval UI, connector gateway, or platform execution filters.
|
package/docs/LOGGING.md
CHANGED
|
@@ -6,8 +6,8 @@ Operational logs should answer four questions without becoming a transcript of u
|
|
|
6
6
|
|
|
7
7
|
1. Did deployment and relay connection succeed?
|
|
8
8
|
2. Is the daemon reconnecting, superseded, or unhealthy?
|
|
9
|
-
3.
|
|
10
|
-
4.
|
|
9
|
+
3. Is an infrastructure, protocol, deployment, or relay failure occurring?
|
|
10
|
+
4. When debug logging is explicitly enabled, which bounded per-tool event should be correlated?
|
|
11
11
|
|
|
12
12
|
Logs are not a command history, content audit trail, or substitute for MCP client approvals.
|
|
13
13
|
|
|
@@ -26,24 +26,15 @@ The default foreground level is `info`. Platform autostart services use `warn`.
|
|
|
26
26
|
| Level | Intended events |
|
|
27
27
|
|---|---|
|
|
28
28
|
| `error` | Broken request handlers, relay transport errors, unrecoverable local failures |
|
|
29
|
-
| `warn` |
|
|
30
|
-
| `info` | Deployment/startup transitions
|
|
31
|
-
| `debug` |
|
|
29
|
+
| `warn` | Relay disconnections/send failures, malformed or oversized messages, superseded daemons, service/autostart problems |
|
|
30
|
+
| `info` | Deployment/startup transitions and successful relay connection |
|
|
31
|
+
| `debug` | Every per-tool start/success/failure/cancellation/timing event, shortened correlation IDs, reconnect timing |
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
All per-tool outcomes are deliberately absent from `info`, `warn`, and `error`. The MCP response already reports success or failure to the caller; duplicating ordinary failures in daemon logs creates noise without adding operational information. A normal sequence of hundreds of successful or failed tool invocations should not fill the user's terminal or service logs.
|
|
34
34
|
|
|
35
35
|
## Event fields
|
|
36
36
|
|
|
37
|
-
Default
|
|
38
|
-
|
|
39
|
-
- component;
|
|
40
|
-
- tool name;
|
|
41
|
-
- duration in milliseconds;
|
|
42
|
-
- coarse error class;
|
|
43
|
-
- relay close code;
|
|
44
|
-
- bounded reconnect delay.
|
|
45
|
-
|
|
46
|
-
Shortened random call identifiers are debug-only. They are useful only for correlating adjacent local events and are not stable audit identifiers.
|
|
37
|
+
Default operational events may include component, relay close code, bounded reconnect delay, provider name, or a coarse infrastructure error class. Per-tool fields—tool name, duration, outcome class, and shortened call identifier—are debug-only. Call identifiers are useful only for correlating adjacent local events and are not stable audit identifiers.
|
|
47
38
|
|
|
48
39
|
The implementation intentionally omits:
|
|
49
40
|
|
|
@@ -54,7 +45,7 @@ The implementation intentionally omits:
|
|
|
54
45
|
- OAuth request bodies;
|
|
55
46
|
- connection passwords, daemon secrets, authorization codes, and access tokens.
|
|
56
47
|
|
|
57
|
-
Unexpected daemon and Worker 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 normal operational logs.
|
|
48
|
+
Unexpected daemon, service, and Worker 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 ordinary tool failures and those details are not copied into normal operational logs.
|
|
58
49
|
|
|
59
50
|
## Bounding and redaction
|
|
60
51
|
|
|
@@ -71,7 +62,7 @@ logs/daemon.out.log
|
|
|
71
62
|
logs/daemon.err.log
|
|
72
63
|
```
|
|
73
64
|
|
|
74
|
-
They are owner-only where the platform supports Unix-style permissions. Existing files are tail-trimmed on UTF-8/line boundaries before daemon startup. Because background services use `warn
|
|
65
|
+
They are owner-only where the platform supports Unix-style permissions. Existing files are tail-trimmed on UTF-8/line boundaries before daemon startup. Because background services use `warn` and per-tool events are debug-only, neither successful nor failed ordinary tool traffic should cause sustained log growth.
|
|
75
66
|
|
|
76
67
|
Each managed job also has owner-only `runner.out.log` and `runner.err.log` files for runner startup/crash diagnostics. Child-step stdout and stderr are captured into bounded job results according to `capture_output`; they are not copied into runner or daemon operational logs. Runner logs are retained with the bounded job directory and removed by job retention cleanup.
|
|
77
68
|
|
package/docs/MANAGED_JOBS.md
CHANGED
|
@@ -64,6 +64,14 @@ machine-mcp resource remove racknerd-key
|
|
|
64
64
|
|
|
65
65
|
Removing an alias affects new jobs. A job already accepted has an owner-only resource snapshot specification and continues independently.
|
|
66
66
|
|
|
67
|
+
Generate a new Ed25519 resource rather than exposing a private-key creation command through a model-generated shell string:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
machine-mcp resource generate-ssh-key racknerd-key ~/.ssh/ai-racknerd-maint-ed25519
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Canonical-full MCP clients also receive `generate_ssh_key_resource`. It generates or reuses the pair locally, verifies correspondence, registers the private file, and returns no private bytes. Public-key installation in a cloud or remote account should be a separate reviewed step or managed job.
|
|
74
|
+
|
|
67
75
|
## Resource injection modes
|
|
68
76
|
|
|
69
77
|
A managed step is argv-based and supports three local resource modes:
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -30,10 +30,10 @@ Remote autostart logs are stored under the state root in `logs/daemon.out.log` a
|
|
|
30
30
|
Logging is level-based:
|
|
31
31
|
|
|
32
32
|
```text
|
|
33
|
-
error unrecoverable local/transport failures
|
|
34
|
-
warn
|
|
35
|
-
info startup/deploy/connect transitions
|
|
36
|
-
debug
|
|
33
|
+
error unrecoverable local/transport/service failures
|
|
34
|
+
warn relay disconnect/send failures, malformed relay events, supersession and service problems
|
|
35
|
+
info startup/deploy/connect transitions
|
|
36
|
+
debug all per-tool starts/successes/failures/cancellations/timing, correlation and reconnect details
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Foreground mode defaults to `info`; autostart uses `warn`. Use `--verbose` or `--log-level debug` only for diagnosis. `--quiet` is an alias for `--log-level error`.
|
|
@@ -42,6 +42,24 @@ Normal logs intentionally omit tool arguments, file/patch/image content, command
|
|
|
42
42
|
|
|
43
43
|
See [LOGGING.md](LOGGING.md) for the event contract and MCP-host boundary. Cloudflare observability is sampled and is not a complete audit log.
|
|
44
44
|
|
|
45
|
+
## Full capability acceptance
|
|
46
|
+
|
|
47
|
+
Run:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
machine-mcp full-test --workspace /path/to/project
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The command uses disposable local directories and performs actual read/write, process, shell, environment, SSH-key, sandbox authorized-key, SSH-client, managed-job and finally-cleanup operations. It also checks whether the Google Cloud OS Login command exists and whether `sudo -n true` is currently permitted, without changing either system. `ok` covers core Machine Bridge functionality; `operator_workflow_ready` additionally reports the local SSH/Google CLI prerequisites. No external cloud, account, or server change is made.
|
|
54
|
+
|
|
55
|
+
Generate and register an operator key locally:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
machine-mcp resource generate-ssh-key NAME [PRIVATE_KEY_PATH]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
An authorized canonical-full MCP client can use `generate_ssh_key_resource`. Both paths validate the key pair and return only metadata and a public fingerprint. They do not install the public key in Google, modify `authorized_keys`, or grant remote `sudo`; those remain explicit managed-job/local-operator operations.
|
|
62
|
+
|
|
45
63
|
## Managed jobs and local recovery
|
|
46
64
|
|
|
47
65
|
Register local-only resources from the terminal:
|
|
@@ -108,9 +126,9 @@ Defense-in-depth limits include:
|
|
|
108
126
|
|
|
109
127
|
## Upgrade behavior
|
|
110
128
|
|
|
111
|
-
|
|
129
|
+
Policy revision 3 makes named profiles canonical. A state entry labelled `full` is repaired to writes, direct processes, process sessions, shell execution, unrestricted direct filesystem paths, absolute path output, the complete parent environment, and the complete tool catalog. CLI capability overrides are stored as `custom`. The exact pre-0.4 implicit-default shape is still migrated to the current `full` default; explicit restrictive and identified custom profiles remain preserved.
|
|
112
130
|
|
|
113
|
-
`full`
|
|
131
|
+
`full` removes Machine Bridge's own profile/path/environment/shell denials. It does not override operating-system access controls, endpoint security, remote authentication, cloud IAM, `sudo`, or independent MCP-host/platform policy.
|
|
114
132
|
|
|
115
133
|
Inspect effective policy with:
|
|
116
134
|
|
package/docs/TESTING.md
CHANGED
|
@@ -25,11 +25,15 @@ The suite includes:
|
|
|
25
25
|
- one-shot timeout, descendant termination, cancellation, and process-session interaction;
|
|
26
26
|
- layered fixed runtime diagnostics for filesystem, direct process, shell, managed-job storage, and resource availability;
|
|
27
27
|
- local resource CLI registration, permission checks, dynamic reload, state-path redaction, and content non-disclosure;
|
|
28
|
+
- real Ed25519 and RSA generation, idempotent reuse, public/private correspondence, mode enforcement, incomplete/mismatched/symlink rejection, and private-content non-disclosure;
|
|
29
|
+
- real-machine canonical-full sandbox acceptance for outside-workspace I/O, direct/shell execution, full environment inheritance, SSH prerequisites, temporary authorized-key writing, and detached cleanup without external state changes;
|
|
30
|
+
- deterministic injected atomic-replace failures and repeated Windows full-sandbox runs to catch transient file-sharing races;
|
|
31
|
+
- canonical named-profile repair and full-only tool exposure parity between local and Worker policy filters;
|
|
28
32
|
- managed-job staging/local approval/cancel-before-start, detachment, job-scoped temporary files, resource hash verification/redaction, discard capture, finally execution, cancellation escalation, plan scrubbing, and dead-runner recovery;
|
|
29
33
|
- daemon/startup locking and state corruption recovery;
|
|
30
34
|
- guarded state-root removal, schema migration, policy-origin persistence, and legacy implicit-default migration;
|
|
31
35
|
- no filename-based sensitive-file denial under unrestricted policy;
|
|
32
|
-
- log redaction, control-character handling, message/field bounds,
|
|
36
|
+
- log redaction, control-character handling, message/field bounds, suppression of both successful and failed per-tool events outside debug, and service warning-level configuration;
|
|
33
37
|
- CLI parsing, policy profiles, and client configuration boundaries;
|
|
34
38
|
- live stdio MCP initialization, discovery, calls, rich content, sessions, cancellation, managed-job acceptance, and a detached job/finally phase that survives stdio shutdown;
|
|
35
39
|
- live local Worker OAuth registration, consent, PKCE, token replay rejection, throttling, CORS, protocol negotiation, dynamic tool advertisement, rich content, daemon replacement, and cancellation.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "machine-bridge-mcp",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "Cross-client MCP bridge for
|
|
3
|
+
"version": "0.6.2",
|
|
4
|
+
"description": "Cross-client MCP bridge for canonical local automation, files, Git, SSH resources, managed jobs, and processes over stdio or an OAuth-protected remote relay.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"engines": {
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"prepublishOnly": "npm run check && npm run version:check && npm run release:check",
|
|
40
40
|
"worker:types": "wrangler types src/worker/worker-configuration.d.ts",
|
|
41
41
|
"typecheck": "npm run worker:types && tsc -p tsconfig.json --noEmit",
|
|
42
|
-
"syntax": "sh -n mbm && node --check bin/machine-mcp.mjs && node --check src/local/cli.mjs && node --check src/local/daemon.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/managed-jobs.mjs && node --check src/local/job-runner.mjs && node --check scripts/sync-worker-version.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/daemon-self-test.mjs && node --check tests/managed-jobs-test.mjs",
|
|
43
|
-
"check": "npm run typecheck && npm run syntax && npm run catalog:test && npm run self-test && npm run managed-jobs:test && npm run stdio:integration-test && npm run worker:integration-test",
|
|
42
|
+
"syntax": "sh -n mbm && node --check bin/machine-mcp.mjs && node --check src/local/cli.mjs && node --check src/local/daemon.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/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/daemon-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",
|
|
43
|
+
"check": "npm run typecheck && npm run syntax && npm run catalog:test && npm run self-test && npm run atomic-fs: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",
|
|
44
44
|
"worker:dry-run": "wrangler deploy --dry-run",
|
|
45
45
|
"worker:integration-test": "node tests/worker-integration-test.mjs",
|
|
46
46
|
"release:check": "node scripts/github-release.mjs --check",
|
|
@@ -48,7 +48,10 @@
|
|
|
48
48
|
"release:backfill": "node scripts/github-release.mjs --backfill",
|
|
49
49
|
"stdio:integration-test": "node tests/stdio-integration-test.mjs",
|
|
50
50
|
"catalog:test": "node tests/catalog-test.mjs",
|
|
51
|
-
"managed-jobs:test": "node tests/managed-jobs-test.mjs"
|
|
51
|
+
"managed-jobs:test": "node tests/managed-jobs-test.mjs",
|
|
52
|
+
"ssh-key:test": "node tests/ssh-key-test.mjs",
|
|
53
|
+
"full-access:test": "node tests/full-access-test.mjs",
|
|
54
|
+
"atomic-fs:test": "node tests/atomic-fs-test.mjs"
|
|
52
55
|
},
|
|
53
56
|
"dependencies": {
|
|
54
57
|
"wrangler": "4.110.0",
|
|
@@ -68,7 +71,10 @@
|
|
|
68
71
|
"stdio",
|
|
69
72
|
"coding-agent",
|
|
70
73
|
"oauth",
|
|
71
|
-
"cross-platform"
|
|
74
|
+
"cross-platform",
|
|
75
|
+
"ssh",
|
|
76
|
+
"automation",
|
|
77
|
+
"managed-jobs"
|
|
72
78
|
],
|
|
73
79
|
"repository": {
|
|
74
80
|
"type": "git",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { renameSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
const TRANSIENT_REPLACE_ERRORS = new Set(["EACCES", "EBUSY", "EPERM", "ENOTEMPTY"]);
|
|
4
|
+
const WAIT_BUFFER = new Int32Array(new SharedArrayBuffer(4));
|
|
5
|
+
|
|
6
|
+
export function replaceFileSync(source, target, options = {}) {
|
|
7
|
+
const rename = typeof options.rename === "function" ? options.rename : renameSync;
|
|
8
|
+
const attempts = clampInteger(options.attempts, 8, 1, 32);
|
|
9
|
+
const baseDelayMs = clampInteger(options.baseDelayMs, 15, 0, 1000);
|
|
10
|
+
let lastError;
|
|
11
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
12
|
+
try {
|
|
13
|
+
rename(source, target);
|
|
14
|
+
return { attempts: attempt };
|
|
15
|
+
} catch (error) {
|
|
16
|
+
lastError = error;
|
|
17
|
+
if (!isTransientReplaceError(error) || attempt === attempts) throw error;
|
|
18
|
+
sleepSync(Math.min(baseDelayMs * attempt, 250));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
throw lastError || new Error("atomic file replacement failed");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isTransientReplaceError(error) {
|
|
25
|
+
return TRANSIENT_REPLACE_ERRORS.has(String(error?.code || ""));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function sleepSync(milliseconds) {
|
|
29
|
+
if (milliseconds <= 0) return;
|
|
30
|
+
Atomics.wait(WAIT_BUFFER, 0, 0, milliseconds);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function clampInteger(value, fallback, minimum, maximum) {
|
|
34
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
35
|
+
const number = Number.isFinite(parsed) ? parsed : fallback;
|
|
36
|
+
return Math.min(Math.max(number, minimum), maximum);
|
|
37
|
+
}
|
package/src/local/cli.mjs
CHANGED
|
@@ -5,10 +5,12 @@ import process from "node:process";
|
|
|
5
5
|
import readline from "node:readline/promises";
|
|
6
6
|
import { LocalDaemon } from "./daemon.mjs";
|
|
7
7
|
import { runStdioServer } from "./stdio.mjs";
|
|
8
|
-
import { DEFAULT_POLICY_PROFILE, DEFAULT_POLICY_REVISION, POLICY_PROFILES, normalizePolicy, policyProfile } from "./tools.mjs";
|
|
8
|
+
import { assertCanonicalFullPolicy, DEFAULT_POLICY_PROFILE, DEFAULT_POLICY_REVISION, POLICY_PROFILES, normalizePolicy, policyProfile, toolsForPolicy } from "./tools.mjs";
|
|
9
9
|
import { classifyOperationalError, createLogger, normalizeLogLevel, sanitizeLogText } from "./log.mjs";
|
|
10
10
|
import { activeManagedJobs, inspectResourceFile, loadManagedJobPlan, ManagedJobManager, publicResourceRegistry, validateResourceName } from "./managed-jobs.mjs";
|
|
11
11
|
import { runWrangler } from "./shell.mjs";
|
|
12
|
+
import { generateRegisteredSshKey } from "./resource-operations.mjs";
|
|
13
|
+
import { runFullAccessTest } from "./full-access-test.mjs";
|
|
12
14
|
import {
|
|
13
15
|
acquireDaemonLock,
|
|
14
16
|
acquireStartupLock,
|
|
@@ -60,6 +62,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
60
62
|
case "client-config": return clientConfigCommand(args);
|
|
61
63
|
case "status": return statusCommand(args);
|
|
62
64
|
case "doctor": return doctorCommand(args);
|
|
65
|
+
case "full-test": return fullTestCommand(args);
|
|
63
66
|
case "workspace": return workspaceCommand(args);
|
|
64
67
|
case "service":
|
|
65
68
|
case "autostart": return serviceCommand(args);
|
|
@@ -84,6 +87,7 @@ const COMMAND_OPTIONS = {
|
|
|
84
87
|
"client-config": new Set(["workspace", "stateDir", "profile", "client", "json"]),
|
|
85
88
|
status: new Set(["workspace", "stateDir"]),
|
|
86
89
|
doctor: new Set(["workspace", "stateDir"]),
|
|
90
|
+
"full-test": new Set(["workspace", "stateDir", "json"]),
|
|
87
91
|
"rotate-secrets": new Set(["workspace", "stateDir", "workerName", "noPrintCredentials", "printMcpCredentials", "printCredentials", "quiet"]),
|
|
88
92
|
workspace: new Set(["workspace", "stateDir"]),
|
|
89
93
|
service: new Set(["workspace", "stateDir", "quiet"]),
|
|
@@ -123,7 +127,7 @@ function toKebab(value) {
|
|
|
123
127
|
|
|
124
128
|
export function validatePositionals(command, args) {
|
|
125
129
|
const count = args._.length;
|
|
126
|
-
if (["start", "stdio", "status", "doctor", "rotate-secrets"].includes(command)) {
|
|
130
|
+
if (["start", "stdio", "status", "doctor", "full-test", "rotate-secrets"].includes(command)) {
|
|
127
131
|
if (count > 1) throw new Error(`${command} accepts at most one positional workspace path`);
|
|
128
132
|
if (args.workspace && count) throw new Error("workspace path was provided both positionally and with --workspace");
|
|
129
133
|
return;
|
|
@@ -148,7 +152,7 @@ export function validatePositionals(command, args) {
|
|
|
148
152
|
}
|
|
149
153
|
if (command === "resource") {
|
|
150
154
|
const action = String(args._[0] || "list");
|
|
151
|
-
const max = action === "add" ? 3 : action === "remove" || action === "check" ? 2 : 1;
|
|
155
|
+
const max = action === "add" || action === "generate-ssh-key" ? 3 : action === "remove" || action === "check" ? 2 : 1;
|
|
152
156
|
if (count > max) throw new Error(`resource ${action} received too many positional arguments`);
|
|
153
157
|
return;
|
|
154
158
|
}
|
|
@@ -510,6 +514,42 @@ async function resourceCommand(args) {
|
|
|
510
514
|
return;
|
|
511
515
|
}
|
|
512
516
|
|
|
517
|
+
if (action === "generate-ssh-key") {
|
|
518
|
+
const name = validateResourceName(args._[1]);
|
|
519
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
520
|
+
if (!home) throw new Error("HOME or USERPROFILE is required to choose a default SSH key path");
|
|
521
|
+
const requestedPath = args._[2] ? expandHome(args._[2]) : join(home, ".ssh", `machine-mcp-${name}-ed25519`);
|
|
522
|
+
const key = await generateRegisteredSshKey({
|
|
523
|
+
workspace,
|
|
524
|
+
stateDir: args.stateDir,
|
|
525
|
+
name,
|
|
526
|
+
targetPath: requestedPath,
|
|
527
|
+
comment: `machine-mcp:${name}`,
|
|
528
|
+
});
|
|
529
|
+
const result = {
|
|
530
|
+
name: key.name,
|
|
531
|
+
created: key.created,
|
|
532
|
+
private_key_path: key.privateKeyPath,
|
|
533
|
+
public_key_path: key.publicKeyPath,
|
|
534
|
+
fingerprint: key.fingerprint,
|
|
535
|
+
key_type: key.keyType,
|
|
536
|
+
private_mode: key.privateMode,
|
|
537
|
+
public_mode: key.publicMode,
|
|
538
|
+
private_key_content_exposed: key.privateKeyContentExposed,
|
|
539
|
+
registered: key.registered,
|
|
540
|
+
available_to_new_jobs_immediately: key.availableToNewJobsImmediately,
|
|
541
|
+
};
|
|
542
|
+
if (args.json) console.log(JSON.stringify(result, null, 2));
|
|
543
|
+
else {
|
|
544
|
+
console.log(`${key.created ? "Generated and registered" : "Reused and registered"} SSH key resource: ${name}`);
|
|
545
|
+
console.log(`Private key: ${key.privateKeyPath}`);
|
|
546
|
+
console.log(`Public key: ${key.publicKeyPath}`);
|
|
547
|
+
console.log(`Fingerprint: ${key.fingerprint}`);
|
|
548
|
+
console.log("Private key content was not printed or sent through MCP.");
|
|
549
|
+
}
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
513
553
|
if (action === "remove") {
|
|
514
554
|
const name = validateResourceName(args._[1]);
|
|
515
555
|
const lock = acquireStartupLock(state);
|
|
@@ -884,6 +924,14 @@ async function doctorCommand(args) {
|
|
|
884
924
|
const storedPolicyOrigin = state.policy?.origin;
|
|
885
925
|
state.policy = resolvePolicy({}, state.policy);
|
|
886
926
|
checks.push({ name: "policy", ok: true, detail: formatPolicySummary(state.policy) });
|
|
927
|
+
if (state.policy.profile === "full") {
|
|
928
|
+
try {
|
|
929
|
+
assertCanonicalFullPolicy(state.policy);
|
|
930
|
+
checks.push({ name: "full-policy-contract", ok: true, detail: `${toolsForPolicy(state.policy).length} tools exposed` });
|
|
931
|
+
} catch (error) {
|
|
932
|
+
checks.push({ name: "full-policy-contract", ok: false, detail: sanitizeLines(error?.message || error) });
|
|
933
|
+
}
|
|
934
|
+
}
|
|
887
935
|
const health = state.worker?.url ? await workerHealth(state.worker.url) : { ok: false, error: "no worker url" };
|
|
888
936
|
checks.push({ name: "worker-health", ok: health.ok, detail: health.ok ? state.worker.url : health.error });
|
|
889
937
|
const diagnosticRuntime = new LocalDaemon({
|
|
@@ -917,6 +965,15 @@ async function doctorCommand(args) {
|
|
|
917
965
|
}, null, 2));
|
|
918
966
|
}
|
|
919
967
|
|
|
968
|
+
async function fullTestCommand(args) {
|
|
969
|
+
const workspace = await chooseWorkspace(args, { promptOnFirstRun: false, save: false, allowPositional: true });
|
|
970
|
+
const state = loadState(workspace, { stateDir: args.stateDir });
|
|
971
|
+
const policy = resolvePolicy({}, state.policy);
|
|
972
|
+
const result = await runFullAccessTest({ workspace, policy });
|
|
973
|
+
console.log(JSON.stringify(result, null, 2));
|
|
974
|
+
if (!result.ok) process.exitCode = 1;
|
|
975
|
+
}
|
|
976
|
+
|
|
920
977
|
async function rotateSecretsCommand(args) {
|
|
921
978
|
const workspace = await chooseWorkspace(args, { promptOnFirstRun: false, save: false, allowPositional: true });
|
|
922
979
|
const state = loadState(workspace, { stateDir: args.stateDir });
|
|
@@ -1188,7 +1245,10 @@ Commands:
|
|
|
1188
1245
|
service uninstall Remove only the autostart entry
|
|
1189
1246
|
status Print redacted local profile state and Worker health
|
|
1190
1247
|
doctor Check Node, Wrangler, Cloudflare login, Worker health
|
|
1248
|
+
full-test Run real local full-profile capability tests in a temporary sandbox
|
|
1191
1249
|
rotate-secrets Rotate MCP password and daemon secret in local state
|
|
1250
|
+
resource generate-ssh-key NAME [PATH]
|
|
1251
|
+
Generate/reuse an Ed25519 key locally and register its private file by alias
|
|
1192
1252
|
uninstall Delete known Worker(s), remove autostart and local state
|
|
1193
1253
|
|
|
1194
1254
|
Start options:
|