agent-device-proxy 0.1.0 → 0.1.1
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 +27 -26
- package/dist/src/36.js +2 -2
- package/dist/src/agent-device.js +1 -1
- package/dist/src/bin.js +1 -1
- package/package.json +1 -1
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,12 +147,12 @@ 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
158
|
## Proxy-backed `agent-device` contract
|
|
@@ -167,36 +170,34 @@ import {
|
|
|
167
170
|
createAgentDeviceProxyClient,
|
|
168
171
|
ensureMetroRuntime,
|
|
169
172
|
runAgentDeviceCommand,
|
|
170
|
-
} from "agent-device-proxy"
|
|
173
|
+
} from "agent-device-proxy"
|
|
171
174
|
```
|
|
172
175
|
|
|
173
176
|
`runAgentDeviceCommand()` translates `agent-device install` / `reinstall` into
|
|
174
177
|
artifact upload + remote install, and forwards the other commands through
|
|
175
|
-
`agentDeviceExec()`. If `
|
|
178
|
+
`agentDeviceExec()`. If `AGENT_DEVICE_PROXY_METRO_PROJECT_ROOT` is set, the `agent-device`
|
|
176
179
|
bin will automatically attach Metro hints for iOS commands that require them.
|
|
177
180
|
|
|
178
181
|
## Embedded server
|
|
179
182
|
|
|
180
183
|
```js
|
|
181
|
-
import { startAgentDeviceProxyServer } from "agent-device-proxy/server"
|
|
184
|
+
import { startAgentDeviceProxyServer } from "agent-device-proxy/server"
|
|
182
185
|
|
|
183
|
-
startAgentDeviceProxyServer()
|
|
186
|
+
startAgentDeviceProxyServer()
|
|
184
187
|
```
|
|
185
188
|
|
|
186
189
|
## Metro runtime helper (Linux/Vercel)
|
|
187
190
|
|
|
188
191
|
```js
|
|
189
|
-
import { ensureMetroRuntime } from "agent-device-proxy/metro-runtime"
|
|
192
|
+
import { ensureMetroRuntime } from "agent-device-proxy/metro-runtime"
|
|
190
193
|
|
|
191
194
|
const metro = await ensureMetroRuntime({
|
|
192
195
|
projectRoot: "/workspace/RNCLI83",
|
|
193
|
-
publicHost: "10.0.0.10"
|
|
194
|
-
})
|
|
196
|
+
publicHost: "10.0.0.10",
|
|
197
|
+
})
|
|
195
198
|
|
|
196
199
|
// use metro.ios_runtime when opening iOS app through agent-device-proxy
|
|
197
200
|
// await agentDeviceProxy.agentDeviceExec({ argv: [...], ios_runtime: metro.ios_runtime })
|
|
198
201
|
|
|
199
|
-
await metro.stop()
|
|
202
|
+
await metro.stop()
|
|
200
203
|
```
|
|
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/agent-device.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{createAgentDeviceProxyClient as _}from"./224.js";import{runAgentDeviceCommand as
|
|
2
|
+
import{createAgentDeviceProxyClient as _}from"./224.js";import{runAgentDeviceCommand as E}from"./111.js";function e(_){let E=process.env[_];return E&&E.trim()?E.trim():void 0}function r(_){let E=e(_);if(!E)return;let r=Number.parseInt(E,10);return Number.isInteger(r)?r:void 0}function t(_){let E=e(_);if(!E)return;let r=E.toLowerCase();return!!["1","true","yes","on"].includes(r)||!["0","false","no","off"].includes(r)&&void 0}(async function(){var T;let s,O=_({baseUrl:function(_){let E=e(_);if(!E)throw Error(`${_} is required`);return E}("AGENT_DEVICE_PROXY_BASE_URL"),...e("AGENT_DEVICE_PROXY_BEARER_TOKEN")?{bearerToken:e("AGENT_DEVICE_PROXY_BEARER_TOKEN")}:{},...r("AGENT_DEVICE_PROXY_TIMEOUT_MS")?{timeoutMs:r("AGENT_DEVICE_PROXY_TIMEOUT_MS")}:{}}),o=await E({argv:process.argv.slice(2),client:O,cwd:process.cwd(),metro:(T=process.cwd(),(s=e("AGENT_DEVICE_PROXY_METRO_PROJECT_ROOT"))?{projectRoot:s||T,...r("AGENT_DEVICE_PROXY_METRO_PORT")?{port:r("AGENT_DEVICE_PROXY_METRO_PORT")}:{},...e("AGENT_DEVICE_PROXY_METRO_LISTEN_HOST")?{listenHost:e("AGENT_DEVICE_PROXY_METRO_LISTEN_HOST")}:{},...e("AGENT_DEVICE_PROXY_METRO_STATUS_HOST")?{statusHost:e("AGENT_DEVICE_PROXY_METRO_STATUS_HOST")}:{},...e("AGENT_DEVICE_PROXY_METRO_PUBLIC_HOST")?{publicHost:e("AGENT_DEVICE_PROXY_METRO_PUBLIC_HOST")}:{},...r("AGENT_DEVICE_PROXY_METRO_STARTUP_TIMEOUT_MS")?{startupTimeoutMs:r("AGENT_DEVICE_PROXY_METRO_STARTUP_TIMEOUT_MS")}:{},...r("AGENT_DEVICE_PROXY_METRO_PROBE_TIMEOUT_MS")?{probeTimeoutMs:r("AGENT_DEVICE_PROXY_METRO_PROBE_TIMEOUT_MS")}:{},...void 0!==t("AGENT_DEVICE_PROXY_METRO_REUSE_EXISTING")?{reuseExisting:t("AGENT_DEVICE_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
3
|
`),process.exitCode=1});
|
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";_();
|