Files
wehub-resource-sync bb5c75ce05
Component Security Validation / Security Audit (push) Has been cancelled
Deploy to Cloudflare Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:58 +08:00

252 lines
7.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Docker Sandbox Executor
* Runs inside Docker container using Claude Agent SDK
*/
import { query } from '@anthropic-ai/claude-agent-sdk';
import fs from 'fs/promises';
import path from 'path';
import { spawn } from 'child_process';
// Parse command line arguments
const args = process.argv.slice(2);
const prompt = args[0] || 'Hello, Claude!';
const componentsToInstall = args[1] || '';
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
// Validate API key
if (!anthropicApiKey) {
console.error('❌ Error: ANTHROPIC_API_KEY environment variable is required');
process.exit(1);
}
console.log('🐳 Docker Sandbox Executor');
console.log('═══════════════════════════════════════\n');
/**
* Install Claude Code components if specified
*/
async function installComponents() {
if (!componentsToInstall || componentsToInstall.trim() === '') {
return true;
}
console.log('📦 Installing components...');
console.log(` Components: ${componentsToInstall}\n`);
return new Promise((resolve) => {
const installCmd = `npx claude-code-templates@latest ${componentsToInstall} --yes`;
const child = spawn('sh', ['-c', installCmd], {
stdio: 'inherit',
env: process.env
});
child.on('close', (code) => {
if (code === 0) {
console.log('\n✅ Components installed successfully\n');
resolve(true);
} else {
console.log('\n⚠️ Component installation had warnings (continuing...)\n');
resolve(true); // Continue even if installation has warnings
}
});
child.on('error', (error) => {
console.error(`❌ Installation error: ${error.message}`);
resolve(false);
});
});
}
/**
* Execute Claude Code query using Agent SDK
*/
async function executeQuery() {
try {
console.log('🤖 Executing Claude Code...');
console.log(` Prompt: "${prompt.substring(0, 80)}${prompt.length > 80 ? '...' : ''}"\n`);
console.log('─'.repeat(60));
console.log('📝 CLAUDE OUTPUT:');
console.log('─'.repeat(60) + '\n');
// Enhance prompt with working directory context
const enhancedPrompt = `${prompt}\n\nNote: Your current working directory is /app. When creating files, save them in the current directory (/app) so they can be captured in the output.`;
// query() returns an async generator - we need to iterate it
const generator = query({
prompt: enhancedPrompt,
options: {
apiKey: anthropicApiKey,
model: 'claude-sonnet-4-5',
permissionMode: 'bypassPermissions', // Auto-allow all tool uses
}
});
let assistantResponses = [];
let messageCount = 0;
// Iterate through the async generator
for await (const message of generator) {
messageCount++;
if (message.type === 'assistant') {
// Extract text from assistant message content
if (message.message && message.message.content) {
const content = Array.isArray(message.message.content)
? message.message.content
: [message.message.content];
content.forEach(block => {
if (block.type === 'text') {
console.log(block.text);
assistantResponses.push(block.text);
}
});
}
} else if (message.type === 'result') {
// Show final result metadata
console.log('\n' + '─'.repeat(60));
console.log(`✅ Execution completed (${message.num_turns} turn${message.num_turns > 1 ? 's' : ''})`);
console.log(` Duration: ${message.duration_ms}ms`);
console.log(` Cost: $${message.total_cost_usd.toFixed(5)}`);
console.log('─'.repeat(60) + '\n');
}
}
// Show response summary
const responseText = assistantResponses.join('\n');
if (responseText) {
console.log('📄 Response Summary:');
console.log(` ${messageCount} message(s) received`);
console.log(` ${assistantResponses.length} assistant response(s)`);
console.log(` ${responseText.length} characters generated`);
console.log('');
}
return true;
} catch (error) {
console.error('\n❌ Execution error:', error.message);
if (error.stack) {
console.error('\nStack trace:');
console.error(error.stack);
}
return false;
}
}
/**
* Find and copy generated files to output directory
*/
async function copyGeneratedFiles() {
try {
console.log('📁 Searching for generated files...\n');
// Common file extensions to look for
const extensions = [
'js', 'jsx', 'ts', 'tsx',
'py', 'html', 'css', 'scss',
'json', 'md', 'yaml', 'yml',
'txt', 'sh', 'bash'
];
// Search for files in multiple directories
const { execSync } = await import('child_process');
const findPattern = extensions.map(ext => `-name "*.${ext}"`).join(' -o ');
// Search in /app and /tmp for generated files
const searchPaths = ['/app', '/tmp'];
let allFiles = [];
for (const searchPath of searchPaths) {
const findCmd = `find ${searchPath} -type f \\( ${findPattern} \\) ! -path "*/node_modules/*" ! -path "*/.npm/*" ! -path "/app/execute.js" ! -path "/app/package*.json" -newer /app/execute.js 2>/dev/null | head -50`;
try {
const output = execSync(findCmd, { encoding: 'utf8' });
const files = output.trim().split('\n').filter(f => f.trim());
allFiles = allFiles.concat(files);
} catch (error) {
// Continue to next search path
}
}
if (allFiles.length === 0) {
console.log('️ No generated files found\n');
return;
}
console.log(`📦 Found ${allFiles.length} file(s):\n`);
// Copy files to output directory preserving structure
let copiedCount = 0;
for (const file of allFiles) {
try {
// Determine relative path based on source directory
let relativePath;
if (file.startsWith('/app/')) {
relativePath = file.replace('/app/', '');
} else if (file.startsWith('/tmp/')) {
relativePath = file.replace('/tmp/', '');
} else {
relativePath = path.basename(file);
}
const outputPath = path.join('/output', relativePath);
// Create directory structure
await fs.mkdir(path.dirname(outputPath), { recursive: true });
// Copy file
await fs.copyFile(file, outputPath);
console.log(` ✅ ${relativePath}`);
copiedCount++;
} catch (error) {
console.log(` ⚠️ Failed to copy: ${file}`);
}
}
if (copiedCount > 0) {
console.log(`\n✅ Copied ${copiedCount} file(s) to output directory\n`);
}
} catch (error) {
console.error('❌ Error copying files:', error.message);
}
}
/**
* Main execution flow
*/
async function main() {
try {
// Step 1: Install components
const installSuccess = await installComponents();
if (!installSuccess) {
console.error('❌ Component installation failed');
process.exit(1);
}
// Step 2: Execute Claude query
const executeSuccess = await executeQuery();
if (!executeSuccess) {
console.error('❌ Query execution failed');
process.exit(1);
}
// Step 3: Copy generated files
await copyGeneratedFiles();
console.log('🎉 Docker sandbox execution completed successfully!');
process.exit(0);
} catch (error) {
console.error('❌ Fatal error:', error.message);
process.exit(1);
}
}
// Run main function
main();