mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
More progress
This commit is contained in:
parent
85aa329ef0
commit
ac0c172655
9 changed files with 375 additions and 269 deletions
|
|
@ -154,6 +154,7 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
|
|||
}
|
||||
|
||||
let isTaskFinished = false
|
||||
let isTaskAborted = false
|
||||
|
||||
client.on(IpcMessageType.Disconnect, () => {
|
||||
console.log(`[cli#runExercise | ${language} / ${exercise}] disconnect`)
|
||||
|
|
@ -216,39 +217,59 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
|
|||
}
|
||||
|
||||
if (eventName === RooCodeEventName.TaskAborted) {
|
||||
isTaskFinished = true
|
||||
isTaskAborted = true
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`[cli#runExercise | ${language} / ${exercise}] StartNewTask (${taskSocketPath})`)
|
||||
|
||||
client.sendMessage({
|
||||
type: IpcMessageType.TaskCommand,
|
||||
type: IpcMessageType.VSCodeCommand,
|
||||
origin: IpcOrigin.Client,
|
||||
clientId: client.clientId!,
|
||||
data: {
|
||||
commandName: TaskCommandName.StartNewTask,
|
||||
data: {
|
||||
configuration: {
|
||||
...rooCodeDefaults,
|
||||
openRouterApiKey: process.env.OPENROUTER_API_KEY!,
|
||||
...run.settings,
|
||||
},
|
||||
text: prompt,
|
||||
newTab: true,
|
||||
},
|
||||
},
|
||||
data: "workbench.action.closeWindow",
|
||||
})
|
||||
|
||||
// client.sendMessage({
|
||||
// type: IpcMessageType.TaskCommand,
|
||||
// origin: IpcOrigin.Client,
|
||||
// clientId: client.clientId!,
|
||||
// data: {
|
||||
// commandName: TaskCommandName.StartNewTask,
|
||||
// data: {
|
||||
// configuration: {
|
||||
// ...rooCodeDefaults,
|
||||
// openRouterApiKey: process.env.OPENROUTER_API_KEY!,
|
||||
// ...run.settings,
|
||||
// },
|
||||
// text: prompt,
|
||||
// newTab: true,
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
|
||||
console.log(`[cli#runExercise | ${language} / ${exercise}] StartNewTask`)
|
||||
|
||||
try {
|
||||
await pWaitFor(() => isTaskFinished, { interval: 1_000, timeout: 300 * 1_000 })
|
||||
client.disconnect()
|
||||
return true
|
||||
await pWaitFor(() => isTaskFinished || isTaskAborted, { interval: 1_000, timeout: 300 * 1_000 })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
client.disconnect()
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
client.sendMessage({
|
||||
type: IpcMessageType.VSCodeCommand,
|
||||
origin: IpcOrigin.Client,
|
||||
clientId: client.clientId!,
|
||||
data: "workbench.action.closeWindow",
|
||||
})
|
||||
|
||||
console.log(`[cli#runExercise | ${language} / ${exercise}] VSCodeCommand (workbench.action.closeWindow)`)
|
||||
|
||||
client.disconnect()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
return isTaskFinished
|
||||
}
|
||||
|
||||
const runUnitTest = async ({ task }: { task: Task }) => {
|
||||
|
|
|
|||
119
benchmark/packages/ipc/src/client.ts
Normal file
119
benchmark/packages/ipc/src/client.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import EventEmitter from "node:events"
|
||||
import * as crypto from "node:crypto"
|
||||
|
||||
import ipc from "node-ipc"
|
||||
|
||||
import { IpcOrigin, IpcMessageType, IpcMessage, ipcMessageSchema, TaskCommand, TaskEvent } from "@benchmark/types"
|
||||
|
||||
export type IpcClientEvents = {
|
||||
[IpcMessageType.Connect]: []
|
||||
[IpcMessageType.Disconnect]: []
|
||||
[IpcMessageType.Ack]: [clientId: string]
|
||||
[IpcMessageType.TaskCommand]: [data: TaskCommand]
|
||||
[IpcMessageType.TaskEvent]: [data: TaskEvent]
|
||||
}
|
||||
|
||||
export class IpcClient extends EventEmitter<IpcClientEvents> {
|
||||
private readonly _socketPath: string
|
||||
private readonly _id: string
|
||||
private readonly _log: (...args: unknown[]) => void
|
||||
private _isConnected = false
|
||||
private _clientId?: string
|
||||
|
||||
constructor(socketPath: string, log = console.log) {
|
||||
super()
|
||||
|
||||
this._socketPath = socketPath
|
||||
this._id = `benchmark-${crypto.randomBytes(6).toString("hex")}`
|
||||
this._log = log
|
||||
|
||||
ipc.config.silent = true
|
||||
|
||||
ipc.connectTo(this._id, this.socketPath, () => {
|
||||
ipc.of[this._id]?.on("connect", () => this.onConnect())
|
||||
ipc.of[this._id]?.on("disconnect", () => this.onDisconnect())
|
||||
ipc.of[this._id]?.on("message", (data) => this.onMessage(data))
|
||||
})
|
||||
}
|
||||
|
||||
private onConnect() {
|
||||
if (this._isConnected) {
|
||||
return
|
||||
}
|
||||
|
||||
this.log("[client#onConnect]")
|
||||
this._isConnected = true
|
||||
this.emit(IpcMessageType.Connect)
|
||||
}
|
||||
|
||||
private onDisconnect() {
|
||||
if (!this._isConnected) {
|
||||
return
|
||||
}
|
||||
|
||||
this.log("[client#onDisconnect]")
|
||||
this._isConnected = false
|
||||
this.emit(IpcMessageType.Disconnect)
|
||||
}
|
||||
|
||||
private onMessage(data: unknown) {
|
||||
if (typeof data !== "object") {
|
||||
this._log("[client#onMessage] invalid data", data)
|
||||
return
|
||||
}
|
||||
|
||||
const result = ipcMessageSchema.safeParse(data)
|
||||
|
||||
if (!result.success) {
|
||||
this.log("[client#onMessage] invalid payload", data)
|
||||
return
|
||||
}
|
||||
|
||||
const payload = result.data
|
||||
|
||||
if (payload.origin === IpcOrigin.Server) {
|
||||
switch (payload.type) {
|
||||
case IpcMessageType.Ack:
|
||||
this._clientId = payload.data.clientId
|
||||
this.emit(IpcMessageType.Ack, payload.data.clientId)
|
||||
break
|
||||
case IpcMessageType.TaskEvent:
|
||||
this.emit(IpcMessageType.TaskEvent, payload.data)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private log(...args: unknown[]) {
|
||||
this._log(...args)
|
||||
}
|
||||
|
||||
public sendMessage(message: IpcMessage) {
|
||||
ipc.of[this._id]?.emit("message", message)
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
try {
|
||||
ipc.disconnect(this._id)
|
||||
// @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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,246 +1,2 @@
|
|||
import EventEmitter from "node:events"
|
||||
import { Socket } from "node:net"
|
||||
import * as crypto from "node:crypto"
|
||||
|
||||
import ipc from "node-ipc"
|
||||
|
||||
import { IpcOrigin, IpcMessageType, IpcMessage, ipcMessageSchema, TaskCommand, TaskEvent } from "@benchmark/types"
|
||||
|
||||
/**
|
||||
* IpcClient
|
||||
*/
|
||||
|
||||
export type IpcClientEvents = {
|
||||
[IpcMessageType.Connect]: []
|
||||
[IpcMessageType.Disconnect]: []
|
||||
[IpcMessageType.Ack]: [clientId: string]
|
||||
[IpcMessageType.TaskCommand]: [data: TaskCommand]
|
||||
[IpcMessageType.TaskEvent]: [data: TaskEvent]
|
||||
}
|
||||
|
||||
export class IpcClient extends EventEmitter<IpcClientEvents> {
|
||||
private readonly _socketPath: string
|
||||
private readonly _id: string
|
||||
private readonly _log: (...args: unknown[]) => void
|
||||
private _isConnected = false
|
||||
private _clientId?: string
|
||||
|
||||
constructor(socketPath: string, log = console.log) {
|
||||
super()
|
||||
|
||||
this._socketPath = socketPath
|
||||
this._id = `benchmark-${crypto.randomBytes(6).toString("hex")}`
|
||||
this._log = log
|
||||
|
||||
ipc.config.silent = true
|
||||
|
||||
ipc.connectTo(this._id, this.socketPath, () => {
|
||||
ipc.of[this._id]?.on("connect", () => this.onConnect())
|
||||
ipc.of[this._id]?.on("disconnect", () => this.onDisconnect())
|
||||
ipc.of[this._id]?.on("message", (data) => this.onMessage(data))
|
||||
})
|
||||
}
|
||||
|
||||
private onConnect() {
|
||||
if (this._isConnected) {
|
||||
return
|
||||
}
|
||||
|
||||
this.log("[client#onConnect]")
|
||||
this._isConnected = true
|
||||
this.emit(IpcMessageType.Connect)
|
||||
}
|
||||
|
||||
private onDisconnect() {
|
||||
if (!this._isConnected) {
|
||||
return
|
||||
}
|
||||
|
||||
this.log("[client#onDisconnect]")
|
||||
this._isConnected = false
|
||||
this.emit(IpcMessageType.Disconnect)
|
||||
}
|
||||
|
||||
private onMessage(data: unknown) {
|
||||
if (typeof data !== "object") {
|
||||
this._log("[client#onMessage] invalid data", data)
|
||||
return
|
||||
}
|
||||
|
||||
const result = ipcMessageSchema.safeParse(data)
|
||||
|
||||
if (!result.success) {
|
||||
this.log("[client#onMessage] invalid payload", data)
|
||||
return
|
||||
}
|
||||
|
||||
const payload = result.data
|
||||
|
||||
if (payload.origin === IpcOrigin.Server) {
|
||||
switch (payload.type) {
|
||||
case IpcMessageType.Ack:
|
||||
this._clientId = payload.data.clientId
|
||||
this.emit(IpcMessageType.Ack, payload.data.clientId)
|
||||
break
|
||||
case IpcMessageType.TaskEvent:
|
||||
this.emit(IpcMessageType.TaskEvent, payload.data)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private log(...args: unknown[]) {
|
||||
this._log(...args)
|
||||
}
|
||||
|
||||
public sendMessage(message: IpcMessage) {
|
||||
ipc.of[this._id]?.emit("message", message)
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
try {
|
||||
ipc.disconnect(this._id)
|
||||
// @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
|
||||
*/
|
||||
|
||||
type IpcServerEvents = {
|
||||
[IpcMessageType.Connect]: [clientId: string]
|
||||
[IpcMessageType.Disconnect]: [clientId: string]
|
||||
[IpcMessageType.TaskCommand]: [clientId: string, data: TaskCommand]
|
||||
[IpcMessageType.TaskEvent]: [relayClientId: string | undefined, data: TaskEvent]
|
||||
}
|
||||
|
||||
export class IpcServer extends EventEmitter<IpcServerEvents> {
|
||||
private readonly _socketPath: string
|
||||
private readonly _log: (...args: unknown[]) => void
|
||||
private readonly _clients: Map<string, Socket>
|
||||
|
||||
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: IpcMessageType.Ack, origin: IpcOrigin.Server, data: { clientId } })
|
||||
this.emit(IpcMessageType.Connect, clientId)
|
||||
}
|
||||
|
||||
private onDisconnect(destroyedSocket: Socket) {
|
||||
let disconnectedClientId: string | 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(IpcMessageType.Disconnect, disconnectedClientId)
|
||||
}
|
||||
}
|
||||
|
||||
private onMessage(data: unknown) {
|
||||
if (typeof data !== "object") {
|
||||
this.log("[server#onMessage] invalid data", data)
|
||||
return
|
||||
}
|
||||
|
||||
const result = ipcMessageSchema.safeParse(data)
|
||||
|
||||
if (!result.success) {
|
||||
this.log("[server#onMessage] invalid payload", result.error)
|
||||
return
|
||||
}
|
||||
|
||||
const payload = result.data
|
||||
|
||||
if (payload.origin === IpcOrigin.Client) {
|
||||
switch (payload.type) {
|
||||
case IpcMessageType.TaskCommand:
|
||||
this.emit(IpcMessageType.TaskCommand, payload.clientId, payload.data)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private log(...args: unknown[]) {
|
||||
this._log(...args)
|
||||
}
|
||||
|
||||
public broadcast(message: IpcMessage) {
|
||||
this.log("[server#broadcast] message =", message)
|
||||
ipc.server.broadcast("message", message)
|
||||
}
|
||||
|
||||
public send(client: string | Socket, message: IpcMessage) {
|
||||
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
|
||||
}
|
||||
}
|
||||
export * from "./client.js"
|
||||
export * from "./server.js"
|
||||
|
|
|
|||
126
benchmark/packages/ipc/src/server.ts
Normal file
126
benchmark/packages/ipc/src/server.ts
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import EventEmitter from "node:events"
|
||||
import { Socket } from "node:net"
|
||||
import * as crypto from "node:crypto"
|
||||
|
||||
import ipc from "node-ipc"
|
||||
|
||||
import { IpcOrigin, IpcMessageType, IpcMessage, ipcMessageSchema, TaskCommand, TaskEvent } from "@benchmark/types"
|
||||
|
||||
type IpcServerEvents = {
|
||||
[IpcMessageType.Connect]: [clientId: string]
|
||||
[IpcMessageType.Disconnect]: [clientId: string]
|
||||
[IpcMessageType.TaskCommand]: [clientId: string, data: TaskCommand]
|
||||
[IpcMessageType.TaskEvent]: [relayClientId: string | undefined, data: TaskEvent]
|
||||
[IpcMessageType.VSCodeCommand]: [clientId: string, data: string]
|
||||
}
|
||||
|
||||
export class IpcServer extends EventEmitter<IpcServerEvents> {
|
||||
private readonly _socketPath: string
|
||||
private readonly _log: (...args: unknown[]) => void
|
||||
private readonly _clients: Map<string, Socket>
|
||||
|
||||
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: IpcMessageType.Ack, origin: IpcOrigin.Server, data: { clientId } })
|
||||
this.emit(IpcMessageType.Connect, clientId)
|
||||
}
|
||||
|
||||
private onDisconnect(destroyedSocket: Socket) {
|
||||
let disconnectedClientId: string | 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(IpcMessageType.Disconnect, disconnectedClientId)
|
||||
}
|
||||
}
|
||||
|
||||
private onMessage(data: unknown) {
|
||||
if (typeof data !== "object") {
|
||||
this.log("[server#onMessage] invalid data", data)
|
||||
return
|
||||
}
|
||||
|
||||
const result = ipcMessageSchema.safeParse(data)
|
||||
|
||||
if (!result.success) {
|
||||
this.log("[server#onMessage] invalid payload", result.error)
|
||||
return
|
||||
}
|
||||
|
||||
const payload = result.data
|
||||
|
||||
if (payload.origin === IpcOrigin.Client) {
|
||||
switch (payload.type) {
|
||||
case IpcMessageType.TaskCommand:
|
||||
this.emit(IpcMessageType.TaskCommand, payload.clientId, payload.data)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private log(...args: unknown[]) {
|
||||
this._log(...args)
|
||||
}
|
||||
|
||||
public broadcast(message: IpcMessage) {
|
||||
this.log("[server#broadcast] message =", message)
|
||||
ipc.server.broadcast("message", message)
|
||||
}
|
||||
|
||||
public send(client: string | Socket, message: IpcMessage) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -98,6 +98,7 @@ export enum IpcMessageType {
|
|||
Ack = "Ack",
|
||||
TaskCommand = "TaskCommand",
|
||||
TaskEvent = "TaskEvent",
|
||||
VSCodeCommand = "VSCodeCommand",
|
||||
}
|
||||
|
||||
export enum IpcOrigin {
|
||||
|
|
@ -123,6 +124,12 @@ export const ipcMessageSchema = z.discriminatedUnion("type", [
|
|||
relayClientId: z.string().optional(),
|
||||
data: taskEventSchema,
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(IpcMessageType.VSCodeCommand),
|
||||
origin: z.literal(IpcOrigin.Client),
|
||||
clientId: z.string(),
|
||||
data: z.string(),
|
||||
}),
|
||||
])
|
||||
|
||||
export type IpcMessage = z.infer<typeof ipcMessageSchema>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { openClineInNewTab } from "../activate/registerCommands"
|
|||
|
||||
import { RooCodeSettings, RooCodeEvents, RooCodeEventName } from "../schemas"
|
||||
import { IpcOrigin, IpcMessageType, TaskCommandName, TaskEvent } from "../schemas/ipc"
|
||||
import { formatLog } from "./formatLog"
|
||||
import { RooCodeAPI } from "./interface"
|
||||
import { IpcServer } from "./ipc"
|
||||
|
||||
|
|
@ -26,20 +27,28 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
this.registerListeners(this.sidebarProvider)
|
||||
|
||||
if (socketPath) {
|
||||
this.ipc = new IpcServer(socketPath)
|
||||
this.ipc = new IpcServer(socketPath, (...args: unknown[]) => formatLog(this.outputChannel, ...args))
|
||||
|
||||
this.ipc.listen()
|
||||
|
||||
this.outputChannel.appendLine(
|
||||
`IPC server started: socketPath=${socketPath}, pid=${process.pid}, ppid=${process.ppid}`,
|
||||
`[API] ipc server started: socketPath=${socketPath}, pid=${process.pid}, ppid=${process.ppid}`,
|
||||
)
|
||||
|
||||
this.ipc.on(IpcMessageType.TaskCommand, async (_clientId, { commandName, data }) => {
|
||||
this.outputChannel.appendLine(`[API] TaskCommand -> ${commandName}`)
|
||||
|
||||
switch (commandName) {
|
||||
case TaskCommandName.StartNewTask:
|
||||
this.startNewTask(data)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
this.ipc.on(IpcMessageType.VSCodeCommand, async (_clientId, command) => {
|
||||
this.outputChannel.appendLine(`[API] VSCodeCommand -> ${command}`)
|
||||
await vscode.commands.executeCommand(command)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -66,6 +75,8 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
let provider: ClineProvider
|
||||
|
||||
if (newTab) {
|
||||
await vscode.commands.executeCommand("workbench.action.closeAllEditors")
|
||||
|
||||
if (!this.tabProvider) {
|
||||
this.tabProvider = await openClineInNewTab({ context: this.context, outputChannel: this.outputChannel })
|
||||
this.registerListeners(this.tabProvider)
|
||||
|
|
|
|||
52
src/exports/formatLog.ts
Normal file
52
src/exports/formatLog.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* @fileoverview Utility for robust object logging with special handling for various data types
|
||||
*/
|
||||
|
||||
import * as vscode from "vscode"
|
||||
|
||||
/**
|
||||
* Formats and logs values to a VSCode output channel with special handling for various data types
|
||||
*
|
||||
* Features:
|
||||
* - Explicit handling for null and undefined values
|
||||
* - Special handling for Error objects to preserve stack traces
|
||||
* - Handles circular references in objects
|
||||
* - Properly formats special types like BigInt, functions, and symbols
|
||||
* - Pretty prints objects with indentation for better readability
|
||||
*
|
||||
* @param outputChannel - The VSCode output channel to log to
|
||||
* @param args - The values to log
|
||||
*/
|
||||
export function formatLog(outputChannel: vscode.OutputChannel, ...args: unknown[]): void {
|
||||
for (const arg of args) {
|
||||
if (arg === null) {
|
||||
outputChannel.appendLine("null")
|
||||
} else if (arg === undefined) {
|
||||
outputChannel.appendLine("undefined")
|
||||
} else if (typeof arg === "string") {
|
||||
outputChannel.appendLine(arg)
|
||||
} else if (arg instanceof Error) {
|
||||
// Special handling for Error objects to preserve stack traces
|
||||
outputChannel.appendLine(`Error: ${arg.message}\n${arg.stack || ""}`)
|
||||
} else {
|
||||
try {
|
||||
outputChannel.appendLine(
|
||||
JSON.stringify(
|
||||
arg,
|
||||
(key, value) => {
|
||||
// Handle special types that JSON.stringify doesn't handle well
|
||||
if (typeof value === "bigint") return `BigInt(${value})`
|
||||
if (typeof value === "function") return `Function: ${value.name || "anonymous"}`
|
||||
if (typeof value === "symbol") return value.toString()
|
||||
return value
|
||||
},
|
||||
2,
|
||||
),
|
||||
) // Pretty print with 2 spaces
|
||||
} catch (error) {
|
||||
// Handle circular references or other JSON.stringify errors
|
||||
outputChannel.appendLine(`[Non-serializable object: ${Object.prototype.toString.call(arg)}]`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ type IpcServerEvents = {
|
|||
[IpcMessageType.Disconnect]: [clientId: string]
|
||||
[IpcMessageType.TaskCommand]: [clientId: string, data: TaskCommand]
|
||||
[IpcMessageType.TaskEvent]: [relayClientId: string | undefined, data: TaskEvent]
|
||||
[IpcMessageType.VSCodeCommand]: [clientId: string, data: string]
|
||||
}
|
||||
|
||||
export class IpcServer extends EventEmitter<IpcServerEvents> {
|
||||
|
|
@ -98,6 +99,12 @@ export class IpcServer extends EventEmitter<IpcServerEvents> {
|
|||
case IpcMessageType.TaskCommand:
|
||||
this.emit(IpcMessageType.TaskCommand, payload.clientId, payload.data)
|
||||
break
|
||||
case IpcMessageType.VSCodeCommand:
|
||||
this.emit(IpcMessageType.VSCodeCommand, payload.clientId, payload.data)
|
||||
break
|
||||
default:
|
||||
throw new Error(`[server#onMessage] unhandled payload: ${JSON.stringify(payload)}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ export enum IpcMessageType {
|
|||
Ack = "Ack",
|
||||
TaskCommand = "TaskCommand",
|
||||
TaskEvent = "TaskEvent",
|
||||
VSCodeCommand = "VSCodeCommand",
|
||||
}
|
||||
|
||||
export enum IpcOrigin {
|
||||
|
|
@ -108,6 +109,12 @@ export const ipcMessageSchema = z.discriminatedUnion("type", [
|
|||
relayClientId: z.string().optional(),
|
||||
data: taskEventSchema,
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(IpcMessageType.VSCodeCommand),
|
||||
origin: z.literal(IpcOrigin.Client),
|
||||
clientId: z.string(),
|
||||
data: z.string(),
|
||||
}),
|
||||
])
|
||||
|
||||
export type IpcMessage = z.infer<typeof ipcMessageSchema>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue