UNPKG

create-director-app

Version:

NPX package to clone director scripts from script generation service

288 lines (269 loc) 9.34 kB
import fetch from "node-fetch"; function stripContextIdBlocks(script) { const regex = /(\s*)if\s*\(\s*!contextId\s*\)\s*\{([\s\S]*?)\n\1\}/g; return script.replace(regex, (match, indent, content) => { const dedentedContent = content .split("\n") .map((line) => { if (line.startsWith(indent + " ")) { return indent + line.substring(indent.length + 2); } return line; }) .join("\n"); return dedentedContent; }); } function replaceStagehandConfig(script) { const configFunctionRegex = /const stagehandConfig = \(\)[^{]*\{[\s\S]*?\n\};\n/g; let modifiedScript = script.replace(configFunctionRegex, ""); modifiedScript = modifiedScript.replace(/stagehandConfig\(\)/g, "StagehandConfig"); if (!modifiedScript.includes("import StagehandConfig")) { const importRegex = /^import[\s\S]*?from[\s\S]*?;$/gm; const imports = modifiedScript.match(importRegex); if (imports) { const lastImport = imports[imports.length - 1]; const lastImportIndex = modifiedScript.lastIndexOf(lastImport); modifiedScript = modifiedScript.slice(0, lastImportIndex + lastImport.length) + '\nimport StagehandConfig from "./stagehand.config.js";' + modifiedScript.slice(lastImportIndex + lastImport.length); } else { modifiedScript = 'import StagehandConfig from "./stagehand.config.js";\n' + modifiedScript; } } return modifiedScript; } /** * Cleans up imports and removes contextId variable */ function cleanupImportsAndVariables(script) { let modifiedScript = script.replace(/import\s*\{([^}]*)\}\s*from\s*["']@browserbasehq\/stagehand["'];?/, (match, imports) => { const importList = imports .split(",") .map((imp) => imp.trim()) .filter((imp) => { const cleaned = imp.replace(/type\s+/, ""); return cleaned !== "ConstructorParams" && cleaned !== "LogLine"; }); return `import { ${importList.join(", ")} } from "@browserbasehq/stagehand";`; }); // Remove contextId variable declaration modifiedScript = modifiedScript.replace(/const\s+contextId\s*=\s*["'][^"']*["'];\s*\n?/g, ""); // Replace 'export default runWorkflow;' with 'runWorkflow();' modifiedScript = modifiedScript.replace(/export\s+default\s+runWorkflow\s*;?\s*$/m, "runWorkflow();"); return modifiedScript; } export async function fetchTemplate(jwtToken, endpoint) { try { const response = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${jwtToken}`, }, body: JSON.stringify({ template: jwtToken, }), }); if (!response.ok) { if (response.status === 403) { throw new Error("Token expired. Please regenerate the command and try again."); } else if (response.status === 404) { throw new Error("Script not found. Token may be invalid or expired."); } throw new Error(`HTTP error! status: ${response.status}`); } const result = (await response.json()); if (!result.success || !result.data || !result.data.script) { throw new Error(result.error || "Failed to fetch script"); } let processedScript = stripContextIdBlocks(result.data.script); processedScript = replaceStagehandConfig(processedScript); processedScript = cleanupImportsAndVariables(processedScript); const templateData = { name: result.data.name, script: processedScript, files: createProjectFiles(processedScript, result.data.name), packageJson: createPackageJson(result.data.name), instructions: result.data.instructions || createDefaultInstructions(result.data.name), }; return templateData; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to fetch template: ${error.message}`); } throw new Error("Failed to fetch template: Unknown error"); } } function createProjectFiles(scriptContent, templateId) { return [ { path: "index.ts", content: scriptContent, type: "file", }, { path: "stagehand.config.ts", content: createStagehandConfig(), type: "file", }, { path: "tsconfig.json", content: createTsConfig(), type: "file", }, { path: ".cursorrules", content: createCursorRules(), type: "file", }, { path: ".gitignore", content: createGitignore(), type: "file", }, { path: ".env.example", content: createEnvFile(), type: "file", }, ]; } function createPackageJson(templateId) { return { name: templateId, version: "1.0.0", description: "Stagehand workflow project", type: "module", main: "index.js", scripts: { start: "tsx index.ts", build: "tsc", dev: "tsx watch index.ts", postinstall: "playwright install chromium", }, dependencies: { "@browserbasehq/stagehand": "^2.2.1", zod: "^3.24.3", dotenv: "^16.4.7", }, devDependencies: { "@types/node": "^22.5.5", tsx: "^4.19.2", typescript: "^5.7.2", }, }; } function createStagehandConfig() { return `import type { ConstructorParams } from "@browserbasehq/stagehand"; import dotenv from "dotenv"; dotenv.config(); const StagehandConfig: ConstructorParams = { verbose: 1 /* Verbosity level for logging: 0 = silent, 1 = info, 2 = all */, domSettleTimeoutMs: 30_000 /* Timeout for DOM to settle in milliseconds */, modelName: "gemini-2.0-flash" /* Name of the model to use */, modelClientOptions: { apiKey: process.env.GOOGLE_API_KEY, } /* Configuration options for the model client */, env: "LOCAL" /* Environment to run in: LOCAL or BROWSERBASE */, localBrowserLaunchOptions: { viewport: { width: 1024, height: 768, }, } /* Configuration options for the local browser */, }; export default StagehandConfig; `; } function createTsConfig() { return `{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "lib": ["ES2022", "DOM"], "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "moduleResolution": "node", "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "outDir": "./dist", "rootDir": "./", "declaration": true, "declarationMap": true, "sourceMap": true }, "include": ["./**/*.ts"], "exclude": ["node_modules", "dist"] }`; } function createCursorRules() { return `# Stagehand Cursor Rules You are an expert TypeScript and Playwright developer working with Stagehand. ## Key Guidelines: - Always use Stagehand's act(), observe(), and extract() methods - Prefer observe() before act() for better reliability - Follow the existing code patterns and style - Use proper error handling with try/catch blocks - Always close Stagehand instances in finally blocks ## Stagehand Methods: - page.act(instruction) - Perform actions on the page - page.observe(instruction) - Plan actions before executing - page.extract({instruction, schema}) - Extract structured data - page.goto(url) - Navigate to a page ## Environment Variables: - GOOGLE_API_KEY - For Google AI models - BROWSERBASE_API_KEY - For Browserbase - BROWSERBASE_PROJECT_ID - Project ID ## Best Practices: - Use descriptive action descriptions for better debugging - Log important steps in the workflow - Handle errors gracefully - Test workflows in isolation when possible Always ensure proper async/await usage and clean resource management.`; } function createGitignore() { return `node_modules/ dist/ .env *.log .DS_Store`; } function createEnvFile() { return `# Google AI configuration GOOGLE_API_KEY=your_google_api_key_here`; } function createDefaultInstructions(templateId) { return `# ${templateId} This is a Stagehand workflow project. ## Setup 1. Install dependencies: \`\`\`bash npm install \`\`\` 2. Configure your environment variables: - Copy \`.env.example\` to \`.env\` - Add your Browserbase API key and project ID - Add your Google API key 3. Run the workflow: \`\`\`bash npm start \`\`\` ## Project Structure - \`index.ts\` - Your workflow implementation - \`package.json\` - Node.js dependencies - \`.env\` - Environment variables ## Environment Variables - \`BROWSERBASE_API_KEY\` - Your Browserbase API key - \`BROWSERBASE_PROJECT_ID\` - Your Browserbase project ID - \`GOOGLE_API_KEY\` - Your Google API key for the AI model`; } //# sourceMappingURL=template-fetcher.js.map