agent-device-proxy 0.1.0 → 0.1.2
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 +31 -37
- package/dist/src/36.js +2 -2
- package/dist/src/bin.js +1 -1
- package/dist/src/index.js +1 -1
- package/package.json +2 -3
- package/dist/src/111.js +0 -1
- package/dist/src/agent-device.js +0 -3
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# agent-device-proxy
|
|
2
2
|
|
|
3
3
|
Reusable macOS agent-device-proxy (simple mode) for:
|
|
4
|
+
|
|
4
5
|
- `agent-device` command execution
|
|
5
6
|
- Metro resolve/probe
|
|
6
7
|
- artifact upload/install for `.app` and `.apk`
|
|
7
8
|
|
|
8
9
|
Default hardening:
|
|
10
|
+
|
|
9
11
|
- streamed artifact uploads (no in-memory full body for uploads)
|
|
10
12
|
- ZIP entry validation before `.app` extraction
|
|
11
13
|
- strict command status (`exit_code != 0` -> HTTP `502`)
|
|
@@ -49,8 +51,9 @@ pnpm dev:agent-device-proxy
|
|
|
49
51
|
|
|
50
52
|
Use `agent-device-proxy/.env.example` as the host-side env template.
|
|
51
53
|
Use one shared bearer token value on both sides:
|
|
54
|
+
|
|
52
55
|
- host: `AGENT_DEVICE_PROXY_BEARER_TOKEN`
|
|
53
|
-
- client: `
|
|
56
|
+
- client: `AGENT_DEVICE_PROXY_BEARER_TOKEN`
|
|
54
57
|
- generate once: `openssl rand -hex 32`
|
|
55
58
|
|
|
56
59
|
```bash
|
|
@@ -72,9 +75,9 @@ pnpm --filter agent-device-proxy start
|
|
|
72
75
|
Optional hardening envs:
|
|
73
76
|
|
|
74
77
|
```bash
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
AGENT_DEVICE_PROXY_STRICT_COMMAND_STATUS=true
|
|
79
|
+
AGENT_DEVICE_PROXY_ARTIFACT_TTL_HOURS=24
|
|
80
|
+
AGENT_DEVICE_PROXY_ARTIFACT_MAX_TOTAL_BYTES=5368709120
|
|
78
81
|
```
|
|
79
82
|
|
|
80
83
|
### 2) Client Linux/Vercel side
|
|
@@ -96,19 +99,19 @@ the `agent-device-proxy` API directly.
|
|
|
96
99
|
In your workflow/agent worker:
|
|
97
100
|
|
|
98
101
|
```ts
|
|
99
|
-
import { createAgentDeviceProxyClient } from "agent-device-proxy"
|
|
100
|
-
import { ensureMetroRuntime } from "agent-device-proxy/metro-runtime"
|
|
102
|
+
import { createAgentDeviceProxyClient } from "agent-device-proxy"
|
|
103
|
+
import { ensureMetroRuntime } from "agent-device-proxy/metro-runtime"
|
|
101
104
|
|
|
102
105
|
const agentDeviceProxy = createAgentDeviceProxyClient({
|
|
103
106
|
baseUrl: "http://<remote-mac-ip>:9123",
|
|
104
|
-
bearerToken: process.env.
|
|
105
|
-
})
|
|
107
|
+
bearerToken: process.env.AGENT_DEVICE_PROXY_BEARER_TOKEN,
|
|
108
|
+
})
|
|
106
109
|
|
|
107
110
|
const metro = await ensureMetroRuntime({
|
|
108
111
|
projectRoot: "/workspace/RNCLI83",
|
|
109
112
|
port: 8081,
|
|
110
|
-
publicHost: process.env.
|
|
111
|
-
})
|
|
113
|
+
publicHost: process.env.AGENT_DEVICE_PROXY_METRO_PUBLIC_HOST, // must be reachable from remote mac
|
|
114
|
+
})
|
|
112
115
|
|
|
113
116
|
try {
|
|
114
117
|
// iOS: pass ios_runtime so remote mac can connect to Metro
|
|
@@ -117,12 +120,12 @@ try {
|
|
|
117
120
|
filePath: "/workspace/ios-build/RNCLI83.app",
|
|
118
121
|
platform: "ios",
|
|
119
122
|
device: "iPhone 17 Pro",
|
|
120
|
-
})
|
|
123
|
+
})
|
|
121
124
|
|
|
122
125
|
await agentDeviceProxy.agentDeviceExec({
|
|
123
126
|
argv: ["open", "RNCLI83", "--platform", "ios", "--device", "iPhone 17 Pro"],
|
|
124
127
|
ios_runtime: metro.ios_runtime,
|
|
125
|
-
})
|
|
128
|
+
})
|
|
126
129
|
|
|
127
130
|
// Android: no Metro hint needed
|
|
128
131
|
await agentDeviceProxy.installApp({
|
|
@@ -130,9 +133,9 @@ try {
|
|
|
130
133
|
filePath: "/workspace/android/app/build/outputs/apk/debug/app-debug.apk",
|
|
131
134
|
platform: "android",
|
|
132
135
|
serial: "emulator-5554",
|
|
133
|
-
})
|
|
136
|
+
})
|
|
134
137
|
} finally {
|
|
135
|
-
await metro.stop()
|
|
138
|
+
await metro.stop()
|
|
136
139
|
}
|
|
137
140
|
```
|
|
138
141
|
|
|
@@ -144,21 +147,15 @@ If not reachable directly, use a tunnel and pass that tunnel endpoint in `ios_ru
|
|
|
144
147
|
### Migration note
|
|
145
148
|
|
|
146
149
|
Older docs may reference `host-agent`, `@platform/host-agent`, or `HOST_AGENT_*` env names.
|
|
147
|
-
Use `agent-device-proxy`, `agent-device-proxy/*`, and `
|
|
150
|
+
Use `agent-device-proxy`, `agent-device-proxy/*`, and `AGENT_DEVICE_PROXY_*` env names instead.
|
|
148
151
|
|
|
149
152
|
## Client
|
|
150
153
|
|
|
151
154
|
```js
|
|
152
|
-
import { createAgentDeviceProxyClient } from "agent-device-proxy"
|
|
155
|
+
import { createAgentDeviceProxyClient } from "agent-device-proxy"
|
|
153
156
|
```
|
|
154
157
|
|
|
155
|
-
##
|
|
156
|
-
|
|
157
|
-
The package also exposes a proxy-backed `agent-device` contract for Linux sandboxes:
|
|
158
|
-
|
|
159
|
-
- install the package in the sandbox
|
|
160
|
-
- run `npx agent-device ...`
|
|
161
|
-
- the command is forwarded to the remote macOS `agent-device-proxy` host
|
|
158
|
+
## Auxiliary helpers
|
|
162
159
|
|
|
163
160
|
For Node consumers, use:
|
|
164
161
|
|
|
@@ -166,37 +163,34 @@ For Node consumers, use:
|
|
|
166
163
|
import {
|
|
167
164
|
createAgentDeviceProxyClient,
|
|
168
165
|
ensureMetroRuntime,
|
|
169
|
-
|
|
170
|
-
} from "agent-device-proxy";
|
|
166
|
+
} from "agent-device-proxy"
|
|
171
167
|
```
|
|
172
168
|
|
|
173
|
-
`
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
169
|
+
Use `installApp()` for artifact handoff and `agentDeviceExec()` for explicit
|
|
170
|
+
proxy-side execution from Node. This package does not install an `agent-device`
|
|
171
|
+
bin shim; use the real `agent-device` package for CLI execution and native
|
|
172
|
+
remote-daemon support.
|
|
177
173
|
|
|
178
174
|
## Embedded server
|
|
179
175
|
|
|
180
176
|
```js
|
|
181
|
-
import { startAgentDeviceProxyServer } from "agent-device-proxy/server"
|
|
177
|
+
import { startAgentDeviceProxyServer } from "agent-device-proxy/server"
|
|
182
178
|
|
|
183
|
-
startAgentDeviceProxyServer()
|
|
179
|
+
startAgentDeviceProxyServer()
|
|
184
180
|
```
|
|
185
181
|
|
|
186
182
|
## Metro runtime helper (Linux/Vercel)
|
|
187
183
|
|
|
188
184
|
```js
|
|
189
|
-
import { ensureMetroRuntime } from "agent-device-proxy/metro-runtime"
|
|
185
|
+
import { ensureMetroRuntime } from "agent-device-proxy/metro-runtime"
|
|
190
186
|
|
|
191
187
|
const metro = await ensureMetroRuntime({
|
|
192
188
|
projectRoot: "/workspace/RNCLI83",
|
|
193
|
-
publicHost: "10.0.0.10"
|
|
194
|
-
})
|
|
189
|
+
publicHost: "10.0.0.10",
|
|
190
|
+
})
|
|
195
191
|
|
|
196
192
|
// use metro.ios_runtime when opening iOS app through agent-device-proxy
|
|
197
193
|
// await agentDeviceProxy.agentDeviceExec({ argv: [...], ios_runtime: metro.ios_runtime })
|
|
198
194
|
|
|
199
|
-
await metro.stop()
|
|
195
|
+
await metro.stop()
|
|
200
196
|
```
|
|
201
|
-
|
|
202
|
-
Public API reference: [docs/AGENT_PROXY_API.md](../../docs/AGENT_PROXY_API.md)
|
package/dist/src/36.js
CHANGED
|
@@ -2,7 +2,7 @@ import t from"node:http";import{node_path as e,mkdirSync as r,promises as i,rand
|
|
|
2
2
|
[output truncated]`.trim()),p&&(l=`${l}
|
|
3
3
|
[output truncated]`.trim()),o(t))}s.stdout.on("data",t=>{v("stdout",t)}),s.stderr.on("data",t=>{v("stderr",t)}),s.on("close",(t,e)=>{y({exitCode:m?124:"number"==typeof t?t:1,stdout:u,stderr:m?`${l}
|
|
4
4
|
proxy timeout after ${a}ms`.trim():l,signal:e??"",timedOut:m,stdoutTruncated:_,stderrTruncated:p})}),s.on("error",t=>{y({exitCode:1,stdout:u,stderr:`${l}
|
|
5
|
-
${t.message}`.trim(),signal:"",timedOut:m,stdoutTruncated:_,stderrTruncated:p})})})}function A({result:t,action:e,strictCommandStatus:r}){if(r&&0!==t.exitCode)throw new c(502,"command_failed",`${e} failed`,{exit_code:t.exitCode,timed_out:t.timedOut,signal:t.signal,stdout:t.stdout,stderr:t.stderr})}function T(t,e){return e<=0?"":Buffer.from(t,"utf8").subarray(0,e).toString("utf8")}let O=new Map([["/v1/agent-device/exec","/api/agent-device/exec"],["/v1/agent-device/install","/api/agent-device/install"],["/v1/artifacts/upload","/api/artifacts/upload"],["/v1/metro/resolve","/api/metro/resolve"],["/v1/metro/probe","/api/metro/probe"]]);function x(t,e,r,i,a){a?
|
|
5
|
+
${t.message}`.trim(),signal:"",timedOut:m,stdoutTruncated:_,stderrTruncated:p})})})}function A({result:t,action:e,strictCommandStatus:r}){if(r&&0!==t.exitCode)throw new c(502,"command_failed",`${e} failed`,{exit_code:t.exitCode,timed_out:t.timedOut,signal:t.signal,stdout:t.stdout,stderr:t.stderr})}function T(t,e){return e<=0?"":Buffer.from(t,"utf8").subarray(0,e).toString("utf8")}let O=new Map([["/v1/agent-device/exec","/api/agent-device/exec"],["/v1/agent-device/install","/api/agent-device/install"],["/v1/artifacts/upload","/api/artifacts/upload"],["/v1/metro/resolve","/api/metro/resolve"],["/v1/metro/probe","/api/metro/probe"]]);function x(t,e,r,i,a){a?D(t,e,r,i):D(t,e,{ok:!0,data:r,request_id:i},i)}function R(t,e,r,i,a,n,o=null){n?D(t,e,{error:i},a):D(t,e,{ok:!1,error:{code:r,message:i,...o?{details:o}:{}},request_id:a},a)}function D(t,e,r,i){t.statusCode=e,t.setHeader("content-type","application/json; charset=utf-8"),t.setHeader("x-request-id",i),t.end(JSON.stringify(r))}async function C(t,e){let r=(await S(t,e)).toString("utf8");if(!r.trim())throw new c(400,"invalid_request","body must be valid JSON");try{let t=JSON.parse(r);if(!t||"object"!=typeof t||Array.isArray(t))throw new c(400,"invalid_request","body must be valid JSON object");return t}catch{throw new c(400,"invalid_request","body must be valid JSON")}}async function S(t,e){let r=[],i=0;try{for await(let a of t){let t=Buffer.isBuffer(a)?a:Buffer.from(a);if((i+=t.length)>e)throw new c(413,"invalid_request","request body too large");r.push(t)}}catch(t){if(t instanceof c)throw t;throw new c(400,"invalid_request","invalid request body")}return Buffer.concat(r)}function N(t){if(null==t||""===t)return null;if(!(t&&"object"==typeof t&&!Array.isArray(t)))throw new c(400,"invalid_request","ios_runtime must be an object");let e=$(t.launch_url,"ios_runtime.launch_url",null),r=$(t.metro_bundle_url,"ios_runtime.metro_bundle_url",new Set(["http:","https:"])),i=function(t,e){let r=B(t,e);if(!r)return null;if(r.includes(":"))throw new c(400,"invalid_request",`${e} must not include a port`);if(!/^[A-Za-z0-9._-]{1,253}$/.test(r))throw new c(400,"invalid_request",`${e} must contain only host-safe characters`);return r}(t.metro_host,"ios_runtime.metro_host"),a=function(t,e){if(null==t||""===t)return null;let r=Number.parseInt(String(t),10);if(!Number.isInteger(r)||r<1||r>65535)throw new c(400,"invalid_request",`${e} must be an integer in range 1..65535`);return r}(t.metro_port,"ios_runtime.metro_port");return e||r||i||a?{launch_url:e||"",metro_bundle_url:r||"",metro_host:i||"",metro_port:a||0}:null}function I(t){if(!t)return null;if(t.metro_bundle_url){let e=new URL(t.metro_bundle_url);return{bundle_url:e.toString(),host:e.hostname,port:function(t){if(t.port){let e=Number.parseInt(t.port,10);if(Number.isInteger(e)&&e>=1&&e<=65535)return e}return"https:"===t.protocol?443:80}(e),status_url:M(e)}}if(t.metro_host&&t.metro_port){let e=new URL(`http://${t.metro_host}:${t.metro_port}/index.bundle?platform=ios&dev=true&minify=false`);return{bundle_url:e.toString(),host:e.hostname,port:Number.parseInt(e.port,10),status_url:M(e)}}return null}async function q({statusUrl:t,timeoutMs:e}){let r=Date.now();try{let i=await fetch(t,{signal:AbortSignal.timeout(e)});return{reachable:i.ok,status_code:i.status,latency_ms:Date.now()-r,detail:i.ok?"ok":`HTTP ${i.status}`}}catch(t){return{reachable:!1,status_code:0,latency_ms:Date.now()-r,detail:t instanceof Error?t.message:String(t)}}}function $(t,e,r){let i,a=B(t,e);if(!a)return null;try{i=new URL(a)}catch{throw new c(400,"invalid_request",`${e} must be an absolute URL`)}if(r&&!r.has(i.protocol))throw new c(400,"invalid_request",`${e} must use ${Array.from(r).join(" or ")}`);return i.toString()}function B(t,e){if(null==t||""===t)return"";if("string"!=typeof t)throw new c(400,"invalid_request",`${e} must be a string`);return t.trim()}function M(t){let e=new URL(t.toString()),r=e.pathname.endsWith("/")&&e.pathname.length>1?e.pathname.slice(0,-1):e.pathname,i=r;if(r.endsWith("/index.bundle"))i=r.slice(0,-13);else if(r.endsWith(".bundle")){let t=r.lastIndexOf("/");i=t>=0?r.slice(0,t):""}return e.pathname=`${i||""}/status`.replace(/\/{2,}/g,"/"),e.search="",e.hash="",e.toString()}function P(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function z(t,e){let r=j(t,e);if(!r)throw new c(400,"invalid_request",`${e} is required`);return r}function L(t,e,r){let i=z(t,e).toLowerCase();if(!r.includes(i))throw new c(400,"invalid_request",`${e} must be one of: ${r.join(", ")}`);return i}function j(t,e){if(null==t||""===t)return"";if("string"!=typeof t)throw new c(400,"invalid_request",`${e} must be a string`);return t.trim()}function G(t,e,r){if(null==t||""===t)return r;if("boolean"!=typeof t)throw new c(400,"invalid_request",`${e} must be a boolean`);return t}function U(t,e){let r=t[e];return"string"==typeof r?r.trim():Array.isArray(r)&&r.length>0?String(r[0]).trim():""}function k(t={}){return Y(t).server}function X(t={}){let{server:e,config:r,artifactStore:i}=Y(t);return function(t){let e=[t,"zip","unzip"],r=e.filter(t=>{var e,r;return!("string"==typeof(e=t)&&e.trim()&&0===u("sh",["-lc",`command -v ${(r=e,`'${r.replaceAll("'","'\\''")}'`)} >/dev/null 2>&1`],{stdio:"ignore"}).status)});if(r.length>0)throw Error(`agent-device-proxy missing required commands in PATH: ${r.join(", ")}`);process.stdout.write(`agent-device-proxy dependency preflight ok: ${e.join(", ")}
|
|
6
6
|
`)}(r.command),e.listen(r.port,r.host,()=>{process.stdout.write(`agent-device-proxy listening on http://${r.host}:${r.port}
|
|
7
7
|
`),process.stdout.write(`api health endpoint: http://${r.host}:${r.port}/api/health
|
|
8
8
|
`),process.stdout.write(`api exec endpoint: http://${r.host}:${r.port}/api/agent-device/exec
|
|
@@ -15,4 +15,4 @@ ${t.message}`.trim(),signal:"",timedOut:m,stdoutTruncated:_,stderrTruncated:p})}
|
|
|
15
15
|
`),process.stdout.write(`artifacts dir: ${r.artifactsDir}
|
|
16
16
|
`),r.workspaceRoot&&process.stdout.write(`workspace root constraint: ${r.workspaceRoot}
|
|
17
17
|
`)}),i.maybeCleanupArtifacts().catch(t=>{process.stderr.write(`agent-device-proxy artifact cleanup warning: ${t instanceof Error?t.message:String(t)}
|
|
18
|
-
`)}),e}function Y(n={}){let s,u,d,v,y,E,T,S,D,I,B,M,k,X,V,F,J,W=n.config||(s=process.env.AGENT_DEVICE_PROXY_HOST??"127.0.0.1",u=l("AGENT_DEVICE_PROXY_PORT",9123,{min:1,max:65535}),d=(process.env.AGENT_DEVICE_PROXY_DAEMON_BASE_URL??"").trim(),v=(process.env.AGENT_DEVICE_PROXY_DAEMON_AUTH_TOKEN??"").trim(),y=l("AGENT_DEVICE_PROXY_MAX_BODY_BYTES",1048576,{min:1024,max:0x1000000}),E=l("AGENT_PROXY_ARTIFACT_MAX_BYTES",0x20000000,{min:1048576,max:0xffffffff}),T=l("AGENT_DEVICE_PROXY_EXEC_TIMEOUT_MS",24e4,{min:1e3,max:36e5}),S=l("AGENT_PROXY_COMMAND_MAX_OUTPUT_BYTES",1048576,{min:4096,max:0x4000000}),D=l("AGENT_PROXY_ARTIFACT_UNZIP_TIMEOUT_MS",18e4,{min:1e3,max:18e5}),I=l("AGENT_PROXY_ARTIFACT_TTL_HOURS",24,{min:1,max:720}),B=l("AGENT_PROXY_ARTIFACT_MAX_TOTAL_BYTES",0x140000000,{min:1048576,max:0x1900000000}),M=function(t,e){let r=process.env[t];if(void 0===r||""===r)return e;let i=r.trim().toLowerCase();if("1"===i||"true"===i||"yes"===i||"on"===i)return!0;if("0"===i||"false"===i||"no"===i||"off"===i)return!1;throw Error(`${t} must be a boolean-like value`)}("AGENT_PROXY_STRICT_COMMAND_STATUS",!0),k=(process.env.AGENT_DEVICE_PROXY_BEARER_TOKEN??"").trim(),X=(process.env.AGENT_DEVICE_PROXY_COMMAND??"agent-device").trim()||"agent-device",V=e.resolve(process.env.AGENT_PROXY_ROOT_DIR??process.cwd()),F=function(t){let r=(t??"").trim();if(!r)return"";if(!e.isAbsolute(r))throw Error("AGENT_DEVICE_PROXY_WORKSPACE_ROOT must be absolute path");return e.resolve(r)}(process.env.AGENT_DEVICE_PROXY_WORKSPACE_ROOT??""),J=e.resolve(process.env.AGENT_PROXY_ARTIFACTS_DIR??e.join(V,".agent-device-proxy-artifacts")),{host:s,port:u,daemonBaseUrl:d,daemonAuthToken:v,maxJsonBodyBytes:y,maxArtifactBytes:E,commandTimeoutMs:T,commandMaxOutputBytes:S,unzipTimeoutMs:D,artifactTtlHours:I,artifactMaxTotalBytes:B,strictCommandStatus:M,bearerToken:k,command:X,rootDir:V,workspaceRoot:F,artifactsDir:J,baseCommandEnv:{PATH:function(t,r){let i=[e.join(r,"Library","Android","sdk","platform-tools"),e.join(r,"Library","Android","sdk","emulator"),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin"],a=new Set((t||"").split(e.delimiter).filter(Boolean));for(let t of i)o(t)&&a.add(t);return Array.from(a).join(e.delimiter)}(process.env.PATH||"",process.env.HOME||process.env.USERPROFILE||""),HOME:process.env.HOME||"",USER:process.env.USER||"",TMPDIR:process.env.TMPDIR||"/tmp",LANG:process.env.LANG||"en_US.UTF-8",LC_ALL:process.env.LC_ALL||"",LC_CTYPE:process.env.LC_CTYPE||"",SHELL:process.env.SHELL||"",TERM:process.env.TERM||"",ANDROID_HOME:process.env.ANDROID_HOME||"",ANDROID_SDK_ROOT:process.env.ANDROID_SDK_ROOT||"",JAVA_HOME:process.env.JAVA_HOME||"",XDG_RUNTIME_DIR:process.env.XDG_RUNTIME_DIR||""}});r(W.artifactsDir,{recursive:!0});let K=function(t,{runCommand:r}){let n=null,s=0;async function u(){let e=Date.now();if(n)return await n;e-s<6e4||(n=w(t).finally(()=>{s=Date.now(),n=null}),await n)}return{storeArtifactFromRequest:async function({req:n,headers:o}){let s;await u();let d=a(),l=e.join(t.artifactsDir,d),w=e.join(l,o.filename);await i.mkdir(l,{recursive:!0});try{s=await f(n,w,t.maxArtifactBytes)}catch(t){throw await g(l),t}if(o.expected_sha256&&o.expected_sha256!==s.sha256)throw await g(l),new c(400,"invalid_request","artifact sha256 mismatch");let v=w,y=!1,E=s.sizeBytes;if("app"===o.artifact_type){let a=e.join(l,"unpacked");await i.mkdir(a,{recursive:!0}),await _({archivePath:w,timeoutMs:t.unzipTimeoutMs,runCommand:r,env:t.baseCommandEnv});let n=await p({archivePath:w,timeoutMs:t.unzipTimeoutMs,runCommand:r,env:t.baseCommandEnv,maxOutputBytes:t.commandMaxOutputBytes});if(n>t.maxArtifactBytes)throw await g(l),new c(413,"invalid_request","unzipped app archive too large",{uncompressed_bytes:n,max_bytes:t.maxArtifactBytes});let o=await r({command:"unzip",argv:["-q",w,"-d",a],cwd:l,env:t.baseCommandEnv,timeoutMs:t.unzipTimeoutMs,maxOutputBytes:t.commandMaxOutputBytes});if(0!==o.exitCode)throw await g(l),new c(400,"invalid_request","failed to unzip app archive",{exit_code:o.exitCode,stdout:o.stdout,stderr:o.stderr});let u=await m(a);if(!u)throw await g(l),new c(400,"invalid_request","zip archive does not contain a .app bundle");let d=await h(a);if(d>t.maxArtifactBytes)throw await g(l),new c(413,"invalid_request","unzipped app archive too large",{uncompressed_bytes:d,max_bytes:t.maxArtifactBytes});E=s.sizeBytes+d,v=u,y=!0}let b={artifact_id:d,artifact_type:o.artifact_type,archive:o.archive,filename:o.filename,size_bytes:E,sha256:s.sha256,created_at:new Date().toISOString(),raw_path:w,ready_path:v,extracted:y};return await i.writeFile(e.join(l,"metadata.json"),JSON.stringify(b,null,2),"utf8"),b},readArtifactMetadata:async function(r){let a,n=String(r||"").trim();if(!/^[a-z0-9-]{8,128}$/i.test(n))throw new c(400,"invalid_request","artifact_id format is invalid");let s=e.join(t.artifactsDir,n,"metadata.json");if(!o(s))throw new c(404,"not_found","artifact_id not found");try{a=JSON.parse(await i.readFile(s,"utf8"))}catch{throw new c(500,"internal_error","artifact metadata is unreadable")}if(!a||"object"!=typeof a||!a.ready_path||!a.artifact_type)throw new c(500,"internal_error","artifact metadata is invalid");if(!o(a.ready_path))throw new c(404,"not_found","artifact file is missing");return a},maybeCleanupArtifacts:u}}(W,{runCommand:b});return{server:t.createServer(async(t,r)=>{var i,n;let s,u,d,l="string"==typeof(s=t.headers["x-request-id"])&&s.trim()?s.trim().slice(0,128):a(),{path:f,legacy:_}=(d=(u=new URL(t.url||"/","http://127.0.0.1")).pathname.startsWith("/v1/"),{path:O.get(u.pathname)||u.pathname,legacy:d});try{if("GET"===t.method&&"/agent-device/health"===f)return void await H({req:t,res:r,requestId:l,config:W,endpoint:"health"});if("GET"===t.method&&("/api/health"===f||"/healthz"===f)){let t={status:"ok",service:"agent-device-proxy",checked_at:new Date().toISOString()};if("/healthz"===f){r.statusCode=200,r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("x-request-id",l),r.end(JSON.stringify(t));return}x(r,200,t,l,_);return}if("POST"!==t.method)return void R(r,404,"not_found","not found",l,_);if((i=W.bearerToken)&&(t.headers.authorization||"")!==`Bearer ${i}`)return void R(r,401,"unauthorized","unauthorized",l,_);if("/agent-device/rpc"===f)return void await H({req:t,res:r,requestId:l,config:W,endpoint:"rpc"});if("/api/artifacts/upload"===f){let e=function(t){let e=L(U(t,"x-artifact-type"),"x-artifact-type",["app","apk"]),r=L(U(t,"x-artifact-archive"),"x-artifact-archive",["zip","raw"]),i=(U(t,"x-artifact-filename")||`${e}-${Date.now()}${"zip"===r?".zip":".bin"}`).replace(/[^a-zA-Z0-9._-]/g,"_").slice(-160)||`artifact-${Date.now()}`,a=U(t,"x-artifact-sha256").toLowerCase();if(a&&!/^[a-f0-9]{64}$/.test(a))throw new c(400,"invalid_request","x-artifact-sha256 must be a 64-char lowercase hex sha256");if("app"===e&&"zip"!==r)throw new c(400,"invalid_request","x-artifact-type=app requires x-artifact-archive=zip");if("apk"===e&&"raw"!==r)throw new c(400,"invalid_request","x-artifact-type=apk requires x-artifact-archive=raw");return{artifact_type:e,archive:r,filename:i,expected_sha256:a}}(t.headers),i=await K.storeArtifactFromRequest({req:t,headers:e});x(r,200,i,l,_);return}if("/api/metro/resolve"===f){let e=await N(t,W.maxJsonBodyBytes),i=C(e?.ios_runtime??e),a=q(i);if(!a)throw new c(400,"invalid_metro_runtime","ios_runtime metro hints are required (metro_bundle_url or metro_host+metro_port)");x(r,200,a,l,_);return}if("/api/metro/probe"===f){let e=await N(t,W.maxJsonBodyBytes),i=C(e?.ios_runtime??e),a=q(i);if(!a)throw new c(400,"invalid_metro_runtime","ios_runtime metro hints are required (metro_bundle_url or metro_host+metro_port)");let n=function(t){if(null==t||""===t)return 2500;let e=Number.parseInt(String(t),10);return!Number.isInteger(e)||e<100||e>6e4?2500:e}(e?.timeout_ms),o=await $({statusUrl:a.status_url,timeoutMs:n});x(r,200,{...a,...o},l,_);return}if("/api/agent-device/exec"===f){let i,s=await N(t,W.maxJsonBodyBytes),u=function(t){if(!P(t))throw new c(400,"invalid_request","body must be valid JSON object");if(!Array.isArray(t.argv)||t.argv.some(t=>"string"!=typeof t))throw new c(400,"invalid_request","argv must be an array of strings");if(0===t.argv.length)throw new c(400,"invalid_request","argv must contain at least one argument");let e={argv:t.argv,cwd:j(t.cwd,"cwd"),run_id:j(t.run_id,"run_id"),ios_session_id:j(t.ios_session_id,"ios_session_id"),tenant_id:j(t.tenant_id,"tenant_id"),ios_runtime:function(t,e){if(null==t||""===t)return null;if(!t||"object"!=typeof t||Array.isArray(t))throw new c(400,"invalid_request",`${e} must be an object`);return t}(t.ios_runtime,"ios_runtime")};if(e.tenant_id&&!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(e.tenant_id))throw new c(400,"invalid_request","tenant_id must match ^[a-z0-9][a-z0-9_-]{0,63}$");return e}(s),d={...u,ios_runtime:C(u.ios_runtime)},f=await b({command:W.command,argv:d.argv,cwd:function(t,r){if(!t)return r.rootDir;if(!e.isAbsolute(t))throw new c(400,"invalid_request","cwd must be absolute path");let i=e.resolve(t);if(r.workspaceRoot&&i!==r.workspaceRoot&&!i.startsWith(`${r.workspaceRoot}${e.sep}`))throw new c(400,"invalid_request","cwd is outside configured workspace root");if(!o(i))throw new c(400,"invalid_request","cwd does not exist");return i}(d.cwd,W),env:(n=W.baseCommandEnv,i={...n,AGENT_PROXY_REQUEST_ID:a()},d.tenant_id&&(i.AGENT_DEVICE_PROXY_TENANT_ID=d.tenant_id),d.run_id&&(i.AGENT_DEVICE_PROXY_RUN_ID=d.run_id),d.ios_session_id&&(i.AGENT_DEVICE_PROXY_IOS_SESSION_ID=d.ios_session_id),d.ios_runtime&&(d.ios_runtime.launch_url&&(i.AGENT_DEVICE_PROXY_IOS_LAUNCH_URL=d.ios_runtime.launch_url),d.ios_runtime.metro_bundle_url&&(i.AGENT_DEVICE_PROXY_METRO_BUNDLE_URL=d.ios_runtime.metro_bundle_url),d.ios_runtime.metro_host&&(i.AGENT_DEVICE_PROXY_METRO_HOST=d.ios_runtime.metro_host),d.ios_runtime.metro_port&&(i.AGENT_DEVICE_PROXY_METRO_PORT=String(d.ios_runtime.metro_port))),i),timeoutMs:W.commandTimeoutMs,maxOutputBytes:W.commandMaxOutputBytes});A({result:f,action:`agent-device exec (${d.argv[0]||"command"})`,strictCommandStatus:W.strictCommandStatus}),x(r,200,{exit_code:f.exitCode,stdout:f.stdout,stderr:f.stderr,timed_out:f.timedOut},l,_);return}if("/api/agent-device/install"===f){let e=await N(t,W.maxJsonBodyBytes),i=function(t){if(!P(t))throw new c(400,"invalid_request","body must be valid JSON object");let e=z(t.app,"app"),r=z(t.artifact_id,"artifact_id"),i=L(t.platform,"platform",["ios","android","apple"]),a=j(t.device,"device"),n=j(t.session,"session"),o=j(t.udid,"udid"),s=j(t.serial,"serial"),u=G(t.reinstall,"reinstall",!1);return{app:e,artifact_id:r,platform:i,device:a,session:n,udid:o,serial:s,reinstall:u,json:G(t.json,"json",!0)}}(e),n=await K.readArtifactMetadata(i.artifact_id),o=function(t,e){if("android"===t.platform&&"apk"!==e.artifact_type)throw new c(400,"invalid_request","android install requires artifact_type=apk");if(("ios"===t.platform||"apple"===t.platform)&&"app"!==e.artifact_type)throw new c(400,"invalid_request","ios install requires artifact_type=app");let r=[t.reinstall?"reinstall":"install",t.app,e.ready_path,"--platform",t.platform];return t.device&&r.push("--device",t.device),t.session&&r.push("--session",t.session),t.udid&&r.push("--udid",t.udid),t.serial&&r.push("--serial",t.serial),t.json&&r.push("--json"),r}(i,n),s=await b({command:W.command,argv:o,cwd:W.rootDir,env:{...W.baseCommandEnv,AGENT_PROXY_REQUEST_ID:a(),AGENT_PROXY_ARTIFACT_ID:i.artifact_id},timeoutMs:W.commandTimeoutMs,maxOutputBytes:W.commandMaxOutputBytes});A({result:s,action:"agent-device install",strictCommandStatus:W.strictCommandStatus}),x(r,200,{artifact_id:i.artifact_id,artifact_type:n.artifact_type,artifact_path:n.ready_path,exit_code:s.exitCode,stdout:s.stdout,stderr:s.stderr,output:function(t){try{return JSON.parse(t)}catch{return null}}(s.stdout)},l,_);return}R(r,404,"not_found","not found",l,_)}catch(e){let t;R(r,(t=function(t,e="internal error"){return t instanceof c?t:new c(500,"internal_error",t instanceof Error?t.message:e)}(e)).statusCode,t.code,t.message,l,_,t.details)}}),config:W,artifactStore:K}}async function H(t){var e,r;let{req:i,res:a,requestId:n,config:o,endpoint:s}=t;if(!o.daemonBaseUrl)throw new c(503,"daemon_unavailable","AGENT_DEVICE_PROXY_DAEMON_BASE_URL is required for daemon forwarding");let u=new URL(s,`${o.daemonBaseUrl.replace(/\/+$/,"")}/`),d=new Headers,l=i.headers["content-type"];"string"==typeof l&&l.trim()&&d.set("content-type",l);let f=o.daemonAuthToken||("string"!=typeof(e=i.headers.authorization)?"":e.startsWith("Bearer ")?e.slice(7):"")||("string"==typeof(r=i.headers["x-agent-device-token"])?r:"");f&&(d.set("authorization",`Bearer ${f}`),d.set("x-agent-device-token",f));let _="rpc"===s?new Uint8Array(await D(i,o.maxJsonBodyBytes)):void 0,p=await fetch(u,{method:i.method||("health"===s?"GET":"POST"),headers:d,..._?{body:_}:{}}),m=await p.text(),h=p.headers.get("content-type");a.statusCode=p.status,h?a.setHeader("content-type",h):a.setHeader("content-type","application/json; charset=utf-8"),a.setHeader("x-request-id",n),a.end(m)}export{k as createagentDeviceProxyServer,X as startAgentDeviceProxyServer};
|
|
18
|
+
`)}),e}function Y(n={}){let s,u,d,v,y,E,T,D,S,$,B,M,k,X,H,F,J,W=n.config||(s=process.env.AGENT_DEVICE_PROXY_HOST??"127.0.0.1",u=l("AGENT_DEVICE_PROXY_PORT",9123,{min:1,max:65535}),d=(process.env.AGENT_DEVICE_PROXY_DAEMON_BASE_URL??"").trim(),v=(process.env.AGENT_DEVICE_PROXY_DAEMON_AUTH_TOKEN??"").trim(),y=l("AGENT_DEVICE_PROXY_MAX_BODY_BYTES",1048576,{min:1024,max:0x1000000}),E=l("AGENT_DEVICE_PROXY_ARTIFACT_MAX_BYTES",0x20000000,{min:1048576,max:0xffffffff}),T=l("AGENT_DEVICE_PROXY_EXEC_TIMEOUT_MS",24e4,{min:1e3,max:36e5}),D=l("AGENT_DEVICE_PROXY_COMMAND_MAX_OUTPUT_BYTES",1048576,{min:4096,max:0x4000000}),S=l("AGENT_DEVICE_PROXY_ARTIFACT_UNZIP_TIMEOUT_MS",18e4,{min:1e3,max:18e5}),$=l("AGENT_DEVICE_PROXY_ARTIFACT_TTL_HOURS",24,{min:1,max:720}),B=l("AGENT_DEVICE_PROXY_ARTIFACT_MAX_TOTAL_BYTES",0x140000000,{min:1048576,max:0x1900000000}),M=function(t,e){let r=process.env[t];if(void 0===r||""===r)return e;let i=r.trim().toLowerCase();if("1"===i||"true"===i||"yes"===i||"on"===i)return!0;if("0"===i||"false"===i||"no"===i||"off"===i)return!1;throw Error(`${t} must be a boolean-like value`)}("AGENT_DEVICE_PROXY_STRICT_COMMAND_STATUS",!0),k=(process.env.AGENT_DEVICE_PROXY_BEARER_TOKEN??"").trim(),X=(process.env.AGENT_DEVICE_PROXY_COMMAND??"agent-device").trim()||"agent-device",H=e.resolve(process.env.AGENT_DEVICE_PROXY_ROOT_DIR??process.cwd()),F=function(t){let r=(t??"").trim();if(!r)return"";if(!e.isAbsolute(r))throw Error("AGENT_DEVICE_PROXY_WORKSPACE_ROOT must be absolute path");return e.resolve(r)}(process.env.AGENT_DEVICE_PROXY_WORKSPACE_ROOT??""),J=e.resolve(process.env.AGENT_DEVICE_PROXY_ARTIFACTS_DIR??e.join(H,".agent-device-proxy-artifacts")),{host:s,port:u,daemonBaseUrl:d,daemonAuthToken:v,maxJsonBodyBytes:y,maxArtifactBytes:E,commandTimeoutMs:T,commandMaxOutputBytes:D,unzipTimeoutMs:S,artifactTtlHours:$,artifactMaxTotalBytes:B,strictCommandStatus:M,bearerToken:k,command:X,rootDir:H,workspaceRoot:F,artifactsDir:J,baseCommandEnv:{PATH:function(t,r){let i=[e.join(r,"Library","Android","sdk","platform-tools"),e.join(r,"Library","Android","sdk","emulator"),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin"],a=new Set((t||"").split(e.delimiter).filter(Boolean));for(let t of i)o(t)&&a.add(t);return Array.from(a).join(e.delimiter)}(process.env.PATH||"",process.env.HOME||process.env.USERPROFILE||""),HOME:process.env.HOME||"",USER:process.env.USER||"",TMPDIR:process.env.TMPDIR||"/tmp",LANG:process.env.LANG||"en_US.UTF-8",LC_ALL:process.env.LC_ALL||"",LC_CTYPE:process.env.LC_CTYPE||"",SHELL:process.env.SHELL||"",TERM:process.env.TERM||"",ANDROID_HOME:process.env.ANDROID_HOME||"",ANDROID_SDK_ROOT:process.env.ANDROID_SDK_ROOT||"",JAVA_HOME:process.env.JAVA_HOME||"",XDG_RUNTIME_DIR:process.env.XDG_RUNTIME_DIR||""}});r(W.artifactsDir,{recursive:!0});let K=function(t,{runCommand:r}){let n=null,s=0;async function u(){let e=Date.now();if(n)return await n;e-s<6e4||(n=w(t).finally(()=>{s=Date.now(),n=null}),await n)}return{storeArtifactFromRequest:async function({req:n,headers:o}){let s;await u();let d=a(),l=e.join(t.artifactsDir,d),w=e.join(l,o.filename);await i.mkdir(l,{recursive:!0});try{s=await f(n,w,t.maxArtifactBytes)}catch(t){throw await g(l),t}if(o.expected_sha256&&o.expected_sha256!==s.sha256)throw await g(l),new c(400,"invalid_request","artifact sha256 mismatch");let v=w,y=!1,E=s.sizeBytes;if("app"===o.artifact_type){let a=e.join(l,"unpacked");await i.mkdir(a,{recursive:!0}),await _({archivePath:w,timeoutMs:t.unzipTimeoutMs,runCommand:r,env:t.baseCommandEnv});let n=await p({archivePath:w,timeoutMs:t.unzipTimeoutMs,runCommand:r,env:t.baseCommandEnv,maxOutputBytes:t.commandMaxOutputBytes});if(n>t.maxArtifactBytes)throw await g(l),new c(413,"invalid_request","unzipped app archive too large",{uncompressed_bytes:n,max_bytes:t.maxArtifactBytes});let o=await r({command:"unzip",argv:["-q",w,"-d",a],cwd:l,env:t.baseCommandEnv,timeoutMs:t.unzipTimeoutMs,maxOutputBytes:t.commandMaxOutputBytes});if(0!==o.exitCode)throw await g(l),new c(400,"invalid_request","failed to unzip app archive",{exit_code:o.exitCode,stdout:o.stdout,stderr:o.stderr});let u=await m(a);if(!u)throw await g(l),new c(400,"invalid_request","zip archive does not contain a .app bundle");let d=await h(a);if(d>t.maxArtifactBytes)throw await g(l),new c(413,"invalid_request","unzipped app archive too large",{uncompressed_bytes:d,max_bytes:t.maxArtifactBytes});E=s.sizeBytes+d,v=u,y=!0}let b={artifact_id:d,artifact_type:o.artifact_type,archive:o.archive,filename:o.filename,size_bytes:E,sha256:s.sha256,created_at:new Date().toISOString(),raw_path:w,ready_path:v,extracted:y};return await i.writeFile(e.join(l,"metadata.json"),JSON.stringify(b,null,2),"utf8"),b},readArtifactMetadata:async function(r){let a,n=String(r||"").trim();if(!/^[a-z0-9-]{8,128}$/i.test(n))throw new c(400,"invalid_request","artifact_id format is invalid");let s=e.join(t.artifactsDir,n,"metadata.json");if(!o(s))throw new c(404,"not_found","artifact_id not found");try{a=JSON.parse(await i.readFile(s,"utf8"))}catch{throw new c(500,"internal_error","artifact metadata is unreadable")}if(!a||"object"!=typeof a||!a.ready_path||!a.artifact_type)throw new c(500,"internal_error","artifact metadata is invalid");if(!o(a.ready_path))throw new c(404,"not_found","artifact file is missing");return a},maybeCleanupArtifacts:u}}(W,{runCommand:b});return{server:t.createServer(async(t,r)=>{var i,n;let s,u,d,l="string"==typeof(s=t.headers["x-request-id"])&&s.trim()?s.trim().slice(0,128):a(),{path:f,legacy:_}=(d=(u=new URL(t.url||"/","http://127.0.0.1")).pathname.startsWith("/v1/"),{path:O.get(u.pathname)||u.pathname,legacy:d});try{if("GET"===t.method&&"/agent-device/health"===f)return void await V({req:t,res:r,requestId:l,config:W,endpoint:"health"});if("GET"===t.method&&("/api/health"===f||"/healthz"===f)){let t={status:"ok",service:"agent-device-proxy",checked_at:new Date().toISOString()};if("/healthz"===f){r.statusCode=200,r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("x-request-id",l),r.end(JSON.stringify(t));return}x(r,200,t,l,_);return}if("POST"!==t.method)return void R(r,404,"not_found","not found",l,_);if((i=W.bearerToken)&&(t.headers.authorization||"")!==`Bearer ${i}`)return void R(r,401,"unauthorized","unauthorized",l,_);if("/agent-device/rpc"===f)return void await V({req:t,res:r,requestId:l,config:W,endpoint:"rpc"});if("/api/artifacts/upload"===f){let e=function(t){let e=L(U(t,"x-artifact-type"),"x-artifact-type",["app","apk"]),r=L(U(t,"x-artifact-archive"),"x-artifact-archive",["zip","raw"]),i=(U(t,"x-artifact-filename")||`${e}-${Date.now()}${"zip"===r?".zip":".bin"}`).replace(/[^a-zA-Z0-9._-]/g,"_").slice(-160)||`artifact-${Date.now()}`,a=U(t,"x-artifact-sha256").toLowerCase();if(a&&!/^[a-f0-9]{64}$/.test(a))throw new c(400,"invalid_request","x-artifact-sha256 must be a 64-char lowercase hex sha256");if("app"===e&&"zip"!==r)throw new c(400,"invalid_request","x-artifact-type=app requires x-artifact-archive=zip");if("apk"===e&&"raw"!==r)throw new c(400,"invalid_request","x-artifact-type=apk requires x-artifact-archive=raw");return{artifact_type:e,archive:r,filename:i,expected_sha256:a}}(t.headers),i=await K.storeArtifactFromRequest({req:t,headers:e});x(r,200,i,l,_);return}if("/api/metro/resolve"===f){let e=await C(t,W.maxJsonBodyBytes),i=N(e?.ios_runtime??e),a=I(i);if(!a)throw new c(400,"invalid_metro_runtime","ios_runtime metro hints are required (metro_bundle_url or metro_host+metro_port)");x(r,200,a,l,_);return}if("/api/metro/probe"===f){let e=await C(t,W.maxJsonBodyBytes),i=N(e?.ios_runtime??e),a=I(i);if(!a)throw new c(400,"invalid_metro_runtime","ios_runtime metro hints are required (metro_bundle_url or metro_host+metro_port)");let n=function(t){if(null==t||""===t)return 2500;let e=Number.parseInt(String(t),10);return!Number.isInteger(e)||e<100||e>6e4?2500:e}(e?.timeout_ms),o=await q({statusUrl:a.status_url,timeoutMs:n});x(r,200,{...a,...o},l,_);return}if("/api/agent-device/exec"===f){let i,s=await C(t,W.maxJsonBodyBytes),u=function(t){if(!P(t))throw new c(400,"invalid_request","body must be valid JSON object");if(!Array.isArray(t.argv)||t.argv.some(t=>"string"!=typeof t))throw new c(400,"invalid_request","argv must be an array of strings");if(0===t.argv.length)throw new c(400,"invalid_request","argv must contain at least one argument");let e={argv:t.argv,cwd:j(t.cwd,"cwd"),run_id:j(t.run_id,"run_id"),ios_session_id:j(t.ios_session_id,"ios_session_id"),tenant_id:j(t.tenant_id,"tenant_id"),ios_runtime:function(t,e){if(null==t||""===t)return null;if(!t||"object"!=typeof t||Array.isArray(t))throw new c(400,"invalid_request",`${e} must be an object`);return t}(t.ios_runtime,"ios_runtime")};if(e.tenant_id&&!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(e.tenant_id))throw new c(400,"invalid_request","tenant_id must match ^[a-z0-9][a-z0-9_-]{0,63}$");return e}(s),d={...u,ios_runtime:N(u.ios_runtime)},f=await b({command:W.command,argv:d.argv,cwd:function(t,r){if(!t)return r.rootDir;if(!e.isAbsolute(t))throw new c(400,"invalid_request","cwd must be absolute path");let i=e.resolve(t);if(r.workspaceRoot&&i!==r.workspaceRoot&&!i.startsWith(`${r.workspaceRoot}${e.sep}`))throw new c(400,"invalid_request","cwd is outside configured workspace root");if(!o(i))throw new c(400,"invalid_request","cwd does not exist");return i}(d.cwd,W),env:(n=W.baseCommandEnv,i={...n,AGENT_DEVICE_PROXY_REQUEST_ID:a()},d.tenant_id&&(i.AGENT_DEVICE_PROXY_TENANT_ID=d.tenant_id),d.run_id&&(i.AGENT_DEVICE_PROXY_RUN_ID=d.run_id),d.ios_session_id&&(i.AGENT_DEVICE_PROXY_IOS_SESSION_ID=d.ios_session_id),d.ios_runtime&&(d.ios_runtime.launch_url&&(i.AGENT_DEVICE_PROXY_IOS_LAUNCH_URL=d.ios_runtime.launch_url),d.ios_runtime.metro_bundle_url&&(i.AGENT_DEVICE_PROXY_METRO_BUNDLE_URL=d.ios_runtime.metro_bundle_url),d.ios_runtime.metro_host&&(i.AGENT_DEVICE_PROXY_METRO_HOST=d.ios_runtime.metro_host),d.ios_runtime.metro_port&&(i.AGENT_DEVICE_PROXY_METRO_PORT=String(d.ios_runtime.metro_port))),i),timeoutMs:W.commandTimeoutMs,maxOutputBytes:W.commandMaxOutputBytes});A({result:f,action:`agent-device exec (${d.argv[0]||"command"})`,strictCommandStatus:W.strictCommandStatus}),x(r,200,{exit_code:f.exitCode,stdout:f.stdout,stderr:f.stderr,timed_out:f.timedOut},l,_);return}if("/api/agent-device/install"===f){let e=await C(t,W.maxJsonBodyBytes),i=function(t){if(!P(t))throw new c(400,"invalid_request","body must be valid JSON object");let e=z(t.app,"app"),r=z(t.artifact_id,"artifact_id"),i=L(t.platform,"platform",["ios","android","apple"]),a=j(t.device,"device"),n=j(t.session,"session"),o=j(t.udid,"udid"),s=j(t.serial,"serial"),u=G(t.reinstall,"reinstall",!1);return{app:e,artifact_id:r,platform:i,device:a,session:n,udid:o,serial:s,reinstall:u,json:G(t.json,"json",!0)}}(e),n=await K.readArtifactMetadata(i.artifact_id),o=function(t,e){if("android"===t.platform&&"apk"!==e.artifact_type)throw new c(400,"invalid_request","android install requires artifact_type=apk");if(("ios"===t.platform||"apple"===t.platform)&&"app"!==e.artifact_type)throw new c(400,"invalid_request","ios install requires artifact_type=app");let r=[t.reinstall?"reinstall":"install",t.app,e.ready_path,"--platform",t.platform];return t.device&&r.push("--device",t.device),t.session&&r.push("--session",t.session),t.udid&&r.push("--udid",t.udid),t.serial&&r.push("--serial",t.serial),t.json&&r.push("--json"),r}(i,n),s=await b({command:W.command,argv:o,cwd:W.rootDir,env:{...W.baseCommandEnv,AGENT_DEVICE_PROXY_REQUEST_ID:a(),AGENT_DEVICE_PROXY_ARTIFACT_ID:i.artifact_id},timeoutMs:W.commandTimeoutMs,maxOutputBytes:W.commandMaxOutputBytes});A({result:s,action:"agent-device install",strictCommandStatus:W.strictCommandStatus}),x(r,200,{artifact_id:i.artifact_id,artifact_type:n.artifact_type,artifact_path:n.ready_path,exit_code:s.exitCode,stdout:s.stdout,stderr:s.stderr,output:function(t){try{return JSON.parse(t)}catch{return null}}(s.stdout)},l,_);return}R(r,404,"not_found","not found",l,_)}catch(e){let t;R(r,(t=function(t,e="internal error"){return t instanceof c?t:new c(500,"internal_error",t instanceof Error?t.message:e)}(e)).statusCode,t.code,t.message,l,_,t.details)}}),config:W,artifactStore:K}}async function V(t){var e,r;let{req:i,res:a,requestId:n,config:o,endpoint:s}=t;if(!o.daemonBaseUrl)throw new c(503,"daemon_unavailable","AGENT_DEVICE_PROXY_DAEMON_BASE_URL is required for daemon forwarding");let u=new URL(s,`${o.daemonBaseUrl.replace(/\/+$/,"")}/`),d=new Headers,l=i.headers["content-type"];"string"==typeof l&&l.trim()&&d.set("content-type",l);let f=o.daemonAuthToken||("string"!=typeof(e=i.headers.authorization)?"":e.startsWith("Bearer ")?e.slice(7):"")||("string"==typeof(r=i.headers["x-agent-device-token"])?r:"");f&&(d.set("authorization",`Bearer ${f}`),d.set("x-agent-device-token",f));let _="rpc"===s?new Uint8Array(await S(i,o.maxJsonBodyBytes)):void 0,p=await fetch(u,{method:i.method||("health"===s?"GET":"POST"),headers:d,..._?{body:_}:{}}),m=await p.text(),h=p.headers.get("content-type");a.statusCode=p.status,h?a.setHeader("content-type",h):a.setHeader("content-type","application/json; charset=utf-8"),a.setHeader("x-request-id",n),a.end(m)}export{k as createagentDeviceProxyServer,X as startAgentDeviceProxyServer};
|
package/dist/src/bin.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import{startAgentDeviceProxyServer as _}from"./36.js";_();
|
package/dist/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{createAgentDeviceProxyClient}from"./224.js";export{createagentDeviceProxyServer,startAgentDeviceProxyServer}from"./36.js";export{ensureMetroRuntime}from"./403.js";
|
|
1
|
+
export{createAgentDeviceProxyClient}from"./224.js";export{createagentDeviceProxyServer,startAgentDeviceProxyServer}from"./36.js";export{ensureMetroRuntime}from"./403.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-device-proxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -22,8 +22,7 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"bin": {
|
|
25
|
-
"agent-device-proxy": "./dist/src/bin.js"
|
|
26
|
-
"agent-device": "./dist/src/agent-device.js"
|
|
25
|
+
"agent-device-proxy": "./dist/src/bin.js"
|
|
27
26
|
},
|
|
28
27
|
"scripts": {
|
|
29
28
|
"build": "rslib build",
|
package/dist/src/111.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ensureMetroRuntime as t}from"./403.js";let e=new Set(["open","reopen","reload","push","trigger-app-event"]);async function r(r){if(!Array.isArray(r.argv)||0===r.argv.length)throw Error("argv must contain at least one agent-device command");let n=r.argv[0];if("install"===n||"reinstall"===n)return await i(r,"reinstall"===n);let l=null;try{var u;return function(t,r){if(!r)return!1;let i=function(t,e){let r=t.indexOf(e);if(-1===r)return;let i=t[r+1];if(!(!i||i.startsWith("--")))return i}(t,"--platform");return("ios"===i||"apple"===i)&&e.has(t[0]||"")}(r.argv,r.metro)&&(l=await t(r.metro)),u=await r.client.agentDeviceExec({argv:r.argv,...r.cwd?{cwd:r.cwd}:{},...l?{ios_runtime:l.ios_runtime}:{}}),o(u)?{exitCode:a(u.exit_code)??0,stdout:s(u.stdout)??JSON.stringify(u),stderr:s(u.stderr)??"",output:u}:{exitCode:0,stdout:JSON.stringify(u),stderr:"",output:u}}finally{await l?.stop()}}async function i(t,e){let r=function(t){let e=t.slice(1),r=[],i=new Map;for(let t=0;t<e.length;t+=1){let n=e[t];if(!n.startsWith("--")){r.push(n);continue}let a=n.slice(2),s=e[t+1];if(s&&!s.startsWith("--")){i.set(a,s),t+=1;continue}i.set(a,!0)}if(r.length<2)throw Error("agent-device install requires <app> and <path-to-binary>");let[a,s]=r,o={app:a,filePath:s,platform:function(t,e){let r=t.get(e);if("string"!=typeof r||!r.trim())throw Error(`agent-device install requires --${e}`);return r}(i,"platform")},l=n(i,"device"),u=n(i,"session"),f=n(i,"udid"),c=n(i,"serial");return l&&(o.device=l),u&&(o.session=u),f&&(o.udid=f),c&&(o.serial=c),!0===i.get("json")&&(o.json=!0),o}(t.argv),i=await t.client.installApp({...r,...e?{reinstall:!0}:{}}),l=o(i)&&o(i.install)?i.install:{},u=a(l.exit_code)??0;return{exitCode:u,stdout:s(l.stdout)??JSON.stringify(i),stderr:s(l.stderr)??"",output:i}}function n(t,e){let r=t.get(e);return"string"==typeof r&&r.trim()?r:void 0}function a(t){if("number"==typeof t&&Number.isInteger(t))return t}function s(t){return"string"==typeof t?t:void 0}function o(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}export{r as runAgentDeviceCommand};
|
package/dist/src/agent-device.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import{createAgentDeviceProxyClient as _}from"./224.js";import{runAgentDeviceCommand as e}from"./111.js";function r(_){let e=process.env[_];return e&&e.trim()?e.trim():void 0}function t(_){let e=r(_);if(!e)return;let t=Number.parseInt(e,10);return Number.isInteger(t)?t:void 0}function T(_){let e=r(_);if(!e)return;let t=e.toLowerCase();return!!["1","true","yes","on"].includes(t)||!["0","false","no","off"].includes(t)&&void 0}(async function(){var E;let s,O=_({baseUrl:function(_){let e=r(_);if(!e)throw Error(`${_} is required`);return e}("AGENT_PROXY_BASE_URL"),...r("AGENT_PROXY_BEARER_TOKEN")?{bearerToken:r("AGENT_PROXY_BEARER_TOKEN")}:{},...t("AGENT_PROXY_TIMEOUT_MS")?{timeoutMs:t("AGENT_PROXY_TIMEOUT_MS")}:{}}),o=await e({argv:process.argv.slice(2),client:O,cwd:process.cwd(),metro:(E=process.cwd(),(s=r("AGENT_PROXY_METRO_PROJECT_ROOT"))?{projectRoot:s||E,...t("AGENT_PROXY_METRO_PORT")?{port:t("AGENT_PROXY_METRO_PORT")}:{},...r("AGENT_PROXY_METRO_LISTEN_HOST")?{listenHost:r("AGENT_PROXY_METRO_LISTEN_HOST")}:{},...r("AGENT_PROXY_METRO_STATUS_HOST")?{statusHost:r("AGENT_PROXY_METRO_STATUS_HOST")}:{},...r("AGENT_PROXY_METRO_PUBLIC_HOST")?{publicHost:r("AGENT_PROXY_METRO_PUBLIC_HOST")}:{},...t("AGENT_PROXY_METRO_STARTUP_TIMEOUT_MS")?{startupTimeoutMs:t("AGENT_PROXY_METRO_STARTUP_TIMEOUT_MS")}:{},...t("AGENT_PROXY_METRO_PROBE_TIMEOUT_MS")?{probeTimeoutMs:t("AGENT_PROXY_METRO_PROBE_TIMEOUT_MS")}:{},...void 0!==T("AGENT_PROXY_METRO_REUSE_EXISTING")?{reuseExisting:T("AGENT_PROXY_METRO_REUSE_EXISTING")}:{}}:null)});o.stdout&&(process.stdout.write(o.stdout),o.stdout.endsWith("\n")||process.stdout.write("\n")),o.stderr&&(process.stderr.write(o.stderr),o.stderr.endsWith("\n")||process.stderr.write("\n")),process.exitCode=o.exitCode})().catch(_=>{process.stderr.write(`${_ instanceof Error?_.message:String(_)}
|
|
3
|
-
`),process.exitCode=1});
|