Finish manually fixing linter errors for tools dir (partial).
- More changes are to come, this is truly a partial change in order to not disrupt as many people as possible. Part of https://b.corp.google.com/issues/411384603
This commit is contained in:
parent
328846c6e3
commit
abb60a4d10
|
@ -49,30 +49,10 @@ export interface FileEntry {
|
||||||
modifiedTime: Date;
|
modifiedTime: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Result from the LS tool
|
|
||||||
*/
|
|
||||||
export interface LSToolResult extends ToolResult {
|
|
||||||
/**
|
|
||||||
* List of file entries
|
|
||||||
*/
|
|
||||||
entries: FileEntry[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The directory that was listed
|
|
||||||
*/
|
|
||||||
listedPath: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Total number of entries found
|
|
||||||
*/
|
|
||||||
totalEntries: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the LS tool that lists directory contents
|
* Implementation of the LS tool that lists directory contents
|
||||||
*/
|
*/
|
||||||
export class LSTool extends BaseTool<LSToolParams, LSToolResult> {
|
export class LSTool extends BaseTool<LSToolParams, ToolResult> {
|
||||||
/**
|
/**
|
||||||
* The root directory that this tool is grounded in.
|
* The root directory that this tool is grounded in.
|
||||||
* All path operations will be restricted to this directory.
|
* All path operations will be restricted to this directory.
|
||||||
|
@ -184,12 +164,9 @@ export class LSTool extends BaseTool<LSToolParams, LSToolResult> {
|
||||||
return shortenPath(relativePath);
|
return shortenPath(relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private errorResult(params: LSToolParams, llmContent: string, returnDisplay: string): LSToolResult {
|
private errorResult(llmContent: string, returnDisplay: string): ToolResult {
|
||||||
return {
|
return {
|
||||||
entries: [],
|
llmContent,
|
||||||
listedPath: params.path,
|
|
||||||
totalEntries: 0,
|
|
||||||
llmContent: llmContent,
|
|
||||||
returnDisplay: `**Error:** ${returnDisplay}`,
|
returnDisplay: `**Error:** ${returnDisplay}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -199,11 +176,10 @@ export class LSTool extends BaseTool<LSToolParams, LSToolResult> {
|
||||||
* @param params Parameters for the LS operation
|
* @param params Parameters for the LS operation
|
||||||
* @returns Result of the LS operation
|
* @returns Result of the LS operation
|
||||||
*/
|
*/
|
||||||
async execute(params: LSToolParams): Promise<LSToolResult> {
|
async execute(params: LSToolParams): Promise<ToolResult> {
|
||||||
const validationError = this.validateToolParams(params);
|
const validationError = this.validateToolParams(params);
|
||||||
if (validationError) {
|
if (validationError) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
params,
|
|
||||||
`Error: Invalid parameters provided. Reason: ${validationError}`,
|
`Error: Invalid parameters provided. Reason: ${validationError}`,
|
||||||
`Failed to execute tool.`);
|
`Failed to execute tool.`);
|
||||||
}
|
}
|
||||||
|
@ -212,13 +188,11 @@ export class LSTool extends BaseTool<LSToolParams, LSToolResult> {
|
||||||
const stats = fs.statSync(params.path);
|
const stats = fs.statSync(params.path);
|
||||||
if (!stats) {
|
if (!stats) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
params,
|
|
||||||
`Directory does not exist: ${params.path}`,
|
`Directory does not exist: ${params.path}`,
|
||||||
`Directory does not exist.`);
|
`Directory does not exist.`);
|
||||||
}
|
}
|
||||||
if (!stats.isDirectory()) {
|
if (!stats.isDirectory()) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
params,
|
|
||||||
`Path is not a directory: ${params.path}`,
|
`Path is not a directory: ${params.path}`,
|
||||||
`Path is not a directory.`);
|
`Path is not a directory.`);
|
||||||
}
|
}
|
||||||
|
@ -227,7 +201,6 @@ export class LSTool extends BaseTool<LSToolParams, LSToolResult> {
|
||||||
const entries: FileEntry[] = [];
|
const entries: FileEntry[] = [];
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
params,
|
|
||||||
`Directory is empty: ${params.path}`,
|
`Directory is empty: ${params.path}`,
|
||||||
`Directory is empty.`);
|
`Directory is empty.`);
|
||||||
}
|
}
|
||||||
|
@ -270,15 +243,11 @@ export class LSTool extends BaseTool<LSToolParams, LSToolResult> {
|
||||||
.join('\n');
|
.join('\n');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entries,
|
|
||||||
listedPath: params.path,
|
|
||||||
totalEntries: entries.length,
|
|
||||||
llmContent: `Directory listing for ${params.path}:\n${directoryContent}`,
|
llmContent: `Directory listing for ${params.path}:\n${directoryContent}`,
|
||||||
returnDisplay: `Found ${entries.length} item(s).`,
|
returnDisplay: `Found ${entries.length} item(s).`,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return this.errorResult(
|
return this.errorResult(
|
||||||
params,
|
|
||||||
`Error listing directory: ${error instanceof Error ? error.message : String(error)}`,
|
`Error listing directory: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
'Failed to list directory.');
|
'Failed to list directory.');
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import {
|
||||||
spawn,
|
spawn,
|
||||||
SpawnOptions,
|
SpawnOptions,
|
||||||
ChildProcessWithoutNullStreams,
|
ChildProcessWithoutNullStreams,
|
||||||
exec,
|
|
||||||
} from 'child_process'; // Added 'exec'
|
} from 'child_process'; // Added 'exec'
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
@ -16,6 +15,7 @@ import {
|
||||||
ToolExecuteConfirmationDetails,
|
ToolExecuteConfirmationDetails,
|
||||||
} from '../ui/types.js'; // Adjust path as needed
|
} from '../ui/types.js'; // Adjust path as needed
|
||||||
import { BackgroundTerminalAnalyzer } from '../utils/BackgroundTerminalAnalyzer.js';
|
import { BackgroundTerminalAnalyzer } from '../utils/BackgroundTerminalAnalyzer.js';
|
||||||
|
import { getErrorMessage, isNodeError } from '../utils/errors.js';
|
||||||
|
|
||||||
// --- Interfaces ---
|
// --- Interfaces ---
|
||||||
export interface TerminalToolParams {
|
export interface TerminalToolParams {
|
||||||
|
@ -25,19 +25,11 @@ export interface TerminalToolParams {
|
||||||
runInBackground?: boolean;
|
runInBackground?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TerminalToolResult extends ToolResult {
|
|
||||||
// Add specific fields if needed for structured output from polling/LLM
|
|
||||||
// finalStdout?: string;
|
|
||||||
// finalStderr?: string;
|
|
||||||
// llmAnalysis?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Constants ---
|
// --- Constants ---
|
||||||
const MAX_OUTPUT_LENGTH = 10000; // Default max output length
|
const MAX_OUTPUT_LENGTH = 10000; // Default max output length
|
||||||
const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes (for foreground commands)
|
const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes (for foreground commands)
|
||||||
const MAX_TIMEOUT_OVERRIDE_MS = 10 * 60 * 1000; // 10 minutes (max override for foreground)
|
const MAX_TIMEOUT_OVERRIDE_MS = 10 * 60 * 1000; // 10 minutes (max override for foreground)
|
||||||
const BACKGROUND_LAUNCH_TIMEOUT_MS = 15 * 1000; // 15 seconds timeout for *launching* background tasks
|
const BACKGROUND_LAUNCH_TIMEOUT_MS = 15 * 1000; // 15 seconds timeout for *launching* background tasks
|
||||||
const BACKGROUND_POLL_INTERVAL_MS = 5000; // 5 seconds interval for checking background process status
|
|
||||||
const BACKGROUND_POLL_TIMEOUT_MS = 30000; // 30 seconds total polling time for background process status
|
const BACKGROUND_POLL_TIMEOUT_MS = 30000; // 30 seconds total polling time for background process status
|
||||||
|
|
||||||
const BANNED_COMMAND_ROOTS = [
|
const BANNED_COMMAND_ROOTS = [
|
||||||
|
@ -115,7 +107,7 @@ const BANNED_COMMAND_ROOTS = [
|
||||||
// --- Helper Type for Command Queue ---
|
// --- Helper Type for Command Queue ---
|
||||||
interface QueuedCommand {
|
interface QueuedCommand {
|
||||||
params: TerminalToolParams;
|
params: TerminalToolParams;
|
||||||
resolve: (result: TerminalToolResult) => void;
|
resolve: (result: ToolResult) => void;
|
||||||
reject: (error: Error) => void;
|
reject: (error: Error) => void;
|
||||||
confirmationDetails: ToolExecuteConfirmationDetails | false; // Kept for potential future use
|
confirmationDetails: ToolExecuteConfirmationDetails | false; // Kept for potential future use
|
||||||
}
|
}
|
||||||
|
@ -125,7 +117,7 @@ interface QueuedCommand {
|
||||||
*/
|
*/
|
||||||
export class TerminalTool extends BaseTool<
|
export class TerminalTool extends BaseTool<
|
||||||
TerminalToolParams,
|
TerminalToolParams,
|
||||||
TerminalToolResult
|
ToolResult
|
||||||
> {
|
> {
|
||||||
static Name: string = 'execute_bash_command';
|
static Name: string = 'execute_bash_command';
|
||||||
|
|
||||||
|
@ -139,7 +131,7 @@ export class TerminalTool extends BaseTool<
|
||||||
private shouldAlwaysExecuteCommands: Map<string, boolean> = new Map(); // Track confirmation per root command
|
private shouldAlwaysExecuteCommands: Map<string, boolean> = new Map(); // Track confirmation per root command
|
||||||
private shellReady: Promise<void>;
|
private shellReady: Promise<void>;
|
||||||
private resolveShellReady: (() => void) | undefined; // Definite assignment assertion
|
private resolveShellReady: (() => void) | undefined; // Definite assignment assertion
|
||||||
private rejectShellReady: ((reason?: any) => void) | undefined; // Definite assignment assertion
|
private rejectShellReady: ((reason?: unknown) => void) | undefined; // Definite assignment assertion
|
||||||
private readonly backgroundTerminalAnalyzer: BackgroundTerminalAnalyzer;
|
private readonly backgroundTerminalAnalyzer: BackgroundTerminalAnalyzer;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -238,7 +230,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
if (this.bashProcess) {
|
if (this.bashProcess) {
|
||||||
try {
|
try {
|
||||||
this.bashProcess.kill();
|
this.bashProcess.kill();
|
||||||
} catch (e) {
|
} catch {
|
||||||
/* Ignore */
|
/* Ignore */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,12 +298,12 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, 1000); // Increase readiness check timeout slightly
|
}, 1000); // Increase readiness check timeout slightly
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error('Failed to spawn persistent bash:', error);
|
console.error('Failed to spawn persistent bash:', error);
|
||||||
this.rejectShellReady?.(error); // Use optional chaining
|
this.rejectShellReady?.(error); // Use optional chaining
|
||||||
this.bashProcess = null;
|
this.bashProcess = null;
|
||||||
this.clearQueue(
|
this.clearQueue(
|
||||||
new Error(`Failed to spawn persistent bash: ${error.message}`),
|
new Error(`Failed to spawn persistent bash: ${getErrorMessage(error)}`),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,7 +323,6 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
if (!commandOriginal) {
|
if (!commandOriginal) {
|
||||||
return 'Command cannot be empty.';
|
return 'Command cannot be empty.';
|
||||||
}
|
}
|
||||||
const commandLower = commandOriginal.toLowerCase();
|
|
||||||
const commandParts = commandOriginal.split(/[\s;&&|]+/);
|
const commandParts = commandOriginal.split(/[\s;&&|]+/);
|
||||||
|
|
||||||
for (const part of commandParts) {
|
for (const part of commandParts) {
|
||||||
|
@ -340,7 +331,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
const cleanPart =
|
const cleanPart =
|
||||||
part
|
part
|
||||||
.replace(/^[^a-zA-Z0-9]+/, '')
|
.replace(/^[^a-zA-Z0-9]+/, '')
|
||||||
.split(/[\/\\]/)
|
.split(/[/\\]/)
|
||||||
.pop() || part.replace(/^[^a-zA-Z0-9]+/, '');
|
.pop() || part.replace(/^[^a-zA-Z0-9]+/, '');
|
||||||
if (cleanPart && BANNED_COMMAND_ROOTS.includes(cleanPart.toLowerCase())) {
|
if (cleanPart && BANNED_COMMAND_ROOTS.includes(cleanPart.toLowerCase())) {
|
||||||
return `Command contains a banned keyword: '${cleanPart}'. Banned list includes network tools, session control, etc.`;
|
return `Command contains a banned keyword: '${cleanPart}'. Banned list includes network tools, session control, etc.`;
|
||||||
|
@ -354,12 +345,6 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
return 'Timeout must be a positive number of milliseconds.';
|
return 'Timeout must be a positive number of milliseconds.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relax the absolute path restriction slightly if needed, but generally good practice
|
|
||||||
// const firstCommandPart = commandParts[0];
|
|
||||||
// if (firstCommandPart && (firstCommandPart.startsWith('/') || firstCommandPart.startsWith('\\'))) {
|
|
||||||
// return 'Executing commands via absolute paths (starting with \'/\' or \'\\\') is restricted. Use commands available in PATH or relative paths.';
|
|
||||||
// }
|
|
||||||
|
|
||||||
return null; // Parameters are valid
|
return null; // Parameters are valid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +360,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
params.command
|
params.command
|
||||||
.trim()
|
.trim()
|
||||||
.split(/[\s;&&|]+/)[0]
|
.split(/[\s;&&|]+/)[0]
|
||||||
?.split(/[\/\\]/)
|
?.split(/[/\\]/)
|
||||||
.pop() || 'unknown';
|
.pop() || 'unknown';
|
||||||
|
|
||||||
if (this.shouldAlwaysExecuteCommands.get(rootCommand)) {
|
if (this.shouldAlwaysExecuteCommands.get(rootCommand)) {
|
||||||
|
@ -399,7 +384,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Command Execution and Queueing (unchanged structure) ---
|
// --- Command Execution and Queueing (unchanged structure) ---
|
||||||
async execute(params: TerminalToolParams): Promise<TerminalToolResult> {
|
async execute(params: TerminalToolParams): Promise<ToolResult> {
|
||||||
const validationError = this.validateToolParams(params);
|
const validationError = this.validateToolParams(params);
|
||||||
if (validationError) {
|
if (validationError) {
|
||||||
return {
|
return {
|
||||||
|
@ -447,9 +432,14 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
// **** Core execution logic call ****
|
// **** Core execution logic call ****
|
||||||
const result = await this.executeCommandInShell(params);
|
const result = await this.executeCommandInShell(params);
|
||||||
resolve(result); // Resolve the specific command's promise
|
resolve(result); // Resolve the specific command's promise
|
||||||
} catch (error: any) {
|
} catch (error: unknown) {
|
||||||
console.error(`Error executing command "${params.command}":`, error);
|
console.error(`Error executing command "${params.command}":`, error);
|
||||||
reject(error); // Use the specific command's reject handler
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
reject(new Error('Unknown error occurred: ' + JSON.stringify(error)));
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.isExecuting = false;
|
this.isExecuting = false;
|
||||||
// Use setImmediate to avoid potential deep recursion
|
// Use setImmediate to avoid potential deep recursion
|
||||||
|
@ -460,16 +450,16 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
// --- **** MODIFIED: Core Command Execution Logic **** ---
|
// --- **** MODIFIED: Core Command Execution Logic **** ---
|
||||||
private executeCommandInShell(
|
private executeCommandInShell(
|
||||||
params: TerminalToolParams,
|
params: TerminalToolParams,
|
||||||
): Promise<TerminalToolResult> {
|
): Promise<ToolResult> {
|
||||||
// Define temp file paths here to be accessible throughout
|
// Define temp file paths here to be accessible throughout
|
||||||
let tempStdoutPath: string | null = null;
|
let tempStdoutPath: string | null = null;
|
||||||
let tempStderrPath: string | null = null;
|
let tempStderrPath: string | null = null;
|
||||||
let originalResolve: (
|
let originalResolve: (
|
||||||
value: TerminalToolResult | PromiseLike<TerminalToolResult>,
|
value: ToolResult | PromiseLike<ToolResult>,
|
||||||
) => void; // To pass to polling
|
) => void; // To pass to polling
|
||||||
let originalReject: (reason?: any) => void;
|
let originalReject: (reason?: unknown) => void;
|
||||||
|
|
||||||
const promise = new Promise<TerminalToolResult>((resolve, reject) => {
|
const promise = new Promise<ToolResult>((resolve, reject) => {
|
||||||
originalResolve = resolve; // Assign outer scope resolve
|
originalResolve = resolve; // Assign outer scope resolve
|
||||||
originalReject = reject; // Assign outer scope reject
|
originalReject = reject; // Assign outer scope reject
|
||||||
|
|
||||||
|
@ -492,11 +482,11 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
const tempDir = os.tmpdir();
|
const tempDir = os.tmpdir();
|
||||||
tempStdoutPath = path.join(tempDir, `term_out_${commandUUID}.log`);
|
tempStdoutPath = path.join(tempDir, `term_out_${commandUUID}.log`);
|
||||||
tempStderrPath = path.join(tempDir, `term_err_${commandUUID}.log`);
|
tempStderrPath = path.join(tempDir, `term_err_${commandUUID}.log`);
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
// If temp dir setup fails, reject immediately
|
// If temp dir setup fails, reject immediately
|
||||||
return reject(
|
return reject(
|
||||||
new Error(
|
new Error(
|
||||||
`Failed to determine temporary directory: ${err.message}`,
|
`Failed to determine temporary directory: ${getErrorMessage(err)}`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -530,7 +520,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
if (!isBackgroundTask && this.bashProcess && !this.bashProcess.killed) {
|
if (!isBackgroundTask && this.bashProcess && !this.bashProcess.killed) {
|
||||||
try {
|
try {
|
||||||
this.bashProcess.stdin.write('\x03'); // Ctrl+C for foreground timeout
|
this.bashProcess.stdin.write('\x03'); // Ctrl+C for foreground timeout
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
console.error('Error writing SIGINT on timeout:', e);
|
console.error('Error writing SIGINT on timeout:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -664,8 +654,8 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
// --- Cleanup Logic ---
|
// --- Cleanup Logic ---
|
||||||
// Pass listeners to allow cleanup even if they are nullified later
|
// Pass listeners to allow cleanup even if they are nullified later
|
||||||
const cleanupListeners = (listeners?: {
|
const cleanupListeners = (listeners?: {
|
||||||
onStdoutData: any;
|
onStdoutData: ((data: Buffer) => void) | null;
|
||||||
onStderrData: any;
|
onStderrData: ((data: Buffer) => void) | null;
|
||||||
}) => {
|
}) => {
|
||||||
if (launchTimeoutId) clearTimeout(launchTimeoutId);
|
if (launchTimeoutId) clearTimeout(launchTimeoutId);
|
||||||
launchTimeoutId = null;
|
launchTimeoutId = null;
|
||||||
|
@ -750,10 +740,10 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
if (this.currentCwd !== latestCwd) {
|
if (this.currentCwd !== latestCwd) {
|
||||||
this.currentCwd = latestCwd;
|
this.currentCwd = latestCwd;
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
// Only warn if the command itself succeeded
|
// Only warn if the command itself succeeded
|
||||||
cwdUpdateError = `\nWarning: Failed to verify/update current working directory after command: ${e.message}`;
|
cwdUpdateError = `\nWarning: Failed to verify/update current working directory after command: ${getErrorMessage(e)}`;
|
||||||
console.error(
|
console.error(
|
||||||
'Failed to update CWD after successful command:',
|
'Failed to update CWD after successful command:',
|
||||||
e,
|
e,
|
||||||
|
@ -916,7 +906,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
'Shell stdin is not writable or process closed when attempting to write command.',
|
'Shell stdin is not writable or process closed when attempting to write command.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
console.error(
|
console.error(
|
||||||
`Error writing command "${params.command}" to bash stdin (sync):`,
|
`Error writing command "${params.command}" to bash stdin (sync):`,
|
||||||
e,
|
e,
|
||||||
|
@ -931,7 +921,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
}
|
}
|
||||||
originalReject(
|
originalReject(
|
||||||
new Error(
|
new Error(
|
||||||
`Shell stdin write exception: ${e.message}. Command likely did not execute.`,
|
`Shell stdin write exception: ${getErrorMessage(e)}. Command likely did not execute.`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -950,7 +940,7 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
tempStdoutPath: string, // Path to redirected stdout
|
tempStdoutPath: string, // Path to redirected stdout
|
||||||
tempStderrPath: string, // Path to redirected stderr
|
tempStderrPath: string, // Path to redirected stderr
|
||||||
resolve: (
|
resolve: (
|
||||||
value: TerminalToolResult | PromiseLike<TerminalToolResult>,
|
value: ToolResult | PromiseLike<ToolResult>,
|
||||||
) => void, // The original promise's resolve
|
) => void, // The original promise's resolve
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// This function manages its own lifecycle but resolves the outer promise
|
// This function manages its own lifecycle but resolves the outer promise
|
||||||
|
@ -969,21 +959,21 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
);
|
);
|
||||||
if (status === 'Unknown') llmAnalysis = `LLM analysis failed: ${summary}`;
|
if (status === 'Unknown') llmAnalysis = `LLM analysis failed: ${summary}`;
|
||||||
else llmAnalysis = summary;
|
else llmAnalysis = summary;
|
||||||
} catch (llmError: any) {
|
} catch (llmerror: unknown) {
|
||||||
console.error(
|
console.error(
|
||||||
`LLM analysis failed for PID ${pid} command "${command}":`,
|
`LLM analysis failed for PID ${pid} command "${command}":`,
|
||||||
llmError,
|
llmerror,
|
||||||
);
|
);
|
||||||
llmAnalysis = `LLM analysis failed: ${llmError.message}`; // Include error in analysis placeholder
|
llmAnalysis = `LLM analysis failed: ${getErrorMessage(llmerror)}`; // Include error in analysis placeholder
|
||||||
}
|
}
|
||||||
// --- End LLM Call ---
|
// --- End LLM Call ---
|
||||||
|
|
||||||
try {
|
try {
|
||||||
finalStdout = await fs.readFile(tempStdoutPath, 'utf-8');
|
finalStdout = await fs.readFile(tempStdoutPath, 'utf-8');
|
||||||
finalStderr = await fs.readFile(tempStderrPath, 'utf-8');
|
finalStderr = await fs.readFile(tempStderrPath, 'utf-8');
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
console.error(`Error reading temp output files for PID ${pid}:`, err);
|
console.error(`Error reading temp output files for PID ${pid}:`, err);
|
||||||
fileReadError = `\nWarning: Failed to read temporary output files (${err.message}). Final output may be incomplete.`;
|
fileReadError = `\nWarning: Failed to read temporary output files (${getErrorMessage(err)}). Final output may be incomplete.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Clean up temp files ---
|
// --- Clean up temp files ---
|
||||||
|
@ -1009,13 +999,12 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
if (!filePath) return;
|
if (!filePath) return;
|
||||||
try {
|
try {
|
||||||
await fs.unlink(filePath);
|
await fs.unlink(filePath);
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
// Ignore errors like file not found (it might have been deleted already or failed to create)
|
// Ignore errors like file not found (it might have been deleted already or failed to create)
|
||||||
if (err.code !== 'ENOENT') {
|
if (!isNodeError(err) || err.code !== 'ENOENT') {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Failed to delete temporary file '${filePath}': ${err.message}`,
|
`Failed to delete temporary file '${filePath}': ${getErrorMessage(err)}`,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1122,10 +1111,10 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Shell stdin not writable for pwd command.');
|
throw new Error('Shell stdin not writable for pwd command.');
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
console.error('Exception writing pwd command:', e);
|
console.error('Exception writing pwd command:', e);
|
||||||
cleanupPwdListeners(
|
cleanupPwdListeners(
|
||||||
new Error(`Exception writing pwd command: ${e.message}`),
|
new Error(`Exception writing pwd command: ${getErrorMessage(e)}`),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1145,7 +1134,6 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
|
|
||||||
// --- Clear Queue (unchanged) ---
|
// --- Clear Queue (unchanged) ---
|
||||||
private clearQueue(error: Error) {
|
private clearQueue(error: Error) {
|
||||||
const queuedCount = this.commandQueue.length;
|
|
||||||
const queue = this.commandQueue;
|
const queue = this.commandQueue;
|
||||||
this.commandQueue = [];
|
this.commandQueue = [];
|
||||||
queue.forEach(({ resolve, params }) =>
|
queue.forEach(({ resolve, params }) =>
|
||||||
|
@ -1196,13 +1184,12 @@ Use this tool for running build steps (\`npm install\`, \`make\`), linters (\`es
|
||||||
proc.kill('SIGKILL'); // Force kill if needed
|
proc.kill('SIGKILL'); // Force kill if needed
|
||||||
}
|
}
|
||||||
}, 500); // 500ms grace period
|
}, 500); // 500ms grace period
|
||||||
} catch (e: any) {
|
} catch (e: unknown) {
|
||||||
// Catch errors if process already exited etc.
|
// Catch errors if process already exited etc.
|
||||||
console.warn(
|
console.warn(
|
||||||
`Error trying to kill bash process PID: ${pid}: ${e.message}`,
|
`Error trying to kill bash process PID: ${pid}: ${getErrorMessage(e)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: We cannot reliably clean up temp files for background tasks
|
// Note: We cannot reliably clean up temp files for background tasks
|
||||||
|
|
|
@ -99,6 +99,7 @@ export abstract class BaseTool<
|
||||||
* @param params Parameters to validate
|
* @param params Parameters to validate
|
||||||
* @returns An error message string if invalid, null otherwise
|
* @returns An error message string if invalid, null otherwise
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
validateToolParams(params: TParams): string | null {
|
validateToolParams(params: TParams): string | null {
|
||||||
// Implementation would typically use a JSON Schema validator
|
// Implementation would typically use a JSON Schema validator
|
||||||
// This is a placeholder that should be implemented by derived classes
|
// This is a placeholder that should be implemented by derived classes
|
||||||
|
@ -121,6 +122,7 @@ export abstract class BaseTool<
|
||||||
* @returns Whether or not execute should be confirmed by the user.
|
* @returns Whether or not execute should be confirmed by the user.
|
||||||
*/
|
*/
|
||||||
shouldConfirmExecute(
|
shouldConfirmExecute(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
params: TParams,
|
params: TParams,
|
||||||
): Promise<ToolCallConfirmationDetails | false> {
|
): Promise<ToolCallConfirmationDetails | false> {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
|
|
|
@ -25,17 +25,12 @@ export interface WriteFileToolParams {
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Standardized result from the WriteFile tool
|
|
||||||
*/
|
|
||||||
export interface WriteFileToolResult extends ToolResult {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the WriteFile tool that writes files to the filesystem
|
* Implementation of the WriteFile tool that writes files to the filesystem
|
||||||
*/
|
*/
|
||||||
export class WriteFileTool extends BaseTool<
|
export class WriteFileTool extends BaseTool<
|
||||||
WriteFileToolParams,
|
WriteFileToolParams,
|
||||||
WriteFileToolResult
|
ToolResult
|
||||||
> {
|
> {
|
||||||
static readonly Name: string = 'write_file';
|
static readonly Name: string = 'write_file';
|
||||||
private shouldAlwaysWrite = false;
|
private shouldAlwaysWrite = false;
|
||||||
|
@ -143,7 +138,7 @@ export class WriteFileTool extends BaseTool<
|
||||||
let currentContent = '';
|
let currentContent = '';
|
||||||
try {
|
try {
|
||||||
currentContent = fs.readFileSync(params.file_path, 'utf8');
|
currentContent = fs.readFileSync(params.file_path, 'utf8');
|
||||||
} catch (error) {
|
} catch {
|
||||||
// File may not exist, which is fine
|
// File may not exist, which is fine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +179,7 @@ export class WriteFileTool extends BaseTool<
|
||||||
* @param params Parameters for the file writing
|
* @param params Parameters for the file writing
|
||||||
* @returns Result of the file writing operation
|
* @returns Result of the file writing operation
|
||||||
*/
|
*/
|
||||||
async execute(params: WriteFileToolParams): Promise<WriteFileToolResult> {
|
async execute(params: WriteFileToolParams): Promise<ToolResult> {
|
||||||
const validationError = this.validateToolParams(params);
|
const validationError = this.validateToolParams(params);
|
||||||
if (validationError) {
|
if (validationError) {
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in New Issue