agent-device 0.14.9 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +7 -4
  2. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.14.9.apk → agent-device-android-snapshot-helper-0.15.0.apk} +0 -0
  3. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.15.0.apk.sha256 +1 -0
  4. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.14.9.manifest.json → agent-device-android-snapshot-helper-0.15.0.manifest.json} +6 -6
  5. package/dist/src/1769.js +7 -0
  6. package/dist/src/2151.js +429 -0
  7. package/dist/src/221.js +4 -4
  8. package/dist/src/2842.js +1 -0
  9. package/dist/src/3572.js +1 -0
  10. package/dist/src/4057.js +1 -1
  11. package/dist/src/840.js +2 -0
  12. package/dist/src/9542.js +2 -2
  13. package/dist/src/9639.js +2 -2
  14. package/dist/src/android-adb.d.ts +38 -9
  15. package/dist/src/android-adb.js +1 -1
  16. package/dist/src/android-snapshot-helper.d.ts +23 -0
  17. package/dist/src/cli.js +60 -57
  18. package/dist/src/contracts.d.ts +1 -0
  19. package/dist/src/finders.d.ts +1 -0
  20. package/dist/src/index.d.ts +19 -22
  21. package/dist/src/internal/companion-tunnel.js +1 -1
  22. package/dist/src/internal/daemon.js +51 -23
  23. package/dist/src/remote-config.d.ts +17 -14
  24. package/dist/src/selectors.d.ts +2 -0
  25. package/dist/src/server.js +2 -20
  26. package/ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +7 -1
  27. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +128 -47
  28. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +734 -10
  29. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +93 -7
  30. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +5 -0
  31. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +9 -0
  32. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +1 -0
  33. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +1 -2
  34. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +26 -0
  35. package/package.json +25 -11
  36. package/server.json +3 -3
  37. package/skills/agent-device/SKILL.md +2 -7
  38. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.14.9.apk.sha256 +0 -1
  39. package/dist/src/180.js +0 -1
  40. package/dist/src/6108.js +0 -26
  41. package/dist/src/6642.js +0 -1
  42. package/dist/src/7462.js +0 -1
  43. package/dist/src/8809.js +0 -8
  44. package/dist/src/command-schema.js +0 -382
@@ -180,6 +180,7 @@ declare type RawSnapshotNode = {
180
180
  surface?: string;
181
181
  hiddenContentAbove?: boolean;
182
182
  hiddenContentBelow?: boolean;
183
+ presentationHints?: string[];
183
184
  };
184
185
 
185
186
  export declare type Rect = {
@@ -64,6 +64,7 @@ declare type RawSnapshotNode = {
64
64
  surface?: string;
65
65
  hiddenContentAbove?: boolean;
66
66
  hiddenContentBelow?: boolean;
67
+ presentationHints?: string[];
67
68
  };
68
69
 
69
70
  declare type Rect = {
@@ -10,9 +10,6 @@ export declare type AgentDeviceClient = {
10
10
  shutdown?: boolean;
11
11
  }) => Promise<SessionCloseResult>;
12
12
  };
