diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ad40c00 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,156 @@ +# AI Agent Guidelines + +This document contains critical rules and guidelines for AI agents working on this codebase. + +## Security Rules + +### CRITICAL: No Dynamic Values in Logs + +**All log statements MUST use static strings only. NEVER include dynamic values, regardless of severity.** + +#### Bad Examples (DO NOT DO THIS): +```typescript +// BAD - Contains dynamic values +await logger.info(`Task created: ${taskId}`) +await logger.error(`Failed to process ${filename}`) +console.log(`User ${userId} logged in`) +console.error(`Error for ${provider}:`, error) +``` + +#### Good Examples (DO THIS): +```typescript +// GOOD - Static strings only +await logger.info('Task created') +await logger.error('Failed to process file') +console.log('User logged in') +console.error('Error occurred:', error) +``` + +#### Rationale: +- **Prevents data leakage**: Dynamic values in logs can expose sensitive information (user IDs, file paths, credentials, etc.) to end users +- **Security by default**: Logs are displayed directly in the UI and returned in API responses +- **No exceptions**: This applies to ALL log levels (info, error, success, command, console.log, console.error, console.warn, etc.) + +#### Sensitive Data That Must NEVER Appear in Logs: +- API keys and tokens (ANTHROPIC_API_KEY, OPENAI_API_KEY, GITHUB_TOKEN, etc.) +- Vercel credentials (VERCEL_TOKEN, VERCEL_TEAM_ID, VERCEL_PROJECT_ID) +- User IDs and personal information +- File paths and repository URLs +- Branch names and commit messages +- Error details that may contain sensitive context +- Any dynamic values that could reveal system internals + +### Credential Redaction + +The `redactSensitiveInfo()` function in `lib/utils/logging.ts` automatically redacts known sensitive patterns, but this is a **backup measure only**. The primary defense is to never log dynamic values in the first place. + +#### Current Redaction Patterns: +- API keys (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.) +- GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_) +- Vercel credentials (VERCEL_TOKEN, VERCEL_TEAM_ID, VERCEL_PROJECT_ID) +- Bearer tokens +- JSON fields (teamId, projectId) +- Environment variables containing KEY, TOKEN, SECRET, PASSWORD, TEAM_ID, PROJECT_ID + +## Code Quality Guidelines + +### Logging Best Practices + +1. **Use descriptive static messages** + ```typescript + // Instead of logging the value, log the action + await logger.info('Sandbox created successfully') + await logger.info('Dependencies installed') + await logger.error('Build failed') + ``` + +2. **Server-side logging for debugging** + ```typescript + // Use console.error for server-side debugging (not shown to users) + // But still avoid sensitive data + console.error('Sandbox creation error:', error) + ``` + +3. **Progress updates** + ```typescript + // Use static progress messages + await logger.updateProgress(50, 'Installing dependencies') + await logger.updateProgress(75, 'Running build') + ``` + +### Error Handling + +1. **Generic error messages to users** + ```typescript + await logger.error('Operation failed') + // NOT: await logger.error(`Operation failed: ${error.message}`) + ``` + +2. **Detailed server-side logging** + ```typescript + console.error('Detailed error for debugging:', error) + // This appears in server logs, not user-facing logs + ``` + +## Testing Changes + +When making changes that involve logging: + +1. **Search for dynamic values** + ```bash + # Check for logger statements with template literals + grep -r "logger\.(info|error|success|command)\(\`.*\$\{" . + + # Check for console statements with template literals + grep -r "console\.(log|error|warn|info)\(\`.*\$\{" . + ``` + +2. **Verify no sensitive data exposure** + - Test the feature in the UI + - Check the logs displayed to users + - Ensure no sensitive information is visible + +## Configuration Security + +### Environment Variables + +Never expose these in logs or to the client: +- `VERCEL_TOKEN` - Vercel API token +- `VERCEL_TEAM_ID` - Vercel team identifier +- `VERCEL_PROJECT_ID` - Vercel project identifier +- `ANTHROPIC_API_KEY` - Anthropic/Claude API key +- `OPENAI_API_KEY` - OpenAI API key +- `GITHUB_TOKEN` - GitHub API token +- `JWE_SECRET` - Encryption secret +- `ENCRYPTION_KEY` - Encryption key +- Any user-provided API keys + +### Client-Safe Variables + +Only these variables should be exposed to the client (via `NEXT_PUBLIC_` prefix): +- `NEXT_PUBLIC_AUTH_PROVIDERS` - Available auth providers +- `NEXT_PUBLIC_GITHUB_CLIENT_ID` - GitHub OAuth client ID (public) + +## Compliance Checklist + +Before submitting changes, verify: + +- [ ] No template literals with `${}` in any log statements +- [ ] All logger calls use static strings +- [ ] All console calls use static strings (for user-facing logs) +- [ ] No sensitive data in error messages +- [ ] Tested in UI to confirm no data leakage +- [ ] Server-side debugging logs don't expose credentials + +## Questions? + +If you need to log information for debugging purposes: +1. Use server-side console logs (not shown to users) +2. Still avoid logging sensitive credentials +3. Consider adding better error handling instead of logging details +4. Use generic user-facing messages + +--- + +**Remember: When in doubt, use a static string. No exceptions.** + diff --git a/app/api/tasks/[taskId]/files/route.ts b/app/api/tasks/[taskId]/files/route.ts index c9e2dc4..d71658e 100644 --- a/app/api/tasks/[taskId]/files/route.ts +++ b/app/api/tasks/[taskId]/files/route.ts @@ -104,7 +104,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ } catch (branchError: unknown) { if (branchError && typeof branchError === 'object' && 'status' in branchError && branchError.status === 404) { // Branch doesn't exist yet (task is still processing) - console.log(`Branch ${task.branchName} doesn't exist yet, returning empty file list`) + console.log('Branch does not exist yet, returning empty file list') return NextResponse.json({ success: true, files: [], @@ -144,7 +144,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ masterError.status === 404 ) { // Neither main nor master exists, or head branch doesn't exist - console.log(`Could not compare branches for ${task.branchName}`) + console.log('Could not compare branches') return NextResponse.json({ success: true, files: [], @@ -171,7 +171,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ changes: file.changes || 0, })) || [] - console.log(`Found ${files.length} changed files in branch ${task.branchName}`) + console.log('Found changed files in branch') } catch (error: unknown) { console.error('Error fetching file changes from GitHub:', error) diff --git a/app/api/tasks/[taskId]/route.ts b/app/api/tasks/[taskId]/route.ts index 2502cee..b1e3a5d 100644 --- a/app/api/tasks/[taskId]/route.ts +++ b/app/api/tasks/[taskId]/route.ts @@ -89,7 +89,7 @@ export async function PATCH(request: NextRequest, { params }: RouteParams) { if (killResult.success) { await logger.success('Sandbox killed successfully') } else { - await logger.error(`Failed to kill sandbox: ${killResult.error}`) + await logger.error('Failed to kill sandbox') } } catch (killError) { console.error('Failed to kill sandbox during stop:', killError) diff --git a/app/api/tasks/route.ts b/app/api/tasks/route.ts index e5c9131..2358456 100644 --- a/app/api/tasks/route.ts +++ b/app/api/tasks/route.ts @@ -122,7 +122,7 @@ export async function POST(request: NextRequest) { }) .where(eq(tasks.id, taskId)) - await logger.success(`Generated AI branch name: ${aiBranchName}`) + await logger.success('Generated AI branch name') } catch (error) { console.error('Error generating AI branch name:', error) @@ -139,7 +139,7 @@ export async function POST(request: NextRequest) { .where(eq(tasks.id, taskId)) const logger = createTaskLogger(taskId) - await logger.info(`Using fallback branch name: ${fallbackBranchName}`) + await logger.info('Using fallback branch name') } catch (dbError) { console.error('Error updating task with fallback branch name:', dbError) } @@ -278,7 +278,7 @@ async function processTask( const taskStartTime = Date.now() try { - console.log(`[TASK ${taskId}] Starting task processing at ${new Date().toISOString()}`) + console.log('Starting task processing') // Update task status to processing with real-time logging await logger.updateStatus('processing', 'Task created, preparing to start...') @@ -310,13 +310,13 @@ async function processTask( } if (aiBranchName) { - await logger.info(`Using AI-generated branch name: ${aiBranchName}`) + await logger.info('Using AI-generated branch name') } else { await logger.info('AI branch name not ready, will use fallback during sandbox creation') } - await logger.updateProgress(15, 'Creating sandbox environment...') - console.log(`[TASK ${taskId}] Creating sandbox at ${new Date().toISOString()}`) + await logger.updateProgress(15, 'Creating sandbox environment') + console.log('Creating sandbox') // Create sandbox with progress callback and 5-minute timeout const sandboxResult = await createSandbox( @@ -371,7 +371,7 @@ async function processTask( const { sandbox: createdSandbox, domain, branchName } = sandboxResult sandbox = createdSandbox || null - console.log(`[TASK ${taskId}] Sandbox created successfully at ${new Date().toISOString()}`) + console.log('Sandbox created successfully') // Update sandbox URL and branch name (only update branch name if not already set by AI) const updateData: { sandboxUrl?: string; updatedAt: Date; branchName?: string } = { @@ -393,8 +393,8 @@ async function processTask( } // Log agent execution start - await logger.updateProgress(50, `Installing and executing ${selectedAgent} agent...`) - console.log(`[TASK ${taskId}] Starting ${selectedAgent} agent execution at ${new Date().toISOString()}`) + await logger.updateProgress(50, 'Installing and executing agent') + console.log('Starting agent execution') // Execute selected agent with timeout (different timeouts per agent) const getAgentTimeout = (agent: string) => { @@ -447,9 +447,7 @@ async function processTask( }) if (mcpServers.length > 0) { - await logger.info( - `Found ${mcpServers.length} connected MCP servers: ${mcpServers.map((s) => s.name).join(', ')}`, - ) + await logger.info('Found connected MCP servers') // Store MCP server IDs in the task await db @@ -484,15 +482,15 @@ async function processTask( agentTimeoutPromise, ]) - console.log(`[TASK ${taskId}] Agent execution completed at ${new Date().toISOString()}`) + console.log('Agent execution completed') if (agentResult.success) { // Log agent completion - await logger.success(`${selectedAgent} agent execution completed`) - await logger.info(agentResult.output || 'Code changes applied successfully') + await logger.success('Agent execution completed') + await logger.info('Code changes applied successfully') if (agentResult.agentResponse) { - await logger.info(`Agent Response: ${agentResult.agentResponse}`) + await logger.info('Agent response received') } // Agent execution logs are already logged in real-time by the agent @@ -508,7 +506,7 @@ async function processTask( if (shutdownResult.success) { await logger.success('Sandbox shutdown completed') } else { - await logger.error(`Sandbox shutdown failed: ${shutdownResult.error}`) + await logger.error('Sandbox shutdown failed') } // Check if push failed and handle accordingly @@ -521,14 +519,11 @@ async function processTask( await logger.updateStatus('completed') await logger.updateProgress(100, 'Task completed successfully') - const totalTaskTime = ((Date.now() - taskStartTime) / 1000).toFixed(2) - console.log( - `[TASK ${taskId}] Task completed successfully at ${new Date().toISOString()} (total time: ${totalTaskTime}s)`, - ) + console.log('Task completed successfully') } } else { // Agent failed, but we still want to capture its logs - await logger.error(`${selectedAgent} agent execution failed`) + await logger.error('Agent execution failed') // Agent execution logs are already logged in real-time by the agent // No need to log them again here @@ -546,7 +541,7 @@ async function processTask( if (shutdownResult.success) { await logger.info('Sandbox shutdown completed after error') } else { - await logger.error(`Sandbox shutdown failed: ${shutdownResult.error}`) + await logger.error('Sandbox shutdown failed') } } catch (shutdownError) { console.error('Failed to shutdown sandbox after error:', shutdownError) @@ -557,7 +552,7 @@ async function processTask( const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' // Log the error and update task status - await logger.error(`Error: ${errorMessage}`) + await logger.error('Error occurred during task processing') await logger.updateStatus('error', errorMessage) } } diff --git a/components/task-details.tsx b/components/task-details.tsx index f6a304c..06fc9ab 100644 --- a/components/task-details.tsx +++ b/components/task-details.tsx @@ -306,7 +306,7 @@ export function TaskDetails({ task }: TaskDetailsProps) { newDiffsCache[filename] = result.data } } catch (err) { - console.error(`Error fetching diff for ${filename}:`, err) + console.error('Error fetching diff for file:', err) } }) diff --git a/components/task-form.tsx b/components/task-form.tsx index 3580c1e..b6e07c4 100644 --- a/components/task-form.tsx +++ b/components/task-form.tsx @@ -239,7 +239,7 @@ export function TaskForm({ setLoadingRepos(false) return } catch { - console.warn(`Failed to parse cached repos for ${selectedOwner}, fetching fresh data`) + console.warn('Failed to parse cached repos, fetching fresh data') sessionStorage.removeItem(cacheKey) } } diff --git a/lib/api-keys/user-keys.ts b/lib/api-keys/user-keys.ts index 26b9b44..6dba789 100644 --- a/lib/api-keys/user-keys.ts +++ b/lib/api-keys/user-keys.ts @@ -97,7 +97,7 @@ export async function getUserApiKey(provider: Provider): Promise 0) { - await logger.info(`Adding ${mcpServers.length} MCP servers: ${mcpServers.map((s) => s.name).join(', ')}`) + await logger.info('Adding MCP servers') for (const server of mcpServers) { const serverName = server.name.toLowerCase().replace(/[^a-z0-9]/g, '-') @@ -103,10 +103,10 @@ export async function installClaudeCLI( const addResult = await runCommandInSandbox(sandbox, 'sh', ['-c', addMcpCmd]) if (addResult.success) { - await logger.info(`Successfully added local MCP server: ${server.name}`) + await logger.info('Successfully added local MCP server') } else { const redactedError = redactSensitiveInfo(addResult.error || 'Unknown error') - await logger.info(`Failed to add MCP server ${server.name}: ${redactedError}`) + await logger.info('Failed to add MCP server') } } else { // Remote HTTP/SSE server @@ -123,10 +123,10 @@ export async function installClaudeCLI( const addResult = await runCommandInSandbox(sandbox, 'sh', ['-c', addMcpCmd]) if (addResult.success) { - await logger.info(`Successfully added remote MCP server: ${server.name}`) + await logger.info('Successfully added remote MCP server') } else { const redactedError = redactSensitiveInfo(addResult.error || 'Unknown error') - await logger.info(`Failed to add MCP server ${server.name}: ${redactedError}`) + await logger.info('Failed to add MCP server') } } } @@ -235,9 +235,9 @@ export async function executeClaudeInSandbox( // Check MCP configuration status const mcpList = await runCommandInSandbox(sandbox, 'sh', ['-c', `${envPrefix} claude mcp list`]) - await logger.info(`MCP servers list: ${JSON.stringify(mcpList.output)}`) + await logger.info('MCP servers list retrieved') if (mcpList.error) { - await logger.info(`MCP list error: ${JSON.stringify(mcpList.error)}`) + await logger.info('MCP list error occurred') } // Try multiple command formats to see what works @@ -274,7 +274,7 @@ export async function executeClaudeInSandbox( // Log the output if (result.output && result.output.trim()) { - await logger.info(`everything: ${JSON.stringify(result.output)}`) + await logger.info('Claude CLI execution output available') const redactedOutput = redactSensitiveInfo(result.output.trim()) await logger.info(redactedOutput) @@ -295,12 +295,12 @@ export async function executeClaudeInSandbox( // Log more details for debugging if (logger) { - await logger.info(`Claude CLI exit code: ${result.exitCode}`) + await logger.info('Claude CLI execution completed') if (result.output) { - await logger.info(`Claude CLI output length: ${result.output.length} characters`) + await logger.info('Claude CLI output available') } if (result.error) { - await logger.error(`Claude CLI error: ${result.error}`) + await logger.error('Claude CLI error occurred') } } diff --git a/lib/sandbox/agents/codex.ts b/lib/sandbox/agents/codex.ts index 71168ef..d82ffaa 100644 --- a/lib/sandbox/agents/codex.ts +++ b/lib/sandbox/agents/codex.ts @@ -93,7 +93,7 @@ export async function executeCodexInSandbox( if (logger) { const keyType = isVercelKey ? 'Vercel AI Gateway' : 'OpenAI' - await logger.info(`Using ${keyType} API key: ${apiKey.substring(0, 10)}...${apiKey.substring(apiKey.length - 4)}`) + await logger.info('Using API key for authentication') } // According to the official Codex CLI docs, we should use 'exec' for non-interactive execution @@ -128,7 +128,7 @@ export async function executeCodexInSandbox( }) if (logger) { - await logger.info(`Codex CLI version test: ${versionTestResult.exitCode === 0 ? 'SUCCESS' : 'FAILED'}`) + await logger.info('Codex CLI version test completed') } // Create configuration file based on API key type @@ -170,7 +170,7 @@ log_requests = true // Add MCP servers configuration if provided if (mcpServers && mcpServers.length > 0) { - await logger.info(`Configuring ${mcpServers.length} MCP servers: ${mcpServers.map((s) => s.name).join(', ')}`) + await logger.info('Configuring MCP servers') // Check if we need experimental RMCP client (for remote servers) const hasRemoteServers = mcpServers.some((s) => s.type === 'remote') @@ -203,7 +203,7 @@ command = "${executable}" .join(', ')} }\n` } - await logger.info(`Added local MCP server: ${server.name} (${server.command})`) + await logger.info('Added local MCP server') } else { // Remote HTTP/SSE server configToml += ` @@ -215,7 +215,7 @@ url = "${server.baseUrl}" configToml += `bearer_token = "${server.oauthClientSecret}"\n` } - await logger.info(`Added remote MCP server: ${server.name} (${server.baseUrl})`) + await logger.info('Added remote MCP server') } } } @@ -232,7 +232,7 @@ url = "${server.baseUrl}" }) if (logger) { - await logger.info(`Codex config setup: ${configSetupResult.exitCode === 0 ? 'SUCCESS' : 'FAILED'}`) + await logger.info('Codex config setup completed') } // Debug: Check if the config file was created correctly (without logging sensitive contents) @@ -250,13 +250,13 @@ url = "${server.baseUrl}" // Debug: List files in the current directory before running Codex const lsDebugResult = await runCommandInSandbox(sandbox, 'ls', ['-la']) if (logger) { - await logger.info(`Current directory contents:\n${lsDebugResult.output || 'No output from ls -la'}`) + await logger.info('Current directory contents retrieved') } // Debug: Show current working directory const pwdResult = await runCommandInSandbox(sandbox, 'pwd', []) if (logger) { - await logger.info(`Current working directory: ${pwdResult.output || 'Unknown'}`) + await logger.info('Current working directory retrieved') } // Use exec command with Vercel AI Gateway configuration diff --git a/lib/sandbox/agents/cursor.ts b/lib/sandbox/agents/cursor.ts index 61c5ca8..d4d21bd 100644 --- a/lib/sandbox/agents/cursor.ts +++ b/lib/sandbox/agents/cursor.ts @@ -61,7 +61,7 @@ export async function executeCursorInSandbox( for (const checkCmd of postInstallChecks) { const checkResult = await runAndLogCommand(sandbox, 'sh', ['-c', checkCmd], logger) if (logger && checkResult.output) { - await logger.info(`Post-install check "${checkCmd}": ${checkResult.output}`) + await logger.info('Post-install check completed') } } @@ -114,7 +114,7 @@ export async function executeCursorInSandbox( for (const searchCmd of searchPaths) { const searchResult = await runAndLogCommand(sandbox, 'sh', ['-c', searchCmd], logger) if (logger && searchResult.output) { - await logger.info(`Search result for "${searchCmd}": ${searchResult.output}`) + await logger.info('Search completed') } } @@ -138,7 +138,7 @@ export async function executeCursorInSandbox( // Configure MCP servers if provided if (mcpServers && mcpServers.length > 0) { - await logger.info(`Configuring ${mcpServers.length} MCP servers: ${mcpServers.map((s) => s.name).join(', ')}`) + await logger.info('Configuring MCP servers') // Create mcp.json configuration file const mcpConfig: { @@ -166,7 +166,7 @@ export async function executeCursorInSandbox( try { envObject = JSON.parse(server.env) } catch (e) { - await logger.info(`Warning: Failed to parse env for ${server.name}`) + await logger.info('Warning: Failed to parse env for MCP server') } } @@ -175,7 +175,7 @@ export async function executeCursorInSandbox( ...(args.length > 0 ? { args } : {}), ...(envObject ? { env: envObject } : {}), } - await logger.info(`Added local MCP server: ${server.name} (${server.command})`) + await logger.info('Added local MCP server') } else { // Remote HTTP/SSE server mcpConfig.mcpServers[serverName] = { @@ -194,7 +194,7 @@ export async function executeCursorInSandbox( mcpConfig.mcpServers[serverName].headers = headers } - await logger.info(`Added remote MCP server: ${server.name} (${server.baseUrl})`) + await logger.info('Added remote MCP server') } } @@ -233,9 +233,9 @@ EOF` logger, ) if (logger) { - await logger.info(`Pre-execution cursor-agent check: ${preExecCheck.success ? 'FOUND' : 'NOT FOUND'}`) + await logger.info('Pre-execution cursor-agent check completed') if (preExecCheck.output) { - await logger.info(`cursor-agent location: ${preExecCheck.output}`) + await logger.info('cursor-agent location found') } } @@ -248,7 +248,7 @@ EOF` if (logger) { await logger.command(logCommand) if (selectedModel) { - await logger.info(`Executing cursor-agent with model: ${selectedModel}`) + await logger.info('Executing cursor-agent with model') } await logger.info('Executing cursor-agent directly without shell wrapper') } @@ -340,11 +340,11 @@ EOF` if (isCompleted) { if (logger) { - await logger.info(`Cursor completed successfully in ${attempts} seconds`) + await logger.info('Cursor completed successfully') } } else { if (logger) { - await logger.info(`Cursor execution ended after ${attempts} seconds, checking for changes...`) + await logger.info('Cursor execution ended, checking for changes') } } @@ -363,7 +363,7 @@ EOF` if (result.error && result.error.trim()) { const redactedError = redactSensitiveInfo(result.error) - await logger.info(`Cursor stderr: ${redactedError}`) + await logger.info('Cursor stderr available') } // Cursor CLI execution completed diff --git a/lib/sandbox/agents/gemini.ts b/lib/sandbox/agents/gemini.ts index dd48b2f..c0d120d 100644 --- a/lib/sandbox/agents/gemini.ts +++ b/lib/sandbox/agents/gemini.ts @@ -88,7 +88,7 @@ export async function executeGeminiInSandbox( // Configure MCP servers if provided if (mcpServers && mcpServers.length > 0) { - await logger.info(`Configuring ${mcpServers.length} MCP servers: ${mcpServers.map((s) => s.name).join(', ')}`) + await logger.info('Configuring MCP servers') // Create Gemini settings.json configuration file const settingsConfig: { @@ -116,7 +116,7 @@ export async function executeGeminiInSandbox( try { envObject = JSON.parse(server.env) } catch (e) { - await logger.info(`Warning: Failed to parse env for ${server.name}`) + await logger.info('Warning: Failed to parse env for MCP server') } } @@ -125,7 +125,7 @@ export async function executeGeminiInSandbox( ...(args.length > 0 ? { args } : {}), ...(envObject ? { env: envObject } : {}), } - await logger.info(`Added local MCP server: ${server.name} (${server.command})`) + await logger.info('Added local MCP server') } else { // Remote HTTP server settingsConfig.mcpServers[serverName] = { @@ -144,7 +144,7 @@ export async function executeGeminiInSandbox( settingsConfig.mcpServers[serverName].headers = headers } - await logger.info(`Added remote MCP server: ${server.name} (${server.baseUrl})`) + await logger.info('Added remote MCP server') } } @@ -205,7 +205,7 @@ EOF` // Add model selection if provided if (selectedModel) { args.push('-m', selectedModel) - await logger.info(`Using model: ${selectedModel}`) + await logger.info('Using selected model') } // Use YOLO mode to auto-approve all tools (bypass approval prompts) @@ -218,7 +218,7 @@ EOF` args.push(instruction) // Log what we're trying to do - await logger.info(`Executing Gemini CLI with ${authMethod} authentication`) + await logger.info('Executing Gemini CLI with authentication') const redactedCommand = `gemini ${args.slice(0, -1).join(' ')} "${instruction.substring(0, 100)}..."` await logger.command(redactedCommand) @@ -285,12 +285,12 @@ EOF` } // Log more details for debugging - await logger.info(`Gemini CLI exit code: ${result.exitCode}`) + await logger.info('Gemini CLI execution completed') if (result.output) { - await logger.info(`Gemini CLI output length: ${result.output.length} characters`) + await logger.info('Gemini CLI output available') } if (result.error) { - await logger.error(`Gemini CLI error: ${result.error}`) + await logger.error('Gemini CLI error occurred') } // Check if any files were modified diff --git a/lib/sandbox/agents/opencode.ts b/lib/sandbox/agents/opencode.ts index a5c3f48..66afcd6 100644 --- a/lib/sandbox/agents/opencode.ts +++ b/lib/sandbox/agents/opencode.ts @@ -98,7 +98,7 @@ export async function executeOpenCodeInSandbox( if (npmBinCheck.success && npmBinCheck.output) { const globalBinPath = npmBinCheck.output.trim() - console.log(`Global npm bin path: ${globalBinPath}`) + console.log('Global npm bin path retrieved') // Try running opencode from the global bin path const directPathCheck = await runAndLogCommand( @@ -134,7 +134,7 @@ export async function executeOpenCodeInSandbox( // Configure MCP servers if provided if (mcpServers && mcpServers.length > 0) { - await logger.info(`Configuring ${mcpServers.length} MCP servers: ${mcpServers.map((s) => s.name).join(', ')}`) + await logger.info('Configuring MCP servers') // Create OpenCode opencode.json configuration file const opencodeConfig: { @@ -162,7 +162,7 @@ export async function executeOpenCodeInSandbox( try { envObject = JSON.parse(server.env) } catch (e) { - await logger.info(`Warning: Failed to parse env for ${server.name}`) + await logger.info('Warning: Failed to parse env for MCP server') } } @@ -173,7 +173,7 @@ export async function executeOpenCodeInSandbox( ...(envObject ? { environment: envObject } : {}), } - await logger.info(`Added local MCP server: ${server.name} (${server.command})`) + await logger.info('Added local MCP server') } else { // Remote MCP server opencodeConfig.mcp[serverName] = { @@ -194,7 +194,7 @@ export async function executeOpenCodeInSandbox( opencodeConfig.mcp[serverName].headers = headers } - await logger.info(`Added remote MCP server: ${server.name} (${server.baseUrl})`) + await logger.info('Added remote MCP server') } } @@ -304,7 +304,7 @@ EOF` if (logger) { await logger.info('Executing OpenCode run command in non-interactive mode...') if (selectedModel) { - await logger.info(`Using selected model: ${selectedModel}`) + await logger.info('Using selected model') } } @@ -357,7 +357,7 @@ EOF` if (hasChanges) { console.log('OpenCode made changes to files:', hasChanges) if (logger) { - await logger.info(`Files changed: ${hasChanges}`) + await logger.info('Files checked for changes') } } diff --git a/lib/sandbox/creation.ts b/lib/sandbox/creation.ts index f4402d4..b6ae25f 100644 --- a/lib/sandbox/creation.ts +++ b/lib/sandbox/creation.ts @@ -32,7 +32,7 @@ async function runAndLogCommand(sandbox: Sandbox, command: string, args: string[ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): Promise { try { - await logger.info(`Repository URL: ${redactSensitiveInfo(config.repoUrl)}`) + await logger.info('Processing repository URL') // Check for cancellation before starting if (config.onCancellationCheck && (await config.onCancellationCheck())) { @@ -117,10 +117,10 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): throw new Error('Sandbox creation timed out. Try with a smaller repository or fewer dependencies.') } - await logger.error(`Sandbox creation failed: ${errorMessage}`) + await logger.error('Sandbox creation failed') if (errorResponse) { - await logger.error(`HTTP Status: ${errorResponse.status}`) - await logger.error(`Response: ${redactSensitiveInfo(JSON.stringify(errorResponse.data))}`) + await logger.error('HTTP error occurred') + await logger.error('Error response received') } throw error } @@ -197,7 +197,7 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): // If primary package manager fails, try npm as fallback (unless it was already npm) if (!installResult.success && packageManager !== 'npm') { - await logger.info(`${packageManager} failed, trying npm as fallback...`) + await logger.info('Package manager failed, trying npm as fallback') if (config.onProgress) { await config.onProgress(37, `${packageManager} failed, trying npm fallback...`) @@ -277,10 +277,10 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): if (!pipInstall.success) { await logger.info('pip install failed') - await logger.info(`pip exit code: ${pipInstall.exitCode}`) + await logger.info('pip install failed with exit code') - if (pipInstall.output) await logger.info(`pip stdout: ${pipInstall.output}`) - if (pipInstall.error) await logger.info(`pip stderr: ${pipInstall.error}`) + if (pipInstall.output) await logger.info('pip stdout available') + if (pipInstall.error) await logger.info('pip stderr available') // Don't throw error, just log it and continue await logger.info('Warning: Failed to install Python dependencies, but continuing with sandbox setup') @@ -298,10 +298,10 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): // Log sandbox readiness based on project type if (packageJsonCheck.success) { await logger.info('Node.js project detected, sandbox ready for development') - await logger.info(`Sandbox available at: ${domain}`) + await logger.info('Sandbox available') } else if (requirementsTxtCheck.success) { await logger.info('Python project detected, sandbox ready for development') - await logger.info(`Sandbox available at: ${domain}`) + await logger.info('Sandbox available') // Check if there's a common Python web framework entry point const flaskAppCheck = await runCommandInSandbox(sandbox, 'test', ['-f', 'app.py']) @@ -314,7 +314,7 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): } } else { await logger.info('Project type not detected, sandbox ready for general development') - await logger.info(`Sandbox available at: ${domain}`) + await logger.info('Sandbox available') } // Check for cancellation before Git configuration @@ -341,16 +341,15 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): } // Add debugging information about Git state - await logger.info('Debugging Git repository state...') + await logger.info('Debugging Git repository state') const gitStatusDebug = await runCommandInSandbox(sandbox, 'git', ['status', '--porcelain']) - await logger.info(`Git status (porcelain): ${gitStatusDebug.output || 'Clean working directory'}`) + await logger.info('Git status checked') const gitBranchDebug = await runCommandInSandbox(sandbox, 'git', ['branch', '-a']) - await logger.info(`Available branches: ${gitBranchDebug.output || 'No branches listed'}`) + await logger.info('Git branches checked') const gitRemoteDebug = await runCommandInSandbox(sandbox, 'git', ['remote', '-v']) - const redactedRemotes = gitRemoteDebug.output ? redactSensitiveInfo(gitRemoteDebug.output) : 'No remotes configured' - await logger.info(`Git remotes: ${redactedRemotes}`) + await logger.info('Git remotes checked') // Configure Git to use GitHub token for authentication if (process.env.GITHUB_TOKEN) { @@ -366,25 +365,25 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): if (config.existingBranchName) { // Checkout existing branch for continuing work - await logger.info(`Checking out existing branch: ${config.existingBranchName}`) + await logger.info('Checking out existing branch') const checkoutResult = await runAndLogCommand(sandbox, 'git', ['checkout', config.existingBranchName], logger) if (!checkoutResult.success) { - throw new Error(`Failed to checkout existing branch ${config.existingBranchName}`) + throw new Error('Failed to checkout existing branch') } // Get the latest changes from remote - await logger.info('Pulling latest changes from remote...') + await logger.info('Pulling latest changes from remote') const pullResult = await runAndLogCommand(sandbox, 'git', ['pull', 'origin', config.existingBranchName], logger) if (pullResult.output) { - await logger.info(`Git pull output: ${pullResult.output}`) + await logger.info('Git pull completed') } branchName = config.existingBranchName } else if (config.preDeterminedBranchName) { // Use the AI-generated branch name - await logger.info(`Using pre-determined branch name: ${config.preDeterminedBranchName}`) + await logger.info('Using pre-determined branch name') // First check if the branch already exists locally const branchExistsLocal = await runCommandInSandbox(sandbox, 'git', [ @@ -396,7 +395,7 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): if (branchExistsLocal.success) { // Branch exists locally, just check it out - await logger.info(`Branch ${config.preDeterminedBranchName} already exists locally, checking it out`) + await logger.info('Branch already exists locally, checking it out') const checkoutBranch = await runAndLogCommand( sandbox, 'git', @@ -405,10 +404,8 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): ) if (!checkoutBranch.success) { - await logger.info( - `Failed to checkout existing branch ${config.preDeterminedBranchName}: ${checkoutBranch.error}`, - ) - throw new Error(`Failed to checkout Git branch ${config.preDeterminedBranchName}`) + await logger.info('Failed to checkout existing branch') + throw new Error('Failed to checkout Git branch') } branchName = config.preDeterminedBranchName @@ -423,7 +420,7 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): if (branchExistsRemote.success && branchExistsRemote.output?.trim()) { // Branch exists on remote, check it out and track it - await logger.info(`Branch ${config.preDeterminedBranchName} exists on remote, checking it out`) + await logger.info('Branch exists on remote, checking it out') const checkoutRemoteBranch = await runAndLogCommand( sandbox, 'git', @@ -432,16 +429,14 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): ) if (!checkoutRemoteBranch.success) { - await logger.info( - `Failed to checkout remote branch ${config.preDeterminedBranchName}: ${checkoutRemoteBranch.error}`, - ) - throw new Error(`Failed to checkout remote Git branch ${config.preDeterminedBranchName}`) + await logger.info('Failed to checkout remote branch') + throw new Error('Failed to checkout remote Git branch') } branchName = config.preDeterminedBranchName } else { // Branch doesn't exist, create it - await logger.info(`Creating new branch: ${config.preDeterminedBranchName}`) + await logger.info('Creating new branch') const createBranch = await runAndLogCommand( sandbox, 'git', @@ -450,16 +445,16 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): ) if (!createBranch.success) { - await logger.info(`Failed to create branch ${config.preDeterminedBranchName}: ${createBranch.error}`) + await logger.info('Failed to create branch') // Add debugging information const gitStatus = await runCommandInSandbox(sandbox, 'git', ['status']) - await logger.info(`Git status: ${gitStatus.output || 'No output'}`) + await logger.info('Git status retrieved') const gitBranch = await runCommandInSandbox(sandbox, 'git', ['branch', '-a']) - await logger.info(`Git branches: ${gitBranch.output || 'No output'}`) - throw new Error(`Failed to create Git branch ${config.preDeterminedBranchName}`) + await logger.info('Git branches retrieved') + throw new Error('Failed to create Git branch') } - await logger.info(`Successfully created branch: ${config.preDeterminedBranchName}`) + await logger.info('Successfully created branch') branchName = config.preDeterminedBranchName } } @@ -469,22 +464,22 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): const suffix = generateId() branchName = `agent/${timestamp}-${suffix}` - await logger.info(`No predetermined branch name, using timestamp-based: ${branchName}`) + await logger.info('No predetermined branch name, using timestamp-based branch') const createBranch = await runAndLogCommand(sandbox, 'git', ['checkout', '-b', branchName], logger) if (!createBranch.success) { - await logger.info(`Failed to create branch ${branchName}: ${createBranch.error}`) + await logger.info('Failed to create branch') // Add debugging information for fallback branch creation too const gitStatus = await runCommandInSandbox(sandbox, 'git', ['status']) - await logger.info(`Git status: ${gitStatus.output || 'No output'}`) + await logger.info('Git status retrieved') const gitBranch = await runCommandInSandbox(sandbox, 'git', ['branch', '-a']) - await logger.info(`Git branches: ${gitBranch.output || 'No output'}`) + await logger.info('Git branches retrieved') const gitLog = await runCommandInSandbox(sandbox, 'git', ['log', '--oneline', '-5']) - await logger.info(`Recent commits: ${gitLog.output || 'No commits'}`) - throw new Error(`Failed to create Git branch ${branchName}`) + await logger.info('Recent commits retrieved') + throw new Error('Failed to create Git branch') } - await logger.info(`Successfully created fallback branch: ${branchName}`) + await logger.info('Successfully created fallback branch') } return { @@ -496,7 +491,7 @@ export async function createSandbox(config: SandboxConfig, logger: TaskLogger): } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' console.error('Sandbox creation error:', error) - await logger.error(`Error: ${errorMessage}`) + await logger.error('Error occurred during sandbox creation') return { success: false, diff --git a/lib/sandbox/git.ts b/lib/sandbox/git.ts index 8042ebe..58b7240 100644 --- a/lib/sandbox/git.ts +++ b/lib/sandbox/git.ts @@ -22,7 +22,7 @@ export async function pushChangesToBranch( // Add all changes const addResult = await runCommandInSandbox(sandbox, 'git', ['add', '.']) if (!addResult.success) { - await logger.info(`Failed to add changes: ${addResult.error}`) + await logger.info('Failed to add changes') return { success: false } } @@ -30,7 +30,7 @@ export async function pushChangesToBranch( const commitResult = await runCommandInSandbox(sandbox, 'git', ['commit', '-m', commitMessage]) if (!commitResult.success) { - await logger.info(`Failed to commit changes: ${commitResult.error}`) + await logger.info('Failed to commit changes') return { success: false } } @@ -40,11 +40,11 @@ export async function pushChangesToBranch( const pushResult = await runCommandInSandbox(sandbox, 'git', ['push', 'origin', branchName]) if (pushResult.success) { - await logger.info(`Successfully pushed changes to branch: ${branchName}`) + await logger.info('Successfully pushed changes to branch') return { success: true } } else { const errorMsg = pushResult.error || 'Unknown error' - await logger.info(`Failed to push to branch ${branchName}: ${errorMsg}`) + await logger.info('Failed to push to branch') // Check if it's a permission issue if (errorMsg.includes('Permission') || errorMsg.includes('access_denied') || errorMsg.includes('403')) { @@ -59,7 +59,7 @@ export async function pushChangesToBranch( } } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred' - await logger.info(`Error pushing changes: ${errorMessage}`) + await logger.info('Error pushing changes') return { success: false } } } diff --git a/lib/sandbox/package-manager.ts b/lib/sandbox/package-manager.ts index 2190f45..6fe70a1 100644 --- a/lib/sandbox/package-manager.ts +++ b/lib/sandbox/package-manager.ts @@ -10,19 +10,19 @@ export async function detectPackageManager(sandbox: Sandbox, logger: TaskLogger) // Check for lock files in order of preference const pnpmLockCheck = await runCommandInSandbox(sandbox, 'test', ['-f', 'pnpm-lock.yaml']) if (pnpmLockCheck.success) { - await logger.info('Detected pnpm (pnpm-lock.yaml found)') + await logger.info('Detected pnpm package manager') return 'pnpm' } const yarnLockCheck = await runCommandInSandbox(sandbox, 'test', ['-f', 'yarn.lock']) if (yarnLockCheck.success) { - await logger.info('Detected yarn (yarn.lock found)') + await logger.info('Detected yarn package manager') return 'yarn' } const npmLockCheck = await runCommandInSandbox(sandbox, 'test', ['-f', 'package-lock.json']) if (npmLockCheck.success) { - await logger.info('Detected npm (package-lock.json found)') + await logger.info('Detected npm package manager') return 'npm' } @@ -50,19 +50,19 @@ export async function installDependencies( if (!configStore.success) { await logger.error('Failed to configure pnpm store directory') } else { - await logger.info('Configured pnpm to use /tmp/pnpm-store') + await logger.info('Configured pnpm store directory') } installCommand = ['pnpm', 'install', '--frozen-lockfile'] - logMessage = `Attempting pnpm install with ${timeoutMinutes}-minute timeout using /tmp/pnpm-store...` + logMessage = 'Attempting pnpm install with timeout' break case 'yarn': installCommand = ['yarn', 'install', '--frozen-lockfile'] - logMessage = `Attempting yarn install with ${timeoutMinutes}-minute timeout...` + logMessage = 'Attempting yarn install with timeout' break case 'npm': installCommand = ['npm', 'install', '--no-audit', '--no-fund'] - logMessage = `Attempting npm install with ${timeoutMinutes}-minute timeout...` + logMessage = 'Attempting npm install with timeout' break } @@ -82,21 +82,21 @@ export async function installDependencies( ]) if (installWithTimeout.success) { - await logger.info(`Node.js dependencies installed with ${packageManager}`) + await logger.info('Node.js dependencies installed') return { success: true } } else if ('timedOut' in installWithTimeout && installWithTimeout.timedOut) { - await logger.error(`${packageManager} install timed out`) + await logger.error('Package manager install timed out') return { success: false, error: installWithTimeout.error } } else { - await logger.error(`${packageManager} install failed`) + await logger.error('Package manager install failed') // Type guard to ensure we have a CommandResult if ('exitCode' in installWithTimeout) { - await logger.error(`${packageManager} exit code: ${installWithTimeout.exitCode}`) - if (installWithTimeout.output) await logger.error(`${packageManager} stdout: ${installWithTimeout.output}`) - if (installWithTimeout.error) await logger.error(`${packageManager} stderr: ${installWithTimeout.error}`) + await logger.error('Install failed with exit code') + if (installWithTimeout.output) await logger.error('Install stdout available') + if (installWithTimeout.error) await logger.error('Install stderr available') } else { - await logger.error(`${packageManager} error: ${installWithTimeout.error}`) + await logger.error('Install error occurred') } return { success: false, error: installWithTimeout.error } diff --git a/lib/session/get-oauth-token.ts b/lib/session/get-oauth-token.ts index 62136f8..3042a31 100644 --- a/lib/session/get-oauth-token.ts +++ b/lib/session/get-oauth-token.ts @@ -78,7 +78,7 @@ export async function getOAuthToken( return null } catch (error) { - console.error(`Error fetching ${provider} token for user ${userId}:`, error) + console.error('Error fetching OAuth token:', error) return null } } diff --git a/lib/vercel-client/teams.ts b/lib/vercel-client/teams.ts index ab06f4e..743257e 100644 --- a/lib/vercel-client/teams.ts +++ b/lib/vercel-client/teams.ts @@ -20,6 +20,6 @@ export async function fetchTeams(accessToken: string) { } const { teams } = (await response.json()) as { teams: VercelTeam[] } - console.log(`Successfully fetched ${teams?.length || 0} teams`) + console.log('Successfully fetched teams') return teams || [] }