terminal-commands 0.1.3 → 0.2.1

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/README.md CHANGED
@@ -16,15 +16,42 @@ approval prompts are enabled.
16
16
  - Passes only a small environment allowlist to child processes, excluding API keys and other secrets.
17
17
  - Restricts dedicated filesystem tools to the approved roots.
18
18
 
19
+ File reads, bounded directory listings and writes use a bundled native helper
20
+ with Go's handle-based `os.Root` API. Approved roots are pinned and checked
21
+ against their original file identities; a concurrent parent-directory or root
22
+ symlink swap cannot redirect an operation into another directory. File writes
23
+ truncate only an already safely opened regular file. The helper is required:
24
+ missing, crashed or unsupported helpers refuse file IO, with no path-string
25
+ fallback. Relative paths and the originally configured root aliases remain
26
+ supported. Roots should be ordinary directories on a trusted local filesystem;
27
+ this boundary does not prevent privileged mount changes or hard-link aliases.
28
+
29
+ Process working directories use the same checked root identities and native
30
+ directory handles during startup. Unix wrappers use `fchdir` followed by `exec`,
31
+ preserving the supervised PID/process group. Windows holds canonical ancestors
32
+ against rename through process creation; existing `taskkill /T` supervision
33
+ includes the wrapper's child. An approved executable still has the user's
34
+ ordinary OS permissions; the directory boundary is not a process sandbox.
35
+
36
+ See [Go's traversal-resistant APIs](https://go.dev/blog/osroot) for the native
37
+ filesystem boundary and its mount/hard-link limitations.
38
+
19
39
  Command-line programs still execute with your operating-system user permissions. A program can access more than the dedicated filesystem tools, so review every command approval carefully.
20
40
 
21
- The MCP command surface exposes process lifecycle operations separately: `start_process` starts
22
- an approved executable and returns a session ID, `read_process_output` reads bounded output,
23
- `list_processes` lists sessions, and `terminate_process` stops a session. This keeps terminal
24
- workflows available while making each model-callable operation explicit.
41
+ The local MCP server exposes the low-level process supervisor (`start_process`,
42
+ `read_process_output`, `list_processes`, and `terminate_process`) **only to the local
43
+ companion**. The public gateway exposes `submit_process` and `get_process_task`, which
44
+ return immediately with a task ID and then retrieve task status and bounded output.
45
+ There is no legacy public direct-execution API.
25
46
 
26
47
  ## Run
27
48
 
49
+ Building from source requires Node.js 20+ and Go 1.27.2+. `npm test` builds the
50
+ host's helper, while `npm pack` builds all six supported release targets. The
51
+ published package includes them, so installed users need only Node.js. Helpers
52
+ are built from the reviewed `native/fs-helper` source without CGO or downloaded
53
+ runtime executables. Unsupported OS/architecture combinations fail closed.
54
+
28
55
  ```bash
29
56
  npm install
30
57
  npm run build
@@ -133,3 +160,73 @@ Point the client's remote MCP server configuration at the same `http://127.0.0.1
133
160
  it registers remote MCP servers; the connection and tool-discovery steps are otherwise identical.
134
161
 
135
162
  For a published or independently hosted server, deploy a stable public HTTPS `/mcp` endpoint and implement OAuth 2.1. Do not expose this local server directly to the internet.
163
+
164
+ ## Protocol-v2 production companion
165
+
166
+ The protocol-v2 companion is released as `terminal-commands@0.2.0`. Upgrade
167
+ and reconnect paired machines before deploying gateway v0.2.0; the gateway
168
+ intentionally rejects older protocol handshakes.
169
+
170
+ A local process is always approval-gated, and a repeated task ID returns its
171
+ existing session instead of spawning a duplicate. The supervisor enforces a
172
+ bounded process limit (32 running and 512 retained by default) and removes old
173
+ completed sessions after one hour. Task-ID tombstones remain in the live
174
+ companion to prevent replay after session history expires.
175
+
176
+ The local supervisor is currently memory-backed. If the local server itself
177
+ restarts, an already-running OS process may become unsupervised; a gateway task
178
+ will remain `unknown` until it can be reconciled, never automatically rerun.
179
+
180
+ ## Local approval boundary and replay safety
181
+
182
+ The CLI generates a fresh 256-bit `MACHINE_TERMINAL_LOCAL_AGENT_TOKEN` for its local
183
+ MCP child and gateway companion. Only the approved companion can use destructive
184
+ tools over HTTP. Direct HTTP clients (including tunnels) are **denied by default**, even for
185
+ reads; stdio clients are **read-only by default**. Never expose the
186
+ local agent token in logs or shell arguments. Trusted local stdio administrators
187
+ can explicitly set `MACHINE_TERMINAL_TRUST_STDIO=1`; this grants local execution
188
+ without per-call companion confirmation and must not be enabled for untrusted clients.
189
+
190
+ The CLI also creates a private, persistent `device-secret` paired with the device ID.
191
+ The gateway stores only its hash, requires the matching secret on reconnect, and closes
192
+ device WebSockets when their OAuth access token expires. Losing the secret requires an
193
+ explicit re-pair: remove the server-side device registration with authorized
194
+ administrative access, then connect with a new secret. Signing out alone does not
195
+ rotate the device identity.
196
+
197
+ Process startup writes replay-resistant task receipts **before** executing commands.
198
+ The CLI saves them in `task-receipts.json` under its private config directory.
199
+ Receipts survive local-service restarts, expire after 30 days, and are bounded to
200
+ 20,000 recent task IDs. The gateway retains idempotency metadata beyond output
201
+ retention, so old cloud requests still cannot be silently dispatched again.
202
+
203
+ On Unix the process supervisor runs commands in their own process groups; termination
204
+ sends SIGTERM and escalates to SIGKILL after a short grace period. On Windows it
205
+ uses process termination plus taskkill /T /F for forced descendant cleanup.
206
+ Programs that create independent/detached processes may still escape group cleanup;
207
+ Terminal Commands is not a full OS sandbox.
208
+
209
+ ## Revoke a lost or compromised device
210
+
211
+ From a trusted terminal running the v0.2.0 CLI, use:
212
+
213
+ ```bash
214
+ terminal-commands revoke-device --device-id <paired-device-id>
215
+ ```
216
+
217
+ The command requires an interactive confirmation and a **fresh Auth0 device login**
218
+ with the separate `terminal:devices:revoke` permission. It never saves the
219
+ elevated management token. The Auth0 API and device-code client must first be
220
+ configured to grant this permission to authorized account owners.
221
+
222
+ Revocation persists on the gateway, closes the current WebSocket and rejects
223
+ future connections using the revoked **device ID** even with its original
224
+ secret. Revocation cannot terminate processes already running on the machine,
225
+ revoke stolen Auth0 tokens, or block registration under another ID. Immediately
226
+ also revoke compromised Auth0 credentials and investigate the host. Restoring
227
+ the same ID requires a reviewed administrative reset, not a fresh login.
228
+
229
+ Use `terminal-commands paired-devices` to list paired device IDs even when
230
+ machines are offline, including revoked status. The CLI fetches paginated,
231
+ owner-scoped metadata with the same fresh elevated OAuth permission; secrets
232
+ and pairing credential hashes are never returned.