@summerxzp/termbridge-win32-x64 0.3.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/README.txt ADDED
@@ -0,0 +1,53 @@
1
+ TermBridge
2
+ ==========
3
+
4
+ Remote Terminal Runtime for AI Agents.
5
+
6
+ Quick Start
7
+ -----------
8
+
9
+ 1. Place termbridge-mcp and termbridge-auth-helper in the same directory.
10
+ 2. Import mcp-config.json into your MCP client (TraeCode / Claude Code / Codex / OpenCode).
11
+ 3. Adjust the "command" path in mcp-config.json to point to termbridge-mcp.
12
+ 4. Install SKILL.md into your AI Agent's skill directory.
13
+ 5. Restart your MCP client.
14
+
15
+ Files
16
+ -----
17
+
18
+ termbridge-mcp MCP server binary (main entry point)
19
+ termbridge CLI binary (human admin tool, optional)
20
+ termbridge-auth-helper Credential helper (must be in same directory as termbridge-mcp)
21
+ mcp-config.json MCP server configuration template
22
+ SKILL.md Agent Terminal Protocol operational guide
23
+ resources/agentd/ Remote daemon binary (auto-deployed to target host by
24
+ bootstrap_host, do not run locally; Linux x86_64 only)
25
+
26
+ Upgrading
27
+ ---------
28
+
29
+ 1. Replace termbridge-mcp, termbridge-auth-helper (and termbridge if present).
30
+ 2. IMPORTANT: Overwrite the SKILL.md in your AI Agent's skill directory as well.
31
+ It is a separate copy outside this folder, e.g.:
32
+ - Claude Code / OpenCode: ~/.claude/skills/termbridge/SKILL.md
33
+ - Trae: .trae/skills/termbridge/SKILL.md
34
+ SKILL.md evolves with each release; agents only read their own copy.
35
+ 3. Restart your MCP client. Existing terminal sessions are unaffected.
36
+
37
+ First Connection
38
+ ----------------
39
+
40
+ Use the bootstrap_host MCP tool for first-time SSH key deployment.
41
+ Password is prompted via native OS dialog (never enters LLM context).
42
+
43
+ Documentation
44
+ -------------
45
+
46
+ Full docs: https://github.com/summerxzp/TermBridge#readme
47
+ Getting started: docs/getting-started.md
48
+ Architecture decisions: docs/adr/
49
+
50
+ License
51
+ -------
52
+
53
+ MIT
package/SKILL.md ADDED
@@ -0,0 +1,274 @@
1
+ ---
2
+ name: "termbridge"
3
+ description: "Operate remote Linux hosts via TermBridge terminal runtime. Invoke when user asks to run commands on, manage, debug, or deploy to a remote SSH host."
4
+ metadata:
5
+ version: "0.3.0"
6
+ mcp-server: termbridge
7
+ ---
8
+
9
+ # TermBridge
10
+
11
+ TermBridge is a persistent, recoverable terminal runtime for AI agents. It exposes a remote SSH host's shell as a PTY session you can send input to, read output from, and resume after disconnect.
12
+
13
+ > **Version check**: `metadata.version` in this file is the TermBridge release this SKILL.md was packaged with. The MCP server reports its own version in the `serverInfo.version` field of the initialize handshake. If the two differ (e.g. the user updated termbridge but this skill was not re-synced from the release package), tell the user: "SKILL.md 版本落后,请从 release 包重新复制 SKILL.md 到 agent 的 skill 目录" and do not assume behaviors from newer releases.
14
+
15
+ Use it when the user wants to operate on a remote Linux host: run commands, debug services, edit files, deploy code, inspect logs.
16
+
17
+ ## Core Workflow
18
+
19
+ ```
20
+ 1. list_hosts → confirm host alias is visible
21
+ 2. bootstrap_host (first time) → deploy SSH public key (one-time, key-auth hosts only)
22
+ 3. open_session(host) → get session_id
23
+ 4. send_input(cmd) → run command (raw bytes, no auto-\n)
24
+ 5. read_output(wait_for=marker) → wait for completion
25
+ 6. read_output(since_cursor=...) → read full output
26
+ 7. close_session → done
27
+ ```
28
+
29
+ For an already-running persistent session, skip step 3 and `attach_remote_session` instead.
30
+
31
+ > **Host policy (ADR-0017 §3.3)**: `hosts.toml` defines per-host defaults (`auth` = `key` / `password` / `auto`; `session` = `standard` / `persistent`).
32
+ > - **`auth=password` host** → every `open_session` prompts the user for a password (out-of-band, never via MCP args). **Never call `bootstrap_host` on such a host** unless the user explicitly asks to switch to key auth.
33
+ > - **`bootstrap_host` does not modify host policy** — hosts.toml stays untouched; `open_session` keeps prompting until the user edits it.
34
+
35
+ > **Scope guidance**: For standard commands (ls, cat, grep, systemctl status), the workflow above is sufficient. For timeout / disconnect / TUI programs / retry scenarios, consult the **7 Rules** and **Decision Table** below.
36
+
37
+ ## Input Semantics
38
+
39
+ `send_input()` sends **exactly the bytes you provide**. Nothing is added or modified.
40
+
41
+ ### For shell commands: always include terminating LF
42
+
43
+ ```
44
+ # BAD: command stays in input buffer, never submitted
45
+ send_input("ls -la")
46
+
47
+ # GOOD: LF terminates the command, shell executes it
48
+ send_input("ls -la\n")
49
+ ```
50
+
51
+ ### For interactive input: do NOT add LF unless submitting
52
+
53
+ Interactive prompts (sudo password, `read -p`, vim, REPL, mysql) expect input without automatic LF. Only add `\n` when the user intends to submit the input.
54
+
55
+ ```
56
+ # sudo password prompt: send password + LF to submit
57
+ send_input("my_password\n")
58
+
59
+ # vim normal mode command: no LF
60
+ send_input(":w")
61
+ ```
62
+
63
+ ### Empty output ≠ command not executed
64
+
65
+ PTY is a stream. `read_output` immediately after `send_input` may return empty because the command is still running. Use the marker pattern (Rule 1) to wait for completion before reading.
66
+
67
+ ## Tool Reference
68
+
69
+ | Tool | Purpose |
70
+ |------|---------|
71
+ | `list_hosts` | List hosts from `~/.ssh/config` |
72
+ | `bootstrap_host` | Deploy ed25519 public key (first-time only, idempotent). Do NOT call on `auth=password` hosts unless the user asks to switch to key auth |
73
+ | `open_session` | Open new PTY session (persistent=true for cross-restart). May trigger a user password prompt on `auth=password` hosts — do not assume immediate return |
74
+ | `attach_remote_session` | Attach to existing persistent session on daemon |
75
+ | `list_remote_sessions` | List daemon-hosted sessions on a host |
76
+ | `send_input` | Write raw bytes to PTY (no auto-append `\n`) |
77
+ | `read_output` | Read output: default / wait_for / tail_lines / since_cursor. `strip_ansi=true` removes terminal control sequences |
78
+ | `send_control` | Ctrl+C / Ctrl+D / Ctrl+Z |
79
+ | `resize` | Resize PTY (cols, rows) |
80
+ | `detach_session` | Detach but keep remote PTY alive |
81
+ | `close_session` | Close and terminate remote shell |
82
+ | `reconnect_session` | Recover a Lost session |
83
+ | `sftp_*` | File transfer / mkdir / list / remove / chmod |
84
+ | `detect_remote_env` | Probe remote OS / shell / tools (independent SSH exec) |
85
+ | `get_session_timeline` | Get session event timeline |
86
+
87
+ ## The 7 Rules
88
+
89
+ ### Rule 1: Completion — use marker, not prompt guessing
90
+
91
+ For commands that return control to the shell, use a completion marker with a unique request ID and exit code:
92
+
93
+ ```bash
94
+ command
95
+ printf '\n__TB_DONE__:%s:%s\n' "$REQID" "$?"
96
+ ```
97
+
98
+ - `$REQID`: unique ID per command (e.g. 5-hex `a3f1c`), prevents stale marker match
99
+ - `$?`: exit code (0 = success)
100
+ - `\n` prefix isolates marker on its own line
101
+ - `printf` (not `echo`) avoids PTY echo re-matching the marker literal
102
+
103
+ **Never** use `command && echo DONE` (fails on error, marker disappears) or `command; echo DONE` (no exit code).
104
+
105
+ ### Rule 2: Timeout ≠ failure
106
+
107
+ `read_output` timeout only ends this call. The remote command keeps running, session stays Ready.
108
+
109
+ On timeout, choose:
110
+ - **Continue waiting** → call `read_output(wait_for=marker)` again
111
+ - **Interrupt** → `send_control("ctrl_c")`, then `read_output` to confirm
112
+ - **Let it run in background** → use `since_cursor` to poll periodically
113
+
114
+ **Never auto-retry on timeout.** The original command may still be running; retrying creates duplicate execution.
115
+
116
+ ### Rule 3: Disconnect = UNKNOWN state
117
+
118
+ If `send_input` or `read_output` returns a connection error, the remote execution state is **UNKNOWN** (not FAILED). The command may have:
119
+ - completed before disconnect
120
+ - partially executed
121
+ - never started
122
+
123
+ **Never blindly retry.** Instead:
124
+ 1. `reconnect_session(session_id)`
125
+ 2. Run an idempotency check (see Rule 4)
126
+ 3. Only retry if confirmed NOT-RUN
127
+
128
+ ### Rule 4: Retry = reconnect + idempotency check first
129
+
130
+ Before retrying any command with side effects:
131
+
132
+ ```bash
133
+ # Idempotency check examples
134
+ test -f /tmp/marker && echo EXISTS || echo MISSING # file created?
135
+ systemctl is-active <service> # service restarted?
136
+ dpkg -l | grep -q "^ii <pkg> " && echo INSTALLED # package installed?
137
+ grep -q "expected" /etc/config && echo APPLIED # config applied?
138
+ ```
139
+
140
+ - EXISTS / INSTALLED / APPLIED / active → command already ran, do NOT retry
141
+ - MISSING / inactive → safe to retry
142
+
143
+ ### Rule 5: Interactive/TUI — no marker
144
+
145
+ These programs occupy the foreground PTY and never return to shell, so markers never appear:
146
+
147
+ | Type | Examples | Exit with |
148
+ |------|----------|-----------|
149
+ | Full-screen TUI | `vim`, `nano`, `htop`, `top`, `less` | `:q` / `q` |
150
+ | Long-running monitor | `tail -f`, `journalctl -f`, `watch` | `send_control("ctrl_c")` |
151
+ | Nested shell | `ssh`, `bash`, `python`, `mysql` | `exit` / `send_control("ctrl_d")` |
152
+ | Interactive prompt | `read -p`, `passwd`, `sudo` (no TTY) | complete input, returns to shell |
153
+
154
+ For these, do NOT use completion markers. Use application-specific exit keys or `send_control`.
155
+
156
+ **Judgment criterion**: if the shell prompt will NOT reappear after the command, do not use marker mode.
157
+
158
+ ### Rule 6: Cursor — for full output, use since_cursor
159
+
160
+ `wait_for` returns only the match context (matched line ± context_lines), **not** the full command output.
161
+
162
+ To get full output:
163
+
164
+ ```
165
+ 1. cursor_before = read_output(tail_lines=0).cursor
166
+ 2. reqid = generate_unique_id()
167
+ 3. send_input("command; printf '\\n__TB_DONE__:%s:%s\\n' \"$reqid\" \"$?\"\n")
168
+ 4. r = read_output(wait_for="__TB_DONE__:<reqid>:", timeout_secs=60)
169
+ 5. exit_code = parse from r.matched_text
170
+ 6. full_output = read_output(since_cursor=cursor_before)
171
+ ```
172
+
173
+ **Never** pass both `wait_for` and `since_cursor` in the same call — `since_cursor` takes priority and `wait_for` is silently ignored.
174
+
175
+ For precise text matching, strip ANSI sequences with regex `\x1b\[[0-9;?]*[a-zA-Z]`.
176
+
177
+ ### Rule 7: Persistent — detach keeps PTY alive, attach resumes
178
+
179
+ - `detach_session` → local client disconnects, remote PTY keeps running, RingBuffer keeps accumulating
180
+ - `list_remote_sessions(host)` → list daemon-hosted sessions
181
+ - `attach_remote_session(host, remote_session_id)` → resume, returns buffered output since last cursor
182
+
183
+ Daemon crash = all detached sessions lost (Phase 3 does not recover). Must `open_session(persistent=true)` to rebuild.
184
+
185
+ ## Decision Table
186
+
187
+ | Situation | Action |
188
+ |-----------|--------|
189
+ | First connection to a key-auth host | `bootstrap_host` (one-time, deploys SSH key) |
190
+ | First connection to an `auth=password` host | `open_session` directly — user is prompted for password; do NOT `bootstrap_host` unless the user explicitly asks to switch to key auth |
191
+ | `open_session` on an `auth=password` host | expect a user password prompt (out-of-band); do not assume immediate return |
192
+ | Subsequent connections | `open_session` (key auth, no password) |
193
+ | Plain command (ls, cat, grep) | `send_input` + marker + `read_output(wait_for)` |
194
+ | Need full command output | record cursor → send → wait_for → `read_output(since_cursor)` |
195
+ | Need clean text output (no ANSI) | `read_output(..., strip_ansi=true)` — removes CSI/OSC/DCS sequences |
196
+ | Long-running command (build) | do NOT retry on timeout; `since_cursor` poll |
197
+ | Watch output (tail -f) | `send_input` + `read_output(tail_lines)` periodically; `ctrl_c` to stop |
198
+ | vim / top / htop | no marker; use app exit keys (`:q`, `q`) or `ctrl_c` |
199
+ | `sudo -n` (non-interactive) | Auto-passthrough: `sudo -n ls`, `sudo -n du`, `sudo -n stat` etc. execute without confirmation. Must be `sudo -n` at line start, no shell chaining (`;` `&&` `|` etc.) |
200
+ | sudo (interactive, no `-n`) | Triggers `POLICY_NEEDS_CONFIRM`. Do NOT send password via `send_input`. Options: (1) use `sudo -n` if NOPASSWD; (2) ask user to run `termbridge session approve <session_id>` for unrestricted session; (3) execute manually in target terminal |
201
+ | SSH disconnect mid-command | state UNKNOWN → `reconnect_session` + idempotency check |
202
+ | Retry a side-effecting command | reconnect first, idempotency check, retry only if NOT-RUN |
203
+ | Cross-restart resume | `list_remote_sessions` → `attach_remote_session` |
204
+ | Resize terminal | `resize(session_id, cols, rows)` before running TUI |
205
+ | Edit remote file | `sftp_transfer(download)` → Edit → `sftp_transfer(upload)`. Local paths allowed under cwd or `$TEMP/termbridge` |
206
+ | Done with session | `close_session` (terminates remote shell) |
207
+ | Want to keep session for later | `detach_session` (PTY keeps running) |
208
+
209
+ ## Key Constraints
210
+
211
+ - **`send_input` is raw bytes**: no auto-append `\n`. You must include `\n` yourself for Enter. No command boundary parsing.
212
+ - **`\r` is preserved**: do not strip carriage returns from input.
213
+ - **PTY echo**: the shell echoes input back. `wait_for("MARKER")` may match the echo — use `$REQID` variable so echo contains the variable name, not the value.
214
+ - **ANSI noise**: PTY output contains control sequences (bracketed paste `\x1b[?2004h/l`, OSC 7 `\x1b]7;...`, color codes, `\x1b[K`). Use `read_output(strip_ansi=true)` for clean text; RingBuffer keeps raw bytes, so cursor stays valid.
215
+ - **Credentials never in MCP params**: `bootstrap_host` accepts only `host`. Passwords are prompted via separate `termbridge-auth-helper` process, never exposed to MCP stdio.
216
+ - **Host policy is user intent (ADR-0017 §2.2)**: `hosts.toml` `auth=password` means password for every connection — it is NOT a gap to "fix". `bootstrap_host` never modifies host policy (returns a `hint` instead), so an `auth=password` host keeps prompting until the user edits hosts.toml. Never bootstrap a password-policy host as a convenience optimization.
217
+ - **Session approval mode (0.2.1+)**: Sessions start in `standard` mode (PolicyManager enforces blocklist + confirm). Users can elevate a session to `unrestricted` via `termbridge session approve <session_id>` (CLI, human-only). In unrestricted mode, **only confirm guardrails are skipped** (sudo, `rm -rf /tmp/x`, etc. execute without confirmation); **hard-deny rules still apply** (`rm -rf /`, `mkfs`, `dd of=/dev/`, etc. are still blocked). This does NOT bypass SSH credentials, path safety, or protocol invariants. Approval is session-scoped — closing the session resets to standard. Agent cannot self-approve; only the human user can via CLI. TermBridge Policy is a secondary guardrail (defense-in-depth), not a primary approval system — command approval is the Coding Agent's responsibility.
218
+
219
+ ## Anti-Patterns
220
+
221
+ ```python
222
+ # BAD: timeout → retry (creates duplicate execution)
223
+ r = read_output(wait_for="MARKER", timeout_secs=5)
224
+ if r.timed_out: send_input("command\n") # original may still be running!
225
+
226
+ # BAD: send_input error → retry without reconnect (session Lost)
227
+ try: send_input("cmd\n")
228
+ except: send_input("cmd\n") # will fail again, session is Lost
229
+
230
+ # BAD: marker on top (never appears, false "hung" diagnosis)
231
+ send_input("top\n")
232
+ read_output(wait_for="__TB_DONE__:", timeout_secs=10) # top is TUI!
233
+
234
+ # BAD: depend on wait_for return as full output
235
+ r = read_output(wait_for="MARKER")
236
+ full = r.output # only match context, not full output
237
+
238
+ # BAD: echo marker (PTY echo re-matches)
239
+ send_input("echo __TB_DONE__:abc:0\n") # echo itself contains the marker!
240
+
241
+ # BAD: auto-bootstrap on a password-policy host (violates user intent, ADR-0017 §3.3)
242
+ bootstrap_host(host="prod") # policy says auth=password — user wants password auth;
243
+ # bootstrap also does NOT change hosts.toml, so the prompt stays
244
+
245
+ # BAD: sudo without -n (triggers POLICY_NEEDS_CONFIRM, blocks automation)
246
+ send_input("sudo systemctl restart nginx\n") # blocked!
247
+
248
+ # GOOD: sudo -n for NOPASSWD hosts (auto-passthrough)
249
+ send_input("sudo -n systemctl restart nginx\n")
250
+
251
+ # GOOD: if sudo needs password, ask user to approve session first
252
+ # User runs: termbridge session approve <session_id>
253
+ # Then: sudo commands execute without policy confirmation
254
+ ```
255
+
256
+ ## Quick Reference: Happy Path
257
+
258
+ ```
259
+ # First time on a host
260
+ list_hosts
261
+ bootstrap_host(host="myserver") # key-auth host only; auth=password hosts: skip (rule §3.3)
262
+ open_session(host="myserver", persistent=true, name="work")
263
+
264
+ # Run a command with completion marker
265
+ reqid = "a3f1c"
266
+ cursor = read_output(session_id, tail_lines=0).cursor
267
+ send_input(session_id, "systemctl status nginx; printf '\\n__TB_DONE__:%s:%s\\n' \"$reqid\" \"$?\"\n")
268
+ r = read_output(session_id, wait_for="__TB_DONE__:a3f1c:", timeout_secs=30)
269
+ exit_code = parse_exit_code(r.matched_text)
270
+ full_output = read_output(session_id, since_cursor=cursor)
271
+
272
+ # Done
273
+ close_session(session_id)
274
+ ```
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "termbridge": {
4
+ "command": "termbridge-mcp",
5
+ "args": []
6
+ }
7
+ }
8
+ }
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@summerxzp/termbridge-win32-x64",
3
+ "version": "0.3.0",
4
+ "description": "TermBridge runtime for win32-x64 (完整 release 目录,含 trio 二进制 / resources/agentd / SKILL.md)",
5
+ "license": "Apache-2.0",
6
+ "os": [
7
+ "win32"
8
+ ],
9
+ "cpu": [
10
+ "x64"
11
+ ],
12
+ "files": [
13
+ "*"
14
+ ]
15
+ }
Binary file
Binary file
package/termbridge.exe ADDED
Binary file