agent-device 0.18.2 → 0.18.3

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.
@@ -5,6 +5,9 @@ Version-matched operating guide for normal agent-device work.
5
5
  Core loop:
6
6
  devices/apps -> open -> snapshot or snapshot -i -> get/is/find/wait or press/fill/scroll/back -> verify -> close
7
7
 
8
+ Fresh machine or first iOS run:
9
+ Run agent-device doctor --platform ios first. Besides preflight checks it warms the iOS XCTest runner build cache in the background, so the first open skips the runner build (~10s). To block until fully warm instead, run agent-device prepare ios-runner.
10
+
8
11
  Command shape:
9
12
  Plans should use agent-device commands, not raw platform tools, pseudo commands, package-manager aliases, or helper prose.
10
13
  If the user asks for a command plan, final output should be command lines only: no intro sentence, numbered list, Markdown fence, shell pipe, grep/head/tail helper, or explanatory bullets.
@@ -207,6 +210,7 @@ Logs:
207
210
  Do not cat a full stale log into agent context. Open or grep only the relevant window when needed.
208
211
  logs clear --restart is the compact command to clear old logs and start a fresh capture; do not split it into logs stop, logs clear, logs start.
209
212
  On iOS simulators, logs scope by bundle id and resolved app executable, so use this instead of raw simctl log stream predicates.
213
+ On iOS physical devices, logs clear --restart relaunches the session app through devicectl process launch --console so stdout/stderr can be captured.
210
214
  For iOS simulator launch-time stdout/stderr, use --launch-console on the direct app launch:
211
215
  agent-device open MyApp --platform ios --relaunch --launch-console ./artifacts/app.console.log
212
216
  --launch-console is only for direct iOS simulator app launches, not URL opens.
@@ -425,7 +429,7 @@ Choose the next help topic:
425
429
  Remote/cloud config, leases, and local service tunnels: help remote.
426
430
 
427
431
  React Native dev loop:
428
- Before QA/dogfood runs, use doctor to separate environment setup from app failures:
432
+ Before QA/dogfood runs, use doctor to separate environment setup from app failures (on iOS simulators doctor also warms the runner build cache in the background):
429
433
  agent-device doctor --platform android
430
434
  agent-device doctor --platform ios
431
435
  agent-device doctor --platform android --app com.example.app
@@ -543,6 +547,7 @@ Direct proxy flow for a remote Mac/simulator:
543
547
  agent-device devices --platform ios
544
548
  agent-device open Maps --platform ios --device "iPhone 17 Pro"
545
549
  agent-device snapshot -i --platform ios --device "iPhone 17 Pro"
550
+ agent-device artifacts --json
546
551
  agent-device close
547
552
  agent-device disconnect
548
553
 
package/dist/src/cli.js CHANGED
@@ -21,7 +21,7 @@ Waiting for approval for 10 minutes...
21
21
  ${e}
22
22
  `)}}function rC(e,t){(e?.stderr??process.stderr).write(t)}function r$(e,t){if(!e)return!1;let r=Date.parse(e);return!Number.isFinite(r)||r<=(t?.()??Date.now())}function rN(e){return"string"==typeof e&&e.trim().length>0}async function rR(e){await new Promise(t=>setTimeout(t,e))}let rP=new Set(["daemonAuthToken","metroBearerToken"]);function rx(e){let r=function(e){try{return tL(e)}catch(r){let t=$(r);throw new R("COMMAND_FAILED",`${e.provider} connection profile returned invalid remote config.`,{generatedConfigPath:e.configPath,cause:t.message},t)}}({configPath:function(e){var r;let s=function e(t){return Array.isArray(t)?t.map(e):t&&"object"==typeof t?Object.fromEntries(Object.entries(t).filter(([,e])=>void 0!==e).sort(([e],[t])=>e.localeCompare(t)).map(([t,r])=>[t,e(r)])):t}(Object.fromEntries(Object.entries(e.profile).filter(([e])=>!rP.has(e)))),a=n.join(e.stateDir,"remote-connections","generated");t.mkdirSync(a,{recursive:!0,mode:448});let i=n.join(a,`${e.provider.replaceAll(/[^a-zA-Z0-9._-]/g,"_")||"generated"}-${(r=s,o.createHash("sha256").update(JSON.stringify(r)).digest("hex").slice(0,16))}.json`);t.writeFileSync(i,`${JSON.stringify(s,null,2)}
23
23
  `,{mode:384});try{t.chmodSync(i,384)}catch{}return i}({stateDir:e.stateDir,provider:e.provider,profile:e.profile}),cwd:e.cwd,env:e.env,provider:function(e){let[t="",...r]=e;return`${t.toUpperCase()}${r.join("")}`}(e.provider)});return{flags:{...tQ(r.profile),...e.flags,...e.extraFlags??{},remoteConfig:r.resolvedPath},remoteConfigPath:r.resolvedPath}}async function rE(e){var t;let r=await rh({stateDir:e.stateDir,flags:e.flags,env:e.env,io:{env:e.env,fetch:e.fetchImpl}}),n=await rT({cloudBaseUrl:r.cloudBaseUrl,accessToken:r.accessToken,fetchImpl:e.fetchImpl}),s=(t={stateDir:e.stateDir,cloudBaseUrl:r.cloudBaseUrl,daemonBaseUrl:"string"==typeof n.daemonBaseUrl?n.daemonBaseUrl:"",session:e.flags.session},o.createHash("sha256").update(`${t.stateDir}\0${t.cloudBaseUrl}\0${t.daemonBaseUrl}\0${t.session??""}`).digest("hex").slice(0,16));return rx({stateDir:e.stateDir,provider:"cloud",profile:{...n,leaseProvider:n.leaseProvider??"cloud",clientId:n.clientId??s,runId:n.runId??`cloud-${s}`},cwd:e.cwd,env:e.env,flags:e.flags,extraFlags:{daemonAuthToken:r.accessToken}})}async function rT(e){let t=e.fetchImpl??fetch,r=await t(new URL("/api/control-plane/connection-profile",e.cloudBaseUrl),{method:"GET",headers:{authorization:`Bearer ${e.accessToken}`},signal:AbortSignal.timeout(15e3)});var n=await rp(r,{invalidJsonMessage:`Cloud connection profile endpoint returned invalid JSON (${r.status}).`,rejectedMessage:"Cloud connection profile endpoint rejected the request."});if(!n||"object"!=typeof n||Array.isArray(n))throw new R("COMMAND_FAILED","Cloud connection profile response is invalid.");let o=n.connection;if(!o||"object"!=typeof o)throw new R("COMMAND_FAILED","Cloud connection profile response is missing profile.");if(void 0!==o.remoteConfigProfile){var s=o.remoteConfigProfile;if(!s||"object"!=typeof s||Array.isArray(s))throw new R("COMMAND_FAILED","Cloud connection profile remoteConfigProfile is invalid.");if(0===Object.keys(s).length)throw new R("COMMAND_FAILED","Cloud connection profile remoteConfigProfile is empty.");return s}throw new R("COMMAND_FAILED","Cloud connection profile did not include remoteConfigProfile.")}function rU(e){return{metroProjectRoot:e.metroProjectRoot,metroKind:e.metroKind,metroPublicBaseUrl:e.metroPublicBaseUrl,metroProxyBaseUrl:e.metroProxyBaseUrl,metroPreparePort:e.metroPreparePort,metroListenHost:e.metroListenHost,metroStatusHost:e.metroStatusHost,metroStartupTimeoutMs:e.metroStartupTimeoutMs,metroProbeTimeoutMs:e.metroProbeTimeoutMs,metroRuntimeFile:e.metroRuntimeFile,metroNoReuseExisting:e.metroNoReuseExisting,metroNoInstallDeps:e.metroNoInstallDeps}}let rM=[{provider:el.browserStack,buildProfileFields:function(e){rO(e.env,"BROWSERSTACK_USERNAME","connect browserstack"),rO(e.env,"BROWSERSTACK_ACCESS_KEY","connect browserstack");let t=rL(e.flags.platform,"connect browserstack requires --platform ios|android."),r=rB(e.flags.device,"connect browserstack requires --device <name>.");return{platform:t,device:r,providerOsVersion:rB(e.flags.providerOsVersion,"connect browserstack requires --provider-os-version <version>."),providerApp:rB(e.flags.providerApp,"connect browserstack requires --provider-app <bs://app-id-or-local-path>."),providerProject:e.flags.providerProject,providerBuild:e.flags.providerBuild,providerSessionName:e.flags.providerSessionName}}},{provider:el.awsDeviceFarm,buildProfileFields:function(e){let{env:t,flags:r}=e;return{platform:rL(r.platform,"connect aws-device-farm requires --platform ios|android."),device:r.device,awsProjectArn:function(e,t,r,n){return rB(rV(e,t,r),n)}(r.awsProjectArn,t,["AGENT_DEVICE_AWS_DEVICE_FARM_PROJECT_ARN","AWS_DEVICE_FARM_PROJECT_ARN"],"connect aws-device-farm requires --aws-project-arn <arn> or AWS_DEVICE_FARM_PROJECT_ARN."),awsDeviceArn:function(e,t,r,n){return rB(rV(e,t,r),n)}(r.awsDeviceArn,t,["AGENT_DEVICE_AWS_DEVICE_FARM_DEVICE_ARN","AWS_DEVICE_FARM_DEVICE_ARN"],"connect aws-device-farm requires --aws-device-arn <arn> or AWS_DEVICE_FARM_DEVICE_ARN."),awsAppArn:rV(r.awsAppArn,t,["AGENT_DEVICE_AWS_DEVICE_FARM_APP_ARN","AWS_DEVICE_FARM_APP_ARN"]),awsRegion:rV(r.awsRegion,t,["AWS_REGION","AWS_DEFAULT_REGION"]),awsInteractionMode:r.awsInteractionMode,providerSessionName:r.providerSessionName}}}];function rL(e,t){if("android"===e||"ios"===e)return e;throw new R("INVALID_ARGS",t)}function rB(e,t){if(e)return e;throw new R("INVALID_ARGS",t)}function rO(e,t,r){let n=e?.[t];if(n)return n;throw new R("INVALID_ARGS",`${r} requires ${t} in the environment.`)}function rV(e,t,r){return e||r.map(e=>t?.[e]).find(e=>!!e)}function rj(e,t,r){var n;if(e.json)return void J({success:!0,data:t});let o=r?.();o&&(n=o,process.stdout.write(n.endsWith("\n")?n:`${n}
