Compare commits

...
3 changed files with 115 additions and 7 deletions
+28 -5
View File
@@ -42,12 +42,35 @@ export async function spawnVSCode(workspacePath: string, vsixPath?: string): Pro
stdio: "inherit",
})
// Find the generated VSIX file
// Find the generated VSIX file(s)
const files = fs.readdirSync(clineRoot)
const vsixFile = files.find((file) => file.endsWith(".vsix"))
if (vsixFile) {
vsixPath = path.join(clineRoot, vsixFile)
console.log(`Using built VSIX: ${vsixPath}`)
const vsixFiles = files.filter((file) => file.endsWith(".vsix"))
if (vsixFiles.length > 0) {
// Get file stats to find the most recent one
const vsixFilesWithStats = vsixFiles.map((file) => {
const filePath = path.join(clineRoot, file)
return {
file,
path: filePath,
mtime: fs.statSync(filePath).mtime,
}
})
// Sort by modification time (most recent first)
vsixFilesWithStats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime())
// Use the most recent VSIX
vsixPath = vsixFilesWithStats[0].path
console.log(`Using most recent VSIX: ${vsixPath} (modified ${vsixFilesWithStats[0].mtime.toISOString()})`)
// Log all found VSIX files for debugging
if (vsixFiles.length > 1) {
console.log(`Found ${vsixFiles.length} VSIX files:`)
vsixFilesWithStats.forEach((f) => {
console.log(` - ${f.file} (modified ${f.mtime.toISOString()})`)
})
}
} else {
console.warn("Could not find generated VSIX file")
}
+87
View File
@@ -1,5 +1,6 @@
import { Anthropic } from "@anthropic-ai/sdk"
import cloneDeep from "clone-deep"
import { execa } from "execa"
import getFolderSize from "get-folder-size"
import { setTimeout as setTimeoutPromise } from "node:timers/promises"
import os from "os"
@@ -8,6 +9,8 @@ import pWaitFor from "p-wait-for"
import * as path from "path"
import { serializeError } from "serialize-error"
import * as vscode from "vscode"
import { Logger } from "../../services/logging/Logger"
const { IS_TEST } = process.env
import { ApiHandler, buildApiHandler } from "../../api"
import { AnthropicHandler } from "../../api/providers/anthropic"
import { ClineHandler } from "../../api/providers/cline"
@@ -1131,7 +1134,91 @@ export class Task {
// Tools
/**
* Executes a command directly in Node.js using execa
* This is used in test mode to capture the full output without using the VS Code terminal
* Commands are automatically terminated after 30 seconds using Promise.race
*/
private async executeCommandInNode(command: string): Promise<[boolean, ToolResponse]> {
try {
// Create a child process
const childProcess = execa(command, {
shell: true,
cwd,
reject: false,
all: true, // Merge stdout and stderr
})
// Set up variables to collect output
let output = ""
// Collect output in real-time
if (childProcess.all) {
childProcess.all.on("data", (data) => {
output += data.toString()
})
}
// Create a timeout promise that rejects after 30 seconds
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
if (childProcess.pid) {
childProcess.kill("SIGKILL") // Use SIGKILL for more forceful termination
}
reject(new Error("Command timeout after 30s"))
}, 30000)
})
// Race between command completion and timeout
const result = await Promise.race([childProcess, timeoutPromise]).catch((error) => {
// If we get here due to timeout, return a partial result with timeout flag
Logger.info(`Command timed out after 30s: ${command}`)
return {
stdout: "",
stderr: "",
exitCode: 124, // Standard timeout exit code
timedOut: true,
}
})
// Check if timeout occurred
const wasTerminated = result.timedOut === true
// Use collected output or result output
if (!output) {
output = result.stdout || result.stderr || ""
}
Logger.info(`Command executed in Node: ${command}\nOutput:\n${output}`)
// Add termination message if the command was terminated
if (wasTerminated) {
output += "\nCommand was taking a while to run so it was auto terminated after 30s"
}
// Format the result similar to terminal output
return [
false,
`Command executed${wasTerminated ? " (terminated after 30s)" : ""} with exit code ${
result.exitCode
}.${output.length > 0 ? `\nOutput:\n${output}` : ""}`,
]
} catch (error) {
// Handle any errors that might occur
const errorMessage = error instanceof Error ? error.message : String(error)
return [false, `Error executing command: ${errorMessage}`]
}
}
async executeCommandTool(command: string): Promise<[boolean, ToolResponse]> {
// Check if we're in test mode
if (IS_TEST === "true") {
// In test mode, execute the command directly in Node
Logger.info("Executing command in Node: " + command)
return this.executeCommandInNode(command)
}
Logger.info("Executing command in VS code terminal: " + command)
const terminalInfo = await this.terminalManager.getOrCreateTerminal(cwd)
terminalInfo.terminal.show() // weird visual bug when creating new terminals (even manually) where there's an empty space at the top.
const process = this.terminalManager.runCommand(terminalInfo, command)
-2
View File
@@ -503,8 +503,6 @@ export function createMessageCatcher(webviewProvider: WebviewProvider): vscode.D
// Intercept outgoing messages from extension to webview
webviewProvider.controller.postMessageToWebview = async (message: ExtensionMessage) => {
Logger.log("Cline message received: " + JSON.stringify(message))
// Check for completion_result message
if (message.type === "partialMessage" && message.partialMessage?.say === "completion_result") {
// Complete the current task