13
- simulators: {
14
- ensure: (options: EnsureSimulatorOptions) => Promise<EnsureSimulatorResult>;
15
- };
16
13
  apps: {
17
14
  install: (options: AppDeployOptions) => Promise<AppDeployResult>;
18
15
  reinstall: (options: AppDeployOptions) => Promise<AppDeployResult>;
@@ -261,7 +258,7 @@ export declare type AppInstallFromSourceResult = {
261
258
  declare type ApplePlatform = 'ios' | 'macos';
262
259
 
263
260
  export declare type AppListOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
264
- appsFilter?: 'all' | 'user-installed';
261
+ appsFilter?: AppsFilter;
265
262
  };
266
263
 
267
264
  export declare type AppOpenOptions = AgentDeviceRequestOverrides & AgentDeviceSelectionOptions & {
@@ -291,6 +288,8 @@ export declare type AppPushOptions = ClientCommandBaseOptions & {
291
288
  payload: string | Record<string, unknown>;
292
289
  };
293
290
 
291
+ declare type AppsFilter = 'user-installed' | 'all';
292
+
294
293
  export declare type AppStateCommandOptions = DeviceCommandBaseOptions;
295
294
 
296
295
  export declare type AppStateCommandResult = DaemonResponseData & {
@@ -365,6 +364,7 @@ export declare type CaptureScreenshotOptions = AgentDeviceRequestOverrides & {
365
364
  overlayRefs?: boolean;
366
365
  fullscreen?: boolean;
367
366
  maxSize?: number;
367
+ stabilize?: boolean;
368
368
  surface?: 'app' | 'frontmost-app' | 'desktop' | 'menubar';
369
369
  };
370
370
 
@@ -380,6 +380,7 @@ export declare type CaptureSnapshotOptions = AgentDeviceRequestOverrides & Agent
380
380
  depth?: number;
381
381
  scope?: string;
382
382
  raw?: boolean;
383
+ forceFull?: boolean;
383
384
  };
384
385
 
385
386
  export declare type CaptureSnapshotResult = {
@@ -390,6 +391,7 @@ export declare type CaptureSnapshotResult = {
390
391
  visibility?: SnapshotVisibility;
391
392
  androidSnapshot?: AndroidSnapshotBackendMetadata;
392
393
  warnings?: string[];
394
+ unchanged?: SnapshotUnchanged;
393
395
  identifiers: AgentDeviceIdentifiers;
394
396
  };
395
397
 
@@ -536,24 +538,6 @@ declare type DeviceTarget = 'mobile' | 'tv' | 'desktop';
536
538
 
537
539
  export declare type ElementTarget = RefTarget | SelectorTarget;
538
540
 
539
- export declare type EnsureSimulatorOptions = AgentDeviceRequestOverrides & {
540
- device: string;
541
- runtime?: string;
542
- boot?: boolean;
543
- reuseExisting?: boolean;
544
- iosSimulatorDeviceSet?: string;
545
- };
546
-
547
- export declare type EnsureSimulatorResult = {
548
- udid: string;
549
- device: string;
550
- runtime: string;
551
- created: boolean;
552
- booted: boolean;
553
- iosSimulatorDeviceSet?: string | null;
554
- identifiers: AgentDeviceIdentifiers;
555
- };
556
-
557
541
  export declare type FileInputRef = {
558
542
  kind: 'path';
559
543
  path: string;
@@ -638,7 +622,11 @@ export declare type KeyboardCommandResult = DaemonResponseData & {
638
622
  action?: 'status' | 'dismiss';
639
623
  visible?: boolean;
640
624
  inputType?: string | null;
625
+ inputMethodPackage?: string | null;
641
626
  type?: string | null;
627
+ focusedPackage?: string | null;
628
+ focusedResourceId?: string | null;
629
+ inputOwner?: 'app' | 'ime' | 'unknown';
642
630
  wasVisible?: boolean;
643
631
  dismissed?: boolean;
644
632
  attempts?: number;
@@ -851,6 +839,7 @@ declare type RawSnapshotNode = {
851
839
  surface?: string;
852
840
  hiddenContentAbove?: boolean;
853
841
  hiddenContentBelow?: boolean;
842
+ presentationHints?: string[];
854
843
  };
855
844
 
856
845
  declare type RecordingQuality = 5 | 6 | 7 | 8 | 9 | 10;
@@ -896,6 +885,7 @@ declare type RepeatedPressOptions = {
896
885
  export declare type ReplayRunOptions = AgentDeviceRequestOverrides & {
897
886
  path: string;
898
887
  update?: boolean;
888
+ maestro?: boolean;
899
889
  env?: string[];
900
890
  };
901
891
 
@@ -1016,6 +1006,13 @@ export declare type SnapshotNode = RawSnapshotNode & {
1016
1006
  ref: string;
1017
1007
  };
1018
1008
 
1009
+ declare type SnapshotUnchanged = {
1010
+ ageMs: number;
1011
+ nodeCount: number;
1012
+ interactiveOnly?: boolean;
1013
+ scope?: string;
1014
+ };
1015
+
1019
1016
  export declare type SnapshotVisibility = {
1020
1017
  partial: boolean;
1021
1018
  visibleNodeCount: number;
@@ -1 +1 @@
1
- import e from"node:fs";import{setTimeout as r}from"node:timers/promises";import{normalizeBaseUrl as t,ENV_COMPANION_TUNNEL_UNREGISTER_PATH as n,MissingCompanionEnvError as s,ENV_COMPANION_TUNNEL_SERVER_BASE_URL as a,ENV_COMPANION_TUNNEL_STATE_PATH as o,ENV_COMPANION_TUNNEL_LAUNCH_URL as i,ENV_COMPANION_TUNNEL_SCOPE_LEASE_ID as c,ENV_COMPANION_TUNNEL_SCOPE_TENANT_ID as f,ENV_COMPANION_TUNNEL_DEVICE_PORT as l,METRO_COMPANION_RUN_ARG as u,ENV_COMPANION_TUNNEL_SESSION as d,ENV_COMPANION_TUNNEL_SCOPE_RUN_ID as m,ENV_COMPANION_TUNNEL_REGISTER_PATH as p,ENV_COMPANION_TUNNEL_LOCAL_BASE_URL as y,REACT_DEVTOOLS_COMPANION_RUN_ARG as g,ENV_COMPANION_TUNNEL_BEARER_TOKEN as w}from"../2301.js";class h extends Error{retryable;retryAfterMs;constructor(e,r,t){super(e),this.retryable=r,this.retryAfterMs=t}}function b(e,r){return{authorization:`Bearer ${r}`,"content-type":"application/json",...e.includes("ngrok")?{"ngrok-skip-browser-warning":"1"}:{}}}function S(e){return{...e.bridgeScope,...e.session?{session:e.session}:{},local_base_url:t(e.localBaseUrl),...e.devicePort?{device_port:e.devicePort}:{},...e.launchUrl?{launch_url:e.launchUrl}:{}}}async function v(e){let r,n,s=e.registerPath;try{r=await fetch(`${t(e.serverBaseUrl)}${s}`,{method:"POST",headers:b(e.serverBaseUrl,e.bearerToken),body:JSON.stringify(S(e)),signal:AbortSignal.timeout(5e3)})}catch(r){if(r instanceof Error&&"TimeoutError"===r.name)throw Error(`${s} timed out after 5000ms calling ${t(e.serverBaseUrl)}${s}`);throw r}let a=await r.text();try{n=a?JSON.parse(a):{}}catch{let e;throw new h(`Failed to register companion (${r.status}): invalid JSON response: ${(e=a.replaceAll(/\s+/g," ").trim()).length>300?`${e.slice(0,300)}...`:e}`,E(r.status,void 0),I(r,{}))}if(!r.ok||!0!==n.ok||"string"!=typeof n.data?.ws_url)throw new h(`Failed to register companion (${r.status}): ${JSON.stringify(n)}`,E(r.status,n.error?.code),I(r,n));return{wsUrl:n.data.ws_url}}function E(e,r){return"RATE_LIMITED"===r||"UNAUTHORIZED"!==r&&"INVALID_ARGS"!==r&&"SESSION_NOT_FOUND"!==r&&(408===e||409===e||425===e||429===e||e>=500)}function I(e,r){let t=r.error?.details?.retryAfterMs;if("number"==typeof t&&Number.isFinite(t))return B(t);let n=e.headers.get("retry-after");if(!n)return;let s=Number(n);if(Number.isFinite(s))return B(1e3*s);let a=Date.parse(n);if(Number.isFinite(a))return B(a-Date.now())}function B(e){return Math.max(0,Math.min(6e4,Math.ceil(e)))}async function N(e){let r=e.unregisterPath??null;if(r)try{await fetch(`${t(e.serverBaseUrl)}${r}`,{method:"POST",headers:b(e.serverBaseUrl,e.bearerToken),body:JSON.stringify(S(e)),signal:AbortSignal.timeout(2e3)})}catch(e){console.error(e instanceof Error?e.message:String(e))}}async function k(e){return"string"==typeof e?Buffer.from(e,"utf8"):e instanceof ArrayBuffer?Buffer.from(e):ArrayBuffer.isView(e)?Buffer.from(e.buffer,e.byteOffset,e.byteLength):"u">typeof Blob&&e instanceof Blob?Buffer.from(await e.arrayBuffer()):Buffer.from(String(e),"utf8")}async function L(e){return JSON.parse((await k(e.data)).toString("utf8"))}function U(e,r){1===e.readyState&&e.send(JSON.stringify(r))}async function O(e,r){1!==e.readyState&&await new Promise((t,n)=>{let s=()=>{i(),t()},a=()=>{i(),n(Error(`${r} WebSocket failed before opening.`))},o=()=>{i(),n(Error(`${r} WebSocket closed before opening.`))},i=()=>{e.removeEventListener("open",s),e.removeEventListener("error",a),e.removeEventListener("close",o)};e.addEventListener("open",s,{once:!0}),e.addEventListener("error",a,{once:!0}),e.addEventListener("close",o,{once:!0})})}async function $(e){e.readyState>=WebSocket.CLOSING||await new Promise(r=>{let t=()=>{n(),r()},n=()=>{e.removeEventListener("close",t),e.removeEventListener("error",t)};e.addEventListener("close",t,{once:!0}),e.addEventListener("error",t,{once:!0}),e.readyState>=WebSocket.CLOSING&&t()})}function T(e,r,t){try{e.close(1e3===r||r>=3e3&&r<=4999?r:3001,t)}catch{}}function _(r){return!r.statePath||e.existsSync(r.statePath)}async function A(e,r,n){try{let s=await fetch(new URL(r.path,`${t(n.localBaseUrl)}/`),{method:r.method,headers:r.headers,...r.bodyBase64?{body:Buffer.from(r.bodyBase64,"base64")}:{}}),a=Buffer.from(await s.arrayBuffer());U(e,{type:"http-response",requestId:r.requestId,status:s.status,headers:Object.fromEntries(s.headers.entries()),...a.length>0?{bodyBase64:a.toString("base64")}:{}})}catch(t){U(e,{type:"http-error",requestId:r.requestId,message:t instanceof Error?t.message:String(t)})}}async function P(e,r,n,s){var a;let o,i=new WebSocket((a=n.localBaseUrl,(o=new URL(r.path,`${t(a)}/`)).protocol="https:"===o.protocol?"wss:":"ws:",o.toString()));i.binaryType="arraybuffer";let c=!1;i.addEventListener("message",t=>{(async()=>{if(!c)return;let n=await k(t.data);U(e,{type:"ws-frame",streamId:r.streamId,dataBase64:n.toString("base64"),binary:"string"!=typeof t.data})})().catch(e=>{console.error(e instanceof Error?e.message:String(e))})}),i.addEventListener("close",t=>{s.delete(r.streamId),c&&U(e,{type:"ws-close",streamId:r.streamId,code:t.code,reason:t.reason})}),i.addEventListener("error",()=>{c&&U(e,{type:"ws-close",streamId:r.streamId,code:1011,reason:"Upstream WebSocket error."})}),s.set(r.streamId,i);try{await O(i,"Upstream"),c=!0,U(e,{type:"ws-open-result",streamId:r.streamId,success:!0,headers:{}})}catch(t){s.delete(r.streamId),T(i,1011,"open failed"),U(e,{type:"ws-open-result",streamId:r.streamId,success:!1,error:t instanceof Error?t.message:String(t)})}}async function M(e,r,t,n){switch(r.type){case"ping":return void U(e,{type:"pong",timestamp:r.timestamp});case"http-request":return void await A(e,r,t);case"ws-open":return void await P(e,r,t,n);case"ws-frame":return void function(e,r){let t=r.get(e.streamId);if(!t||1!==t.readyState)return;let n=Buffer.from(e.dataBase64,"base64");t.send(e.binary?n:n.toString("utf8"))}(r,n);case"ws-close":return void function(e,r){let t=r.get(e.streamId);if(t){var n;r.delete(e.streamId),T(t,"number"==typeof(n=e.code)&&Number.isInteger(n)&&(1e3===n||n>=3e3&&n<=4999||n>=1001&&n<=1015&&1004!==n&&1005!==n&&1006!==n)?n:1011,e.reason??"bridge requested close")}}(r,n)}}async function C(e){let t=new Map,n=!1,s=null,a=!1,o=()=>{n||(n=!0,a&&N(e).finally(()=>process.exit(0)),s&&T(s,1e3,"companion stopping"),setTimeout(()=>process.exit(0),900).unref())};process.once("SIGTERM",o),process.once("SIGINT",o);let i=setInterval(()=>{_(e)||process.exit(0)},250);i.unref();let c=1e3;for(;!n&&_(e);){let o,i=!1;try{a=!1;let r=await v(e);if(c=1e3,i=!0,a=!0,n||!_(e)){await N(e),i=!1,a=!1;break}let o=new WebSocket(r.wsUrl);s=o,o.binaryType="arraybuffer";try{await O(o,"Bridge"),o.addEventListener("message",r=>{(async()=>{let n=await L(r);await M(o,n,e,t)})().catch(e=>{console.error(e instanceof Error?e.message:String(e))})}),await $(o)}finally{s=null,a=!1,t.forEach(e=>T(e,1012,"bridge disconnected")),t.clear(),i&&(await N(e),i=!1)}}catch(r){if(s=null,a=!1,i&&(await N(e),i=!1),n||!_(e)||(console.error(r instanceof Error?r.message:String(r)),r instanceof h&&!r.retryable))break;o=r instanceof h?r.retryAfterMs:void 0}if(n||!_(e))break;let f=o??c;void 0===o&&(c=Math.min(2*c,6e4)),await r(f)}clearInterval(i)}(async function(e,r){let t=function(e,r){let t=e[0];if(t!==u&&t!==g)return null;let h=r[a]?.trim(),b=r[w]?.trim(),S=r[y]?.trim();if(!h||!b||!S)throw new s("Companion tunnel worker is missing required environment configuration.");let v=r[f]?.trim(),E=r[m]?.trim(),I=r[c]?.trim();if(!v||!E||!I)throw new s("Companion tunnel worker is missing required bridge scope configuration.");let B=r[p]?.trim();if(!B)throw new s("Companion tunnel worker is missing required register path configuration.");return{serverBaseUrl:h,bearerToken:b,localBaseUrl:S,registerPath:B,bridgeScope:{tenantId:v,runId:E,leaseId:I},launchUrl:r[i]?.trim(),statePath:r[o]?.trim(),unregisterPath:r[n]?.trim(),devicePort:function(e){if(!e?.trim())return;let r=Number.parseInt(e,10);if(!Number.isInteger(r)||r<1||r>65535)throw Error("Companion worker received invalid device port configuration.");return r}(r[l]?.trim()),session:r[d]?.trim()}}(e,r);return!!t&&(await C(t),!0)})(process.argv.slice(2),process.env).catch(e=>{if(e instanceof s){console.error(e.message),process.exitCode=1;return}console.error(e instanceof Error?e.stack??e.message:String(e)),process.exitCode=1});
1
+ import e from"node:fs";import{setTimeout as r}from"node:timers/promises";import{normalizeBaseUrl as t,ENV_COMPANION_TUNNEL_UNREGISTER_PATH as n,MissingCompanionEnvError as s,ENV_COMPANION_TUNNEL_SERVER_BASE_URL as a,ENV_COMPANION_TUNNEL_STATE_PATH as o,ENV_COMPANION_TUNNEL_LAUNCH_URL as i,ENV_COMPANION_TUNNEL_SCOPE_LEASE_ID as c,ENV_COMPANION_TUNNEL_SCOPE_TENANT_ID as l,ENV_COMPANION_TUNNEL_DEVICE_PORT as f,METRO_COMPANION_RUN_ARG as u,ENV_COMPANION_TUNNEL_SESSION as d,ENV_COMPANION_TUNNEL_SCOPE_RUN_ID as m,ENV_COMPANION_TUNNEL_REGISTER_PATH as y,ENV_COMPANION_TUNNEL_LOCAL_BASE_URL as p,REACT_DEVTOOLS_COMPANION_RUN_ARG as g,ENV_COMPANION_TUNNEL_BEARER_TOKEN as w}from"../2301.js";class h extends Error{retryable;retryAfterMs;constructor(e,r,t){super(e),this.retryable=r,this.retryAfterMs=t}}function b(e,r){return{authorization:`Bearer ${r}`,"content-type":"application/json",...e.includes("ngrok")?{"ngrok-skip-browser-warning":"1"}:{}}}function S(e){return{...e.bridgeScope,...e.session?{session:e.session}:{},local_base_url:t(e.localBaseUrl),...e.devicePort?{device_port:e.devicePort}:{},...e.launchUrl?{launch_url:e.launchUrl}:{}}}async function v(e){let r,n,s=e.registerPath;try{r=await fetch(`${t(e.serverBaseUrl)}${s}`,{method:"POST",headers:b(e.serverBaseUrl,e.bearerToken),body:JSON.stringify(S(e)),signal:AbortSignal.timeout(5e3)})}catch(r){if(r instanceof Error&&"TimeoutError"===r.name)throw Error(`${s} timed out after 5000ms calling ${t(e.serverBaseUrl)}${s}`);throw r}let a=await r.text();try{n=a?JSON.parse(a):{}}catch{let e;throw new h(`Failed to register companion (${r.status}): invalid JSON response: ${(e=a.replaceAll(/\s+/g," ").trim()).length>300?`${e.slice(0,300)}...`:e}`,I(r.status,void 0),E(r,{}))}if(!r.ok||!0!==n.ok||"string"!=typeof n.data?.ws_url)throw new h(`Failed to register companion (${r.status}): ${JSON.stringify(n)}`,I(r.status,n.error?.code),E(r,n));return{wsUrl:n.data.ws_url}}function I(e,r){return"RATE_LIMITED"===r||"UNAUTHORIZED"!==r&&"INVALID_ARGS"!==r&&"SESSION_NOT_FOUND"!==r&&(408===e||409===e||425===e||429===e||e>=500)}function E(e,r){let t=r.error?.details?.retryAfterMs;if("number"==typeof t&&Number.isFinite(t))return B(t);let n=e.headers.get("retry-after");if(!n)return;let s=Number(n);if(Number.isFinite(s))return B(1e3*s);let a=Date.parse(n);if(Number.isFinite(a))return B(a-Date.now())}function B(e){return Math.max(0,Math.min(6e4,Math.ceil(e)))}async function N(e){let r=e.unregisterPath??null;if(r)try{await fetch(`${t(e.serverBaseUrl)}${r}`,{method:"POST",headers:b(e.serverBaseUrl,e.bearerToken),body:JSON.stringify(S(e)),signal:AbortSignal.timeout(2e3)})}catch(e){console.error(e instanceof Error?e.message:String(e))}}async function k(e){return"string"==typeof e?Buffer.from(e,"utf8"):e instanceof ArrayBuffer?Buffer.from(e):ArrayBuffer.isView(e)?Buffer.from(e.buffer,e.byteOffset,e.byteLength):"u">typeof Blob&&e instanceof Blob?Buffer.from(await e.arrayBuffer()):Buffer.from(String(e),"utf8")}async function L(e){return JSON.parse((await k(e.data)).toString("utf8"))}function U(e,r){1===e.readyState&&e.send(JSON.stringify(r))}async function P(e,r){1!==e.readyState&&await new Promise((t,n)=>{let s=()=>{i(),t()},a=()=>{i(),n(Error(`${r} WebSocket failed before opening.`))},o=()=>{i(),n(Error(`${r} WebSocket closed before opening.`))},i=()=>{e.removeEventListener("open",s),e.removeEventListener("error",a),e.removeEventListener("close",o)};e.addEventListener("open",s,{once:!0}),e.addEventListener("error",a,{once:!0}),e.addEventListener("close",o,{once:!0})})}async function O(e){e.readyState>=WebSocket.CLOSING||await new Promise(r=>{let t=()=>{n(),r()},n=()=>{e.removeEventListener("close",t),e.removeEventListener("error",t)};e.addEventListener("close",t,{once:!0}),e.addEventListener("error",t,{once:!0}),e.readyState>=WebSocket.CLOSING&&t()})}function T(e,r,t){try{e.close(1e3===r||r>=3e3&&r<=4999?r:3001,t)}catch{}}function $(r){return!r.statePath||e.existsSync(r.statePath)}async function M(e,r,n){try{let s=await fetch(new URL(r.path,`${t(n.localBaseUrl)}/`),{method:r.method,headers:r.headers,...r.bodyBase64?{body:Buffer.from(r.bodyBase64,"base64")}:{}}),a=Buffer.from(await s.arrayBuffer());U(e,{type:"http-response",requestId:r.requestId,status:s.status,headers:Object.fromEntries(s.headers.entries()),...a.length>0?{bodyBase64:a.toString("base64")}:{}})}catch(t){U(e,{type:"http-error",requestId:r.requestId,message:t instanceof Error?t.message:String(t)})}}async function _(e,r,n,s){var a;let o,i=new WebSocket((a=n.localBaseUrl,(o=new URL(r.path,`${t(a)}/`)).protocol="https:"===o.protocol?"wss:":"ws:",o.toString()));i.binaryType="arraybuffer";let c=!1;i.addEventListener("message",t=>{(async()=>{if(!c)return;let n=await k(t.data);U(e,{type:"ws-frame",streamId:r.streamId,dataBase64:n.toString("base64"),binary:"string"!=typeof t.data})})().catch(e=>{console.error(e instanceof Error?e.message:String(e))})}),i.addEventListener("close",t=>{s.delete(r.streamId),c&&U(e,{type:"ws-close",streamId:r.streamId,code:t.code,reason:t.reason})}),i.addEventListener("error",()=>{c&&U(e,{type:"ws-close",streamId:r.streamId,code:1011,reason:"Upstream WebSocket error."})}),s.set(r.streamId,i);try{await P(i,"Upstream"),c=!0,U(e,{type:"ws-open-result",streamId:r.streamId,success:!0,headers:{}})}catch(t){s.delete(r.streamId),T(i,1011,"open failed"),U(e,{type:"ws-open-result",streamId:r.streamId,success:!1,error:t instanceof Error?t.message:String(t)})}}async function A(e,r,t,n){switch(r.type){case"ping":return void U(e,{type:"pong",timestamp:r.timestamp});case"http-request":return void await M(e,r,t);case"ws-open":return void await _(e,r,t,n);case"ws-frame":return void function(e,r){let t=r.get(e.streamId);if(!t||1!==t.readyState)return;let n=Buffer.from(e.dataBase64,"base64");t.send(e.binary?n:n.toString("utf8"))}(r,n);case"ws-close":return void function(e,r){let t=r.get(e.streamId);if(t){var n;r.delete(e.streamId),T(t,"number"==typeof(n=e.code)&&Number.isInteger(n)&&(1e3===n||n>=3e3&&n<=4999||n>=1001&&n<=1015&&1004!==n&&1005!==n&&1006!==n)?n:1011,e.reason??"bridge requested close")}}(r,n)}}async function C(e,t={}){let n,s=new Map,a=!1,o=null,i=!1,c=null,l=t.delay??r,f=new Promise(e=>{n=e}),u=()=>{t.exit?.(0)},d=async()=>{(i||c)&&(c??=N(e).finally(()=>{i=!1,c=null}),await c)},m=()=>{!a&&(a=!0,n(),i&&d().finally(u),o&&T(o,1e3,"companion stopping"),t.exit&&setTimeout(u,900).unref())};t.registerProcessSignals&&(process.once("SIGTERM",m),process.once("SIGINT",m));let y=setInterval(()=>{$(e)||(m(),u())},t.leaseCheckIntervalMs??250);y.unref();let p=t.reconnectDelayMs??1e3;try{for(;!a&&$(e);){var g;let r,n=!1;try{i=!1;let r=await v(e);if(p=t.reconnectDelayMs??1e3,n=!0,i=!0,a||!$(e)){await d(),n=!1;break}let c=new WebSocket(r.wsUrl);o=c,c.binaryType="arraybuffer";try{await P(c,"Bridge"),c.addEventListener("message",r=>{(async()=>{let t=await L(r);await A(c,t,e,s)})().catch(e=>{console.error(e instanceof Error?e.message:String(e))})}),await Promise.race([O(c),f])}finally{o=null,s.forEach(e=>T(e,1012,"bridge disconnected")),s.clear(),n&&(await d(),n=!1)}}catch(t){if(o=null,n&&(await d(),n=!1),a||!$(e)||(console.error(t instanceof Error?t.message:String(t)),t instanceof h&&!t.retryable))break;r=t instanceof h?t.retryAfterMs:void 0}if(a||!$(e))break;let c=r??p;void 0===r&&(g=p,p=Math.min(2*g,6e4)),await l(c)}}finally{clearInterval(y),t.registerProcessSignals&&(process.off("SIGTERM",m),process.off("SIGINT",m))}}(async function(e,r){let t=function(e,r){let t=e[0];if(t!==u&&t!==g)return null;let h=r[a]?.trim(),b=r[w]?.trim(),S=r[p]?.trim();if(!h||!b||!S)throw new s("Companion tunnel worker is missing required environment configuration.");let v=r[l]?.trim(),I=r[m]?.trim(),E=r[c]?.trim();if(!v||!I||!E)throw new s("Companion tunnel worker is missing required bridge scope configuration.");let B=r[y]?.trim();if(!B)throw new s("Companion tunnel worker is missing required register path configuration.");return{serverBaseUrl:h,bearerToken:b,localBaseUrl:S,registerPath:B,bridgeScope:{tenantId:v,runId:I,leaseId:E},launchUrl:r[i]?.trim(),statePath:r[o]?.trim(),unregisterPath:r[n]?.trim(),devicePort:function(e){if(!e?.trim())return;let r=Number.parseInt(e,10);if(!Number.isInteger(r)||r<1||r>65535)throw Error("Companion worker received invalid device port configuration.");return r}(r[f]?.trim()),session:r[d]?.trim()}}(e,r);return!!t&&(await C(t,{exit:e=>process.exit(e),registerProcessSignals:!0}),!0)})(process.argv.slice(2),process.env).catch(e=>{if(e instanceof s){console.error(e.message),process.exitCode=1;return}console.error(e instanceof Error?e.stack??e.message:String(e)),process.exitCode=1});