24
- `))}let rG=async({positionals:e,flags:t,client:r})=>{let n,s,a=ep(t.stateDir).baseDir,i=function(e){let t=e[0];if(void 0!==t){if(e.length>1)throw new R("INVALID_ARGS","connect accepts at most one provider positional.");if("cloud"===t||"proxy"===t||ei(t))return t;throw new R("INVALID_ARGS",`Unknown connect provider: ${t}. Supported providers: ${["cloud","proxy",el.browserStack,el.awsDeviceFarm].join(", ")}.`)}}(e);var l=i,c=t;if(l&&c.remoteConfig)throw new R("INVALID_ARGS","connect provider positional and --remote-config are mutually exclusive.");let d=await rF({provider:i,flags:t,stateDir:a}),u=d.flags,f=Object.values(s={leaseProvider:(n=tL({configPath:d.remoteConfigPath,cwd:process.cwd(),env:process.env}).profile).leaseProvider,clientId:n.clientId,deviceKey:n.deviceKey}).some(e=>void 0!==e)?s:void 0,p=function(e,t){if(!e.tenant)throw new R("INVALID_ARGS","connect requires tenant in remote config or via --tenant <id>.");if(!e.runId)throw new R("INVALID_ARGS","connect requires runId in remote config or via --run-id <id>.");if(!e.daemonBaseUrl&&!ei(t?.leaseProvider))throw new R("INVALID_ARGS","connect requires daemonBaseUrl in remote config, config, env, or --daemon-base-url.");return{tenant:e.tenant,runId:e.runId}}(u,f),m=function(e){let{stateDir:t,flags:r,remoteConfigPath:n}=e,s=r.session?null:tJ({stateDir:t}),a=r.session??s?.session??function(e){for(let t=0;t<8;t+=1){let t=`adc-${o.randomBytes(3).toString("hex")}`;if(!tj({stateDir:e,session:t}))return t}return`adc-${Date.now().toString(36)}-${o.randomBytes(2).toString("hex")}`}(t),i=s?.session===a?s:tj({stateDir:t,session:a});return{session:a,previous:i,remoteConfigHash:tH(n),daemon:tF(r)}}({stateDir:a,flags:u,remoteConfigPath:d.remoteConfigPath});!function(e,t){var r,n,o,s,a,i,l,c;if(!(!e||(r=e,n=t,o=r,s=n,[[o.remoteConfigPath,s.remoteConfigPath],[o.remoteConfigHash,s.remoteConfigHash],[o.session,s.session],[o.tenant,s.flags.tenant],[o.runId,s.flags.runId]].every(([e,t])=>e===t)&&(a=r,i=n,[[a.leaseBackend,i.desiredLeaseBackend],[a.platform,i.flags.platform],[a.target,i.flags.target],[a.leaseProvider,i.connection?.leaseProvider],[a.clientId,i.connection?.clientId]].every(([e,t])=>void 0===t||e===t))&&(l=r.daemon,c=n.daemon,["baseUrl","transport","serverMode"].every(e=>(l?.[e]??void 0)===(c?.[e]??void 0)))))&&!t.flags.force)throw new R("INVALID_ARGS","A different remote connection is already active for this session. Re-run connect with --force to replace it.",{session:t.session,remoteConfig:e.remoteConfigPath})}(m.previous,{flags:u,session:m.session,remoteConfigPath:d.remoteConfigPath,remoteConfigHash:m.remoteConfigHash,desiredLeaseBackend:ra(u),connection:f,daemon:m.daemon});let g=function(e){var t,r,n,o,s,a,i;let{flags:l,scope:c,connectionMetadata:d,context:u,remoteConfigPath:f}=e,p=(t=l,u.previous&&!t.force)?u.previous:null,m=new Date().toISOString(),g=(r=l,n=p,o=d,{leaseId:n?.leaseId,leaseBackend:n?.leaseBackend??ra(r),leaseProvider:o?.leaseProvider??n?.leaseProvider,clientId:o?.clientId??n?.clientId,deviceKey:n?.deviceKey??o?.deviceKey}),h=(s=l,a=p,i=m,{platform:s.platform??a?.platform,target:s.target??a?.target,runtime:a?.runtime,metro:a?.metro,connectedAt:a?.connectedAt??i});return{version:1,session:u.session,remoteConfigPath:f,remoteConfigHash:u.remoteConfigHash,daemon:u.daemon,tenant:c.tenant,runId:c.runId,...g,...h,updatedAt:m}}({flags:u,scope:p,connectionMetadata:f,context:m,remoteConfigPath:d.remoteConfigPath});tG({stateDir:a,state:g}),await rK(r,a,u,m.previous);let h=rz(g),v=function(e,t){if(!t.runtime&&(ri(e)||rY(t.remoteConfigPath)))return rZ(t.remoteConfigPath)}(u,g);return rj(u,rX(g,v),()=>[`Connected remote session "${m.session}" tenant "${p.tenant}" run "${p.runId}" ${g.leaseId?`lease ${g.leaseId}`:"lease pending"}`,h?.message,v?.message].filter(e=>!!e).join("\n")),!0};async function rF(e){let{provider:t,flags:r,stateDir:n}=e;if(r.remoteConfig){var s,a,i,l,c,d,u=r;if(!u.remoteConfig)throw new R("INVALID_ARGS","connect requires --remote-config <path>.");let e=tL({configPath:u.remoteConfig,cwd:process.cwd(),env:process.env});return{flags:u,remoteConfigPath:e.resolvedPath}}if(ei(t)){let e,d,u;return e=(function(e){let t=rM.find(t=>t.provider===e)?.buildProfileFields;if(t)return t;throw new R("INVALID_ARGS",`Unsupported cloud WebDriver provider "${e}".`)})((s={provider:t,flags:r,stateDir:n,cwd:process.cwd(),env:process.env}).provider)(s),a=s.provider,i=s.stateDir,l=s.flags.session,c=e.device,d=o.createHash("sha256").update(`${a}\0${i}\0${l??""}\0${c??""}`).digest("hex").slice(0,16),u={tenant:s.flags.tenant??s.provider,sessionIsolation:s.flags.sessionIsolation??"tenant",runId:s.flags.runId??`${s.provider}-${d}`,leaseProvider:s.provider,clientId:d,leaseBackend:s.flags.leaseBackend??ra(s.flags),target:s.flags.target??"mobile",session:s.flags.session,...e,...rU(s.flags)},rx({stateDir:s.stateDir,provider:s.provider,profile:u,cwd:s.cwd,env:s.env,flags:s.flags})}return"proxy"===t||!t&&(d=r).daemonBaseUrl&&!d.tenant&&!d.runId&&!d.leaseId&&!d.leaseBackend&&function(e){try{return new URL(e).pathname.replace(/\/+$/,"").endsWith("/agent-device")}catch{return!1}}(d.daemonBaseUrl)?function(e){var t,r,n;let s=e.flags.daemonBaseUrl??e.env?.AGENT_DEVICE_DAEMON_BASE_URL;if(!s)throw new R("INVALID_ARGS","connect proxy requires --daemon-base-url <url> or AGENT_DEVICE_DAEMON_BASE_URL.");let a=(t=e.stateDir,r=s,n=e.flags.session,o.createHash("sha256").update(`${t}\0${r}\0${n??""}`).digest("hex").slice(0,16)),i={daemonBaseUrl:s,daemonTransport:e.flags.daemonTransport??"http",daemonServerMode:e.flags.daemonServerMode,tenant:e.flags.tenant??"proxy",sessionIsolation:e.flags.sessionIsolation??"tenant",runId:e.flags.runId??`proxy-${a}`,leaseProvider:"proxy",clientId:a,leaseBackend:e.flags.leaseBackend??ra(e.flags),platform:e.flags.platform,target:e.flags.target,device:e.flags.device,udid:e.flags.udid,serial:e.flags.serial,iosSimulatorDeviceSet:e.flags.iosSimulatorDeviceSet,androidDeviceAllowlist:e.flags.androidDeviceAllowlist,session:e.flags.session,...rU(e.flags)};return rx({stateDir:e.stateDir,provider:"proxy",profile:i,cwd:e.cwd,env:e.env,flags:e.flags,extraFlags:{daemonBaseUrl:s,daemonTransport:e.flags.daemonTransport??"http"}})}({flags:r,stateDir:n,cwd:process.cwd(),env:process.env}):await rE({flags:r,stateDir:n,cwd:process.cwd(),env:process.env})}async function rK(e,t,r,n){n&&r.force&&(await rt(n.metro),await rr({stateDir:t,state:n}),await ro(e,n))}let rH=async({flags:e,client:t})=>{let r,{session:n,stateDir:o,state:s}=rW(e);if(!s)return rJ(e,n),!0;let a=s.session;try{r=(await t.sessions.close({session:a,shutdown:e.shutdown})).provider}catch{}await rt(s.metro),await rr({stateDir:o,state:s});let i=!1;if(s.leaseId)try{let e=await rn(t,s);i=e.released,r??=e.provider}catch{}return tK({stateDir:o,session:a}),rj(e,{connected:!1,session:a,released:i,...r?{provider:r}:{}},()=>{var e,t,n;return e=a,[`Disconnected remote session "${e}".`,...(n=t=r,(Array.isArray(n?.warnings)?n.warnings:[]).flatMap(e=>{let t=function(e){if(!e||"object"!=typeof e||"string"!=typeof e.message||0===e.message.length)return;let t="string"==typeof e.code&&e.code.length>0?` (${e.code})`:"";return`Provider release warning${t}: ${e.message}`}(e);return t?[t]:[]})),...(t?.cloudArtifacts?.cloudArtifacts??[]).filter(e=>(e.availability??"ready")==="ready"&&e.url).slice(0,3).map(e=>`${e.name}: ${e.url}`)].join("\n")}),!0},rq=async({positionals:e,flags:t})=>{if("status"!==e[0])throw new R("INVALID_ARGS","connection accepts only: status");let{session:r,state:n}=rW(t);if(!n)return rJ(t,r),!0;let o=rz(n),s=function(e){if(!e.runtime&&rY(e.remoteConfigPath))return rZ(e.remoteConfigPath)}(n);return rj(t,rX(n,s),()=>[`Connected remote session "${n.session}".`,`tenant=${n.tenant} runId=${n.runId} leaseId=${n.leaseId??"pending"} backend=${n.leaseBackend??"pending"}`,`remoteConfig=${n.remoteConfigPath}`,n.runtime?"metro=prepared":"metro=not-prepared",o?.message,s?.message].filter(e=>!!e).join("\n")),!0};function rW(e){let t=e.session??"default",r=ep(e.stateDir).baseDir;return{session:t,stateDir:r,state:tj({stateDir:r,session:t})??(e.session?null:tJ({stateDir:r}))}}function rJ(e,t){rj(e,{connected:!1,session:t},()=>`No remote connection for "${t}".`)}function rz(e){var t;if(e.leaseId)return;let r="proxy"===(t=e.leaseProvider)?"proxy":ei(t)?"direct-device-provider":"remote-provider";return"proxy"===r?{status:"deferred",nextSteps:["agent-device open <app-id> --relaunch","agent-device devices"],message:"Proxy lease allocation is pending; run open when ready to allocate or refresh the device lease. Devices can inspect inventory but do not allocate a proxy lease."}:"direct-device-provider"===r?{status:"deferred",nextSteps:["agent-device open <app-id> --relaunch","agent-device snapshot -i","agent-device artifacts --json","agent-device close"],message:`Hosted ${e.leaseProvider} lease allocation is pending; run open when ready to create the provider session. After close, run "agent-device artifacts --json" to fetch provider video/log links when available.`}:{status:"deferred",nextSteps:["agent-device install-from-source <artifact-url> --platform ios|android","agent-device open <app-id> --relaunch","agent-device snapshot -i","agent-device devices"],message:"Lease allocation is pending; run install-from-source, open, snapshot, or devices when ready to allocate or refresh the lease."+(void 0===e.platform&&void 0===e.leaseBackend?" Add --platform ios|android if the profile does not set a platform.":"")}}function rZ(e){let t=`agent-device metro prepare --remote-config ${e}`;return{status:"deferred",nextStep:t,message:`Metro runtime is not prepared yet; it will be prepared automatically on first open, or run "${t}" to inspect it before launch.`}}function rY(e){try{let t=tL({configPath:e,cwd:process.cwd(),env:process.env}).profile;return!!(t.metroPublicBaseUrl||t.metroProxyBaseUrl||t.metroProjectRoot||t.metroKind)}catch{return!1}}function rX(e,t){let r=rz(e);return{connected:!0,session:e.session,tenant:e.tenant,runId:e.runId,leaseAllocated:!!e.leaseId,leaseId:e.leaseId,leaseBackend:e.leaseBackend,leaseProvider:e.leaseProvider,platform:e.platform,target:e.target,remoteConfig:e.remoteConfigPath,remoteConfigHash:e.remoteConfigHash,daemonBaseUrlFingerprint:function(e){if(e)return o.createHash("sha256").update(e).digest("hex").slice(0,12)}(e.daemon?.baseUrl),metro:e.metro?{prepared:!0,projectRoot:e.metro.projectRoot}:{prepared:!1},...r?{leasePreparation:r}:{},...t?{runtimePreparation:t}:{},connectedAt:e.connectedAt,updatedAt:e.updatedAt}}let rQ=async({positionals:e,flags:r})=>{let n=e[0]??"status",o=ep(r.stateDir).baseDir;if("status"===n){var s;let e,t=(e=ry({stateDir:(s={stateDir:o}).stateDir}))?{authenticated:!0,source:"cli-session",sessionId:e.id,cloudBaseUrl:e.cloudBaseUrl,workspaceId:e.workspaceId,accountId:e.accountId,name:e.name,createdAt:e.createdAt,expiresAt:e.expiresAt,expired:r$(e.expiresAt,s.now)}:{authenticated:!1,source:"none"};return rj(r,t,()=>{var e;return(e=t).authenticated?["Authenticated with cloud CLI session.",`cloud=${e.cloudBaseUrl}`,`session=${e.sessionId}`,e.workspaceId?`workspace=${e.workspaceId}`:null,e.accountId?`account=${e.accountId}`:null,e.expiresAt?`expiresAt=${e.expiresAt}`:null,e.expired?"status=expired":null].filter(e=>!!e).join("\n"):"Not authenticated."}),!0}if("login"===n){let e=await rv({stateDir:o,flags:r,commandLabel:"agent-device auth login"});return rj(r,{authenticated:!0,source:"cli-session",sessionId:e.session.id,cloudBaseUrl:e.session.cloudBaseUrl,workspaceId:e.session.workspaceId,accountId:e.session.accountId,expiresAt:e.session.expiresAt,agentTokenExpiresAt:e.expiresAt},()=>"Authenticated with cloud CLI session."),!0}if("logout"===n){let e,n=(e=rw({stateDir:o}.stateDir),!!t.existsSync(e)&&(t.rmSync(e,{force:!0}),!0));return rj(r,{authenticated:!1,removed:n},()=>n?"Removed stored cloud CLI session.":"No stored cloud CLI session."),!0}throw new R("INVALID_ARGS","auth accepts only: status, login, logout")},r0=`${eS}/`,r1=["content-type","content-range","x-artifact-type","x-artifact-filename","x-artifact-hash","x-artifact-hash-algorithm"],r8=["content-type","content-disposition","x-request-id"];async function r3(e,t,r){var n,o,s,a,i,l,c,d,u;let f,p,m,g=(f=new URL(e.url??"/","http://127.0.0.1").pathname)===eS?"/":f.startsWith(r0)?`/${f.slice(r0.length)}`:f;if("GET"===e.method&&"/health"===g)return void await r2(t,r);if(n=g,o=e.method,"/rpc"===n?"POST"!==o:(s=n,a=o,("/upload"===s||nt(s)||"/upload/finalize"===s?"POST"!==a:!s.startsWith("/upload/direct/")||"PUT"!==a)&&(!n.startsWith("/artifacts/")||"GET"!==o))){t.statusCode=404,t.end("Not found");return}("/rpc"===g&&(m=(await ef(e,r.maxRpcBodyBytes,"Proxy request body is too large.")).toString("utf8")),i=e,l=r.clientToken,(p=function(e,t){let r="string"==typeof e.headers.authorization?e.headers.authorization:"";if(r.toLowerCase().startsWith("bearer "))return r.slice(7);let n=e.headers["x-agent-device-token"];if("string"==typeof n)return n;if(t){let e=function(e){try{let t=JSON.parse(e);return"string"==typeof t.params?.token?t.params.token:""}catch{return""}}(t);if(e)return e}return""}(i,m)).length>0&&ec(p,l))?await r5({req:e,res:t,route:g,options:r,rpcBody:m}):(c=t,d=g,u=function(e){if(!e)return null;try{return JSON.parse(e).id??null}catch{return null}}(m),(c.statusCode=401,c.setHeader("content-type","application/json"),"/rpc"===d)?c.end(JSON.stringify({jsonrpc:"2.0",id:u,error:{code:-32001,message:"Invalid proxy token",data:N(new R("UNAUTHORIZED","Invalid proxy token"))}})):c.end(JSON.stringify({ok:!1,error:"Invalid proxy token",code:"UNAUTHORIZED"})))}async function r2(e,t){let r=await r4(t);e.statusCode=200,e.setHeader("content-type","application/json"),e.end(JSON.stringify(eu("agent-device-proxy",{upstream:r})))}async function r4(e){let t=new URL(eI(e.upstreamBaseUrl,"health")),r=await e.fetchImpl(t,{method:"GET",headers:nr({headers:{}},e.upstreamToken,"/health"),signal:AbortSignal.timeout(e.upstreamTimeoutMs)}),n=await r.text();try{return n?JSON.parse(n):{ok:r.ok,status:r.status}}catch{return{ok:r.ok,status:r.status}}}async function r5(e){var t,r,n;let o,s,{req:a,res:i,route:l,options:c,rpcBody:d}=e,u=(t=c.upstreamBaseUrl,r=l,n=a.url??"/",o=new URL(eI(t,r)),(s=n.indexOf("?"))>=0&&(o.search=n.slice(s)),o),f=a.method??"GET",p=nr(a,c.upstreamToken,l),m=function(e,t,r,n){if("GET"!==e.method&&"HEAD"!==e.method){var o,s;let a;return"/rpc"===t?(o=r??"",s=n,(a=JSON.parse(o)).params={...a.params??{},token:s},JSON.stringify(a)):e}}(a,l,d,c.upstreamToken),g=await c.fetchImpl(u,{method:f,headers:p,signal:AbortSignal.timeout(c.upstreamTimeoutMs),...m?{body:m,duplex:"half"}:{}});await r6({req:a,res:i,route:l,response:g,clientToken:c.clientToken})}async function r6(e){var t,r;let n,{req:o,res:s,route:i,response:l,clientToken:c}=e;(s.statusCode=l.status,function(e,t){for(let r of r8){let n=e.headers.get(r);n&&t.setHeader(r,n)}}(l,s),t=o,(r=s).hasHeader("x-request-id")||r.setHeader("x-request-id","string"==typeof(n=t.headers["x-request-id"])&&n.trim()?n.trim().slice(0,128):a()),nt(i))?await r9({req:o,res:s,response:l,clientToken:c}):await r7(l,s)}async function r9(e){let{req:t,res:r,response:n,clientToken:o}=e,s=await n.text();r.setHeader("content-type",n.headers.get("content-type")??"application/json"),r.end(function(e,t,r){let n;try{n=JSON.parse(e)}catch{return e}if(!n||"object"!=typeof n)return e;let o=n;if(!o.upload||"string"!=typeof o.upload.url)return e;let s=function(e,t){let r;try{r=new URL(e)}catch{return null}if(!r.pathname.startsWith("/upload/"))return null;let n="string"==typeof t.headers.host?t.headers.host:"";if(!n)return null;let o=new URL(t.url??"/","http://127.0.0.1").pathname,s=o.lastIndexOf("/upload/preflight"),a=s>=0?o.slice(0,s):"",i=t.headers["x-forwarded-proto"],l=Array.isArray(i)?i[0]:i,c=new URL(`${l||"http"}://${n}`);return c.pathname=`${a}${r.pathname}`,c.search=r.search,c.toString()}(o.upload.url,t);if(!s)return e;let a=o.upload.headers&&"object"==typeof o.upload.headers?{...o.upload.headers}:{};return Object.assign(a,eg(r)),JSON.stringify({...n,upload:{...o.upload,url:s,headers:a}})}(s,t,o))}async function r7(e,t){e.body?await c(l.fromWeb(e.body),t):t.end()}function ne(e,t){let r=e.trim();if(!r)throw new R("INVALID_ARGS",`Proxy ${t} is required.`);return r}function nt(e){return"/upload/preflight"===e}function nr(e,t,r){let n=new Headers;for(let t of r1){let r=e.headers[t];"string"==typeof r&&r.trim()&&n.set(t,r)}for(let[e,o]of("/rpc"!==r||n.has("content-type")||n.set("content-type","application/json"),Object.entries(eg(t))))n.set(e,o);return n}let nn=async({positionals:e,flags:t})=>{if(e.length>0)throw new R("INVALID_ARGS","proxy does not accept positional arguments.");let r=await no(t);return rj(t,r,()=>(function(e,t={}){let r=t.useColor??K(),n=ns("✓","green",r),o=ns(e.proxyBaseUrl,"cyan",r),s=ns("<tunnel URL>","cyan",r),a=ns(e.token,"yellow",r);return`${n} Proxy listening at ${o}
24
+ `))}let rG=async({positionals:e,flags:t,client:r})=>{let n,s,a=ep(t.stateDir).baseDir,i=function(e){let t=e[0];if(void 0!==t){if(e.length>1)throw new R("INVALID_ARGS","connect accepts at most one provider positional.");if("cloud"===t||"proxy"===t||ei(t))return t;throw new R("INVALID_ARGS",`Unknown connect provider: ${t}. Supported providers: ${["cloud","proxy",el.browserStack,el.awsDeviceFarm].join(", ")}.`)}}(e);var l=i,c=t;if(l&&c.remoteConfig)throw new R("INVALID_ARGS","connect provider positional and --remote-config are mutually exclusive.");let d=await rF({provider:i,flags:t,stateDir:a}),u=d.flags,f=Object.values(s={leaseProvider:(n=tL({configPath:d.remoteConfigPath,cwd:process.cwd(),env:process.env}).profile).leaseProvider,clientId:n.clientId,deviceKey:n.deviceKey}).some(e=>void 0!==e)?s:void 0,p=function(e,t){if(!e.tenant)throw new R("INVALID_ARGS","connect requires tenant in remote config or via --tenant <id>.");if(!e.runId)throw new R("INVALID_ARGS","connect requires runId in remote config or via --run-id <id>.");if(!e.daemonBaseUrl&&!ei(t?.leaseProvider))throw new R("INVALID_ARGS","connect requires daemonBaseUrl in remote config, config, env, or --daemon-base-url.");return{tenant:e.tenant,runId:e.runId}}(u,f),m=function(e){let{stateDir:t,flags:r,remoteConfigPath:n}=e,s=r.session?null:tJ({stateDir:t}),a=r.session??s?.session??function(e){for(let t=0;t<8;t+=1){let t=`adc-${o.randomBytes(3).toString("hex")}`;if(!tj({stateDir:e,session:t}))return t}return`adc-${Date.now().toString(36)}-${o.randomBytes(2).toString("hex")}`}(t),i=s?.session===a?s:tj({stateDir:t,session:a});return{session:a,previous:i,remoteConfigHash:tH(n),daemon:tF(r)}}({stateDir:a,flags:u,remoteConfigPath:d.remoteConfigPath});!function(e,t){var r,n,o,s,a,i,l,c;if(!(!e||(r=e,n=t,o=r,s=n,[[o.remoteConfigPath,s.remoteConfigPath],[o.remoteConfigHash,s.remoteConfigHash],[o.session,s.session],[o.tenant,s.flags.tenant],[o.runId,s.flags.runId]].every(([e,t])=>e===t)&&(a=r,i=n,[[a.leaseBackend,i.desiredLeaseBackend],[a.platform,i.flags.platform],[a.target,i.flags.target],[a.leaseProvider,i.connection?.leaseProvider],[a.clientId,i.connection?.clientId]].every(([e,t])=>void 0===t||e===t))&&(l=r.daemon,c=n.daemon,["baseUrl","transport","serverMode"].every(e=>(l?.[e]??void 0)===(c?.[e]??void 0)))))&&!t.flags.force)throw new R("INVALID_ARGS","A different remote connection is already active for this session. Re-run connect with --force to replace it.",{session:t.session,remoteConfig:e.remoteConfigPath})}(m.previous,{flags:u,session:m.session,remoteConfigPath:d.remoteConfigPath,remoteConfigHash:m.remoteConfigHash,desiredLeaseBackend:ra(u),connection:f,daemon:m.daemon});let g=function(e){var t,r,n,o,s,a,i;let{flags:l,scope:c,connectionMetadata:d,context:u,remoteConfigPath:f}=e,p=(t=l,u.previous&&!t.force)?u.previous:null,m=new Date().toISOString(),g=(r=l,n=p,o=d,{leaseId:n?.leaseId,leaseBackend:n?.leaseBackend??ra(r),leaseProvider:o?.leaseProvider??n?.leaseProvider,clientId:o?.clientId??n?.clientId,deviceKey:n?.deviceKey??o?.deviceKey}),h=(s=l,a=p,i=m,{platform:s.platform??a?.platform,target:s.target??a?.target,runtime:a?.runtime,metro:a?.metro,connectedAt:a?.connectedAt??i});return{version:1,session:u.session,remoteConfigPath:f,remoteConfigHash:u.remoteConfigHash,daemon:u.daemon,tenant:c.tenant,runId:c.runId,...g,...h,updatedAt:m}}({flags:u,scope:p,connectionMetadata:f,context:m,remoteConfigPath:d.remoteConfigPath});tG({stateDir:a,state:g}),await rK(r,a,u,m.previous);let h=rz(g),v=function(e,t){if(!t.runtime&&(ri(e)||rY(t.remoteConfigPath)))return rZ(t.remoteConfigPath)}(u,g);return rj(u,rX(g,v),()=>[`Connected remote session "${m.session}" tenant "${p.tenant}" run "${p.runId}" ${g.leaseId?`lease ${g.leaseId}`:"lease pending"}`,h?.message,v?.message].filter(e=>!!e).join("\n")),!0};async function rF(e){let{provider:t,flags:r,stateDir:n}=e;if(r.remoteConfig){var s,a,i,l,c,d,u=r;if(!u.remoteConfig)throw new R("INVALID_ARGS","connect requires --remote-config <path>.");let e=tL({configPath:u.remoteConfig,cwd:process.cwd(),env:process.env});return{flags:u,remoteConfigPath:e.resolvedPath}}if(ei(t)){let e,d,u;return e=(function(e){let t=rM.find(t=>t.provider===e)?.buildProfileFields;if(t)return t;throw new R("INVALID_ARGS",`Unsupported cloud WebDriver provider "${e}".`)})((s={provider:t,flags:r,stateDir:n,cwd:process.cwd(),env:process.env}).provider)(s),a=s.provider,i=s.stateDir,l=s.flags.session,c=e.device,d=o.createHash("sha256").update(`${a}\0${i}\0${l??""}\0${c??""}`).digest("hex").slice(0,16),u={tenant:s.flags.tenant??s.provider,sessionIsolation:s.flags.sessionIsolation??"tenant",runId:s.flags.runId??`${s.provider}-${d}`,leaseProvider:s.provider,clientId:d,leaseBackend:s.flags.leaseBackend??ra(s.flags),target:s.flags.target??"mobile",session:s.flags.session,...e,...rU(s.flags)},rx({stateDir:s.stateDir,provider:s.provider,profile:u,cwd:s.cwd,env:s.env,flags:s.flags})}return"proxy"===t||!t&&(d=r).daemonBaseUrl&&!d.tenant&&!d.runId&&!d.leaseId&&!d.leaseBackend&&function(e){try{return new URL(e).pathname.replace(/\/+$/,"").endsWith("/agent-device")}catch{return!1}}(d.daemonBaseUrl)?function(e){var t,r,n;let s=e.flags.daemonBaseUrl??e.env?.AGENT_DEVICE_DAEMON_BASE_URL;if(!s)throw new R("INVALID_ARGS","connect proxy requires --daemon-base-url <url> or AGENT_DEVICE_DAEMON_BASE_URL.");let a=(t=e.stateDir,r=s,n=e.flags.session,o.createHash("sha256").update(`${t}\0${r}\0${n??""}`).digest("hex").slice(0,16)),i={daemonBaseUrl:s,daemonTransport:e.flags.daemonTransport??"http",daemonServerMode:e.flags.daemonServerMode,tenant:e.flags.tenant??"proxy",sessionIsolation:e.flags.sessionIsolation??"tenant",runId:e.flags.runId??`proxy-${a}`,leaseProvider:"proxy",clientId:a,leaseBackend:e.flags.leaseBackend??ra(e.flags),platform:e.flags.platform,target:e.flags.target,device:e.flags.device,udid:e.flags.udid,serial:e.flags.serial,iosSimulatorDeviceSet:e.flags.iosSimulatorDeviceSet,androidDeviceAllowlist:e.flags.androidDeviceAllowlist,session:e.flags.session,...rU(e.flags)};return rx({stateDir:e.stateDir,provider:"proxy",profile:i,cwd:e.cwd,env:e.env,flags:e.flags,extraFlags:{daemonBaseUrl:s,daemonTransport:e.flags.daemonTransport??"http"}})}({flags:r,stateDir:n,cwd:process.cwd(),env:process.env}):await rE({flags:r,stateDir:n,cwd:process.cwd(),env:process.env})}async function rK(e,t,r,n){n&&r.force&&(await rt(n.metro),await rr({stateDir:t,state:n}),await ro(e,n))}let rH=async({flags:e,client:t})=>{let r,{session:n,stateDir:o,state:s}=rW(e);if(!s)return rJ(e,n),!0;let a=s.session;try{r=(await t.sessions.close({session:a,shutdown:e.shutdown})).provider}catch{}await rt(s.metro),await rr({stateDir:o,state:s});let i=!1;if(s.leaseId)try{let e=await rn(t,s);i=e.released,r??=e.provider}catch{}return tK({stateDir:o,session:a}),rj(e,{connected:!1,session:a,released:i,...r?{provider:r}:{}},()=>{var e,t,n;return e=a,[`Disconnected remote session "${e}".`,...(n=t=r,(Array.isArray(n?.warnings)?n.warnings:[]).flatMap(e=>{let t=function(e){if(!e||"object"!=typeof e||"string"!=typeof e.message||0===e.message.length)return;let t="string"==typeof e.code&&e.code.length>0?` (${e.code})`:"";return`Provider release warning${t}: ${e.message}`}(e);return t?[t]:[]})),...(t?.cloudArtifacts?.cloudArtifacts??[]).filter(e=>(e.availability??"ready")==="ready"&&e.url).slice(0,3).map(e=>`${e.name}: ${e.url}`)].join("\n")}),!0},rq=async({positionals:e,flags:t})=>{if("status"!==e[0])throw new R("INVALID_ARGS","connection accepts only: status");let{session:r,state:n}=rW(t);if(!n)return rJ(t,r),!0;let o=rz(n),s=function(e){if(!e.runtime&&rY(e.remoteConfigPath))return rZ(e.remoteConfigPath)}(n);return rj(t,rX(n,s),()=>[`Connected remote session "${n.session}".`,`tenant=${n.tenant} runId=${n.runId} leaseId=${n.leaseId??"pending"} backend=${n.leaseBackend??"pending"}`,`remoteConfig=${n.remoteConfigPath}`,n.runtime?"metro=prepared":"metro=not-prepared",o?.message,s?.message].filter(e=>!!e).join("\n")),!0};function rW(e){let t=e.session??"default",r=ep(e.stateDir).baseDir;return{session:t,stateDir:r,state:tj({stateDir:r,session:t})??(e.session?null:tJ({stateDir:r}))}}function rJ(e,t){rj(e,{connected:!1,session:t},()=>`No remote connection for "${t}".`)}function rz(e){var t;if(e.leaseId)return;let r="proxy"===(t=e.leaseProvider)?"proxy":ei(t)?"direct-device-provider":"remote-provider";return"proxy"===r?{status:"deferred",nextSteps:["agent-device open <app-id> --relaunch","agent-device devices"],message:"Proxy lease allocation is pending; run open when ready to allocate or refresh the device lease. Devices can inspect inventory but do not allocate a proxy lease."}:"direct-device-provider"===r?{status:"deferred",nextSteps:["agent-device open <app-id> --relaunch","agent-device snapshot -i","agent-device artifacts --json","agent-device close"],message:`Hosted ${e.leaseProvider} lease allocation is pending; run open when ready to create the provider session. After close, run "agent-device artifacts --json" to fetch provider video/log links when available.`}:{status:"deferred",nextSteps:["agent-device install-from-source <artifact-url> --platform ios|android","agent-device open <app-id> --relaunch","agent-device snapshot -i","agent-device devices"],message:"Lease allocation is pending; run install-from-source, open, snapshot, or devices when ready to allocate or refresh the lease."+(void 0===e.platform&&void 0===e.leaseBackend?" Add --platform ios|android if the profile does not set a platform.":"")}}function rZ(e){let t=`agent-device metro prepare --remote-config ${e}`;return{status:"deferred",nextStep:t,message:`Metro runtime is not prepared yet; it will be prepared automatically on first open, or run "${t}" to inspect it before launch.`}}function rY(e){try{let t=tL({configPath:e,cwd:process.cwd(),env:process.env}).profile;return!!(t.metroPublicBaseUrl||t.metroProxyBaseUrl||t.metroProjectRoot||t.metroKind)}catch{return!1}}function rX(e,t){let r=rz(e);return{connected:!0,session:e.session,tenant:e.tenant,runId:e.runId,leaseAllocated:!!e.leaseId,leaseId:e.leaseId,leaseBackend:e.leaseBackend,leaseProvider:e.leaseProvider,platform:e.platform,target:e.target,remoteConfig:e.remoteConfigPath,remoteConfigHash:e.remoteConfigHash,daemonBaseUrlFingerprint:function(e){if(e)return o.createHash("sha256").update(e).digest("hex").slice(0,12)}(e.daemon?.baseUrl),metro:e.metro?{prepared:!0,projectRoot:e.metro.projectRoot}:{prepared:!1},...r?{leasePreparation:r}:{},...t?{runtimePreparation:t}:{},connectedAt:e.connectedAt,updatedAt:e.updatedAt}}let rQ=async({positionals:e,flags:r})=>{let n=e[0]??"status",o=ep(r.stateDir).baseDir;if("status"===n){var s;let e,t=(e=ry({stateDir:(s={stateDir:o}).stateDir}))?{authenticated:!0,source:"cli-session",sessionId:e.id,cloudBaseUrl:e.cloudBaseUrl,workspaceId:e.workspaceId,accountId:e.accountId,name:e.name,createdAt:e.createdAt,expiresAt:e.expiresAt,expired:r$(e.expiresAt,s.now)}:{authenticated:!1,source:"none"};return rj(r,t,()=>{var e;return(e=t).authenticated?["Authenticated with cloud CLI session.",`cloud=${e.cloudBaseUrl}`,`session=${e.sessionId}`,e.workspaceId?`workspace=${e.workspaceId}`:null,e.accountId?`account=${e.accountId}`:null,e.expiresAt?`expiresAt=${e.expiresAt}`:null,e.expired?"status=expired":null].filter(e=>!!e).join("\n"):"Not authenticated."}),!0}if("login"===n){let e=await rv({stateDir:o,flags:r,commandLabel:"agent-device auth login"});return rj(r,{authenticated:!0,source:"cli-session",sessionId:e.session.id,cloudBaseUrl:e.session.cloudBaseUrl,workspaceId:e.session.workspaceId,accountId:e.session.accountId,expiresAt:e.session.expiresAt,agentTokenExpiresAt:e.expiresAt},()=>"Authenticated with cloud CLI session."),!0}if("logout"===n){let e,n=(e=rw({stateDir:o}.stateDir),!!t.existsSync(e)&&(t.rmSync(e,{force:!0}),!0));return rj(r,{authenticated:!1,removed:n},()=>n?"Removed stored cloud CLI session.":"No stored cloud CLI session."),!0}throw new R("INVALID_ARGS","auth accepts only: status, login, logout")},r0=`${eS}/`,r1=["content-type","content-range","x-artifact-type","x-artifact-filename","x-artifact-hash","x-artifact-hash-algorithm"],r8=["content-type","content-disposition","x-request-id"];async function r3(e,t,r){var n,o,s,a,i,l,c,d,u;let f,p,m,g=(f=new URL(e.url??"/","http://127.0.0.1").pathname)===eS?"/":f.startsWith(r0)?`/${f.slice(r0.length)}`:f;if("GET"===e.method&&"/health"===g)return void await r2(t,r);if(n=g,o=e.method,"/rpc"===n?"POST"!==o:(s=n,a=o,("/upload"===s||nt(s)||"/upload/finalize"===s?"POST"!==a:!s.startsWith("/upload/direct/")||"PUT"!==a)&&(!("/artifacts"===n||"/artifacts/"===n||n.startsWith("/artifacts/"))||"GET"!==o))){t.statusCode=404,t.end("Not found");return}("/rpc"===g&&(m=(await ef(e,r.maxRpcBodyBytes,"Proxy request body is too large.")).toString("utf8")),i=e,l=r.clientToken,(p=function(e,t){let r="string"==typeof e.headers.authorization?e.headers.authorization:"";if(r.toLowerCase().startsWith("bearer "))return r.slice(7);let n=e.headers["x-agent-device-token"];if("string"==typeof n)return n;if(t){let e=function(e){try{let t=JSON.parse(e);return"string"==typeof t.params?.token?t.params.token:""}catch{return""}}(t);if(e)return e}return""}(i,m)).length>0&&ec(p,l))?await r5({req:e,res:t,route:g,options:r,rpcBody:m}):(c=t,d=g,u=function(e){if(!e)return null;try{return JSON.parse(e).id??null}catch{return null}}(m),(c.statusCode=401,c.setHeader("content-type","application/json"),"/rpc"===d)?c.end(JSON.stringify({jsonrpc:"2.0",id:u,error:{code:-32001,message:"Invalid proxy token",data:N(new R("UNAUTHORIZED","Invalid proxy token"))}})):c.end(JSON.stringify({ok:!1,error:"Invalid proxy token",code:"UNAUTHORIZED"})))}async function r2(e,t){let r=await r4(t);e.statusCode=200,e.setHeader("content-type","application/json"),e.end(JSON.stringify(eu("agent-device-proxy",{upstream:r})))}async function r4(e){let t=new URL(eI(e.upstreamBaseUrl,"health")),r=await e.fetchImpl(t,{method:"GET",headers:nr({headers:{}},e.upstreamToken,"/health"),signal:AbortSignal.timeout(e.upstreamTimeoutMs)}),n=await r.text();try{return n?JSON.parse(n):{ok:r.ok,status:r.status}}catch{return{ok:r.ok,status:r.status}}}async function r5(e){var t,r,n;let o,s,{req:a,res:i,route:l,options:c,rpcBody:d}=e,u=(t=c.upstreamBaseUrl,r=l,n=a.url??"/",o=new URL(eI(t,r)),(s=n.indexOf("?"))>=0&&(o.search=n.slice(s)),o),f=a.method??"GET",p=nr(a,c.upstreamToken,l),m=function(e,t,r,n){if("GET"!==e.method&&"HEAD"!==e.method){var o,s;let a;return"/rpc"===t?(o=r??"",s=n,(a=JSON.parse(o)).params={...a.params??{},token:s},JSON.stringify(a)):e}}(a,l,d,c.upstreamToken),g=await c.fetchImpl(u,{method:f,headers:p,signal:AbortSignal.timeout(c.upstreamTimeoutMs),...m?{body:m,duplex:"half"}:{}});await r6({req:a,res:i,route:l,response:g,clientToken:c.clientToken})}async function r6(e){var t,r;let n,{req:o,res:s,route:i,response:l,clientToken:c}=e;(s.statusCode=l.status,function(e,t){for(let r of r8){let n=e.headers.get(r);n&&t.setHeader(r,n)}}(l,s),t=o,(r=s).hasHeader("x-request-id")||r.setHeader("x-request-id","string"==typeof(n=t.headers["x-request-id"])&&n.trim()?n.trim().slice(0,128):a()),nt(i))?await r9({req:o,res:s,response:l,clientToken:c}):await r7(l,s)}async function r9(e){let{req:t,res:r,response:n,clientToken:o}=e,s=await n.text();r.setHeader("content-type",n.headers.get("content-type")??"application/json"),r.end(function(e,t,r){let n;try{n=JSON.parse(e)}catch{return e}if(!n||"object"!=typeof n)return e;let o=n;if(!o.upload||"string"!=typeof o.upload.url)return e;let s=function(e,t){let r;try{r=new URL(e)}catch{return null}if(!r.pathname.startsWith("/upload/"))return null;let n="string"==typeof t.headers.host?t.headers.host:"";if(!n)return null;let o=new URL(t.url??"/","http://127.0.0.1").pathname,s=o.lastIndexOf("/upload/preflight"),a=s>=0?o.slice(0,s):"",i=t.headers["x-forwarded-proto"],l=Array.isArray(i)?i[0]:i,c=new URL(`${l||"http"}://${n}`);return c.pathname=`${a}${r.pathname}`,c.search=r.search,c.toString()}(o.upload.url,t);if(!s)return e;let a=o.upload.headers&&"object"==typeof o.upload.headers?{...o.upload.headers}:{};return Object.assign(a,eg(r)),JSON.stringify({...n,upload:{...o.upload,url:s,headers:a}})}(s,t,o))}async function r7(e,t){e.body?await c(l.fromWeb(e.body),t):t.end()}function ne(e,t){let r=e.trim();if(!r)throw new R("INVALID_ARGS",`Proxy ${t} is required.`);return r}function nt(e){return"/upload/preflight"===e}function nr(e,t,r){let n=new Headers;for(let t of r1){let r=e.headers[t];"string"==typeof r&&r.trim()&&n.set(t,r)}for(let[e,o]of("/rpc"!==r||n.has("content-type")||n.set("content-type","application/json"),Object.entries(eg(t))))n.set(e,o);return n}let nn=async({positionals:e,flags:t})=>{if(e.length>0)throw new R("INVALID_ARGS","proxy does not accept positional arguments.");let r=await no(t);return rj(t,r,()=>(function(e,t={}){let r=t.useColor??K(),n=ns("✓","green",r),o=ns(e.proxyBaseUrl,"cyan",r),s=ns("<tunnel URL>","cyan",r),a=ns(e.token,"yellow",r);return`${n} Proxy listening at ${o}
25
25
 
