Add a script to generate type safe clients for the host bridge (#4269)

* Generate client interfaces and impls. Use the interfaces for the vscode and external clients

* Use interfaces
This commit is contained in:
Sarah Fortune
2025-06-18 12:39:52 -07:00
committed by GitHub
parent bf5e2785c3
commit 30344befe9
5 changed files with 227 additions and 7 deletions
+3 -2
View File
@@ -1,7 +1,8 @@
import { UriServiceClientInterface, WatchServiceClientInterface } from "@/generated/hosts/host-bridge-client-types"
import * as VscodeClient from "./vscode/client/host-grpc-client"
import * as ExternalClient from "@/standalone/host-bridge-client-manager"
const isHostBridgeExternal = process.env.HOST_BRIDGE_ADDRESS !== undefined && process.env.HOST_BRIDGE_ADDRESS !== "vscode"
const Client = isHostBridgeExternal ? ExternalClient : VscodeClient
export const UriServiceClient = Client.UriServiceClient
export const WatchServiceClient = Client.WatchServiceClient
export const UriServiceClient: UriServiceClientInterface = Client.UriServiceClient
export const WatchServiceClient: WatchServiceClientInterface = Client.WatchServiceClient
+5 -3
View File
@@ -1,5 +1,7 @@
import { Channel, createChannel, createClient } from "nice-grpc"
import * as host from "@generated/nice-grpc/index.host"
import { UriServiceClientInterface, WatchServiceClientInterface } from "@/generated/hosts/host-bridge-client-types"
import { FileChangeEvent } from "@/shared/proto/index.host"
/**
* Singleton class to hold the gRPC clients for the host bridge. The clients should be re-used to avoid
@@ -36,7 +38,7 @@ const StubWatchServiceClient = {
subscribeToFile: function (
_r: host.SubscribeToFileRequest,
_h: {
onResponse?: (response: { type: host.FileChangeEvent_ChangeType }) => void | Promise<void>
onResponse?: (response: FileChangeEvent) => void | Promise<void>
onError?: (error: any) => void
onComplete?: () => void
},
@@ -47,5 +49,5 @@ const StubWatchServiceClient = {
const clientManager = HostBridgeClientManager.getInstance()
export const UriServiceClient = clientManager.uriClient
export const WatchServiceClient = StubWatchServiceClient
export const UriServiceClient: UriServiceClientInterface = clientManager.uriClient
export const WatchServiceClient: WatchServiceClientInterface = StubWatchServiceClient
+26 -1
View File
@@ -2,6 +2,7 @@ import * as fs from "fs"
import * as grpc from "@grpc/grpc-js"
import * as protoLoader from "@grpc/proto-loader"
import * as health from "grpc-health-check"
import { StreamingCallbacks } from "@hosts/vscode/host-grpc-handler"
const log = (...args: unknown[]) => {
const timestamp = new Date().toISOString()
@@ -16,4 +17,28 @@ function getPackageDefinition() {
const packageDefinition = { ...clineDef, ...healthDef }
return packageDefinition
}
export { getPackageDefinition, log }
/**
* Converts an AsyncIterable to a callback-based API
* @param stream The AsyncIterable stream to process
* @param callbacks The callbacks to invoke for stream events
*/
async function asyncIteratorToCallbacks<T>(stream: AsyncIterable<T>, callbacks: StreamingCallbacks<T>): Promise<void> {
try {
// Process each item in the stream
for await (const response of stream) {
callbacks.onResponse && callbacks.onResponse(response)
}
// Stream completed successfully
callbacks.onComplete && callbacks.onComplete()
} catch (err) {
const error = err instanceof Error ? err : new Error(String(err))
if (callbacks.onError) {
callbacks.onError(error)
} else {
log(`Host bridge RPC error: ${error}`)
}
}
}
export { getPackageDefinition, log, asyncIteratorToCallbacks }