diff --git a/packages/askui-nodejs/src/execution/agent-os/agent-os-client.ts b/packages/askui-nodejs/src/execution/agent-os/agent-os-client.ts index 22e0a47f1d..7682894b91 100644 --- a/packages/askui-nodejs/src/execution/agent-os/agent-os-client.ts +++ b/packages/askui-nodejs/src/execution/agent-os/agent-os-client.ts @@ -90,7 +90,7 @@ export class AgentOsClient implements DeviceClient { private displayId = 1; - constructor(public url: string) {} + constructor(public url: string, private useHttpProxy: boolean = false) {} private static buildControllerApiConstructor(): grpc.ServiceClientConstructor { const packageDefinition = protoLoader.loadSync( @@ -118,9 +118,17 @@ export class AgentOsClient implements DeviceClient { return `${address}:${AgentOsClient.DEFAULT_PORT}`; } - private static async openChannel(address: string): Promise { + private static async openChannel( + address: string, + useHttpProxy: boolean, + ): Promise { const ControllerApi = AgentOsClient.buildControllerApiConstructor(); const client = new ControllerApi(address, grpc.credentials.createInsecure(), { + // By default, the AgentOS (local or remote) is reached directly because + // grpc-js would otherwise honor grpc_proxy/https_proxy and corporate + // proxies generally cannot tunnel gRPC (CONNECT answered with + // redirects/auth pages). Opt in via the `agentOsUseProxy` client arg. + 'grpc.enable_http_proxy': useHttpProxy ? 1 : 0, 'grpc.max_receive_message_length': 2 ** 30, 'grpc.max_send_message_length': 2 ** 30, }); @@ -192,7 +200,7 @@ export class AgentOsClient implements DeviceClient { this.connectionState = UiControllerClientConnectionState.CONNECTING; const address = AgentOsClient.normalizeAddress(this.url); try { - this.client = await AgentOsClient.openChannel(address); + this.client = await AgentOsClient.openChannel(address, this.useHttpProxy); this.address = address; await this.startSession(); this.connectionState = UiControllerClientConnectionState.CONNECTED; diff --git a/packages/askui-nodejs/src/execution/ui-control-client-dependency-builder.ts b/packages/askui-nodejs/src/execution/ui-control-client-dependency-builder.ts index 5ab219e7c9..acb35c8a79 100644 --- a/packages/askui-nodejs/src/execution/ui-control-client-dependency-builder.ts +++ b/packages/askui-nodejs/src/execution/ui-control-client-dependency-builder.ts @@ -66,7 +66,7 @@ export class UiControlClientDependencyBuilder { } return new LegacyAndroidClient(android); } - return new AgentOsClient(clientArgs.agentOsUrl); + return new AgentOsClient(clientArgs.agentOsUrl, clientArgs.agentOsUseProxy); } static async build(clientArgs: ClientArgsWithDefaults): Promise<{ @@ -102,6 +102,7 @@ export class UiControlClientDependencyBuilder { return { ...clientArgs, agentOsUrl: clientArgs.agentOsUrl ?? clientArgs.uiControllerUrl ?? 'localhost:26000', + agentOsUseProxy: clientArgs.agentOsUseProxy ?? false, aiElementArgs: { additionalLocations: clientArgs.aiElementArgs?.additionalLocations ?? [], onLocationNotExist: clientArgs.aiElementArgs?.onLocationNotExist ?? 'error', diff --git a/packages/askui-nodejs/src/execution/ui-controller-client-interface.ts b/packages/askui-nodejs/src/execution/ui-controller-client-interface.ts index 1b604900e3..1f992ff8de 100644 --- a/packages/askui-nodejs/src/execution/ui-controller-client-interface.ts +++ b/packages/askui-nodejs/src/execution/ui-controller-client-interface.ts @@ -56,6 +56,11 @@ export interface ContextArgs { * to `localhost:23000` to connect to a standalone AgentOS instead. * @property {string} [uiControllerUrl] - **Deprecated.** Use {@link agentOsUrl} instead. Kept * for backwards compatibility: used only when `agentOsUrl` is not set. + * @property {boolean} [agentOsUseProxy] - Default: `false`. By default, the gRPC connection to + * the AgentOS is established directly, ignoring proxy environment variables such as + * `grpc_proxy`, `https_proxy` and `http_proxy` (corporate HTTP proxies generally cannot + * tunnel gRPC). Set to `true` to route the connection through the proxy configured in those + * environment variables, e.g., when the AgentOS is only reachable through a proxy. * @property {string} [inferenceServerUrl] - Default: `'https://inference.askui.com'`. * Address of the AskUI's inference server which is responsible for understanding the * screenshots and extracting data from them and returning commands for the UiController. @@ -97,6 +102,7 @@ export interface ClientArgs { * @deprecated Use {@link agentOsUrl} instead. Used only when `agentOsUrl` is not set. */ readonly uiControllerUrl?: string + readonly agentOsUseProxy?: boolean readonly runtime?: Runtime readonly android?: AndroidArgs readonly inferenceServerUrl?: string @@ -114,6 +120,7 @@ export interface ClientArgs { export interface ClientArgsWithDefaults extends ClientArgs { readonly agentOsUrl: string + readonly agentOsUseProxy: boolean readonly runtime: Runtime readonly inferenceServerUrl: string readonly context: Context