| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- #!/usr/bin/env node
- import path from "path"
- import { fileURLToPath } from "url"
- import { writeFileWithMkdirs } from "./file-utils.mjs"
- import { getFqn, loadServicesFromProtoDescriptor } from "./proto-utils.mjs"
- const WEBVIEW_CLIENTS_FILE = path.resolve("webview-ui/src/services/grpc-client.ts")
- const VSCODE_SERVICES_FILE = path.resolve("src/generated/hosts/vscode/protobus-services.ts")
- const VSCODE_SERVICE_TYPES_FILE = path.resolve("src/generated/hosts/vscode/protobus-service-types.ts")
- const STANDALONE_SERVER_SETUP_FILE = path.resolve("src/generated/hosts/standalone/protobus-server-setup.ts")
- const SCRIPT_NAME = path.relative(process.cwd(), fileURLToPath(import.meta.url))
- export async function main() {
- const { protobusServices } = await loadServicesFromProtoDescriptor()
- await generateWebviewProtobusClients(protobusServices)
- await generateVscodeServiceTypes(protobusServices)
- await generateVscodeProtobusServers(protobusServices)
- await generateStandaloneProtobusServiceSetup(protobusServices)
- console.log(`Generated ProtoBus files at:`)
- console.log(`- ${WEBVIEW_CLIENTS_FILE}`)
- console.log(`- ${VSCODE_SERVICE_TYPES_FILE}`)
- console.log(`- ${VSCODE_SERVICES_FILE}`)
- console.log(`- ${STANDALONE_SERVER_SETUP_FILE}`)
- }
- async function generateWebviewProtobusClients(protobusServices) {
- const clients = []
- for (const [serviceName, def] of Object.entries(protobusServices)) {
- const rpcs = []
- for (const [rpcName, rpc] of Object.entries(def.service)) {
- const requestType = getFqn(rpc.requestType.type.name)
- const responseType = getFqn(rpc.responseType.type.name)
- if (rpc.requestStream) {
- throw new Error("Request streaming is not supported")
- }
- if (!rpc.responseStream) {
- rpcs.push(` static async ${rpcName}(request: ${requestType}): Promise<${responseType}> {
- return this.makeUnaryRequest("${rpcName}", request, ${requestType}.toJSON, ${responseType}.fromJSON)
- }`)
- } else {
- rpcs.push(` static ${rpcName}(request: ${requestType}, callbacks: Callbacks<${responseType}>): ()=>void {
- return this.makeStreamingRequest("${rpcName}", request, ${requestType}.toJSON, ${responseType}.fromJSON, callbacks)
- }`)
- }
- }
- clients.push(`export class ${serviceName}Client extends ProtoBusClient {
- static override serviceName: string = "cline.${serviceName}"
- ${rpcs.join("\n")}
- }`)
- }
- // Create output file
- const output = `// GENERATED CODE -- DO NOT EDIT!
- // Generated by ${SCRIPT_NAME}
- import * as proto from "@shared/proto/index"
- import { ProtoBusClient, Callbacks } from "./grpc-client-base"
- ${clients.join("\n")}
- `
- // Write output file
- await writeFileWithMkdirs(WEBVIEW_CLIENTS_FILE, output)
- }
- /**
- * Generate imports and function to add all the handlers to the server for all services defined in the proto files.
- */
- async function generateVscodeServiceTypes(protobusServices) {
- const servers = []
- for (const [serviceName, def] of Object.entries(protobusServices)) {
- const domain = getDomainName(serviceName)
- servers.push(`// ${domain} Service Handler Types`)
- servers.push(`export type ${serviceName}Handlers = {`)
- for (const [rpcName, rpc] of Object.entries(def.service)) {
- const requestType = getFqn(rpc.requestType.type.name)
- const responseType = getFqn(rpc.responseType.type.name)
- if (rpc.requestStream) {
- throw new Error("Request streaming is not supported")
- }
- if (!rpc.responseStream) {
- servers.push(` ${rpcName}:(controller: Controller, request: ${requestType}) => Promise<${responseType}>`)
- } else {
- servers.push(
- ` ${rpcName}:(controller: Controller, request: ${requestType}, responseStream: StreamingResponseHandler<${responseType}>, requestId?: string) => Promise<void>`,
- )
- }
- }
- servers.push(`}\n`)
- }
- // Create output file
- const output = `// GENERATED CODE -- DO NOT EDIT!
- // Generated by ${SCRIPT_NAME}
- import * as proto from "@shared/proto/index"
- import { Controller } from "@core/controller"
- import { StreamingResponseHandler } from "@/core/controller/grpc-handler"
- ${servers.join("\n")}
- `
- // Write output file
- await writeFileWithMkdirs(VSCODE_SERVICE_TYPES_FILE, output)
- }
- /**
- * Generate imports and function to add all the handlers to the server for all services defined in the proto files.
- */
- async function generateVscodeProtobusServers(protobusServices) {
- const imports = []
- const servers = []
- const serviceMap = []
- for (const [serviceName, def] of Object.entries(protobusServices)) {
- const domain = getDomainName(serviceName)
- const dir = getDirName(serviceName)
- imports.push(`// ${domain} Service`)
- servers.push(`const ${serviceName}Handlers: serviceTypes.${serviceName}Handlers = {`)
- for (const [rpcName, _rpc] of Object.entries(def.service)) {
- imports.push(`import { ${rpcName} } from "@core/controller/${dir}/${rpcName}"`)
- servers.push(` ${rpcName}: ${rpcName},`)
- }
- servers.push(`} \n`)
- serviceMap.push(` "cline.${serviceName}": ${serviceName}Handlers,`)
- imports.push("")
- }
- // Create output file
- const output = `// GENERATED CODE -- DO NOT EDIT!
- // Generated by ${SCRIPT_NAME}
- import * as serviceTypes from "src/generated/hosts/vscode/protobus-service-types"
- ${imports.join("\n")}
- ${servers.join("\n")}
- export const serviceHandlers: Record<string, any> = {
- ${serviceMap.join("\n")}
- }
- `
- // Write output file
- await writeFileWithMkdirs(VSCODE_SERVICES_FILE, output)
- }
- /**
- * Generate imports and function to add all the handlers to the server for all services defined in the proto files.
- */
- async function generateStandaloneProtobusServiceSetup(protobusServices) {
- const imports = []
- const handlerSetup = []
- for (const [name, def] of Object.entries(protobusServices)) {
- const domain = getDomainName(name)
- const dir = getDirName(name)
- imports.push(`// ${domain} Service`)
- handlerSetup.push(` // ${domain} Service`)
- handlerSetup.push(` server.addService(cline.${name}Service, {`)
- for (const [rpcName, rpc] of Object.entries(def.service)) {
- imports.push(`import { ${rpcName} } from "@core/controller/${dir}/${rpcName}"`)
- const requestType = "cline." + rpc.requestType.type.name
- const responseType = "cline." + rpc.responseType.type.name
- if (rpc.requestStream) {
- throw new Error("Request streaming is not supported")
- }
- if (rpc.responseStream) {
- handlerSetup.push(
- ` ${rpcName}: wrapStreamingResponse<${requestType},${responseType}>(${rpcName}, controller),`,
- )
- } else {
- handlerSetup.push(` ${rpcName}: wrapper<${requestType},${responseType}>(${rpcName}, controller),`)
- }
- }
- handlerSetup.push(` });`)
- imports.push("")
- handlerSetup.push("")
- }
- // Create output file
- const output = `// GENERATED CODE -- DO NOT EDIT!
- // Generated by ${SCRIPT_NAME}
- import * as grpc from "@grpc/grpc-js"
- import { cline } from "@generated/grpc-js"
- import { Controller } from "@core/controller"
- import { GrpcHandlerWrapper, GrpcStreamingResponseHandlerWrapper } from "@hosts/external/grpc-types"
- ${imports.join("\n")}
- export function addProtobusServices(
- server: grpc.Server,
- controller: Controller,
- wrapper: GrpcHandlerWrapper,
- wrapStreamingResponse: GrpcStreamingResponseHandlerWrapper,
- ): void {
- ${handlerSetup.join("\n")}
- }
- `
- // Write output file
- await writeFileWithMkdirs(STANDALONE_SERVER_SETUP_FILE, output)
- }
- function getDomainName(serviceName) {
- return serviceName.replace(/Service$/, "")
- }
- function getDirName(serviceName) {
- const domain = getDomainName(serviceName)
- return domain.charAt(0).toLowerCase() + domain.slice(1)
- }
- // Only run main if this script is executed directly
- if (import.meta.url === `file://${process.argv[1]}`) {
- main().catch((error) => {
- console.error(chalk.red("Error:"), error)
- process.exit(1)
- })
- }
|