agent-device 0.14.8 → 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 (49) hide show
  1. package/README.md +8 -6
  2. package/android-snapshot-helper/README.md +4 -2
  3. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.14.8.apk → agent-device-android-snapshot-helper-0.15.0.apk} +0 -0
  4. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.15.0.apk.sha256 +1 -0
  5. package/android-snapshot-helper/dist/{agent-device-android-snapshot-helper-0.14.8.manifest.json → agent-device-android-snapshot-helper-0.15.0.manifest.json} +6 -6
  6. package/dist/src/1769.js +7 -0
  7. package/dist/src/2151.js +429 -0
  8. package/dist/src/221.js +4 -4
  9. package/dist/src/2842.js +1 -0
  10. package/dist/src/3572.js +1 -0
  11. package/dist/src/4057.js +1 -1
  12. package/dist/src/840.js +2 -0
  13. package/dist/src/9542.js +2 -2
  14. package/dist/src/9639.js +2 -2
  15. package/dist/src/9818.js +1 -1
  16. package/dist/src/android-adb.d.ts +49 -11
  17. package/dist/src/android-adb.js +1 -1
  18. package/dist/src/android-snapshot-helper.d.ts +35 -2
  19. package/dist/src/cli.js +60 -57
  20. package/dist/src/contracts.d.ts +2 -0
  21. package/dist/src/finders.d.ts +2 -0
  22. package/dist/src/index.d.ts +25 -22
  23. package/dist/src/internal/companion-tunnel.js +1 -1
  24. package/dist/src/internal/daemon.js +51 -23
  25. package/dist/src/remote-config.d.ts +17 -14
  26. package/dist/src/selectors.d.ts +3 -0
  27. package/dist/src/server.js +2 -20
  28. package/ios-runner/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +7 -1
  29. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +210 -56
  30. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +890 -99
  31. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +94 -7
  32. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +8 -0
  33. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +24 -0
  34. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +2 -0
  35. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +185 -0
  36. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +1 -2
  37. package/ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +26 -0
  38. package/package.json +25 -11
  39. package/server.json +3 -3
  40. package/skills/agent-device/SKILL.md +6 -1
  41. package/skills/dogfood/SKILL.md +3 -1
  42. package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.14.8.apk.sha256 +0 -1
  43. package/dist/src/180.js +0 -1
  44. package/dist/src/6108.js +0 -26
  45. package/dist/src/6642.js +0 -1
  46. package/dist/src/7462.js +0 -1
  47. package/dist/src/8809.js +0 -8
  48. package/dist/src/command-schema.js +0 -381
  49. package/skills/react-devtools/SKILL.md +0 -48
@@ -169,6 +169,7 @@ declare type RawSnapshotNode = {
169
169
  rect?: Rect;
170
170
  enabled?: boolean;
171
171
  selected?: boolean;
172
+ focused?: boolean;
172
173
  hittable?: boolean;
173
174
  depth?: number;
174
175
  parentIndex?: number;
@@ -179,6 +180,7 @@ declare type RawSnapshotNode = {
179
180
  surface?: string;
180
181
  hiddenContentAbove?: boolean;
181
182
  hiddenContentBelow?: boolean;
183
+ presentationHints?: string[];
182
184
  };
183
185
 
184
186
  export declare type Rect = {
@@ -53,6 +53,7 @@ declare type RawSnapshotNode = {
53
53
  rect?: Rect;
54
54
  enabled?: boolean;
55
55
  selected?: boolean;
56
+ focused?: boolean;
56
57
  hittable?: boolean;
57
58
  depth?: number;
58
59
  parentIndex?: number;
@@ -63,6 +64,7 @@ declare type RawSnapshotNode = {
63
64
  surface?: string;
64
65
  hiddenContentAbove?: boolean;
65
66
  hiddenContentBelow?: boolean;
67
+ presentationHints?: string[];
66
68
  };
67
69
 
68
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;
@@ -840,6 +828,7 @@ declare type RawSnapshotNode = {
840
828
  rect?: Rect;
841
829
  enabled?: boolean;
842
830
  selected?: boolean;
831
+ focused?: boolean;
843
832
  hittable?: boolean;
844
833
  depth?: number;
845
834
  parentIndex?: number;
@@ -850,6 +839,7 @@ declare type RawSnapshotNode = {
850
839
  surface?: string;
851
840
  hiddenContentAbove?: boolean;
852
841
  hiddenContentBelow?: boolean;
842
+ presentationHints?: string[];
853
843
  };
854
844
 
855
845
  declare type RecordingQuality = 5 | 6 | 7 | 8 | 9 | 10;
@@ -895,6 +885,7 @@ declare type RepeatedPressOptions = {
895
885
  export declare type ReplayRunOptions = AgentDeviceRequestOverrides & {
896
886
  path: string;
897
887
  update?: boolean;
888
+ maestro?: boolean;
898
889
  env?: string[];
899
890
  };
900
891
 
@@ -987,6 +978,11 @@ declare type SessionRuntimeHints = {
987
978
  export declare type SettingsUpdateOptions = (ClientCommandBaseOptions & {
988
979
  setting: 'wifi' | 'airplane' | 'location';
989
980
  state: 'on' | 'off';
981
+ }) | (ClientCommandBaseOptions & {
982
+ setting: 'location';
983
+ state: 'set';
984
+ latitude: number;
985
+ longitude: number;
990
986
  }) | (ClientCommandBaseOptions & {
991
987
  setting: 'animations';
992
988
  state: 'on' | 'off';
@@ -1010,6 +1006,13 @@ export declare type SnapshotNode = RawSnapshotNode & {
1010
1006
  ref: string;
1011
1007
  };
1012
1008
 
1009
+ declare type SnapshotUnchanged = {
1010
+ ageMs: number;
1011
+ nodeCount: number;
1012
+ interactiveOnly?: boolean;
1013
+ scope?: string;
1014
+ };
1015
+
1013
1016
  export declare type SnapshotVisibility = {
1014
1017
  partial: boolean;
1015
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});