From 8c1a22f5dd09b884fa858ad3c0ae6a3cbb9e3b1b Mon Sep 17 00:00:00 2001 From: cte Date: Fri, 21 Mar 2025 22:33:00 -0700 Subject: [PATCH] IPC tweaks --- benchmark/packages/ipc/scripts/client.ts | 15 +- benchmark/packages/ipc/scripts/server.ts | 27 -- benchmark/packages/ipc/src/index.ts | 264 +++++++++++++++++- benchmark/packages/ipc/src/ipcClient.ts | 123 --------- benchmark/packages/ipc/src/ipcServer.ts | 120 --------- benchmark/packages/ipc/src/types.ts | 63 ----- package-lock.json | 330 ++++++++++++++++++++++- package.json | 2 + src/core/webview/ClineProvider.ts | 10 +- src/exports/api.ts | 28 +- src/exports/ipc.ts | 262 ++++++++++++++++++ src/extension.ts | 2 +- 12 files changed, 892 insertions(+), 354 deletions(-) delete mode 100644 benchmark/packages/ipc/scripts/server.ts delete mode 100644 benchmark/packages/ipc/src/ipcClient.ts delete mode 100644 benchmark/packages/ipc/src/ipcServer.ts delete mode 100644 benchmark/packages/ipc/src/types.ts create mode 100644 src/exports/ipc.ts diff --git a/benchmark/packages/ipc/scripts/client.ts b/benchmark/packages/ipc/scripts/client.ts index f5c06b1594..b774a783b6 100644 --- a/benchmark/packages/ipc/scripts/client.ts +++ b/benchmark/packages/ipc/scripts/client.ts @@ -1,10 +1,12 @@ -import { IpcClient } from "../src/ipcClient" +import { IpcClientMessageType, IpcClient } from "../src/index.js" -async function main(socketPath: string) { +async function main(socketPath: string, prompt: string) { try { const startTime = Date.now() const client = new IpcClient(socketPath) + client.on("message", (data) => console.log(data)) + while (!client.isConnected) { if (Date.now() - startTime > 5000) { throw new Error("Failed to connect to server.") @@ -13,9 +15,10 @@ async function main(socketPath: string) { await new Promise((resolve) => setTimeout(resolve, 1000)) } + client.sendMessage({ type: IpcClientMessageType.StartNewTask, data: { text: prompt } }) + while (client.isConnected) { - client.ping() - await new Promise((resolve) => setTimeout(resolve, 5000)) + await new Promise((resolve) => setTimeout(resolve, 1000)) } process.exit(0) @@ -26,8 +29,8 @@ async function main(socketPath: string) { } if (!process.argv[2]) { - console.error("Usage: npx tsx scripts/client.ts ") + console.error("Usage: npx tsx scripts/client.ts ") process.exit(1) } -main(process.argv[2]) +main(process.argv[2], process.argv[3]) diff --git a/benchmark/packages/ipc/scripts/server.ts b/benchmark/packages/ipc/scripts/server.ts deleted file mode 100644 index ef264228da..0000000000 --- a/benchmark/packages/ipc/scripts/server.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { IpcServer } from "../src/ipcServer" -import { ServerMessageType } from "../src/types" - -async function main(socketPath: string) { - try { - const server = new IpcServer(socketPath) - server.listen() - console.log(`listening @ ${server.socketPath}`) - - while (server.isListening) { - server.broadcast({ type: ServerMessageType.Message, data: { message: "hello" } }) - await new Promise((resolve) => setTimeout(resolve, 1000)) - } - - process.exit(0) - } catch (e) { - console.error(e) - process.exit(1) - } -} - -if (!process.argv[2]) { - console.error("Usage: npx tsx scripts/client.ts ") - process.exit(1) -} - -main(process.argv[2]) diff --git a/benchmark/packages/ipc/src/index.ts b/benchmark/packages/ipc/src/index.ts index e9eb2bb9af..8b0dabcb55 100644 --- a/benchmark/packages/ipc/src/index.ts +++ b/benchmark/packages/ipc/src/index.ts @@ -1,4 +1,262 @@ -export { IpcClient } from "./ipcClient.js" -export { IpcServer } from "./ipcServer.js" +import EventEmitter from "node:events" +import { Socket } from "node:net" +import * as crypto from "node:crypto" -export * from "./types.js" +import ipc from "node-ipc" +import { z } from "zod" + +/** + * IpcClient + */ + +export type IpcClientId = string + +export enum IpcClientMessageType { + Message = "Message", + StartNewTask = "StartNewTask", +} + +export const ipcClientMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal(IpcClientMessageType.StartNewTask), + data: z.object({ + text: z.string(), + images: z.array(z.string()).optional(), + }), + }), +]) + +export type IpcClientMessage = z.infer + +export interface IpcClientEvents { + connect: [] + disconnect: [] + message: [data: IpcServerMessage] +} + +export class IpcClient extends EventEmitter { + private readonly _socketPath: string + private readonly _log: (...args: unknown[]) => void + + private _isConnected = false + private _clientId?: IpcClientId + + constructor(socketPath: string, log = console.log) { + super() + + this._socketPath = socketPath + this._log = log + + ipc.config.silent = true + + ipc.connectTo("benchmarkServer", this.socketPath, () => { + ipc.of.benchmarkServer?.on("connect", (args) => this.onConnect(args)) + ipc.of.benchmarkServer?.on("disconnect", (args) => this.onDisconnect(args)) + ipc.of.benchmarkServer?.on("message", (data) => this.onMessage(data)) + }) + } + + private onConnect(args: unknown) { + if (this._isConnected) { + return + } + + this.log("[client#onConnect]", args) + this._isConnected = true + this.emit("connect") + } + + private onDisconnect(args: unknown) { + if (!this._isConnected) { + return + } + + this.log("[client#onDisconnect]", args) + this._isConnected = false + this.emit("disconnect") + } + + private onMessage(data: unknown) { + if (typeof data !== "object") { + this._log("[client#onMessage] invalid data", data) + return + } + + const result = ipcServerMessageSchema.safeParse(data) + + if (!result.success) { + this.log("[client#onMessage] invalid payload", result.error) + return + } + + this.emit("message", result.data) + } + + private log(...args: unknown[]) { + this._log(...args) + } + + public sendMessage(message: IpcClientMessage) { + ipc.of.benchmarkServer?.emit("message", message) + } + + public disconnect() { + try { + ipc.disconnect("benchmarkServer") + // @TODO: Should we set _disconnect here? + } catch (error) { + this.log("[client#disconnect] error disconnecting", error) + } + } + + public get socketPath() { + return this._socketPath + } + + public get clientId() { + return this._clientId + } + + public get isConnected() { + return this._isConnected + } + + public get isReady() { + return this._isConnected && this._clientId !== undefined + } +} + +/** + * IpcServer + */ + +export enum IpcServerMessageType { + Ack = "Ack", + TaskEvent = "TaskEvent", +} + +export const ipcServerMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal(IpcServerMessageType.Ack), + data: z.object({ clientId: z.string() }), + }), + z.object({ + type: z.literal(IpcServerMessageType.TaskEvent), + data: z.object({ + eventName: z.string(), + data: z.unknown(), + }), + }), +]) + +export type IpcServerMessage = z.infer + +type IpcServerEvents = { + connect: [id: IpcClientId] + disconnect: [id: IpcClientId] + message: [data: IpcClientMessage] +} + +export class IpcServer extends EventEmitter { + private readonly _socketPath: string + private readonly _log: (...args: unknown[]) => void + private readonly _clients: Map + + private _isListening = false + + constructor(socketPath: string, log = console.log) { + super() + + this._socketPath = socketPath + this._log = log + this._clients = new Map() + } + + public listen() { + this._isListening = true + + ipc.config.silent = true + + ipc.serve(this.socketPath, () => { + ipc.server.on("connect", (socket) => this.onConnect(socket)) + ipc.server.on("socket.disconnected", (socket) => this.onDisconnect(socket)) + ipc.server.on("message", (data) => this.onMessage(data)) + }) + + ipc.server.start() + } + + private onConnect(socket: Socket) { + const clientId = crypto.randomBytes(6).toString("hex") + this._clients.set(clientId, socket) + this.log(`[server#onConnect] clientId = ${clientId}, # clients = ${this._clients.size}`) + this.send(socket, { type: IpcServerMessageType.Ack, data: { clientId } }) + this.emit("connect", clientId) + } + + private onDisconnect(destroyedSocket: Socket) { + let disconnectedClientId: IpcClientId | undefined + + for (const [clientId, socket] of this._clients.entries()) { + if (socket === destroyedSocket) { + disconnectedClientId = clientId + this._clients.delete(clientId) + break + } + } + + this.log(`[server#socket.disconnected] clientId = ${disconnectedClientId}, # clients = ${this._clients.size}`) + + if (disconnectedClientId) { + this.emit("disconnect", disconnectedClientId) + } + } + + private onMessage(data: unknown) { + if (typeof data !== "object") { + this.log("[server#onMessage] invalid data", data) + return + } + + const result = ipcClientMessageSchema.safeParse(data) + + if (!result.success) { + this.log("[server#onMessage] invalid payload", result.error) + return + } + + const payload = result.data + this.emit("message", payload) + } + + private log(...args: unknown[]) { + this._log(...args) + } + + public broadcast(message: IpcServerMessage) { + this.log("[server#broadcast] message =", message) + ipc.server.broadcast("message", message) + } + + public send(client: IpcClientId | Socket, message: IpcServerMessage) { + this.log("[server#send] message =", message) + + if (typeof client === "string") { + const socket = this._clients.get(client) + + if (socket) { + ipc.server.emit(socket, "message", message) + } + } else { + ipc.server.emit(client, "message", message) + } + } + + public get socketPath() { + return this._socketPath + } + + public get isListening() { + return this._isListening + } +} diff --git a/benchmark/packages/ipc/src/ipcClient.ts b/benchmark/packages/ipc/src/ipcClient.ts deleted file mode 100644 index 058477e9b1..0000000000 --- a/benchmark/packages/ipc/src/ipcClient.ts +++ /dev/null @@ -1,123 +0,0 @@ -import EventEmitter from "node:events" -import ipc from "node-ipc" - -import { ClientMessage, ClientMessageType, ServerMessage, ServerMessageType, serverMessageSchema } from "./types.js" - -export interface IpcClientEvents { - connect: [] - message: [data: ServerMessage] - disconnect: [] -} - -export class IpcClient extends EventEmitter { - private readonly _socketPath: string - private readonly _log: (...args: unknown[]) => void - - private _isConnected = false - private _clientId?: string - - constructor(socketPath: string, log = console.log) { - super() - - this._socketPath = socketPath - this._log = log - - ipc.config.silent = true - - ipc.connectTo("benchmarkServer", this.socketPath, () => { - ipc.of.benchmarkServer?.on("connect", (args) => this.onConnect(args)) - ipc.of.benchmarkServer?.on("message", (data) => this.onMessage(data)) - ipc.of.benchmarkServer?.on("disconnect", (args) => this.onDisconnect(args)) - }) - } - - private onConnect(args: unknown) { - if (this._isConnected) { - return - } - - this.log("[client#onConnect]", args) - this._isConnected = true - this.emit("connect") - } - - private onMessage(data: unknown) { - if (typeof data !== "object") { - this._log("[client#onMessage] invalid data", data) - return - } - - const result = serverMessageSchema.safeParse(data) - - if (!result.success) { - this.log("[client#onMessage] invalid payload", result.error) - return - } - - const payload = result.data - - switch (payload.type) { - case ServerMessageType.Hello: - this.log(`[client#Hello] ${payload.data.clientId}`) - this._clientId = payload.data.clientId - break - case ServerMessageType.Pong: - this.log(`[client#Pong]`) - break - } - - this.emit("message", payload) - } - - private onDisconnect(args: unknown) { - if (!this._isConnected) { - return - } - - this.log("[client#onDisconnect]", args) - this._isConnected = false - this.emit("disconnect") - } - - public sendMessage(message: ClientMessage) { - ipc.of.benchmarkServer?.emit("message", message) - } - - private log(...args: unknown[]) { - this._log(...args) - } - - public ping() { - if (!this.isReady) { - return false - } - - this.sendMessage({ type: ClientMessageType.Ping, data: { clientId: this._clientId! } }) - return true - } - - public disconnect() { - try { - ipc.disconnect("benchmarkServer") - // @TODO: Should we set _disconnect here? - } catch (error) { - this.log("[client#disconnect] error disconnecting", error) - } - } - - public get socketPath() { - return this._socketPath - } - - public get clientId() { - return this._clientId - } - - public get isConnected() { - return this._isConnected - } - - public get isReady() { - return this._isConnected && this._clientId !== undefined - } -} diff --git a/benchmark/packages/ipc/src/ipcServer.ts b/benchmark/packages/ipc/src/ipcServer.ts deleted file mode 100644 index 0c770c25ae..0000000000 --- a/benchmark/packages/ipc/src/ipcServer.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Socket } from "node:net" -import * as crypto from "node:crypto" -import EventEmitter from "node:events" - -import ipc from "node-ipc" - -import { ClientMessageType, ServerMessage, ServerMessageType, clientMessageSchema } from "./types.js" - -type IpcServerEvents = { - client: [id: string] -} - -export class IpcServer extends EventEmitter { - private readonly _socketPath: string - private readonly _log: (...args: unknown[]) => void - private readonly _clients: Map - - private _isListening = false - - constructor(socketPath: string, log = console.log) { - super() - - this._socketPath = socketPath - this._log = log - this._clients = new Map() - } - - public listen() { - this._isListening = true - - ipc.config.silent = true - - ipc.serve(this.socketPath, () => { - ipc.server.on("connect", (socket) => this.onConnect(socket)) - ipc.server.on("message", (data, socket) => this.onMessage(data, socket)) - ipc.server.on("socket.disconnected", (socket) => this.onDisconnect(socket)) - }) - - ipc.server.start() - } - - private onConnect(socket: Socket) { - const clientId = crypto.randomBytes(6).toString("hex") - this._clients.set(clientId, socket) - this.log(`[server#onConnect] clientId = ${clientId}, # clients = ${this._clients.size}`) - this.send(socket, { type: ServerMessageType.Hello, data: { clientId } }) - this.emit("client", clientId) - } - - private onMessage(data: unknown, socket: Socket) { - if (typeof data !== "object") { - this.log("[server#onMessage] invalid data", data) - return - } - - const result = clientMessageSchema.safeParse(data) - - if (!result.success) { - this.log("[server#onMessage] invalid payload", result.error) - return - } - - const payload = result.data - - switch (payload.type) { - case ClientMessageType.Message: - this.log(`[server#Message] ${payload.data.message}`) - break - case ClientMessageType.Ping: - this.log(`[server#Ping]`) - this.send(socket, { type: ServerMessageType.Pong }) - break - } - } - - private onDisconnect(destroyedSocket: Socket) { - let destroyedClientId: string | undefined - - for (const [clientId, socket] of this._clients.entries()) { - if (socket === destroyedSocket) { - destroyedClientId = clientId - this._clients.delete(clientId) - break - } - } - - this.log(`[server#socket.disconnected] clientId = ${destroyedClientId}, # clients = ${this._clients.size}`) - } - - private log(...args: unknown[]) { - this._log(...args) - } - - public broadcast(message: ServerMessage) { - this.log("[server#broadcast] message =", message) - ipc.server.broadcast("message", message) - } - - public send(client: string | Socket, message: ServerMessage) { - this.log("[server#send] message =", message) - - if (typeof client === "string") { - const socket = this._clients.get(client) - - if (socket) { - ipc.server.emit(socket, "message", message) - } - } else { - ipc.server.emit(client, "message", message) - } - } - - public get socketPath() { - return this._socketPath - } - - public get isListening() { - return this._isListening - } -} diff --git a/benchmark/packages/ipc/src/types.ts b/benchmark/packages/ipc/src/types.ts deleted file mode 100644 index 06459b16b1..0000000000 --- a/benchmark/packages/ipc/src/types.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { z } from "zod" - -/** - * Client - */ - -export enum ClientMessageType { - Ping = "ping", - Message = "message", -} - -export const clientMessageSchema = z.discriminatedUnion("type", [ - z.object({ - type: z.literal(ClientMessageType.Message), - data: z.object({ - clientId: z.string(), - message: z.string(), - }), - }), - z.object({ - type: z.literal(ClientMessageType.Ping), - data: z.object({ - clientId: z.string(), - }), - }), -]) - -export type ClientMessage = z.infer - -/** - * Server - */ - -export enum ServerMessageType { - Hello = "hello", - Pong = "pong", - Message = "message", - Data = "data", -} - -export const serverMessageSchema = z.discriminatedUnion("type", [ - z.object({ - type: z.literal(ServerMessageType.Hello), - data: z.object({ - clientId: z.string(), - }), - }), - z.object({ - type: z.literal(ServerMessageType.Pong), - }), - z.object({ - type: z.literal(ServerMessageType.Message), - data: z.object({ - message: z.string(), - }), - }), - z.object({ - type: z.literal(ServerMessageType.Data), - data: z.unknown(), - }), -]) - -export type ServerMessage = z.infer diff --git a/package-lock.json b/package-lock.json index 73153ad7d7..885601b1e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "js-tiktoken": "^1.0.19", "mammoth": "^1.8.0", "monaco-vscode-textmate-theme-converter": "^0.1.7", + "node-ipc": "^12.0.0", "openai": "^4.78.1", "os-name": "^6.0.0", "p-wait-for": "^5.0.2", @@ -74,6 +75,7 @@ "@types/glob": "^8.1.0", "@types/jest": "^29.5.14", "@types/node": "20.x", + "@types/node-ipc": "^9.2.3", "@types/string-similarity": "^4.0.2", "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.11.0", @@ -6113,6 +6115,16 @@ "form-data": "^4.0.0" } }, + "node_modules/@types/node-ipc": { + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/@types/node-ipc/-/node-ipc-9.2.3.tgz", + "integrity": "sha512-/MvSiF71fYf3+zwqkh/zkVkZj1hl1Uobre9EMFy08mqfJNAmpR0vmPgOUdEIDVgifxHj6G1vYMPLSBLLxoDACQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/pdf-parse": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.4.tgz", @@ -6846,8 +6858,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/bare-events": { "version": "2.5.0", @@ -7521,8 +7532,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/console-control-strings": { "version": "1.1.0", @@ -7574,6 +7584,176 @@ "node": ">=6.6.0" } }, + "node_modules/copyfiles": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", + "license": "MIT", + "dependencies": { + "glob": "^7.0.5", + "minimatch": "^3.0.3", + "mkdirp": "^1.0.4", + "noms": "0.0.0", + "through2": "^2.0.1", + "untildify": "^4.0.0", + "yargs": "^16.1.0" + }, + "bin": { + "copyfiles": "copyfiles", + "copyup": "copyfiles" + } + }, + "node_modules/copyfiles/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/copyfiles/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/copyfiles/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/copyfiles/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/copyfiles/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/copyfiles/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/copyfiles/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/copyfiles/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/copyfiles/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/copyfiles/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/copyfiles/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/copyfiles/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -8041,6 +8221,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/easy-stack": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", + "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/easy-table": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", @@ -8668,6 +8857,28 @@ "node": ">= 0.6" } }, + "node_modules/event-pubsub": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-5.0.3.tgz", + "integrity": "sha512-2QiHxshejKgJrYMzSI9MEHrvhmzxBL+eLyiM5IiyjDBySkgwS2+tdtnO3gbx8pEisu/yOFCIhfCb63gCEu0yBQ==", + "license": "MIT", + "dependencies": { + "copyfiles": "^2.4.0", + "strong-type": "^0.1.3" + }, + "engines": { + "node": ">=13.0.0" + } + }, + "node_modules/event-pubsub/node_modules/strong-type": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/strong-type/-/strong-type-0.1.6.tgz", + "integrity": "sha512-eJe5caH6Pi5oMMeQtIoBPpvNu/s4jiyb63u5tkHNnQXomK+puyQ5i+Z5iTLBr/xUz/pIcps0NSfzzFI34+gAXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -9299,8 +9510,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -10045,7 +10255,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -11395,6 +11604,27 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-message": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", + "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", + "license": "MIT", + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/js-queue": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz", + "integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==", + "license": "MIT", + "dependencies": { + "easy-stack": "^1.0.1" + }, + "engines": { + "node": ">=1.0.0" + } + }, "node_modules/js-tiktoken": { "version": "1.0.19", "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.19.tgz", @@ -12567,12 +12797,61 @@ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, + "node_modules/node-ipc": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-12.0.0.tgz", + "integrity": "sha512-QHJ2gAJiqA3cM7cQiRjLsfCOBRB0TwQ6axYD4FSllQWipEbP6i7Se1dP8EzPKk5J1nCe27W69eqPmCoKyQ61Vg==", + "license": "MIT", + "dependencies": { + "event-pubsub": "5.0.3", + "js-message": "1.0.7", + "js-queue": "2.0.2", + "strong-type": "^1.0.1" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, + "node_modules/noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", + "license": "ISC", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" + } + }, + "node_modules/noms/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/noms/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/noms/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -14951,6 +15230,15 @@ "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, + "node_modules/strong-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strong-type/-/strong-type-1.1.0.tgz", + "integrity": "sha512-X5Z6riticuH5GnhUyzijfDi1SoXas8ODDyN7K8lJeQK+Jfi4dKdoJGL4CXTskY/ATBcN+rz5lROGn1tAUkOX7g==", + "license": "MIT", + "engines": { + "node": ">=12.21.0" + } + }, "node_modules/summary": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/summary/-/summary-2.1.0.tgz", @@ -15089,6 +15377,16 @@ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -15463,6 +15761,15 @@ "node": ">= 0.8" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -16062,6 +16369,15 @@ "node": ">=4.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 03e666cabe..3d5bd35a6e 100644 --- a/package.json +++ b/package.json @@ -370,6 +370,7 @@ "js-tiktoken": "^1.0.19", "mammoth": "^1.8.0", "monaco-vscode-textmate-theme-converter": "^0.1.7", + "node-ipc": "^12.0.0", "openai": "^4.78.1", "os-name": "^6.0.0", "p-wait-for": "^5.0.2", @@ -403,6 +404,7 @@ "@types/glob": "^8.1.0", "@types/jest": "^29.5.14", "@types/node": "20.x", + "@types/node-ipc": "^9.2.3", "@types/string-similarity": "^4.0.2", "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.11.0", diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index fc7d029cf8..629da61526 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -91,9 +91,7 @@ export class ClineProvider extends EventEmitter implements private contextProxy: ContextProxy configManager: ConfigManager customModesManager: CustomModesManager - get cwd() { - return getWorkspacePath() - } + constructor( readonly context: vscode.ExtensionContext, private readonly outputChannel: vscode.OutputChannel, @@ -2745,6 +2743,12 @@ export class ClineProvider extends EventEmitter implements await this.contextProxy.setValues(values) } + // cwd + + get cwd() { + return getWorkspacePath() + } + // dev async resetState() { diff --git a/src/exports/api.ts b/src/exports/api.ts index 5dcbc9747e..5e829671e2 100644 --- a/src/exports/api.ts +++ b/src/exports/api.ts @@ -5,14 +5,16 @@ import { ClineProvider } from "../core/webview/ClineProvider" import { RooCodeAPI, RooCodeEvents, ConfigurationValues, TokenUsage } from "./roo-code" import { MessageHistory } from "./message-history" +import { IpcClientMessageType, IpcServerMessageType, IpcServer } from "./ipc" export class API extends EventEmitter implements RooCodeAPI { private readonly outputChannel: vscode.OutputChannel private readonly provider: ClineProvider private readonly history: MessageHistory private readonly tokenUsage: Record + private readonly ipc?: IpcServer - constructor(outputChannel: vscode.OutputChannel, provider: ClineProvider) { + constructor(outputChannel: vscode.OutputChannel, provider: ClineProvider, socketPath?: string) { super() this.outputChannel = outputChannel @@ -41,6 +43,30 @@ export class API extends EventEmitter implements RooCodeAPI { }) this.on("taskTokenUsageUpdated", (taskId, usage) => (this.tokenUsage[taskId] = usage)) + + if (socketPath) { + this.ipc = new IpcServer(socketPath) + this.ipc.listen() + + this.ipc.on("message", (message) => { + switch (message.type) { + case IpcClientMessageType.StartNewTask: + this.startNewTask(message.data.text, message.data.images) + break + } + }) + } + } + + public override emit( + eventName: K, + ...args: K extends keyof RooCodeEvents ? RooCodeEvents[K] : never + ) { + if (this.ipc) { + this.ipc.broadcast({ type: IpcServerMessageType.TaskEvent, data: { eventName, data: { ...args } } }) + } + + return super.emit(eventName, ...args) } public async startNewTask(text?: string, images?: string[]) { diff --git a/src/exports/ipc.ts b/src/exports/ipc.ts new file mode 100644 index 0000000000..8b0dabcb55 --- /dev/null +++ b/src/exports/ipc.ts @@ -0,0 +1,262 @@ +import EventEmitter from "node:events" +import { Socket } from "node:net" +import * as crypto from "node:crypto" + +import ipc from "node-ipc" +import { z } from "zod" + +/** + * IpcClient + */ + +export type IpcClientId = string + +export enum IpcClientMessageType { + Message = "Message", + StartNewTask = "StartNewTask", +} + +export const ipcClientMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal(IpcClientMessageType.StartNewTask), + data: z.object({ + text: z.string(), + images: z.array(z.string()).optional(), + }), + }), +]) + +export type IpcClientMessage = z.infer + +export interface IpcClientEvents { + connect: [] + disconnect: [] + message: [data: IpcServerMessage] +} + +export class IpcClient extends EventEmitter { + private readonly _socketPath: string + private readonly _log: (...args: unknown[]) => void + + private _isConnected = false + private _clientId?: IpcClientId + + constructor(socketPath: string, log = console.log) { + super() + + this._socketPath = socketPath + this._log = log + + ipc.config.silent = true + + ipc.connectTo("benchmarkServer", this.socketPath, () => { + ipc.of.benchmarkServer?.on("connect", (args) => this.onConnect(args)) + ipc.of.benchmarkServer?.on("disconnect", (args) => this.onDisconnect(args)) + ipc.of.benchmarkServer?.on("message", (data) => this.onMessage(data)) + }) + } + + private onConnect(args: unknown) { + if (this._isConnected) { + return + } + + this.log("[client#onConnect]", args) + this._isConnected = true + this.emit("connect") + } + + private onDisconnect(args: unknown) { + if (!this._isConnected) { + return + } + + this.log("[client#onDisconnect]", args) + this._isConnected = false + this.emit("disconnect") + } + + private onMessage(data: unknown) { + if (typeof data !== "object") { + this._log("[client#onMessage] invalid data", data) + return + } + + const result = ipcServerMessageSchema.safeParse(data) + + if (!result.success) { + this.log("[client#onMessage] invalid payload", result.error) + return + } + + this.emit("message", result.data) + } + + private log(...args: unknown[]) { + this._log(...args) + } + + public sendMessage(message: IpcClientMessage) { + ipc.of.benchmarkServer?.emit("message", message) + } + + public disconnect() { + try { + ipc.disconnect("benchmarkServer") + // @TODO: Should we set _disconnect here? + } catch (error) { + this.log("[client#disconnect] error disconnecting", error) + } + } + + public get socketPath() { + return this._socketPath + } + + public get clientId() { + return this._clientId + } + + public get isConnected() { + return this._isConnected + } + + public get isReady() { + return this._isConnected && this._clientId !== undefined + } +} + +/** + * IpcServer + */ + +export enum IpcServerMessageType { + Ack = "Ack", + TaskEvent = "TaskEvent", +} + +export const ipcServerMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal(IpcServerMessageType.Ack), + data: z.object({ clientId: z.string() }), + }), + z.object({ + type: z.literal(IpcServerMessageType.TaskEvent), + data: z.object({ + eventName: z.string(), + data: z.unknown(), + }), + }), +]) + +export type IpcServerMessage = z.infer + +type IpcServerEvents = { + connect: [id: IpcClientId] + disconnect: [id: IpcClientId] + message: [data: IpcClientMessage] +} + +export class IpcServer extends EventEmitter { + private readonly _socketPath: string + private readonly _log: (...args: unknown[]) => void + private readonly _clients: Map + + private _isListening = false + + constructor(socketPath: string, log = console.log) { + super() + + this._socketPath = socketPath + this._log = log + this._clients = new Map() + } + + public listen() { + this._isListening = true + + ipc.config.silent = true + + ipc.serve(this.socketPath, () => { + ipc.server.on("connect", (socket) => this.onConnect(socket)) + ipc.server.on("socket.disconnected", (socket) => this.onDisconnect(socket)) + ipc.server.on("message", (data) => this.onMessage(data)) + }) + + ipc.server.start() + } + + private onConnect(socket: Socket) { + const clientId = crypto.randomBytes(6).toString("hex") + this._clients.set(clientId, socket) + this.log(`[server#onConnect] clientId = ${clientId}, # clients = ${this._clients.size}`) + this.send(socket, { type: IpcServerMessageType.Ack, data: { clientId } }) + this.emit("connect", clientId) + } + + private onDisconnect(destroyedSocket: Socket) { + let disconnectedClientId: IpcClientId | undefined + + for (const [clientId, socket] of this._clients.entries()) { + if (socket === destroyedSocket) { + disconnectedClientId = clientId + this._clients.delete(clientId) + break + } + } + + this.log(`[server#socket.disconnected] clientId = ${disconnectedClientId}, # clients = ${this._clients.size}`) + + if (disconnectedClientId) { + this.emit("disconnect", disconnectedClientId) + } + } + + private onMessage(data: unknown) { + if (typeof data !== "object") { + this.log("[server#onMessage] invalid data", data) + return + } + + const result = ipcClientMessageSchema.safeParse(data) + + if (!result.success) { + this.log("[server#onMessage] invalid payload", result.error) + return + } + + const payload = result.data + this.emit("message", payload) + } + + private log(...args: unknown[]) { + this._log(...args) + } + + public broadcast(message: IpcServerMessage) { + this.log("[server#broadcast] message =", message) + ipc.server.broadcast("message", message) + } + + public send(client: IpcClientId | Socket, message: IpcServerMessage) { + this.log("[server#send] message =", message) + + if (typeof client === "string") { + const socket = this._clients.get(client) + + if (socket) { + ipc.server.emit(socket, "message", message) + } + } else { + ipc.server.emit(client, "message", message) + } + } + + public get socketPath() { + return this._socketPath + } + + public get isListening() { + return this._isListening + } +} diff --git a/src/extension.ts b/src/extension.ts index 05f8afe969..980d96f42a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -111,7 +111,7 @@ export function activate(context: vscode.ExtensionContext) { registerTerminalActions(context) // Implements the `RooCodeAPI` interface. - return new API(outputChannel, provider) + return new API(outputChannel, provider, "/tmp/roo-code-ipc.sock") } // This method is called when your extension is deactivated