@aptove/bridge 0.1.18 → 0.2.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.
Files changed (2) hide show
  1. package/README.md +112 -95
  2. package/package.json +6 -6
package/README.md CHANGED
@@ -12,8 +12,6 @@ A bridge library and application between Agent Client Protocol (ACP) agents and
12
12
  - **Standalone binary** — run `bridge` as a separate process that spawns your ACP agent over stdio and exposes it over WebSocket to mobile or desktop clients.
13
13
  - **Embedded library** — add `aptove-bridge` as a Rust dependency and run the bridge server in-process alongside your agent, with no subprocess or stdio pipe required.
14
14
 
15
- The [Aptove](https://github.com/aptove/aptove) project is the reference implementation of the embedded library usage — `aptove run` starts both the ACP agent and bridge server in a single process.
16
-
17
15
  ## Transport Modes
18
16
 
19
17
  | Mode | Use Case | Documentation |
@@ -32,7 +30,7 @@ One transport is active at a time. When multiple are enabled in `common.toml`, t
32
30
  - ⚡ **WebSocket Streaming**: Real-time bidirectional communication
33
31
  - 🌐 **Multi-Transport**: Local, Cloudflare, Tailscale — configure and switch between them
34
32
  - 🔑 **Stable Agent Identity**: `agent_id` UUID persisted in `common.toml` for multi-transport dedup on mobile
35
- - 🦀 **Embeddable**: Use as a library with `BridgeServer` for in-process deployment
33
+ - 🦀 **Embeddable**: Use as a library with `StdioBridge` for in-process deployment
36
34
 
37
35
  ---
38
36
 
@@ -48,58 +46,46 @@ aptove-bridge = "0.1"
48
46
  ### Minimal Example
49
47
 
50
48
  ```rust
51
- use aptove_bridge::{BridgeServer, BridgeServeConfig};
49
+ use aptove_bridge::{
50
+ bridge::StdioBridge,
51
+ common_config::CommonConfig,
52
+ tls::TlsConfig,
53
+ };
52
54
 
53
55
  #[tokio::main]
54
56
  async fn main() -> anyhow::Result<()> {
55
- // Build from defaultsreads transport selection from common.toml
56
- // in ~/Library/Application Support/Aptove (macOS) or ~/.config/Aptove (Linux).
57
- // Prompts the user to select a transport if multiple are enabled.
58
- let mut server = BridgeServer::build(&BridgeServeConfig::default())?;
59
-
60
- // Display a pairing QR code so a client can connect
61
- server.show_qr()?;
62
-
63
- // Take the in-process transport — wire it to your agent message loop
64
- let mut transport = server.take_transport();
65
-
66
- // Run your agent loop and bridge listener concurrently
67
- tokio::select! {
68
- _ = my_agent_loop(&mut transport) => {}
69
- res = server.start() => { res?; }
70
- }
71
-
72
- Ok(())
57
+ // Load (or initialise) shared config generates agent_id and auth_token on first run
58
+ let mut config = CommonConfig::load()?;
59
+ config.ensure_agent_id();
60
+ config.ensure_auth_token();
61
+ config.save()?;
62
+
63
+ // Generate (or load) a self-signed TLS cert
64
+ let tls = TlsConfig::load_or_generate(&CommonConfig::config_dir(), &[])?;
65
+
66
+ StdioBridge::new("copilot --acp".to_string(), 8765)
67
+ .with_auth_token(Some(config.auth_token.clone()))
68
+ .with_tls(tls)
69
+ .start()
70
+ .await
73
71
  }
74
72
  ```
75
73
 
76
- `take_transport()` returns an `InProcessTransport` that implements the same `Transport` trait as `StdioTransport` — your agent message loop works identically whether running standalone or embedded.
77
-
78
- ### `BridgeServeConfig`
79
-
80
- | Field | Default | Description |
81
- |-------|---------|-------------|
82
- | `port` | `8080` | WebSocket listen port (overridden by `common.toml` per-transport config) |
83
- | `bind_addr` | `"0.0.0.0"` | Bind address |
84
- | `tls` | `true` | Enable TLS (self-signed cert auto-generated) |
85
- | `auth_token` | `None` | Bearer token required for connections (auto-loaded from `common.toml`) |
86
- | `keep_alive` | `false` | Enable keep-alive agent pool |
87
- | `config_dir` | platform default | Directory for `common.toml`, TLS certs, and credentials |
88
-
89
- Load from disk (reads `bridge.toml` and `common.toml`, generates `agent_id` if absent):
90
-
91
- ```rust
92
- let config = BridgeServeConfig::load()?;
93
- ```
94
-
95
- ### Reference Implementation: Aptove
96
-
97
- The [Aptove project](https://github.com/aptove/aptove) (`aptove run`) is the full reference implementation. Key patterns it uses:
98
-
99
- - `BridgeServer::build_with_trigger_store()` — wires in a `TriggerStore` for webhook support
100
- - `server.show_qr()` — uses the pairing handshake so clients deduplicate agents by `agentId`
101
- - `server.take_transport()` + `run_message_loop()` — connects the in-process transport to the ACP dispatch loop
102
- - `tokio::select!` on `agent_loop` and `server.start()` — clean shutdown when either side exits
74
+ ### `StdioBridge` Builder Methods
75
+
76
+ | Method | Description |
77
+ |--------|-------------|
78
+ | `new(agent_command, port)` | Create a bridge that spawns the given command; listen on `port` |
79
+ | `.with_bind_addr(addr)` | Override bind address (default: `"0.0.0.0"`) |
80
+ | `.with_auth_token(token)` | Require a bearer token for connections |
81
+ | `.with_tls(tls_config)` | Enable TLS with a `TlsConfig` (self-signed cert) |
82
+ | `.with_external_tls()` | Signal that TLS is handled upstream (Tailscale Serve, Cloudflare) |
83
+ | `.with_pairing(manager)` | Enable QR pairing via a `PairingManager` |
84
+ | `.with_agent_pool(pool)` | Enable keep-alive sessions via an `AgentPool` |
85
+ | `.with_working_dir(dir)` | Set the working directory for the spawned agent process |
86
+ | `.with_push_relay(client)` | Enable push notifications via a relay |
87
+ | `.with_webhook_resolver(fn)` | Handle `POST /webhook/<token>` trigger requests |
88
+ | `.start()` | Start the WebSocket listener (runs until shutdown) |
103
89
 
104
90
  ---
105
91
 
@@ -111,13 +97,16 @@ The [Aptove project](https://github.com/aptove/aptove) (`aptove run`) is the ful
111
97
  # Build
112
98
  cargo build --release
113
99
 
114
- # Start with local transport (default no config needed)
115
- ./target/release/bridge run \
116
- --agent-command "gemini --experimental-acp" \
117
- --qr
100
+ # Start the bridge interactive agent selection menu
101
+ ./target/release/bridge
102
+
103
+ # Or specify the agent directly
104
+ ./target/release/bridge run --agent-command "copilot --acp"
118
105
  ```
119
106
 
120
- Scan the QR code with the Aptove mobile app to connect.
107
+ Running `bridge` with no subcommand defaults to `run`. When `--agent-command` is omitted, an interactive menu lets you pick from known agents (Copilot, Gemini, Goose) or enter a custom command.
108
+
109
+ Scan the QR code with the mobile app to connect.
121
110
 
122
111
  ### Configuration — `common.toml`
123
112
 
@@ -125,16 +114,14 @@ All transport settings live in `common.toml`. The file is created automatically
125
114
 
126
115
  **Default location:**
127
116
 
128
- | Runtime | macOS | Linux |
129
- |---------|-------|-------|
130
- | `aptove run` (embedded) | `~/Library/Application Support/Aptove/common.toml` | `~/.config/Aptove/common.toml` |
131
- | `bridge` (standalone) | `~/Library/Application Support/com.aptove.bridge/common.toml` | `~/.config/bridge/common.toml` |
132
-
133
- When using `aptove run`, this config is shared across all workspaces.
117
+ | Platform | Path |
118
+ |----------|------|
119
+ | macOS | `~/Library/Application Support/com.aptove.bridge/common.toml` |
120
+ | Linux | `~/.config/bridge/common.toml` |
134
121
 
135
- Override with `--config-dir`:
122
+ Override with `-c` / `--config-dir`:
136
123
  ```bash
137
- bridge --config-dir ./my-config run --agent-command "gemini --experimental-acp"
124
+ bridge -c ./my-config run --agent-command "copilot --acp"
138
125
  ```
139
126
 
140
127
  #### Example `common.toml`
@@ -168,20 +155,50 @@ tls = true
168
155
 
169
156
  Enable only the transports you need. `agent_id` and `auth_token` are generated automatically on first run and stay stable across restarts.
170
157
 
158
+ #### Config Directory Files
159
+
160
+ All bridge state lives in the config directory. These files are created automatically on first run:
161
+
162
+ | File | Purpose |
163
+ |------|---------|
164
+ | `common.toml` | Main config — `agent_id`, `auth_token`, and transport settings. Permissions `0600`. |
165
+ | `cert.pem` | Self-signed TLS certificate for the local transport WebSocket server. Its fingerprint is embedded in the QR pairing payload for certificate pinning. |
166
+ | `key.pem` | Private key for the TLS certificate. |
167
+ | `cert-extra-sans.json` | Tracks extra Subject Alternative Names (IPs/hostnames) baked into the TLS cert (e.g. `--advertise-addr` or Tailscale IP). When these change, the cert is automatically regenerated. |
168
+
171
169
  ### Commands
172
170
 
173
- #### `run` — Start the bridge
171
+ #### `run` — Start the bridge (default)
172
+
173
+ `run` is the default subcommand — running `bridge` with no arguments is equivalent to `bridge run`.
174
174
 
175
175
  ```bash
176
- bridge run --agent-command "<your-agent-command>"
176
+ # Interactive agent selection
177
+ bridge
178
+
179
+ # Or specify the agent directly
180
+ bridge run --agent-command "copilot --acp"
177
181
  ```
178
182
 
179
- | Flag | Description | Default |
180
- |------|-------------|---------|
181
- | `--agent-command <CMD>` | Command to spawn the ACP agent | Required |
182
- | `--bind <ADDR>` | Address to bind the listener | `0.0.0.0` |
183
- | `--qr` | Display QR code for pairing at startup | Off |
184
- | `--verbose` | Enable info-level logging | Off (warn only) |
183
+ | Flag | Short | Description | Default |
184
+ |------|-------|-------------|---------|
185
+ | `--agent-command <CMD>` | `-a` | Command to spawn the ACP agent | Interactive menu |
186
+ | `--bind <ADDR>` | `-b` | Address to bind the listener | `0.0.0.0` |
187
+ | `--verbose` | | Enable info-level logging | Off (warn only) |
188
+ | `--advertise-addr <ADDR>` | | Override LAN address in QR pairing URL | Auto-detected |
189
+
190
+ When `--agent-command` is omitted, the bridge presents an interactive menu:
191
+
192
+ ```
193
+ Select an agent to run:
194
+ [1] Copilot (copilot --acp)
195
+ [2] Gemini (gemini --experimental-acp)
196
+ [3] Goose (goose acp)
197
+ [4] Custom
198
+ Enter number [1]:
199
+ ```
200
+
201
+ The QR code for pairing is always displayed at startup.
185
202
 
186
203
  Transport selection, port, TLS, and auth token are all read from `common.toml`.
187
204
 
@@ -193,12 +210,6 @@ bridge show-qr
193
210
 
194
211
  Displays the connection QR code for the currently active transport. The bridge must already be running. Use this to pair an additional device without restarting.
195
212
 
196
- To show the QR at initial startup, pass `--qr` to `bridge run` instead:
197
-
198
- ```bash
199
- bridge run --agent-command "aptove stdio" --qr
200
- ```
201
-
202
213
  #### `setup` — Provision Cloudflare infrastructure
203
214
 
204
215
  ```bash
@@ -243,16 +254,13 @@ Prints the active `common.toml` path, `agent_id`, enabled transports, and Tailsc
243
254
  ### Embedded (library)
244
255
 
245
256
  ```
246
- ┌─────────────┐ ┌──────────────────────────────────────────────┐
247
- │ Client App │◄──────────────────►│ Your process
248
- │ (iOS/Android│ WebSocket (TLS) │ ┌─────────────────┐ ┌────────────────────┐
249
- └─────────────┘ │ │ BridgeServer │◄►│ Agent message loop│ │
250
- │ │ (transport) │ │ (InProcessTransport│ │
251
- │ └─────────────────┘ └────────────────────┘ │
252
- └──────────────────────────────────────────────┘
257
+ ┌─────────────┐ ┌──────────────────────────────────┐ ┌──────────────┐
258
+ │ Client App │◄──────────────────►│ Your process │◄──────►│ ACP Agent
259
+ │ (iOS/Android│ WebSocket (TLS) │ StdioBridge (transport listener)│ stdio (your cmd) │
260
+ └─────────────┘ └──────────────────────────────────┘ └──────────────┘
253
261
  ```
254
262
 
255
- No subprocess is spawned. Agent and bridge communicate via in-process channels.
263
+ The agent process is still spawned as a subprocess via stdio — `StdioBridge` handles the WebSocket server, TLS, auth, and pairing in-process alongside your application.
256
264
 
257
265
  ---
258
266
 
@@ -270,13 +278,24 @@ No subprocess is spawned. Agent and bridge communicate via in-process channels.
270
278
 
271
279
  ```bash
272
280
  # Enable verbose logging
273
- bridge run --agent-command "gemini --experimental-acp" --verbose
281
+ bridge run --verbose
274
282
 
275
283
  # Check which transports are configured
276
284
  bridge status
277
285
 
278
286
  # Test agent command independently
279
- echo '{"jsonrpc":"2.0","method":"initialize","id":1}' | gemini --experimental-acp
287
+ echo '{"jsonrpc":"2.0","method":"initialize","id":1}' | copilot --acp
288
+ ```
289
+
290
+ ### Testing with Other ACP-Compatible Agents
291
+
292
+ Please note that the project is extensively tested only with Copilot CLI. Open a bug report if you notice any issues with other ACP-Compatible Agents.
293
+
294
+ The easiest way to test any supported agent is to run `bridge` and pick it from the interactive menu. Alternatively, pass the command directly:
295
+
296
+ ```bash
297
+ bridge run -a "gemini --experimental-acp" --verbose
298
+ bridge run -a "goose acp" --verbose
280
299
  ```
281
300
 
282
301
  ---
@@ -287,19 +306,17 @@ echo '{"jsonrpc":"2.0","method":"initialize","id":1}' | gemini --experimental-ac
287
306
  - **TLS**: self-signed certificate generated on first run. Certificate fingerprint is included in the QR pairing payload and pinned by the mobile app to prevent MITM attacks.
288
307
  - **Pairing codes**: 6-digit, single-use, expire after 60 seconds. Rate-limited to 5 attempts per code.
289
308
  - **`common.toml`**: contains all secrets. Permissions are set to `0600` automatically. Keep it secure.
309
+ - **Agent command**: the `--agent-command` value (or interactive menu selection) is validated at startup — the binary must exist and be executable before the server accepts connections. The command is never persisted to `common.toml`; it must be supplied each time the bridge is started. The bridge is an operator tool: whoever can invoke it already has local shell access, so the agent command is implicitly trusted to the same degree as any other command that user could run.
290
310
 
291
311
  To rotate credentials (invalidates all paired devices):
292
312
 
293
313
  ```bash
294
- # Using aptove run (embedded bridge):
295
- rm ~/Library/Application\ Support/Aptove/common.toml # macOS
296
- rm ~/.config/Aptove/common.toml # Linux
297
- aptove run --qr
298
-
299
- # Using standalone bridge binary:
300
- rm ~/Library/Application\ Support/com.aptove.bridge/common.toml # macOS
301
- rm ~/.config/bridge/common.toml # Linux
302
- bridge run --agent-command "aptove stdio" --qr
314
+ # macOS
315
+ rm ~/Library/Application\ Support/com.aptove.bridge/common.toml
316
+ # Linux
317
+ rm ~/.config/bridge/common.toml
318
+
319
+ bridge
303
320
  ```
304
321
 
305
322
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aptove/bridge",
3
- "version": "0.1.18",
3
+ "version": "0.2.0",
4
4
  "description": "ACP bridge — connects ACP agents to mobile and desktop clients over WebSocket",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,10 +33,10 @@
33
33
  "node": ">=16"
34
34
  },
35
35
  "optionalDependencies": {
36
- "@aptove/bridge-darwin-arm64": "0.1.18",
37
- "@aptove/bridge-darwin-x64": "0.1.18",
38
- "@aptove/bridge-linux-arm64": "0.1.18",
39
- "@aptove/bridge-linux-x64": "0.1.18",
40
- "@aptove/bridge-win32-x64": "0.1.18"
36
+ "@aptove/bridge-darwin-arm64": "0.2.0",
37
+ "@aptove/bridge-darwin-x64": "0.2.0",
38
+ "@aptove/bridge-linux-arm64": "0.2.0",
39
+ "@aptove/bridge-linux-x64": "0.2.0",
40
+ "@aptove/bridge-win32-x64": "0.2.0"
41
41
  }
42
42
  }