agent-device-proxy 0.1.6 → 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.
- package/.env.example +19 -0
- package/ARCHITECTURE.md +263 -0
- package/README.md +43 -159
- package/dist/src/224.js +1 -1
- package/dist/src/36.js +12 -18
- package/dist/src/agent-proxy-server.d.ts +2 -0
- package/dist/src/bin.js +14 -2
- package/dist/src/client.d.ts +2 -0
- package/dist/src/dev.d.ts +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +1 -1
- package/dist/src/server/config.d.ts +15 -0
- package/dist/src/server/daemon-forward.d.ts +12 -0
- package/dist/src/server/daemon-state.d.ts +6 -0
- package/dist/src/server/errors.d.ts +9 -0
- package/dist/src/server/http.d.ts +9 -0
- package/dist/src/server/metro-bridge-descriptor.d.ts +4 -0
- package/dist/src/server/metro-bridge-proxy.d.ts +8 -0
- package/dist/src/server/metro-bridge-types.d.ts +33 -0
- package/dist/src/server/metro-bridge.d.ts +3 -0
- package/dist/src/server/metro.d.ts +25 -0
- package/dist/src/server/routes.d.ts +12 -0
- package/dist/src/server.d.ts +8 -0
- package/dist/src/server.js +1 -1
- package/dist/src/types.d.ts +60 -0
- package/package.json +13 -12
- package/scripts/macos-remote-setup.sh +1117 -0
- package/dist/src/402.js +0 -1
- package/dist/src/403.js +0 -2
- package/dist/src/47.js +0 -1
- package/dist/src/493.js +0 -1
- package/dist/src/metro-runtime.js +0 -1
package/.env.example
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
AGENT_DEVICE_PROXY_HOST=0.0.0.0
|
|
2
|
+
AGENT_DEVICE_PROXY_PORT=9124
|
|
3
|
+
# Shared secret checked by host API (Authorization: Bearer ...).
|
|
4
|
+
# Must match client value: AGENT_DEVICE_PROXY_BEARER_TOKEN.
|
|
5
|
+
# Generate with: openssl rand -hex 32
|
|
6
|
+
AGENT_DEVICE_PROXY_BEARER_TOKEN=replace-with-strong-token
|
|
7
|
+
# Optional override for where agent-device state lives on the macOS host.
|
|
8
|
+
# The proxy resolves the live daemon port/token from
|
|
9
|
+
# AGENT_DEVICE_STATE_DIR/daemon.json (defaults to ~/.agent-device/daemon.json).
|
|
10
|
+
# AGENT_DEVICE_STATE_DIR=/Users/you/.agent-device
|
|
11
|
+
AGENT_DEVICE_PROXY_VALIDATE_DAEMON_STARTUP=true
|
|
12
|
+
AGENT_DEVICE_PROXY_DAEMON_PROBE_TIMEOUT_MS=3000
|
|
13
|
+
|
|
14
|
+
# Host-local Metro bridge used for iOS sandbox-host runs.
|
|
15
|
+
AGENT_DEVICE_PROXY_METRO_BRIDGE_ENABLED=true
|
|
16
|
+
AGENT_DEVICE_PROXY_METRO_BRIDGE_HOST=127.0.0.1
|
|
17
|
+
AGENT_DEVICE_PROXY_METRO_BRIDGE_PORT=8081
|
|
18
|
+
AGENT_DEVICE_PROXY_METRO_BRIDGE_ANDROID_HOST=10.0.2.2
|
|
19
|
+
AGENT_DEVICE_PROXY_METRO_BRIDGE_TIMEOUT_MS=120000
|
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# agent-device-proxy Architecture
|
|
2
|
+
|
|
3
|
+
See [README.md](./README.md) for install and day-to-day usage. This document
|
|
4
|
+
covers the system model, repo integration, and endpoint behavior.
|
|
5
|
+
|
|
6
|
+
## Architecture
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
Vercel Sandbox (Linux) macOS host
|
|
10
|
+
┌───────────────────────┐ ┌──────────────────────────────┐
|
|
11
|
+
│ │ │ agent-device-proxy (:9124) │
|
|
12
|
+
│ Metro dev server │ /api/metro/ │ ┌────────────────────────┐ │
|
|
13
|
+
│ (port 8081 inside │── bridge ───▶│ │ Metro bridge (:8081) │ │
|
|
14
|
+
│ sandbox) │ │ │ HTTP + WebSocket proxy │ │
|
|
15
|
+
│ │ │ └───────────┬────────────┘ │
|
|
16
|
+
│ agent-device CLI │ /agent-device │ │ │
|
|
17
|
+
│ (npm global) │── /rpc ──▶│ ┌───────────▼────────────┐ │
|
|
18
|
+
│ │ │ │ Daemon forwarder │ │
|
|
19
|
+
│ .forfiter/ scripts │ │ │ → agent-device daemon │ │
|
|
20
|
+
│ │ │ └────────────────────────┘ │
|
|
21
|
+
└───────────────────────┘ │ │
|
|
22
|
+
│ │
|
|
23
|
+
│ iOS Simulator / Android Emu │
|
|
24
|
+
│ → 127.0.0.1:8081 (iOS) │
|
|
25
|
+
│ → 10.0.2.2:8081 (Android) │
|
|
26
|
+
└──────────────────────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Why This Exists
|
|
30
|
+
|
|
31
|
+
The QA worker runs in a Vercel sandbox. Metro also runs in that sandbox.
|
|
32
|
+
|
|
33
|
+
The iOS simulator and Android emulator do not run in the sandbox. They run on a
|
|
34
|
+
macOS host machine, and `agent-device` also runs there.
|
|
35
|
+
|
|
36
|
+
That means the app cannot reliably load JavaScript directly from the sandbox's
|
|
37
|
+
public URL:
|
|
38
|
+
|
|
39
|
+
- sandbox Metro is externally reachable as `https://<sandbox>.vercel.run`
|
|
40
|
+
- stock React Native iOS builds do not reliably consume that public HTTPS URL as
|
|
41
|
+
a packager origin
|
|
42
|
+
- Android emulator networking is also host-specific
|
|
43
|
+
|
|
44
|
+
So the supported architecture is:
|
|
45
|
+
|
|
46
|
+
1. Metro runs in the sandbox.
|
|
47
|
+
2. The sandbox helper resolves the public upstream Metro URL.
|
|
48
|
+
3. `agent-device-proxy` creates a host-local HTTP bridge on macOS.
|
|
49
|
+
4. Runtime hints point the app at that host-local bridge:
|
|
50
|
+
- iOS simulator: `127.0.0.1:<bridge-port>`
|
|
51
|
+
- Android emulator: `10.0.2.2:<bridge-port>`
|
|
52
|
+
|
|
53
|
+
The app talks to a normal local HTTP Metro endpoint. The proxy forwards that
|
|
54
|
+
traffic back to the sandbox's public Metro origin.
|
|
55
|
+
|
|
56
|
+
## What Is Bridged
|
|
57
|
+
|
|
58
|
+
`agent-device-proxy` currently exposes:
|
|
59
|
+
|
|
60
|
+
- transparent forwarding for any `/agent-device/*` path to the upstream daemon
|
|
61
|
+
- common examples are `GET /agent-device/health` and `POST /agent-device/rpc`
|
|
62
|
+
- `POST /api/metro/resolve` to normalize Metro runtime hints
|
|
63
|
+
- `POST /api/metro/probe` to probe upstream Metro reachability
|
|
64
|
+
- `POST /api/metro/bridge` to create or reconfigure the host-local Metro bridge
|
|
65
|
+
- `GET /api/health` for proxy health
|
|
66
|
+
- `GET /healthz` for lightweight health
|
|
67
|
+
|
|
68
|
+
The proxy is not the product boundary for artifact semantics in this repo. QA
|
|
69
|
+
artifact selection happens in workflow code, the sandbox must resolve a
|
|
70
|
+
host-downloadable install source plus any required auth, and app installation
|
|
71
|
+
happens through the typed `agent-device` client on the host. If no
|
|
72
|
+
host-downloadable source can be produced, QA blocks instead of falling back to
|
|
73
|
+
sandbox-local installation.
|
|
74
|
+
|
|
75
|
+
## Runtime Model
|
|
76
|
+
|
|
77
|
+
Runtime hints are still relevant, but their role changed.
|
|
78
|
+
|
|
79
|
+
They are no longer meant to point apps at the public sandbox Metro URL. Instead,
|
|
80
|
+
they tell the app which host-local bridge endpoint to use.
|
|
81
|
+
|
|
82
|
+
Typical runtime values returned by `/api/metro/bridge`:
|
|
83
|
+
|
|
84
|
+
- iOS:
|
|
85
|
+
- `metro_host=127.0.0.1`
|
|
86
|
+
- `metro_port=8081`
|
|
87
|
+
- `metro_bundle_url=http://127.0.0.1:8081/index.bundle?...`
|
|
88
|
+
- Android:
|
|
89
|
+
- `metro_host=10.0.2.2`
|
|
90
|
+
- `metro_port=8081`
|
|
91
|
+
- `metro_bundle_url=http://10.0.2.2:8081/index.bundle?...`
|
|
92
|
+
|
|
93
|
+
The upstream public sandbox URL is still tracked and probed, but the app should
|
|
94
|
+
not launch against it directly when the bridge is available.
|
|
95
|
+
|
|
96
|
+
## QA Flow In This Repo
|
|
97
|
+
|
|
98
|
+
The staged sandbox Metro helper is the source of truth for Metro:
|
|
99
|
+
|
|
100
|
+
- `.forfiter/start-metro-runtime.js`
|
|
101
|
+
- is invoked only when the repo-side QA bootstrap determines that the artifact
|
|
102
|
+
needs a Metro-backed dev runtime
|
|
103
|
+
- starts Metro inside the sandbox
|
|
104
|
+
- resolves the public upstream Metro runtime
|
|
105
|
+
- calls `POST /api/metro/bridge`
|
|
106
|
+
- writes `.forfiter/metro-runtime.json`
|
|
107
|
+
- fails closed if a bridge is required but unavailable
|
|
108
|
+
|
|
109
|
+
App launch is no longer owned by a sandbox helper script. The workflow bootstrap
|
|
110
|
+
reads `.forfiter/metro-runtime.json`, installs the app through the typed
|
|
111
|
+
`agent-device` client, and then calls `open({ runtime })`.
|
|
112
|
+
|
|
113
|
+
For iOS React Native dev builds, that bridge-backed runtime remains the
|
|
114
|
+
supported launch path.
|
|
115
|
+
|
|
116
|
+
## Host Env File
|
|
117
|
+
|
|
118
|
+
Use [.env.example](.env.example) as the host-side template.
|
|
119
|
+
|
|
120
|
+
Important defaults:
|
|
121
|
+
|
|
122
|
+
- `AGENT_DEVICE_PROXY_PORT=9124`
|
|
123
|
+
- `AGENT_DEVICE_PROXY_METRO_BRIDGE_ENABLED=true`
|
|
124
|
+
- `AGENT_DEVICE_PROXY_METRO_BRIDGE_HOST=127.0.0.1`
|
|
125
|
+
- `AGENT_DEVICE_PROXY_METRO_BRIDGE_PORT=8081`
|
|
126
|
+
- `AGENT_DEVICE_PROXY_METRO_BRIDGE_ANDROID_HOST=10.0.2.2`
|
|
127
|
+
|
|
128
|
+
The proxy validates the upstream daemon health on startup by default. If
|
|
129
|
+
`~/.agent-device/daemon.json` does not point at a live HTTP daemon, proxy
|
|
130
|
+
startup should fail early.
|
|
131
|
+
|
|
132
|
+
Practical startup modes:
|
|
133
|
+
|
|
134
|
+
- Installed package, auto-discovered daemon:
|
|
135
|
+
- set `AGENT_DEVICE_PROXY_BEARER_TOKEN`
|
|
136
|
+
- ensure `~/.agent-device/daemon.json` exists, or set `AGENT_DEVICE_STATE_DIR`
|
|
137
|
+
- run `agent-device-proxy serve`
|
|
138
|
+
- Installed package, helper-managed local daemon:
|
|
139
|
+
- set `AGENT_DEVICE_PROXY_BEARER_TOKEN`
|
|
140
|
+
- run `agent-device-proxy dev`
|
|
141
|
+
|
|
142
|
+
## Worker Envs
|
|
143
|
+
|
|
144
|
+
The Linux/Vercel worker uses two related connections:
|
|
145
|
+
|
|
146
|
+
### 1. Remote `agent-device` daemon bridge
|
|
147
|
+
|
|
148
|
+
This is what the workflow-side typed `agent-device` client uses:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
AGENT_DEVICE_DAEMON_BASE_URL=http://<mac-host>:9124/agent-device
|
|
152
|
+
AGENT_DEVICE_DAEMON_AUTH_TOKEN=<strong-token>
|
|
153
|
+
AGENT_DEVICE_DAEMON_TRANSPORT=http
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 2. Direct proxy API
|
|
157
|
+
|
|
158
|
+
This is what the staged Metro helper uses:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
AGENT_DEVICE_PROXY_BASE_URL=http://<mac-host>:9124
|
|
162
|
+
AGENT_DEVICE_PROXY_BEARER_TOKEN=<strong-token>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
At runtime inside the sandbox, QA writes `~/.agent-device/config.json` with the
|
|
166
|
+
derived daemon settings and the per-run session/platform lock. The runtime
|
|
167
|
+
agent then uses plain `agent-device` commands without wrapper scripts.
|
|
168
|
+
|
|
169
|
+
## Metro Endpoints
|
|
170
|
+
|
|
171
|
+
### `/api/metro/resolve`
|
|
172
|
+
|
|
173
|
+
Normalizes incoming runtime hints into a resolved upstream runtime.
|
|
174
|
+
|
|
175
|
+
Use this when you want to inspect how the proxy interprets:
|
|
176
|
+
|
|
177
|
+
- `metro_bundle_url`
|
|
178
|
+
- `metro_host + metro_port`
|
|
179
|
+
|
|
180
|
+
### `/api/metro/probe`
|
|
181
|
+
|
|
182
|
+
Probes the upstream Metro endpoint and returns reachability details.
|
|
183
|
+
|
|
184
|
+
This is useful as a diagnostic endpoint. It is not enough by itself to prove
|
|
185
|
+
that the app is using the correct runtime.
|
|
186
|
+
|
|
187
|
+
### `/api/metro/bridge`
|
|
188
|
+
|
|
189
|
+
Creates or reconfigures the host-local Metro bridge and returns the bridge-backed
|
|
190
|
+
runtime hints that apps should use.
|
|
191
|
+
|
|
192
|
+
This is the important endpoint for sandbox-host QA.
|
|
193
|
+
|
|
194
|
+
Example response shape:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"base_url": "http://127.0.0.1:8081",
|
|
199
|
+
"status_url": "http://127.0.0.1:8081/status",
|
|
200
|
+
"bundle_url": "http://127.0.0.1:8081/index.bundle?platform=ios&dev=true&minify=false",
|
|
201
|
+
"ios_runtime": {
|
|
202
|
+
"metro_host": "127.0.0.1",
|
|
203
|
+
"metro_port": 8081,
|
|
204
|
+
"metro_bundle_url": "http://127.0.0.1:8081/index.bundle?platform=ios&dev=true&minify=false"
|
|
205
|
+
},
|
|
206
|
+
"android_runtime": {
|
|
207
|
+
"metro_host": "10.0.2.2",
|
|
208
|
+
"metro_port": 8081,
|
|
209
|
+
"metro_bundle_url": "http://10.0.2.2:8081/index.bundle?platform=android&dev=true&minify=false"
|
|
210
|
+
},
|
|
211
|
+
"upstream": {
|
|
212
|
+
"bundle_url": "https://<sandbox>.vercel.run/index.bundle?...",
|
|
213
|
+
"host": "<sandbox>.vercel.run",
|
|
214
|
+
"port": 443,
|
|
215
|
+
"status_url": "https://<sandbox>.vercel.run/status"
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Operational Notes
|
|
221
|
+
|
|
222
|
+
- The Metro bridge is lazy. It starts listening when `/api/metro/bridge` is
|
|
223
|
+
called.
|
|
224
|
+
- Initial bundle fetch is supported. Websocket and HMR traffic are also proxied
|
|
225
|
+
by the bridge.
|
|
226
|
+
- The current repo QA flow assumes the bridge is available when
|
|
227
|
+
`AGENT_DEVICE_PROXY_BASE_URL` and `AGENT_DEVICE_PROXY_BEARER_TOKEN` are set.
|
|
228
|
+
- Public sandbox Metro URLs are still recorded as upstream metadata, but they
|
|
229
|
+
should be treated as debug or probe inputs, not as the primary app runtime.
|
|
230
|
+
|
|
231
|
+
## Troubleshooting
|
|
232
|
+
|
|
233
|
+
**Proxy startup fails with daemon health check error**
|
|
234
|
+
|
|
235
|
+
The proxy validates the upstream daemon on startup. Make sure `agent-device` is
|
|
236
|
+
running in HTTP mode:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
export AGENT_DEVICE_DAEMON_SERVER_MODE=http
|
|
240
|
+
agent-device session list --json --daemon-transport http
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
If using the helper command, the proxy resolves the live daemon from
|
|
244
|
+
`~/.agent-device/daemon.json` automatically. If that file is missing or stale,
|
|
245
|
+
restart `agent-device` in HTTP mode.
|
|
246
|
+
|
|
247
|
+
**Port 8081 already in use**
|
|
248
|
+
|
|
249
|
+
The Metro bridge binds to port 8081 by default. If another Metro instance or
|
|
250
|
+
process already occupies that port, either stop it or set a different bridge
|
|
251
|
+
port:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
export AGENT_DEVICE_PROXY_METRO_BRIDGE_PORT=8082
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Note: the sandbox scripts and runtime hints must also be updated to match.
|
|
258
|
+
|
|
259
|
+
**Bearer token mismatch**
|
|
260
|
+
|
|
261
|
+
The proxy rejects requests where the `Authorization: Bearer` header does not
|
|
262
|
+
match `AGENT_DEVICE_PROXY_BEARER_TOKEN`. Ensure the same token is exported on
|
|
263
|
+
both the worker side and the macOS host side.
|
package/README.md
CHANGED
|
@@ -1,204 +1,88 @@
|
|
|
1
1
|
# agent-device-proxy
|
|
2
2
|
|
|
3
|
-
Reusable macOS
|
|
3
|
+
Reusable macOS sidecar for remote mobile QA.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Metro resolve/probe
|
|
7
|
-
- artifact upload/install for `.app` and `.apk`
|
|
5
|
+
It has two jobs:
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
- expose a host-local HTTP bridge to a macOS `agent-device` HTTP daemon
|
|
8
|
+
- expose a host-local HTTP Metro bridge for simulator/emulator app traffic
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
- ZIP entry validation before `.app` extraction
|
|
13
|
-
- strict command status (`exit_code != 0` -> HTTP `502`)
|
|
14
|
-
- artifact retention cleanup (TTL + max total bytes)
|
|
10
|
+
## Why
|
|
15
11
|
|
|
16
|
-
|
|
12
|
+
The QA worker and Metro run in a Linux sandbox, while the iOS simulator,
|
|
13
|
+
Android emulator, and `agent-device` run on a macOS host. `agent-device-proxy`
|
|
14
|
+
bridges those two environments so the worker can talk to the host daemon and
|
|
15
|
+
the app can use a normal host-local Metro endpoint.
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
- `.apk` files are sent raw (not zipped)
|
|
20
|
-
|
|
21
|
-
Consumers should pass `.app` / `.apk` paths and call the client API.
|
|
22
|
-
|
|
23
|
-
## Primary endpoints
|
|
24
|
-
|
|
25
|
-
- `GET /api/health`
|
|
26
|
-
- `GET /agent-device/health`
|
|
27
|
-
- `POST /agent-device/rpc`
|
|
28
|
-
- `POST /agent-device/upload`
|
|
29
|
-
- `POST /api/artifacts/upload`
|
|
30
|
-
- `POST /api/agent-device/install`
|
|
31
|
-
- `POST /api/agent-device/exec`
|
|
32
|
-
- `POST /api/metro/resolve`
|
|
33
|
-
- `POST /api/metro/probe`
|
|
34
|
-
|
|
35
|
-
Compatibility aliases remain available under `/v1/*`.
|
|
36
|
-
|
|
37
|
-
## Run
|
|
17
|
+
## Install
|
|
38
18
|
|
|
39
19
|
```bash
|
|
40
|
-
|
|
20
|
+
npm install -g agent-device-proxy
|
|
41
21
|
```
|
|
42
22
|
|
|
43
|
-
|
|
23
|
+
## Run
|
|
24
|
+
|
|
25
|
+
Start the proxy against an already-running `agent-device` HTTP daemon:
|
|
44
26
|
|
|
45
27
|
```bash
|
|
46
|
-
|
|
28
|
+
export AGENT_DEVICE_PROXY_BEARER_TOKEN='<strong-token>' # openssl rand -hex 32
|
|
29
|
+
agent-device-proxy serve
|
|
47
30
|
```
|
|
48
31
|
|
|
49
|
-
|
|
32
|
+
For `serve`, set:
|
|
50
33
|
|
|
51
|
-
|
|
34
|
+
- `AGENT_DEVICE_PROXY_BEARER_TOKEN` (e.g. generated with `openssl rand -hex 32`)
|
|
35
|
+
- a readable `~/.agent-device/daemon.json` (set by `agent-device`) or `AGENT_DEVICE_STATE_DIR`
|
|
52
36
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- host: `AGENT_DEVICE_PROXY_BEARER_TOKEN`
|
|
57
|
-
- client: `AGENT_DEVICE_PROXY_BEARER_TOKEN`
|
|
58
|
-
- generate once: `openssl rand -hex 32`
|
|
37
|
+
If you want the package to prepare the local `agent-device` HTTP daemon first,
|
|
38
|
+
use the helper command instead:
|
|
59
39
|
|
|
60
40
|
```bash
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
cd forfiter/agent-device-proxy && pnpm install
|
|
65
|
-
|
|
66
|
-
# run agent-device HTTP daemon
|
|
67
|
-
AGENT_DEVICE_DAEMON_SERVER_MODE=http \
|
|
68
|
-
agent-device session list --json
|
|
69
|
-
|
|
70
|
-
# run agent-device-proxy API
|
|
71
|
-
AGENT_DEVICE_PROXY_BEARER_TOKEN='<strong-token>' \
|
|
72
|
-
AGENT_DEVICE_PROXY_DAEMON_BASE_URL='http://127.0.0.1:4310' \
|
|
73
|
-
pnpm --filter agent-device-proxy start
|
|
41
|
+
export AGENT_DEVICE_PROXY_BEARER_TOKEN='<strong-token>'
|
|
42
|
+
agent-device-proxy dev
|
|
74
43
|
```
|
|
75
44
|
|
|
76
|
-
|
|
45
|
+
`dev` is macOS-host oriented. It expects:
|
|
77
46
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
AGENT_DEVICE_PROXY_ARTIFACT_MAX_TOTAL_BYTES=5368709120
|
|
82
|
-
```
|
|
47
|
+
- `agent-device` to be installed locally and runnable
|
|
48
|
+
- standard host utilities such as `ps` and `lsof`
|
|
49
|
+
- `AGENT_DEVICE_PROXY_BEARER_TOKEN` to already be set
|
|
83
50
|
|
|
84
|
-
|
|
51
|
+
If you want a host-side env template, use [.env.example](.env.example).
|
|
85
52
|
|
|
86
|
-
|
|
87
|
-
proxy bridge:
|
|
53
|
+
The package also exposes the macOS bootstrap helper as:
|
|
88
54
|
|
|
89
55
|
```bash
|
|
90
|
-
|
|
91
|
-
export AGENT_DEVICE_DAEMON_AUTH_TOKEN="<strong-token>"
|
|
56
|
+
agent-device-proxy-macos-remote-setup --help
|
|
92
57
|
```
|
|
93
58
|
|
|
94
|
-
|
|
95
|
-
and `POST /agent-device/upload`, and forwards them to the configured host daemon
|
|
96
|
-
base URL. This allows native remote-daemon `agent-device install ...` flows to
|
|
97
|
-
upload local artifacts through the same `${AGENT_DEVICE_DAEMON_BASE_URL}` bridge.
|
|
98
|
-
|
|
99
|
-
By default, proxy startup validates the upstream daemon health endpoint and exits
|
|
100
|
-
early if the configured `AGENT_DEVICE_PROXY_DAEMON_BASE_URL` is unreachable.
|
|
101
|
-
This helps catch the common case where the host daemon restarted in socket mode
|
|
102
|
-
instead of HTTP mode.
|
|
59
|
+
## Programmatic Usage
|
|
103
60
|
|
|
104
|
-
|
|
105
|
-
`agent-device-proxy` API directly.
|
|
106
|
-
|
|
107
|
-
In your workflow/agent worker:
|
|
61
|
+
Node client:
|
|
108
62
|
|
|
109
63
|
```ts
|
|
110
64
|
import { createAgentDeviceProxyClient } from "agent-device-proxy"
|
|
111
|
-
import { ensureMetroRuntime } from "agent-device-proxy/metro-runtime"
|
|
112
65
|
|
|
113
|
-
const
|
|
114
|
-
baseUrl: "http://<
|
|
66
|
+
const client = createAgentDeviceProxyClient({
|
|
67
|
+
baseUrl: "http://<mac-host>:9124",
|
|
115
68
|
bearerToken: process.env.AGENT_DEVICE_PROXY_BEARER_TOKEN,
|
|
116
69
|
})
|
|
117
|
-
|
|
118
|
-
const metro = await ensureMetroRuntime({
|
|
119
|
-
projectRoot: "/workspace/RNCLI83",
|
|
120
|
-
port: 8081,
|
|
121
|
-
publicHost: process.env.AGENT_DEVICE_PROXY_METRO_PUBLIC_HOST, // must be reachable from remote mac
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
// iOS: pass ios_runtime so remote mac can connect to Metro
|
|
126
|
-
await agentDeviceProxy.installApp({
|
|
127
|
-
app: "RNCLI83",
|
|
128
|
-
filePath: "/workspace/ios-build/RNCLI83.app",
|
|
129
|
-
platform: "ios",
|
|
130
|
-
device: "iPhone 17 Pro",
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
await agentDeviceProxy.agentDeviceExec({
|
|
134
|
-
argv: ["open", "RNCLI83", "--platform", "ios", "--device", "iPhone 17 Pro"],
|
|
135
|
-
ios_runtime: metro.ios_runtime,
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
// Android: no Metro hint needed
|
|
139
|
-
await agentDeviceProxy.installApp({
|
|
140
|
-
app: "com.rncli83",
|
|
141
|
-
filePath: "/workspace/android/app/build/outputs/apk/debug/app-debug.apk",
|
|
142
|
-
platform: "android",
|
|
143
|
-
serial: "emulator-5554",
|
|
144
|
-
})
|
|
145
|
-
} finally {
|
|
146
|
-
await metro.stop()
|
|
147
|
-
}
|
|
148
70
|
```
|
|
149
71
|
|
|
150
|
-
|
|
72
|
+
Embedded server:
|
|
151
73
|
|
|
152
|
-
|
|
153
|
-
If not reachable directly, use a tunnel and pass that tunnel endpoint in `ios_runtime`.
|
|
154
|
-
|
|
155
|
-
### Migration note
|
|
156
|
-
|
|
157
|
-
Older docs may reference `host-agent`, `@platform/host-agent`, or `HOST_AGENT_*` env names.
|
|
158
|
-
Use `agent-device-proxy`, `agent-device-proxy/*`, and `AGENT_DEVICE_PROXY_*` env names instead.
|
|
159
|
-
|
|
160
|
-
## Client
|
|
161
|
-
|
|
162
|
-
```js
|
|
163
|
-
import { createAgentDeviceProxyClient } from "agent-device-proxy"
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
## Auxiliary helpers
|
|
167
|
-
|
|
168
|
-
For Node consumers, use:
|
|
169
|
-
|
|
170
|
-
```js
|
|
171
|
-
import {
|
|
172
|
-
createAgentDeviceProxyClient,
|
|
173
|
-
ensureMetroRuntime,
|
|
174
|
-
} from "agent-device-proxy"
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Use `installApp()` for artifact handoff and `agentDeviceExec()` only when you
|
|
178
|
-
explicitly need the proxy API from Node. This package does not install an
|
|
179
|
-
`agent-device` bin shim; use the real `agent-device` package for CLI execution
|
|
180
|
-
and native remote-daemon support.
|
|
181
|
-
|
|
182
|
-
## Embedded server
|
|
183
|
-
|
|
184
|
-
```js
|
|
74
|
+
```ts
|
|
185
75
|
import { startAgentDeviceProxyServer } from "agent-device-proxy/server"
|
|
186
76
|
|
|
187
77
|
startAgentDeviceProxyServer()
|
|
188
78
|
```
|
|
189
79
|
|
|
190
|
-
##
|
|
80
|
+
## More Details
|
|
191
81
|
|
|
192
|
-
|
|
193
|
-
import { ensureMetroRuntime } from "agent-device-proxy/metro-runtime"
|
|
82
|
+
See [ARCHITECTURE.md](./ARCHITECTURE.md) for:
|
|
194
83
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// use metro.ios_runtime when opening iOS app through agent-device-proxy
|
|
201
|
-
// await agentDeviceProxy.agentDeviceExec({ argv: [...], ios_runtime: metro.ios_runtime })
|
|
202
|
-
|
|
203
|
-
await metro.stop()
|
|
204
|
-
```
|
|
84
|
+
- system architecture and runtime model
|
|
85
|
+
- QA flow in this repo
|
|
86
|
+
- worker-side env wiring
|
|
87
|
+
- Metro endpoint behavior
|
|
88
|
+
- operational notes and troubleshooting
|
package/dist/src/224.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
function t(t){let i=function(t){if("string"!=typeof t||!t.trim())throw Error("baseUrl is required");return t.trim().replace(/\/+$/,"")}(t.baseUrl),a="string"==typeof t.bearerToken?t.bearerToken.trim():"",s=n(t.timeoutMs,12e4);return{health:async()=>await e({baseUrl:i,method:"GET",endpoint:"/api/health",bearerToken:a,timeoutMs:s}),metroResolve:async t=>await e({baseUrl:i,method:"POST",endpoint:"/api/metro/resolve",bodyJson:{ios_runtime:r(t)},bearerToken:a,timeoutMs:n(o(t),s)}),async metroProbe(t){let u=n(o(t),s);return await e({baseUrl:i,method:"POST",endpoint:"/api/metro/probe",bodyJson:{ios_runtime:r(t),...o(t)?{timeout_ms:o(t)}:{}},bearerToken:a,timeoutMs:u})},async metroBridge(t){let u=n(o(t),s);return await e({baseUrl:i,method:"POST",endpoint:"/api/metro/bridge",bodyJson:{ios_runtime:r(t),...o(t)?{timeout_ms:o(t)}:{}},bearerToken:a,timeoutMs:u})}}}async function e({baseUrl:t,method:r,endpoint:o,bodyJson:i,bearerToken:n,timeoutMs:a}){let s,u,m={};void 0!==i&&(s=JSON.stringify(i),m["content-type"]="application/json"),n&&(m.authorization=`Bearer ${n}`);let l=await fetch(`${t}${o}`,{method:r,headers:m,...void 0!==s?{body:s}:{},signal:AbortSignal.timeout(a)}),p=await l.text();try{u=p?JSON.parse(p):{}}catch{u={raw:p}}if(!l.ok){var c;let t=Error(((c=u)&&"object"==typeof c?"string"==typeof c.error?c.error:c.error&&"object"==typeof c.error&&"string"==typeof c.error.message?c.error.message:null:null)??`agent-device-proxy request failed (${l.status})`);throw t.statusCode=l.status,t.payload=u,t}return u&&"object"==typeof u&&"ok"in u&&"data"in u?u.data:u}function r(t){return i(t)?t.ios_runtime:t}function o(t){if(i(t))return t.timeout_ms}function i(t){return"ios_runtime"in t}function n(t,e){if(null==t||""===t)return e;let r=Number.parseInt(String(t),10);return!Number.isInteger(r)||r<100||r>36e5?e:r}export{t as createAgentDeviceProxyClient};
|