26
26
  Provide this to the agent-device instance connecting:
27
27
 
@@ -1,3 +1,5 @@
1
+ declare type AgentArtifactsResult = CloudArtifactsResult | DaemonArtifactsResult;
2
+
1
3
  declare type AgentDeviceClient = {
2
4
  command: AgentDeviceCommandClient;
3
5
  devices: {
@@ -11,7 +13,7 @@ declare type AgentDeviceClient = {
11
13
  close: (options?: AgentDeviceRequestOverrides & {
12
14
  shutdown?: boolean;
13
15
  }) => Promise<SessionCloseResult>;
14
- artifacts: (options?: CloudArtifactsOptions) => Promise<CloudArtifactsResult>;
16
+ artifacts: (options?: CloudArtifactsOptions) => Promise<AgentArtifactsResult>;
15
17
  };
16
18
  apps: {
17
19
  install: (options: AppInstallOptions) => Promise<AppDeployResult>;
@@ -681,6 +683,22 @@ declare type DaemonArtifact = {
681
683
  path?: string;
682
684
  };
683
685
 
686
+ declare type DaemonArtifactInventoryEntry = {
687
+ id: string;
688
+ filename: string;
689
+ mimeType: string;
690
+ sizeBytes: number;
691
+ createdAt: string;
692
+ expiresAt: string;
693
+ };
694
+
695
+ declare type DaemonArtifactsResult = {
696
+ source: 'daemon';
697
+ status: 'ready';
698
+ artifacts: DaemonArtifactInventoryEntry[];
699
+ message?: string;
700
+ };
701
+
684
702
  declare type DaemonError = {
685
703
  code: string;
686
704
  message: string;
@@ -1,5 +1,5 @@
1
1
  var e,t,r,s;let n,o=process.argv.slice(2);function c(e){return"--help"===e||"-h"===e}function i(e){import("../cli.js").then(({runCli:t})=>t(e)).catch(p)}function p(e){process.stderr.write(`${e instanceof Error?e.message:String(e)}
2
- `),process.exit(1)}1===(t=e=o).length&&("--version"===(r=t[0])||"-V"===r)&&(process.stdout.write(`0.18.2
2
+ `),process.exit(1)}1===(t=e=o).length&&("--version"===(r=t[0])||"-V"===r)&&(process.stdout.write(`0.18.3
3
3
  `),1)||0===e.length&&(import("../cli-help.js").then(({buildUsageText:e})=>{process.stdout.write(`${e()}
4
4
  `),process.exit(1)}).catch(p),1)||void 0!==(n=function(e){var t,r,s,n;switch(e.length){case 1:return"help"===(t=e[0])||c(t)?null:void 0;case 2:return r=e[0],s=e[1],"help"===r?s:(n=r,c(s)?n:void 0);default:return}}(s=e))&&(import("../cli-help.js").then(({buildCommandUsageText:e,buildUsageText:t})=>{var r;if(null===n)return void process.stdout.write(`${t()}
5
5
  `);let o=e("long-press"===(r=n)?"longpress":"metrics"===r?"perf":r);o?process.stdout.write(o):i(s)}).catch(p),1)||("mcp"!==o[0]||o.includes("--help")||o.includes("-h")?i(o):import("../server.js").then(({runAgentDeviceMcpServer:e})=>e()).catch(p));export{};
package/dist/src/lease.js CHANGED
@@ -1 +1 @@
1
- import{PUBLIC_COMMANDS as e}from"./1986.js";import{resolveRequestOrSessionLeaseScope as a,resolveLeaseScope as r}from"./9722.js";import{leaseScopeToAllocateRequest as t,leaseScopeToReleaseRequest as o,leaseScopeToHeartbeatRequest as s}from"./2948.js";import{AppError as i}from"./485.js";async function l(i){let{req:l,sessionName:n,sessionStore:c,leaseRegistry:_,leaseLifecycleProvider:v,cloudArtifactProvider:f}=i,u=r(l);switch(l.command){case e.artifacts:{let e=a(l,c.get(n));return{ok:!0,data:await d(l,e,f)}}case"lease_allocate":{let e,a=_.allocateLease(t(u));try{e=await v?.allocate?.(a,{req:l})}catch(e){throw _.releaseLease(o({leaseId:a.leaseId,tenantId:a.tenantId,runId:a.runId,leaseBackend:a.backend,leaseProvider:a.leaseProvider,deviceKey:a.deviceKey,clientId:a.clientId})),e}return{ok:!0,data:{lease:a,...e?{provider:e}:{}}}}case"lease_heartbeat":{let e=_.heartbeatLease(s(u)),a=await v?.heartbeat?.(e,{req:l});return{ok:!0,data:{lease:e,...a?{provider:a}:{}}}}case"lease_release":{let e=_.releaseLease(o(u)),a=e.lease?await v?.release?.(e.lease,{req:l}):void 0;return{ok:!0,data:{released:e.released,...a?{provider:a}:{}}}}default:return null}}async function d(e,a,r){var t;let o,s=(t=e.flags,"string"==typeof(o=t?.providerSessionId)&&o.length>0?o:void 0);if(!a.leaseProvider)throw new i("INVALID_ARGS","artifacts requires --provider for provider session lookup or an active cloud connection.");if(!a.leaseId&&!s)throw new i("INVALID_ARGS","artifacts requires an active cloud lease or --provider-session <id>.");let l=await r?.listCloudArtifacts?.({provider:a.leaseProvider,leaseId:a.leaseId,providerSessionId:s});if(!l)throw new i("UNSUPPORTED_OPERATION",`Cloud artifacts are not available for provider "${a.leaseProvider}".`);return l}export{l as handleLeaseCommands};
1
+ import{PUBLIC_COMMANDS as e}from"./1986.js";import{listDownloadableArtifacts as a,resolveRequestOrSessionLeaseScope as r,resolveLeaseScope as t}from"./9722.js";import{isProxyLeaseScope as o,leaseScopeToReleaseRequest as s,leaseScopeToAllocateRequest as i,leaseScopeToHeartbeatRequest as l}from"./2948.js";import{AppError as n}from"./485.js";async function d(a){let{req:o,sessionName:n,sessionStore:d,leaseRegistry:_,leaseLifecycleProvider:u,cloudArtifactProvider:v}=a,f=t(o);switch(o.command){case e.artifacts:{let e=r(o,d.get(n));return{ok:!0,data:await c(o,e,v)}}case"lease_allocate":{let e,a=_.allocateLease(i(f));try{e=await u?.allocate?.(a,{req:o})}catch(e){throw _.releaseLease(s({leaseId:a.leaseId,tenantId:a.tenantId,runId:a.runId,leaseBackend:a.backend,leaseProvider:a.leaseProvider,deviceKey:a.deviceKey,clientId:a.clientId})),e}return{ok:!0,data:{lease:a,...e?{provider:e}:{}}}}case"lease_heartbeat":{let e=_.heartbeatLease(l(f)),a=await u?.heartbeat?.(e,{req:o});return{ok:!0,data:{lease:e,...a?{provider:a}:{}}}}case"lease_release":{let e=_.releaseLease(s(f)),a=e.lease?await u?.release?.(e.lease,{req:o}):void 0;return{ok:!0,data:{released:e.released,...a?{provider:a}:{}}}}default:return null}}async function c(e,a,r){var t,s,i;let l,n=(t=e.flags,"string"==typeof(l=t?.providerSessionId)&&l.length>0?l:void 0);return(s=a,i=n,!o(s)&&(s.leaseProvider||i))?await u(a,n,r):await _(a.tenantId)}async function _(e){let r=await a(e);return{source:"daemon",status:"ready",artifacts:r,...0===r.length?{message:"No daemon artifacts available."}:{}}}async function u(e,a,r){if(!e.leaseProvider)throw new n("INVALID_ARGS","artifacts requires --provider for provider session lookup or an active cloud connection.");if(!e.leaseId&&!a)throw new n("INVALID_ARGS","artifacts requires an active cloud lease or --provider-session <id>.");let t=await r?.listCloudArtifacts?.({provider:e.leaseProvider,leaseId:e.leaseId,providerSessionId:a});if(!t)throw new n("UNSUPPORTED_OPERATION",`Cloud artifacts are not available for provider "${e.leaseProvider}".`);return t}export{d as handleLeaseCommands};