@aptove/bridge 0.1.4
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 +316 -0
- package/bin/bridge +73 -0
- package/package.json +42 -0
- package/postinstall.js +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Bridge
|
|
2
|
+
|
|
3
|
+
[](https://crates.io/crates/aptove-bridge)
|
|
4
|
+
[](https://github.com/aptove/bridge/releases/latest)
|
|
5
|
+
[](https://www.npmjs.com/package/@aptove/bridge)
|
|
6
|
+
[](https://discord.gg/gD7AMxBy9y)
|
|
7
|
+
|
|
8
|
+
A bridge library and application between Agent Client Protocol (ACP) agents and clients.
|
|
9
|
+
|
|
10
|
+
`aptove-bridge` can be used in two ways:
|
|
11
|
+
|
|
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
|
+
- **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
|
+
|
|
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
|
+
## Transport Modes
|
|
18
|
+
|
|
19
|
+
| Mode | Use Case | Documentation |
|
|
20
|
+
|------|----------|---------------|
|
|
21
|
+
| **Local** | Same Wi-Fi network, secure pairing with QR code | [docs/transport/local.md](docs/transport/local.md) |
|
|
22
|
+
| **Cloudflare** | Remote access via Cloudflare Zero Trust (internet-accessible) | [docs/transport/cloudflare.md](docs/transport/cloudflare.md) |
|
|
23
|
+
| **Tailscale Serve** | Private overlay network via MagicDNS + HTTPS | [docs/transport/tailscale.md](docs/transport/tailscale.md) |
|
|
24
|
+
| **Tailscale IP** | Direct Tailscale IP with self-signed TLS | [docs/transport/tailscale.md](docs/transport/tailscale.md) |
|
|
25
|
+
|
|
26
|
+
One transport is active at a time. When multiple are enabled in `common.toml`, the bridge prompts you to select one at startup.
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- 📱 **QR Code Pairing**: Secure one-time code pairing via QR scan
|
|
31
|
+
- 🔒 **TLS + Certificate Pinning**: Self-signed certificates with fingerprint validation
|
|
32
|
+
- ⚡ **WebSocket Streaming**: Real-time bidirectional communication
|
|
33
|
+
- 🌐 **Multi-Transport**: Local, Cloudflare, Tailscale — configure and switch between them
|
|
34
|
+
- 🔑 **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
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Using as a Library
|
|
40
|
+
|
|
41
|
+
Add to your `Cargo.toml`:
|
|
42
|
+
|
|
43
|
+
```toml
|
|
44
|
+
[dependencies]
|
|
45
|
+
aptove-bridge = "0.1"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Minimal Example
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
use aptove_bridge::{BridgeServer, BridgeServeConfig};
|
|
52
|
+
|
|
53
|
+
#[tokio::main]
|
|
54
|
+
async fn main() -> anyhow::Result<()> {
|
|
55
|
+
// Build from defaults — reads 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(())
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
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
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Standalone Binary
|
|
107
|
+
|
|
108
|
+
### Quick Start
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Build
|
|
112
|
+
cargo build --release
|
|
113
|
+
|
|
114
|
+
# Start with local transport (default — no config needed)
|
|
115
|
+
./target/release/bridge run \
|
|
116
|
+
--agent-command "gemini --experimental-acp" \
|
|
117
|
+
--qr
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Scan the QR code with the Aptove mobile app to connect.
|
|
121
|
+
|
|
122
|
+
### Configuration — `common.toml`
|
|
123
|
+
|
|
124
|
+
All transport settings live in `common.toml`. The file is created automatically with local transport enabled on first run.
|
|
125
|
+
|
|
126
|
+
**Default location:**
|
|
127
|
+
|
|
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.
|
|
134
|
+
|
|
135
|
+
Override with `--config-dir`:
|
|
136
|
+
```bash
|
|
137
|
+
bridge --config-dir ./my-config run --agent-command "gemini --experimental-acp"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### Example `common.toml`
|
|
141
|
+
|
|
142
|
+
```toml
|
|
143
|
+
agent_id = "550e8400-e29b-41d4-a716-446655440000" # auto-generated UUID
|
|
144
|
+
auth_token = "base64urltoken" # auto-generated
|
|
145
|
+
|
|
146
|
+
[transports.local]
|
|
147
|
+
enabled = true
|
|
148
|
+
port = 8765
|
|
149
|
+
tls = true
|
|
150
|
+
|
|
151
|
+
[transports.cloudflare]
|
|
152
|
+
enabled = true
|
|
153
|
+
hostname = "https://agent.example.com"
|
|
154
|
+
tunnel_id = "abc123"
|
|
155
|
+
tunnel_secret = "..."
|
|
156
|
+
account_id = "..."
|
|
157
|
+
client_id = "client.access"
|
|
158
|
+
client_secret = "xxxxx"
|
|
159
|
+
|
|
160
|
+
[transports.tailscale-serve]
|
|
161
|
+
enabled = true
|
|
162
|
+
|
|
163
|
+
[transports.tailscale-ip]
|
|
164
|
+
enabled = true
|
|
165
|
+
port = 8765
|
|
166
|
+
tls = true
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Enable only the transports you need. `agent_id` and `auth_token` are generated automatically on first run and stay stable across restarts.
|
|
170
|
+
|
|
171
|
+
### Commands
|
|
172
|
+
|
|
173
|
+
#### `run` — Start the bridge
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
bridge run --agent-command "<your-agent-command>"
|
|
177
|
+
```
|
|
178
|
+
|
|
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) |
|
|
185
|
+
|
|
186
|
+
Transport selection, port, TLS, and auth token are all read from `common.toml`.
|
|
187
|
+
|
|
188
|
+
#### `show-qr` — Show QR code for a second device
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
bridge show-qr
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
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
|
+
|
|
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
|
+
#### `setup` — Provision Cloudflare infrastructure
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
bridge setup \
|
|
206
|
+
--api-token "your-api-token" \
|
|
207
|
+
--account-id "your-account-id" \
|
|
208
|
+
--domain "example.com" \
|
|
209
|
+
--subdomain "agent"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Creates the Cloudflare tunnel, DNS record, Access Application, and Service Token. Saves credentials to `common.toml` under `[transports.cloudflare]`. Only needed once.
|
|
213
|
+
|
|
214
|
+
| Flag | Description | Default |
|
|
215
|
+
|------|-------------|---------|
|
|
216
|
+
| `--api-token <TOKEN>` | Cloudflare API token | Required |
|
|
217
|
+
| `--account-id <ID>` | Cloudflare account ID | Required |
|
|
218
|
+
| `--domain <DOMAIN>` | Domain managed by Cloudflare | Required |
|
|
219
|
+
| `--subdomain <SUB>` | Subdomain for the bridge endpoint | `agent` |
|
|
220
|
+
| `--tunnel-name <NAME>` | Name for the Cloudflare tunnel | `aptove-tunnel` |
|
|
221
|
+
|
|
222
|
+
#### `status` — Check configuration
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
bridge status
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Prints the active `common.toml` path, `agent_id`, enabled transports, and Tailscale availability.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Architecture
|
|
233
|
+
|
|
234
|
+
### Standalone
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
┌─────────────┐ ┌───────────────────────┐ ┌──────────────┐
|
|
238
|
+
│ Client App │◄──────────────────►│ bridge binary │◄──────►│ ACP Agent │
|
|
239
|
+
│ (iOS/Android│ WebSocket (TLS) │ (transport listener) │ stdio │ (your cmd) │
|
|
240
|
+
└─────────────┘ └───────────────────────┘ └──────────────┘
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Embedded (library)
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
┌─────────────┐ ┌──────────────────────────────────────────────┐
|
|
247
|
+
│ Client App │◄──────────────────►│ Your process │
|
|
248
|
+
│ (iOS/Android│ WebSocket (TLS) │ ┌─────────────────┐ ┌────────────────────┐ │
|
|
249
|
+
└─────────────┘ │ │ BridgeServer │◄►│ Agent message loop│ │
|
|
250
|
+
│ │ (transport) │ │ (InProcessTransport│ │
|
|
251
|
+
│ └─────────────────┘ └────────────────────┘ │
|
|
252
|
+
└──────────────────────────────────────────────┘
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
No subprocess is spawned. Agent and bridge communicate via in-process channels.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Troubleshooting
|
|
260
|
+
|
|
261
|
+
| Symptom | Cause | Fix |
|
|
262
|
+
|---------|-------|-----|
|
|
263
|
+
| QR code not scanning | Phone camera can't read terminal QR | Increase terminal font size, or copy the pairing URL and open manually |
|
|
264
|
+
| "Unauthorized" on connect | Wrong or missing auth token | Re-scan QR code |
|
|
265
|
+
| TLS handshake failure | Certificate mismatch | Delete the config dir and restart to regenerate certs; re-pair the device |
|
|
266
|
+
| App cannot reach bridge | Firewall blocking the port | Check OS firewall; ensure the port in `common.toml` is open |
|
|
267
|
+
| Transport fails to start | `common.toml` has no `enabled = true` transport | Run `bridge status` to see configured transports |
|
|
268
|
+
|
|
269
|
+
### Debugging
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# Enable verbose logging
|
|
273
|
+
bridge run --agent-command "gemini --experimental-acp" --verbose
|
|
274
|
+
|
|
275
|
+
# Check which transports are configured
|
|
276
|
+
bridge status
|
|
277
|
+
|
|
278
|
+
# Test agent command independently
|
|
279
|
+
echo '{"jsonrpc":"2.0","method":"initialize","id":1}' | gemini --experimental-acp
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Security
|
|
285
|
+
|
|
286
|
+
- **Auth token**: auto-generated 32-byte random value, stored in `common.toml` (`0600`). Transmitted to mobile during QR pairing and stored in the device Keychain.
|
|
287
|
+
- **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
|
+
- **Pairing codes**: 6-digit, single-use, expire after 60 seconds. Rate-limited to 5 attempts per code.
|
|
289
|
+
- **`common.toml`**: contains all secrets. Permissions are set to `0600` automatically. Keep it secure.
|
|
290
|
+
|
|
291
|
+
To rotate credentials (invalidates all paired devices):
|
|
292
|
+
|
|
293
|
+
```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
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Development
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
cargo build --release # Build
|
|
311
|
+
cargo test # Run tests
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## License
|
|
315
|
+
|
|
316
|
+
Apache 2.0 — see [LICENSE](LICENSE)
|
package/bin/bridge
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* bridge - ACP bridge
|
|
5
|
+
*
|
|
6
|
+
* This script finds and executes the platform-specific binary.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { execFileSync } = require('child_process');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
const PLATFORM_PACKAGES = {
|
|
14
|
+
'darwin-arm64': '@aptove/bridge-darwin-arm64',
|
|
15
|
+
'darwin-x64': '@aptove/bridge-darwin-x64',
|
|
16
|
+
'linux-arm64': '@aptove/bridge-linux-arm64',
|
|
17
|
+
'linux-x64': '@aptove/bridge-linux-x64',
|
|
18
|
+
'win32-x64': '@aptove/bridge-win32-x64',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function getBinaryPath() {
|
|
22
|
+
const platformKey = `${process.platform}-${process.arch}`;
|
|
23
|
+
const packageName = PLATFORM_PACKAGES[platformKey];
|
|
24
|
+
|
|
25
|
+
if (!packageName) {
|
|
26
|
+
console.error(`Unsupported platform: ${platformKey}`);
|
|
27
|
+
console.error('Supported platforms: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const binaryName = process.platform === 'win32' ? 'bridge.exe' : 'bridge';
|
|
32
|
+
|
|
33
|
+
const possiblePaths = [
|
|
34
|
+
path.join(__dirname, '..', packageName, 'bin', binaryName),
|
|
35
|
+
path.join(__dirname, '..', '..', packageName, 'bin', binaryName),
|
|
36
|
+
path.join(__dirname, '..', 'node_modules', packageName, 'bin', binaryName),
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
for (const binaryPath of possiblePaths) {
|
|
40
|
+
if (fs.existsSync(binaryPath)) {
|
|
41
|
+
return binaryPath;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const packagePath = require.resolve(`${packageName}/package.json`);
|
|
47
|
+
const binaryPath = path.join(path.dirname(packagePath), 'bin', binaryName);
|
|
48
|
+
if (fs.existsSync(binaryPath)) {
|
|
49
|
+
return binaryPath;
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// Package not found
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.error(`Could not find bridge binary for ${platformKey}`);
|
|
56
|
+
console.error(`Please ensure ${packageName} is installed.`);
|
|
57
|
+
console.error('');
|
|
58
|
+
console.error('Try reinstalling with:');
|
|
59
|
+
console.error(' npm install -g @aptove/bridge');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const binaryPath = getBinaryPath();
|
|
64
|
+
const args = process.argv.slice(2);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
execFileSync(binaryPath, args, { stdio: 'inherit' });
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error.status !== undefined) {
|
|
70
|
+
process.exit(error.status);
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aptove/bridge",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "ACP bridge — connects ACP agents to mobile and desktop clients over WebSocket",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/aptove/bridge.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/aptove/bridge#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/aptove/bridge/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"acp",
|
|
16
|
+
"agent",
|
|
17
|
+
"bridge",
|
|
18
|
+
"websocket",
|
|
19
|
+
"cli"
|
|
20
|
+
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"bridge": "bin/bridge"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"bin",
|
|
26
|
+
"postinstall.js",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"postinstall": "node postinstall.js"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=16"
|
|
34
|
+
},
|
|
35
|
+
"optionalDependencies": {
|
|
36
|
+
"@aptove/bridge-darwin-arm64": "0.1.4",
|
|
37
|
+
"@aptove/bridge-darwin-x64": "0.1.4",
|
|
38
|
+
"@aptove/bridge-linux-arm64": "0.1.4",
|
|
39
|
+
"@aptove/bridge-linux-x64": "0.1.4",
|
|
40
|
+
"@aptove/bridge-win32-x64": "0.1.4"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bridge postinstall script
|
|
3
|
+
*
|
|
4
|
+
* Verifies the correct platform-specific package was installed
|
|
5
|
+
* and the binary is executable.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
|
|
12
|
+
const PLATFORM_PACKAGES = {
|
|
13
|
+
'darwin-arm64': '@aptove/bridge-darwin-arm64',
|
|
14
|
+
'darwin-x64': '@aptove/bridge-darwin-x64',
|
|
15
|
+
'linux-arm64': '@aptove/bridge-linux-arm64',
|
|
16
|
+
'linux-x64': '@aptove/bridge-linux-x64',
|
|
17
|
+
'win32-x64': '@aptove/bridge-win32-x64',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function main() {
|
|
21
|
+
const platformKey = `${process.platform}-${process.arch}`;
|
|
22
|
+
const packageName = PLATFORM_PACKAGES[platformKey];
|
|
23
|
+
|
|
24
|
+
if (!packageName) {
|
|
25
|
+
console.warn(`⚠️ bridge: Unsupported platform ${platformKey}`);
|
|
26
|
+
console.warn(' Supported platforms: darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const packagePath = require.resolve(`${packageName}/package.json`);
|
|
32
|
+
const binaryName = process.platform === 'win32' ? 'bridge.exe' : 'bridge';
|
|
33
|
+
const binaryPath = path.join(path.dirname(packagePath), 'bin', binaryName);
|
|
34
|
+
|
|
35
|
+
if (!fs.existsSync(binaryPath)) {
|
|
36
|
+
console.warn(`⚠️ bridge: Binary not found at ${binaryPath}`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (process.platform !== 'win32') {
|
|
41
|
+
try {
|
|
42
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
// Not critical
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
execSync(`"${binaryPath}" --version`, { stdio: 'pipe' });
|
|
50
|
+
console.log(`✓ bridge installed successfully for ${platformKey}`);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
console.warn(`⚠️ bridge: Binary exists but failed to execute on ${platformKey}`);
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.warn(`⚠️ bridge: Platform package ${packageName} not installed`);
|
|
56
|
+
console.warn(' This is expected on CI or unsupported platforms');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
main();
|