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
cd78de7f56
commit
9b56ad8c35
9 changed files with 136 additions and 69 deletions
|
|
@ -95,23 +95,27 @@ const run = async (toolbox: GluegunToolbox) => {
|
|||
const server = new IpcServer(run.socketPath, () => {})
|
||||
server.listen()
|
||||
|
||||
server.on(IpcMessageType.Connect, (clientId) => {
|
||||
server.send(clientId, {
|
||||
type: IpcMessageType.TaskEvent,
|
||||
origin: IpcOrigin.Server,
|
||||
// TODO: Broacast the set of running tasks.
|
||||
data: { eventName: RooCodeEventName.Connect, taskId: -1 },
|
||||
})
|
||||
})
|
||||
// server.on(IpcMessageType.Connect, (clientId) => {
|
||||
// server.send(clientId, {
|
||||
// type: IpcMessageType.TaskEvent,
|
||||
// origin: IpcOrigin.Server,
|
||||
// data: { eventName: RooCodeEventName.Connect, taskId: -1 },
|
||||
// })
|
||||
// })
|
||||
|
||||
const chunks = inChunksOf(tasks, 3)
|
||||
|
||||
for (const chunk of chunks) {
|
||||
await Promise.all(
|
||||
chunk.map(async (task) => {
|
||||
const runSucceeded = await runExercise({ run, task, server })
|
||||
const passed = runSucceeded ? await runUnitTest({ task }) : false
|
||||
await updateTask(task.id, { passed })
|
||||
if (task.finishedAt === null) {
|
||||
await runExercise({ run, task, server })
|
||||
}
|
||||
|
||||
if (task.passed === null) {
|
||||
const passed = await runUnitTest({ task })
|
||||
await updateTask(task.id, { passed })
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -124,14 +128,7 @@ const run = async (toolbox: GluegunToolbox) => {
|
|||
|
||||
const runExercise = async ({ run, task, server }: { run: Run; task: Task; server: IpcServer }) => {
|
||||
const { language, exercise } = task
|
||||
|
||||
if (task.finishedAt) {
|
||||
console.log(`Test result exists for ${language} / ${exercise}, skipping`)
|
||||
return false
|
||||
}
|
||||
|
||||
const prompt = fs.readFileSync(path.resolve(exercisesPath, `prompts/${language}.md`), "utf-8")
|
||||
|
||||
const dirname = path.dirname(run.socketPath)
|
||||
const taskSocketPath = path.resolve(dirname, `${dirname}/task-${task.id}.sock`)
|
||||
|
||||
|
|
@ -157,11 +154,10 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
|
|||
if (!client.isReady) {
|
||||
client.disconnect()
|
||||
console.log(`[cli#runExercise | ${language} / ${exercise}] unable to connect`)
|
||||
return false
|
||||
return
|
||||
}
|
||||
|
||||
let isTaskFinished = false
|
||||
let isTaskAborted = false
|
||||
|
||||
client.on(IpcMessageType.Disconnect, () => {
|
||||
console.log(`[cli#runExercise | ${language} / ${exercise}] disconnect`)
|
||||
|
|
@ -176,6 +172,7 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
|
|||
|
||||
let taskStartedAt = Date.now()
|
||||
let taskMetricsId: number | undefined
|
||||
let rooTaskId: string | undefined
|
||||
|
||||
client.on(IpcMessageType.TaskEvent, async (taskEvent) => {
|
||||
const { eventName, payload } = taskEvent
|
||||
|
|
@ -191,14 +188,6 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
|
|||
console.log(`[cli#runExercise | ${language} / ${exercise}] taskEvent -> ${eventName}`)
|
||||
}
|
||||
|
||||
// if (eventName === RooCodeEventName.Message) {
|
||||
// const { message: { ts, text, partial } } = payload[0]
|
||||
|
||||
// if (!partial) {
|
||||
// console.log(`${ts}: ${text}`)
|
||||
// }
|
||||
// }
|
||||
|
||||
if (eventName === RooCodeEventName.TaskStarted) {
|
||||
taskStartedAt = Date.now()
|
||||
|
||||
|
|
@ -216,6 +205,7 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
|
|||
|
||||
taskStartedAt = Date.now()
|
||||
taskMetricsId = taskMetrics.id
|
||||
rooTaskId = payload[0]
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
@ -238,14 +228,10 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
|
|||
})
|
||||
}
|
||||
|
||||
if (eventName === RooCodeEventName.TaskCompleted) {
|
||||
if (eventName === RooCodeEventName.TaskCompleted || eventName === RooCodeEventName.TaskAborted) {
|
||||
await updateTask(task.id, { finishedAt: new Date() })
|
||||
isTaskFinished = true
|
||||
}
|
||||
|
||||
if (eventName === RooCodeEventName.TaskAborted) {
|
||||
isTaskAborted = true
|
||||
}
|
||||
})
|
||||
|
||||
client.sendMessage({
|
||||
|
|
@ -269,13 +255,33 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
|
|||
console.log(`[cli#runExercise | ${language} / ${exercise}] starting task`)
|
||||
|
||||
try {
|
||||
await pWaitFor(() => isTaskFinished || isTaskAborted, { interval: 1_000, timeout: 1 * 60 * 1_000 })
|
||||
await pWaitFor(() => isTaskFinished, { interval: 1_000, timeout: 1 * 60 * 1_000 })
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (error) {
|
||||
console.log(`[cli#runExercise | ${language} / ${exercise}] time limit reached`)
|
||||
|
||||
if (rooTaskId) {
|
||||
client.sendMessage({
|
||||
type: IpcMessageType.TaskCommand,
|
||||
origin: IpcOrigin.Client,
|
||||
clientId: client.clientId!,
|
||||
data: { commandName: TaskCommandName.CancelTask, data: rooTaskId },
|
||||
})
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 2_000))
|
||||
}
|
||||
|
||||
await updateTask(task.id, { finishedAt: new Date() })
|
||||
}
|
||||
|
||||
try {
|
||||
client.sendMessage({
|
||||
type: IpcMessageType.VSCodeCommand,
|
||||
origin: IpcOrigin.Client,
|
||||
clientId: client.clientId!,
|
||||
data: "workbench.action.files.saveFiles",
|
||||
})
|
||||
|
||||
client.sendMessage({
|
||||
type: IpcMessageType.VSCodeCommand,
|
||||
origin: IpcOrigin.Client,
|
||||
|
|
@ -287,8 +293,6 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
|
|||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
return isTaskFinished
|
||||
}
|
||||
|
||||
const runUnitTest = async ({ task }: { task: Task }) => {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import { TaskStatus } from "./task-status"
|
|||
import { ConnectionStatus } from "./connection-status"
|
||||
|
||||
export function Run({ run }: { run: db.Run }) {
|
||||
const { tasks, status, runningTaskId, output, outputCounts } = useRunStatus(run)
|
||||
const { tasks, status, output, outputCounts } = useRunStatus(run)
|
||||
const scrollAreaRef = useRef<HTMLDivElement>(null)
|
||||
const [selectedTask, setSelectedTask] = useState<db.Task>()
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ export function Run({ run }: { run: db.Run }) {
|
|||
<TableRow key={task.id}>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
<TaskStatus task={task} runningTaskId={runningTaskId} />
|
||||
<TaskStatus task={task} />
|
||||
<div>
|
||||
{task.language}/{task.exercise}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,19 +4,18 @@ import { type Task } from "@benchmark/db"
|
|||
|
||||
type TaskStatusProps = {
|
||||
task: Task
|
||||
runningTaskId?: number
|
||||
}
|
||||
|
||||
export const TaskStatus = ({ task, runningTaskId }: TaskStatusProps) => {
|
||||
return runningTaskId === task.id ? (
|
||||
<LoaderCircle className="size-4 animate-spin" />
|
||||
) : !task.finishedAt ? (
|
||||
<CircleDashed className="size-4" />
|
||||
) : task.passed === false ? (
|
||||
export const TaskStatus = ({ task }: TaskStatusProps) => {
|
||||
return task.passed === false ? (
|
||||
<CircleSlash className="size-4 text-destructive" />
|
||||
) : task.passed === true ? (
|
||||
<CircleCheck className="size-4 text-green-500" />
|
||||
) : (
|
||||
) : task.startedAt ? (
|
||||
<LoaderCircle className="size-4 animate-spin" />
|
||||
) : task.finishedAt ? (
|
||||
<LoaderCircle className="size-4 animate-spin" />
|
||||
) : (
|
||||
<CircleDashed className="size-4" />
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ import { getTasks } from "@/lib/server/tasks"
|
|||
import { useEventSource } from "@/hooks/use-event-source"
|
||||
|
||||
export const useRunStatus = (run: Run) => {
|
||||
const [runningTaskId, setRunningTaskId] = useState<number>()
|
||||
const [tasksUpdatedAt, setTasksUpdatedAt] = useState<number>()
|
||||
const outputRef = useRef<Map<number, string[]>>(new Map())
|
||||
const [outputCounts, setOutputCounts] = useState<Record<number, number>>({})
|
||||
|
||||
const { data: tasks } = useQuery({
|
||||
queryKey: ["run", run.id, runningTaskId],
|
||||
queryKey: ["run", run.id, tasksUpdatedAt],
|
||||
queryFn: async () => getTasks(run.id),
|
||||
placeholderData: keepPreviousData,
|
||||
refetchInterval: 10_000,
|
||||
|
|
@ -46,13 +46,10 @@ export const useRunStatus = (run: Run) => {
|
|||
}
|
||||
|
||||
switch (eventName) {
|
||||
case RooCodeEventName.Connect:
|
||||
case RooCodeEventName.TaskCreated:
|
||||
case RooCodeEventName.TaskStarted:
|
||||
setRunningTaskId(taskId)
|
||||
break
|
||||
case RooCodeEventName.TaskCompleted:
|
||||
setRunningTaskId(undefined)
|
||||
case RooCodeEventName.TaskAborted:
|
||||
setTasksUpdatedAt(Date.now())
|
||||
break
|
||||
case RooCodeEventName.Message: {
|
||||
const [
|
||||
|
|
@ -79,5 +76,5 @@ export const useRunStatus = (run: Run) => {
|
|||
|
||||
const status = useEventSource({ url, onMessage })
|
||||
|
||||
return { tasks, status, runningTaskId, output: outputRef.current, outputCounts }
|
||||
return { tasks, status, output: outputRef.current, outputCounts }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ import * as crypto from "node:crypto"
|
|||
|
||||
import ipc from "node-ipc"
|
||||
|
||||
import { IpcOrigin, IpcMessageType, IpcMessage, ipcMessageSchema, TaskCommand, TaskEvent } from "@benchmark/types"
|
||||
import { IpcOrigin, IpcMessageType, IpcMessage, ipcMessageSchema, Ack, TaskCommand, TaskEvent } from "@benchmark/types"
|
||||
|
||||
export type IpcClientEvents = {
|
||||
[IpcMessageType.Connect]: []
|
||||
[IpcMessageType.Disconnect]: []
|
||||
[IpcMessageType.Ack]: [clientId: string]
|
||||
[IpcMessageType.Ack]: [data: Ack]
|
||||
[IpcMessageType.TaskCommand]: [data: TaskCommand]
|
||||
[IpcMessageType.TaskEvent]: [data: TaskEvent]
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ export class IpcClient extends EventEmitter<IpcClientEvents> {
|
|||
switch (payload.type) {
|
||||
case IpcMessageType.Ack:
|
||||
this._clientId = payload.data.clientId
|
||||
this.emit(IpcMessageType.Ack, payload.data.clientId)
|
||||
this.emit(IpcMessageType.Ack, payload.data)
|
||||
break
|
||||
case IpcMessageType.TaskEvent:
|
||||
this.emit(IpcMessageType.TaskEvent, payload.data)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,13 @@ export class IpcServer extends EventEmitter<IpcServerEvents> {
|
|||
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.send(socket, {
|
||||
type: IpcMessageType.Ack,
|
||||
origin: IpcOrigin.Server,
|
||||
data: { clientId, pid: process.pid, ppid: process.ppid },
|
||||
})
|
||||
|
||||
this.emit(IpcMessageType.Connect, clientId)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,25 @@ import { z } from "zod"
|
|||
|
||||
import { RooCodeEventName, rooCodeEventsSchema, rooCodeSettingsSchema } from "./roo-code.js"
|
||||
|
||||
/**
|
||||
* Ack
|
||||
*/
|
||||
|
||||
export const ackSchema = z.object({
|
||||
clientId: z.string(),
|
||||
pid: z.number(),
|
||||
ppid: z.number(),
|
||||
})
|
||||
|
||||
export type Ack = z.infer<typeof ackSchema>
|
||||
|
||||
/**
|
||||
* TaskCommand
|
||||
*/
|
||||
|
||||
export enum TaskCommandName {
|
||||
StartNewTask = "StartNewTask",
|
||||
CancelTask = "CancelTask",
|
||||
}
|
||||
|
||||
export const taskCommandSchema = z.discriminatedUnion("commandName", [
|
||||
|
|
@ -20,6 +33,10 @@ export const taskCommandSchema = z.discriminatedUnion("commandName", [
|
|||
newTab: z.boolean().optional(),
|
||||
}),
|
||||
}),
|
||||
z.object({
|
||||
commandName: z.literal(TaskCommandName.CancelTask),
|
||||
data: z.string(),
|
||||
}),
|
||||
])
|
||||
|
||||
export type TaskCommand = z.infer<typeof taskCommandSchema>
|
||||
|
|
@ -110,7 +127,7 @@ export const ipcMessageSchema = z.discriminatedUnion("type", [
|
|||
z.object({
|
||||
type: z.literal(IpcMessageType.Ack),
|
||||
origin: z.literal(IpcOrigin.Server),
|
||||
data: z.object({ clientId: z.string() }),
|
||||
data: ackSchema,
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(IpcMessageType.TaskCommand),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
private tabProvider?: ClineProvider
|
||||
private readonly context: vscode.ExtensionContext
|
||||
private readonly ipc?: IpcServer
|
||||
private readonly taskMap = new Map<string, ClineProvider>()
|
||||
|
||||
constructor(outputChannel: vscode.OutputChannel, provider: ClineProvider, socketPath?: string) {
|
||||
super()
|
||||
|
|
@ -39,7 +40,10 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
|
||||
switch (commandName) {
|
||||
case TaskCommandName.StartNewTask:
|
||||
this.startNewTask(data)
|
||||
await this.startNewTask(data)
|
||||
break
|
||||
case TaskCommandName.CancelTask:
|
||||
await this.cancelTask(data)
|
||||
break
|
||||
}
|
||||
})
|
||||
|
|
@ -102,7 +106,6 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
await provider.postMessageToWebview({ type: "invoke", invoke: "newChat", text, images })
|
||||
|
||||
const { taskId } = await provider.initClineWithTask(text, images)
|
||||
|
||||
return taskId
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +117,15 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
await this.sidebarProvider.finishSubTask(lastMessage)
|
||||
}
|
||||
|
||||
public async cancelTask(taskId: string) {
|
||||
const provider = this.taskMap.get(taskId)
|
||||
|
||||
if (provider) {
|
||||
await provider.cancelTask()
|
||||
this.taskMap.delete(taskId)
|
||||
}
|
||||
}
|
||||
|
||||
public async cancelCurrentTask() {
|
||||
await this.sidebarProvider.cancelTask()
|
||||
}
|
||||
|
|
@ -156,18 +168,33 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
|
|||
|
||||
private registerListeners(provider: ClineProvider) {
|
||||
provider.on("clineCreated", (cline) => {
|
||||
cline.on("taskStarted", () => {
|
||||
this.emit(RooCodeEventName.TaskStarted, cline.taskId)
|
||||
this.taskMap.set(cline.taskId, provider)
|
||||
})
|
||||
|
||||
cline.on("message", (message) => this.emit(RooCodeEventName.Message, { taskId: cline.taskId, ...message }))
|
||||
cline.on("taskStarted", () => this.emit(RooCodeEventName.TaskStarted, cline.taskId))
|
||||
cline.on("taskPaused", () => this.emit(RooCodeEventName.TaskPaused, cline.taskId))
|
||||
cline.on("taskUnpaused", () => this.emit(RooCodeEventName.TaskUnpaused, cline.taskId))
|
||||
cline.on("taskAskResponded", () => this.emit(RooCodeEventName.TaskAskResponded, cline.taskId))
|
||||
cline.on("taskAborted", () => this.emit(RooCodeEventName.TaskAborted, cline.taskId))
|
||||
cline.on("taskSpawned", (childTaskId) => this.emit(RooCodeEventName.TaskSpawned, cline.taskId, childTaskId))
|
||||
cline.on("taskCompleted", (_, usage) => this.emit(RooCodeEventName.TaskCompleted, cline.taskId, usage))
|
||||
|
||||
cline.on("taskTokenUsageUpdated", (_, usage) =>
|
||||
this.emit(RooCodeEventName.TaskTokenUsageUpdated, cline.taskId, usage),
|
||||
)
|
||||
|
||||
cline.on("taskAskResponded", () => this.emit(RooCodeEventName.TaskAskResponded, cline.taskId))
|
||||
|
||||
cline.on("taskAborted", () => {
|
||||
this.emit(RooCodeEventName.TaskAborted, cline.taskId)
|
||||
this.taskMap.delete(cline.taskId)
|
||||
})
|
||||
|
||||
cline.on("taskCompleted", (_, usage) => {
|
||||
this.emit(RooCodeEventName.TaskCompleted, cline.taskId, usage)
|
||||
this.taskMap.delete(cline.taskId)
|
||||
})
|
||||
|
||||
cline.on("taskSpawned", (childTaskId) => this.emit(RooCodeEventName.TaskSpawned, cline.taskId, childTaskId))
|
||||
cline.on("taskPaused", () => this.emit(RooCodeEventName.TaskPaused, cline.taskId))
|
||||
cline.on("taskUnpaused", () => this.emit(RooCodeEventName.TaskUnpaused, cline.taskId))
|
||||
|
||||
this.emit(RooCodeEventName.TaskCreated, cline.taskId)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,25 @@ import { z } from "zod"
|
|||
|
||||
import { RooCodeEventName, rooCodeEventsSchema, rooCodeSettingsSchema } from "./index"
|
||||
|
||||
/**
|
||||
* Ack
|
||||
*/
|
||||
|
||||
export const ackSchema = z.object({
|
||||
clientId: z.string(),
|
||||
pid: z.number(),
|
||||
ppid: z.number(),
|
||||
})
|
||||
|
||||
export type Ack = z.infer<typeof ackSchema>
|
||||
|
||||
/**
|
||||
* TaskCommand
|
||||
*/
|
||||
|
||||
export enum TaskCommandName {
|
||||
StartNewTask = "StartNewTask",
|
||||
CancelTask = "CancelTask",
|
||||
}
|
||||
|
||||
export const taskCommandSchema = z.discriminatedUnion("commandName", [
|
||||
|
|
@ -20,6 +33,10 @@ export const taskCommandSchema = z.discriminatedUnion("commandName", [
|
|||
newTab: z.boolean().optional(),
|
||||
}),
|
||||
}),
|
||||
z.object({
|
||||
commandName: z.literal(TaskCommandName.CancelTask),
|
||||
data: z.string(),
|
||||
}),
|
||||
])
|
||||
|
||||
export type TaskCommand = z.infer<typeof taskCommandSchema>
|
||||
|
|
@ -95,7 +112,7 @@ export const ipcMessageSchema = z.discriminatedUnion("type", [
|
|||
z.object({
|
||||
type: z.literal(IpcMessageType.Ack),
|
||||
origin: z.literal(IpcOrigin.Server),
|
||||
data: z.object({ clientId: z.string(), pid: z.number(), ppid: z.number() }),
|
||||
data: ackSchema,
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(IpcMessageType.TaskCommand),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue