├── .env.example ├── .gitignore ├── .prettierrc ├── actions.ts ├── aiAssistant-create.ts ├── aiAssistant-list.ts ├── aiAssistant-update.ts ├── aiFunctions.ts ├── e2b.Dockerfile ├── e2b.toml ├── functions.ts ├── gh.ts ├── img └── logo-circle.png ├── index.ts ├── log.ts ├── package-lock.json ├── package.json ├── readme.md └── sleep.ts /.env.example: -------------------------------------------------------------------------------- 1 | # Pass your OpenAI API key 2 | OPENAI_API_KEY= 3 | 4 | # Get your E2B API key from here https://e2b.dev/docs/getting-started/api-key 5 | E2B_API_KEY= 6 | 7 | # ID of the OpenAI AI assistant you created by running `npm run create-ai-assistant`, 8 | # then grab the assistant ID from https://platform.openai.com/assistants 9 | AI_ASSISTANT_ID= 10 | 11 | # Your classic GitHub token 12 | GITHUB_TOKEN= 13 | 14 | # Your GitHub username 15 | GIT_USERNAME= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | .idea 4 | .vscode -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "tabWidth": 2, 4 | "semi": false, 5 | "arrowParens": "avoid", 6 | "trailingComma": "all", 7 | "singleAttributePerLine": true, 8 | "importOrder": [ 9 | "", 10 | "^[./]" 11 | ], 12 | "importOrderSeparation": true, 13 | "importOrderSortSpecifiers": true, 14 | "printWidth": 120 15 | } -------------------------------------------------------------------------------- /actions.ts: -------------------------------------------------------------------------------- 1 | import { Sandbox } from '@e2b/sdk' 2 | import path from 'path' 3 | 4 | import { sandboxLog } from './log' 5 | import { branchID, repoDirPath } from './gh' 6 | 7 | export async function makeCommit(sandbox: Sandbox, { message }: { message: string }): Promise { 8 | sandboxLog(`Making commit with message ${message}`) 9 | try { 10 | const processAdd = await sandbox.process.start({ 11 | cmd: 'git add .', 12 | cwd: repoDirPath, 13 | }) 14 | await processAdd.wait() 15 | 16 | const processCommit = await sandbox.process.start({ 17 | cmd: `git commit -m '${message}'`, 18 | cwd: repoDirPath, 19 | }) 20 | await processCommit.wait() 21 | return 'success' 22 | } catch (e) { 23 | return `Error: ${e.message}}` 24 | } 25 | } 26 | 27 | export async function makePullRequest(sandbox: Sandbox, { title }: { title: string }): Promise { 28 | sandboxLog(`Making pull request with title ${title}`) 29 | try { 30 | const processPush = await sandbox.process.start({ 31 | cmd: `git push -u origin ai-developer-${branchID}`, 32 | cwd: repoDirPath, 33 | }) 34 | await processPush.wait() 35 | 36 | const processPR = await sandbox.process.start({ 37 | cmd: `gh pr create --title '${title}' --fill`, 38 | cwd: repoDirPath, 39 | }) 40 | await processPR.wait() 41 | return 'success' 42 | } catch (e) { 43 | return `Error: ${e.message}}` 44 | } 45 | } 46 | 47 | export async function saveCodeToFile( 48 | sandbox: Sandbox, 49 | { code, filename }: { code: string; filename: string }, 50 | ): Promise { 51 | sandboxLog(`Saving code to file ${filename}`) 52 | try { 53 | const dir = path.dirname(filename) 54 | 55 | await sandbox.filesystem.makeDir(dir) 56 | await sandbox.filesystem.write(filename, code) 57 | 58 | return 'success' 59 | } catch (e) { 60 | return `Error: ${e.message}}` 61 | } 62 | } 63 | 64 | export async function makeDir(sandbox: Sandbox, { path }: { path: string }): Promise { 65 | sandboxLog(`Creating dir ${path}`) 66 | try { 67 | await sandbox.filesystem.makeDir(path) 68 | 69 | return 'success' 70 | } catch (e) { 71 | return `Error: ${e.message}}` 72 | } 73 | } 74 | 75 | export async function listFiles(sandbox: Sandbox, { path }: { path: string }): Promise { 76 | sandboxLog(`Listing files in ${path}`) 77 | try { 78 | const files = await sandbox.filesystem.list(path) 79 | const response = files.map(file => (file.isDir ? `dir: ${file.name}` : file.name)).join('\n') 80 | return response 81 | } catch (e) { 82 | return `Error: ${e.message}}` 83 | } 84 | } 85 | 86 | export async function readFile(sandbox: Sandbox, { path }: { path: string }): Promise { 87 | sandboxLog(`Reading file ${path}`) 88 | try { 89 | return await sandbox.filesystem.read(path) 90 | } catch (e) { 91 | return `Error: ${e.message}}` 92 | } 93 | } 94 | 95 | export async function runCode(sandbox: Sandbox, { command }: { command: string }): Promise { 96 | sandboxLog(`Running command ${command}`) 97 | 98 | try { 99 | const process = await sandbox.process.start({ cmd: command, cwd: repoDirPath }) 100 | const output = await process.wait() 101 | 102 | if (output.exitCode !== 0) { 103 | throw new Error(`Command failed with exit code ${output.exitCode}. Error: ${output.stderr}`) 104 | } 105 | 106 | if (!output.stdout) { 107 | console.log(`Command did not produce any output. Error: ${output.stderr}`) 108 | } 109 | 110 | // Replace all non-ASCII characters in the output 111 | const cleanedOutput = output.stdout.replace(/[^\x00-\x7F]/g, '') 112 | 113 | return cleanedOutput 114 | } catch (e) { 115 | return `Error: ${e.message}` 116 | } 117 | } -------------------------------------------------------------------------------- /aiAssistant-create.ts: -------------------------------------------------------------------------------- 1 | import OpenAI from 'openai' 2 | import 'dotenv/config' 3 | 4 | import { functions } from './aiFunctions' 5 | import { assistant_config } from './aiFunctions'; 6 | 7 | const openai = new OpenAI() 8 | 9 | // Run this only once to create the assistant! 10 | // npm run create-ai-assistant 11 | export async function createAIDeveloper() { 12 | const aiDeveloper = await openai.beta.assistants.create({ 13 | instructions: assistant_config.instructions, 14 | tools: [...functions], 15 | model: assistant_config.model, 16 | name: assistant_config.name, 17 | }) 18 | console.log("Assistant ready, AI Developer Assistant ID that should be added to you .env file:", aiDeveloper.id) 19 | } 20 | 21 | createAIDeveloper() 22 | -------------------------------------------------------------------------------- /aiAssistant-list.ts: -------------------------------------------------------------------------------- 1 | import OpenAI from "openai"; 2 | import 'dotenv/config' 3 | 4 | const openai = new OpenAI(); 5 | 6 | export async function listAIDeveloper(): Promise<{name: string, id: string}[]> { 7 | const myAssistants = await openai.beta.assistants.list({ 8 | order: "desc", 9 | //limit: "20", 10 | }); 11 | 12 | return myAssistants.data.map(assistant => ({ 13 | name: assistant.name || 'Default Name', 14 | id: assistant.id 15 | })); 16 | } 17 | -------------------------------------------------------------------------------- /aiAssistant-update.ts: -------------------------------------------------------------------------------- 1 | import OpenAI from 'openai' 2 | import 'dotenv/config' 3 | 4 | import { functions } from './aiFunctions' 5 | import { assistant_config } from './aiFunctions'; 6 | 7 | const openai = new OpenAI() 8 | 9 | // Run this to update the assistant! 10 | // npm run create-ai-assistant 11 | export async function updateAIDeveloper() { 12 | 13 | const aiDeveloper = await openai.beta.assistants.update( 14 | process.env.AI_ASSISTANT_ID!, 15 | { 16 | instructions: assistant_config.instructions, 17 | tools: [...functions], 18 | model: assistant_config.model, 19 | name: assistant_config.name, 20 | } 21 | ) 22 | console.log("Assistant updated, AI Developer Assistant ID:", aiDeveloper.id) 23 | } 24 | 25 | updateAIDeveloper() 26 | 27 | -------------------------------------------------------------------------------- /aiFunctions.ts: -------------------------------------------------------------------------------- 1 | import { AssistantCreateParams } from 'openai/src/resources/beta/assistants/assistants' 2 | 3 | export const assistant_config = 4 | { 5 | instructions: `You are an AI developer. 6 | When given a coding task, write and save code to files, install any packages if needed, make commits, and finally create a PR once done. You're logged in using GitHub CLI and have all the needed credentials. 7 | Start by listing all files inside the repo. You work inside the '/home/user/repo' directory where the repository is already cloned so you don't need to clone it yourself. 8 | You also have the ability to actually run the Python code or Shell commands. You should try these with custom prompt if you do not find other appropriate tool. 9 | Don't argue with me and just complete the task.`, 10 | name: 'AI Developer', 11 | model: 'gpt-4-1106-preview', 12 | //model: 'gpt-3.5-turbo-1106', 13 | } 14 | 15 | export const functions: Array< 16 | | AssistantCreateParams.AssistantToolsCode 17 | | AssistantCreateParams.AssistantToolsRetrieval 18 | | AssistantCreateParams.AssistantToolsFunction 19 | > = [ 20 | // Save code to file 21 | { 22 | type: 'function', 23 | function: { 24 | name: 'saveCodeToFile', 25 | description: 'Save code to file', 26 | parameters: { 27 | type: 'object', 28 | properties: { 29 | code: { 30 | type: 'string', 31 | description: 'The code to save', 32 | }, 33 | filename: { 34 | type: 'string', 35 | description: 'The filename including the path and extension', 36 | }, 37 | }, 38 | }, 39 | }, 40 | }, 41 | // List files 42 | { 43 | type: 'function', 44 | function: { 45 | name: 'listFiles', 46 | description: 'List files in a directory', 47 | parameters: { 48 | type: 'object', 49 | properties: { 50 | path: { 51 | type: 'string', 52 | description: 'The path to the directory', 53 | }, 54 | }, 55 | }, 56 | }, 57 | }, 58 | // Make commit 59 | { 60 | type: 'function', 61 | function: { 62 | name: 'makeCommit', 63 | description: 'Make a commit', 64 | parameters: { 65 | type: 'object', 66 | properties: { 67 | message: { 68 | type: 'string', 69 | description: 'The commit message', 70 | }, 71 | }, 72 | }, 73 | }, 74 | }, 75 | // Make pull request 76 | { 77 | type: 'function', 78 | function: { 79 | name: 'makePullRequest', 80 | description: 'Make a pull request', 81 | parameters: { 82 | type: 'object', 83 | properties: { 84 | title: { 85 | type: 'string', 86 | description: 'The pull request title', 87 | }, 88 | body: { 89 | type: 'string', 90 | description: 'The pull request body', 91 | }, 92 | }, 93 | }, 94 | }, 95 | }, 96 | // Read file 97 | { 98 | type: 'function', 99 | function: { 100 | name: 'readFile', 101 | description: 'Read a file', 102 | parameters: { 103 | type: 'object', 104 | properties: { 105 | path: { 106 | type: 'string', 107 | description: 'The path to the file', 108 | }, 109 | }, 110 | }, 111 | }, 112 | }, 113 | // Run code 114 | { 115 | type: 'function', 116 | function: { 117 | name: 'runCode', 118 | description: 'Run code or commands in the sandbox environment', 119 | parameters: { 120 | type: 'object', 121 | properties: { 122 | command: { 123 | type: 'string', 124 | description: 'The command to run', 125 | }, 126 | }, 127 | }, 128 | }, 129 | }, 130 | ] 131 | -------------------------------------------------------------------------------- /e2b.Dockerfile: -------------------------------------------------------------------------------- 1 | # You can use most Debian-based base images 2 | FROM ubuntu:22.04 3 | 4 | RUN apt update \ 5 | && apt install sudo 6 | 7 | # Install GitHub CLI 8 | RUN type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y) 9 | RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ 10 | && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \ 11 | && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ 12 | && sudo apt update \ 13 | && sudo apt install gh -y -------------------------------------------------------------------------------- /e2b.toml: -------------------------------------------------------------------------------- 1 | # This is a config for E2B sandbox template 2 | 3 | id = "ghxy9unorimamgct46w9" 4 | dockerfile = "e2b.Dockerfile" 5 | name = "ai-developer-sandbox" 6 | -------------------------------------------------------------------------------- /functions.ts: -------------------------------------------------------------------------------- 1 | import { AssistantCreateParams } from 'openai/src/resources/beta/assistants/assistants' 2 | 3 | export const assistant_config = 4 | { 5 | instructions: `You are an AI developer. 6 | When given a coding task, write and save code to files, install any packages if needed, make commits, and finally create a PR once done. You're logged in using GitHub CLI and have all the needed credentials. 7 | Start by listing all files inside the repo. You work inside the '/home/user/repo' directory where the repository is already cloned so you don't need to clone it yourself. 8 | You also have the ability to actually run code. You should try these with custom prompt if you do not find other appropriate tool. 9 | Don't argue with me and just complete the task.`, 10 | name: 'AI Developer', 11 | model: 'gpt-4-1106-preview', 12 | //model: 'gpt-3.5-turbo-1106', 13 | } 14 | 15 | export const functions: Array< 16 | | AssistantCreateParams.AssistantToolsCode 17 | | AssistantCreateParams.AssistantToolsRetrieval 18 | | AssistantCreateParams.AssistantToolsFunction 19 | > = [ 20 | // Save code to file 21 | { 22 | type: 'function', 23 | function: { 24 | name: 'saveCodeToFile', 25 | description: 'Save code to file', 26 | parameters: { 27 | type: 'object', 28 | properties: { 29 | code: { 30 | type: 'string', 31 | description: 'The code to save', 32 | }, 33 | filename: { 34 | type: 'string', 35 | description: 'The filename including the path and extension', 36 | }, 37 | }, 38 | }, 39 | }, 40 | }, 41 | // List files 42 | { 43 | type: 'function', 44 | function: { 45 | name: 'listFiles', 46 | description: 'List files in a directory', 47 | parameters: { 48 | type: 'object', 49 | properties: { 50 | path: { 51 | type: 'string', 52 | description: 'The path to the directory', 53 | }, 54 | }, 55 | }, 56 | }, 57 | }, 58 | // Make commit 59 | { 60 | type: 'function', 61 | function: { 62 | name: 'makeCommit', 63 | description: 'Make a commit', 64 | parameters: { 65 | type: 'object', 66 | properties: { 67 | message: { 68 | type: 'string', 69 | description: 'The commit message', 70 | }, 71 | }, 72 | }, 73 | }, 74 | }, 75 | // Make pull request 76 | { 77 | type: 'function', 78 | function: { 79 | name: 'makePullRequest', 80 | description: 'Make a pull request', 81 | parameters: { 82 | type: 'object', 83 | properties: { 84 | title: { 85 | type: 'string', 86 | description: 'The pull request title', 87 | }, 88 | body: { 89 | type: 'string', 90 | description: 'The pull request body', 91 | }, 92 | }, 93 | }, 94 | }, 95 | }, 96 | // Read file 97 | { 98 | type: 'function', 99 | function: { 100 | name: 'readFile', 101 | description: 'Read a file', 102 | parameters: { 103 | type: 'object', 104 | properties: { 105 | path: { 106 | type: 'string', 107 | description: 'The path to the file', 108 | }, 109 | }, 110 | }, 111 | }, 112 | }, 113 | // Run code 114 | { 115 | type: 'function', 116 | function: { 117 | name: 'runCode', 118 | description: 'Run code or commands in the sandbox environment', 119 | parameters: { 120 | type: 'object', 121 | properties: { 122 | command: { 123 | type: 'string', 124 | description: 'The command to run', 125 | }, 126 | }, 127 | }, 128 | }, 129 | }, 130 | ] 131 | -------------------------------------------------------------------------------- /gh.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { Sandbox } from '@e2b/sdk' 3 | import { customAlphabet } from 'nanoid' 4 | 5 | import { sandboxLog } from './log' 6 | 7 | const GIT_USERNAME = process.env.GIT_USERNAME! 8 | const GITHUB_TOKEN = process.env.GITHUB_TOKEN! 9 | 10 | const gitEmail = 'e2b-assistant[bot]@users.noreply.github.com' 11 | const gitName = 'e2b-assistant[bot]' 12 | 13 | export const rootdir = '/home/user' 14 | export const repoDir = 'repo' 15 | export const repoDirPath = path.posix.join(rootdir, repoDir) 16 | 17 | export const branchID = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6)() 18 | 19 | export async function loginWithGH(sandbox: Sandbox) { 20 | await sandbox.filesystem.write('/home/user/.github-token', GITHUB_TOKEN) 21 | const process = await sandbox.process.start({ 22 | cmd: `gh auth login --with-token < /home/user/.github-token && 23 | git config --global user.email '${gitEmail}' && 24 | git config --global user.name '${gitName}' && 25 | git config --global push.autoSetupRemote true`, 26 | }) 27 | const output = await process.wait() 28 | if (output.exitCode !== 0) { 29 | throw new Error(`Make sure you've set 'GITHUB_TOKEN' env variable correctly. ${output.stderr}`) 30 | } 31 | } 32 | 33 | export async function cloneRepo(sandbox: Sandbox, repo: string) { 34 | sandboxLog(`Cloning repo ${repo}`) 35 | 36 | const process = await sandbox.process.start({ 37 | cmd: `gh repo clone ${repo} ${repoDirPath}`, 38 | }) 39 | const output = await process.wait() 40 | 41 | if (output.exitCode !== 0) { 42 | throw new Error(`Cloning repo failed. ${output.stderr}`) 43 | } 44 | 45 | const processCreateBranch = await sandbox.process.start({ 46 | cmd: `git checkout -b ai-developer-${branchID}`, 47 | cwd: repoDirPath, 48 | }) 49 | await processCreateBranch.wait() 50 | 51 | const setRemote = await sandbox.process.start({ 52 | cmd: `git remote set-url origin https://${GIT_USERNAME}:${GITHUB_TOKEN}@github.com/${repo}.git`, 53 | cwd: repoDirPath, 54 | }) 55 | await setRemote.wait() 56 | } 57 | 58 | export async function listLastEditedRepos(sandbox: Sandbox, username: string) { 59 | sandboxLog(`Listing last 10 edited repos of ${username}`) 60 | 61 | const process = await sandbox.process.start({ 62 | cmd: `gh api users/${username}/repos?sort=updated&direction=desc&per_page=10`, 63 | }) 64 | const output = await process.wait() 65 | 66 | if (output.exitCode !== 0) { 67 | throw new Error(`Listing repos failed. ${output.stderr}`) 68 | } 69 | 70 | const repos = JSON.parse(output.stdout) 71 | return repos.map(repo => `${username}/${repo.name}`) 72 | } -------------------------------------------------------------------------------- /img/logo-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/e2b-dev/ai-developer-with-sandbox/996c4819ff68d83eb1e2fa35b36e8cd54306ea09/img/logo-circle.png -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import 'dotenv/config' 2 | 3 | import OpenAI from 'openai' 4 | import prompts from 'prompts' 5 | import ora from 'ora' 6 | import { MessageContentText } from 'openai/resources/beta/threads' 7 | import { Sandbox } from '@e2b/sdk' 8 | 9 | import { onLog } from './log' 10 | import { cloneRepo, loginWithGH } from './gh' 11 | import { sleep } from './sleep' 12 | import { listFiles, makeCommit, makeDir, makePullRequest, readFile, saveCodeToFile, runCode } from './actions' 13 | 14 | import { listAIDeveloper } from './aiAssistant-list' 15 | 16 | const openai = new OpenAI() 17 | 18 | 19 | // Function to prompt the user to select an AI assistant and return the selected assistant's ID 20 | async function selectAIAssistant(): Promise { 21 | // Fetch the list of AI assistants 22 | const aiAssistants = await listAIDeveloper(); 23 | 24 | // Create a list of choices for the prompt 25 | const aiChoices = aiAssistants.map((assistant, index) => ({ 26 | title: assistant.name, 27 | value: index, 28 | })); 29 | 30 | let { selectedAI } = (await prompts({ 31 | type: 'select', 32 | name: 'selectedAI', 33 | message: 'Choose an AI assistant:', 34 | choices: aiChoices, 35 | })) as any; 36 | 37 | // Return the ID of the selected AI assistant 38 | return aiAssistants[selectedAI].id; 39 | } 40 | 41 | // Set the selected AI assistant 42 | const AI_ASSISTANT_ID = await selectAIAssistant(); 43 | 44 | async function initChat(): Promise<{ repoName: string; task: string }> { 45 | let { repoName } = (await prompts({ 46 | type: 'text', 47 | name: 'repoName', 48 | message: 'Enter repo name (eg: username/repo):', 49 | } as any)) as any 50 | 51 | // Replace any backslashes in the repo name with forward slashes 52 | repoName = repoName.replace(/\\/g, '/'); 53 | 54 | let { task } = (await prompts({ 55 | type: 'text', 56 | name: 'task', 57 | message: 'Enter the task you want the AI developer to work on:', 58 | } as any)) as any 59 | 60 | return { repoName, task } 61 | } 62 | 63 | const { repoName, task } = await initChat() 64 | 65 | const sandbox = await Sandbox.create({ 66 | id: 'ai-developer-sandbox', 67 | onStdout: onLog, 68 | onStderr: onLog, 69 | }) 70 | 71 | sandbox 72 | .addAction(readFile) 73 | .addAction(makeCommit) 74 | .addAction(makePullRequest) 75 | .addAction(saveCodeToFile) 76 | .addAction(makeDir) 77 | .addAction(listFiles) 78 | .addAction(runCode) 79 | 80 | await loginWithGH(sandbox) 81 | await cloneRepo(sandbox, repoName) 82 | 83 | const spinner = ora('Waiting for assistant') 84 | spinner.start() 85 | 86 | const thread = await openai.beta.threads.create({ 87 | messages: [ 88 | { 89 | role: 'user', 90 | content: `Pull this repo: '${repoName}'. Then carefully plan this task and start working on it: ${task}`, 91 | }, 92 | ], 93 | }) 94 | const assistant = await openai.beta.assistants.retrieve(AI_ASSISTANT_ID) 95 | 96 | let run = await openai.beta.threads.runs.create(thread.id, { 97 | assistant_id: assistant.id, 98 | }) 99 | 100 | assistantLoop: while (true) { 101 | await sleep(1000) 102 | 103 | switch (run.status) { 104 | case 'requires_action': { 105 | spinner.stop() 106 | const outputs = await sandbox.openai.actions.run(run) 107 | spinner.start() 108 | 109 | if (outputs.length > 0) { 110 | await openai.beta.threads.runs.submitToolOutputs(thread.id, run.id, { 111 | tool_outputs: outputs, 112 | }) 113 | } 114 | 115 | break 116 | } 117 | case 'completed': { 118 | spinner.stop() 119 | const messages = await openai.beta.threads.messages.list(thread.id) 120 | const textMessages = messages.data[0].content.filter(message => message.type === 'text') as MessageContentText[] 121 | const { userResponse }: { userResponse: string } = await prompts({ 122 | type: 'text', 123 | name: 'userResponse', 124 | message: `${textMessages[0].text.value}\nIf you want to exit write 'exit', otherwise write your response:\n`, 125 | }) 126 | if (userResponse === 'exit') { 127 | break assistantLoop 128 | } 129 | spinner.start() 130 | 131 | await openai.beta.threads.messages.create(thread.id, { 132 | role: 'user', 133 | content: userResponse as string, 134 | }) 135 | 136 | run = await openai.beta.threads.runs.create(thread.id, { 137 | assistant_id: assistant.id, 138 | }) 139 | 140 | break 141 | } 142 | case 'queued': 143 | case 'in_progress': 144 | break 145 | case 'cancelled': 146 | case 'cancelling': 147 | case 'expired': 148 | case 'failed': 149 | break assistantLoop 150 | default: 151 | console.error(`Unknown status: ${run.status}`) 152 | break assistantLoop 153 | } 154 | 155 | run = await openai.beta.threads.runs.retrieve(thread.id, run.id) 156 | } 157 | 158 | spinner.stop() 159 | await sandbox.close() -------------------------------------------------------------------------------- /log.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk' 2 | 3 | const orange = chalk.hex('#FFB766') 4 | 5 | export function onLog(output: { line: string }) { 6 | sandboxLog(output.line) 7 | } 8 | 9 | export function sandboxLog(line: string) { 10 | console.log(`${orange('[Sandbox]')} ${line}`) 11 | } 12 | 13 | export function assistantLog(line: string) { 14 | console.log(`${chalk.blue('[Assistant]')} ${line}`) 15 | } 16 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ai-developer-with-sandbox", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "ai-developer-with-sandbox", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@e2b/sdk": "^0.11.0", 13 | "chalk": "^5.3.0", 14 | "dotenv": "^16.3.1", 15 | "nanoid": "^5.0.3", 16 | "openai": "^4.17.3", 17 | "ora": "^7.0.1", 18 | "prompts": "^2.4.2" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^20.9.0", 22 | "@types/prompts": "^2.4.8", 23 | "tsx": "^4.1.2", 24 | "typescript": "^5.2.2" 25 | }, 26 | "engines": { 27 | "node": ">= 18.0.0", 28 | "npm": ">= 6.0.0" 29 | } 30 | }, 31 | "node_modules/@e2b/sdk": { 32 | "version": "0.11.0", 33 | "resolved": "https://registry.npmjs.org/@e2b/sdk/-/sdk-0.11.0.tgz", 34 | "integrity": "sha512-Ntk6Na9sLm0rMLlnFoaZAzj59t8IsqaXvRS83UgoGuQ3ioEwiCI+axHvIdvWKCTAR2GkCXysDswARc/9TReUOA==", 35 | "dependencies": { 36 | "normalize-path": "^3.0.0", 37 | "openapi-typescript-fetch": "^1.1.3", 38 | "path-browserify": "^1.0.1", 39 | "platform": "^1.3.6", 40 | "rpc-websocket-client": "^1.1.4" 41 | }, 42 | "engines": { 43 | "node": ">=18" 44 | }, 45 | "peerDependencies": { 46 | "openai": "^4.17.4" 47 | } 48 | }, 49 | "node_modules/@esbuild/android-arm": { 50 | "version": "0.18.20", 51 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", 52 | "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", 53 | "cpu": [ 54 | "arm" 55 | ], 56 | "dev": true, 57 | "optional": true, 58 | "os": [ 59 | "android" 60 | ], 61 | "engines": { 62 | "node": ">=12" 63 | } 64 | }, 65 | "node_modules/@esbuild/android-arm64": { 66 | "version": "0.18.20", 67 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", 68 | "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", 69 | "cpu": [ 70 | "arm64" 71 | ], 72 | "dev": true, 73 | "optional": true, 74 | "os": [ 75 | "android" 76 | ], 77 | "engines": { 78 | "node": ">=12" 79 | } 80 | }, 81 | "node_modules/@esbuild/android-x64": { 82 | "version": "0.18.20", 83 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", 84 | "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", 85 | "cpu": [ 86 | "x64" 87 | ], 88 | "dev": true, 89 | "optional": true, 90 | "os": [ 91 | "android" 92 | ], 93 | "engines": { 94 | "node": ">=12" 95 | } 96 | }, 97 | "node_modules/@esbuild/darwin-arm64": { 98 | "version": "0.18.20", 99 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", 100 | "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", 101 | "cpu": [ 102 | "arm64" 103 | ], 104 | "dev": true, 105 | "optional": true, 106 | "os": [ 107 | "darwin" 108 | ], 109 | "engines": { 110 | "node": ">=12" 111 | } 112 | }, 113 | "node_modules/@esbuild/darwin-x64": { 114 | "version": "0.18.20", 115 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", 116 | "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", 117 | "cpu": [ 118 | "x64" 119 | ], 120 | "dev": true, 121 | "optional": true, 122 | "os": [ 123 | "darwin" 124 | ], 125 | "engines": { 126 | "node": ">=12" 127 | } 128 | }, 129 | "node_modules/@esbuild/freebsd-arm64": { 130 | "version": "0.18.20", 131 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", 132 | "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", 133 | "cpu": [ 134 | "arm64" 135 | ], 136 | "dev": true, 137 | "optional": true, 138 | "os": [ 139 | "freebsd" 140 | ], 141 | "engines": { 142 | "node": ">=12" 143 | } 144 | }, 145 | "node_modules/@esbuild/freebsd-x64": { 146 | "version": "0.18.20", 147 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", 148 | "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", 149 | "cpu": [ 150 | "x64" 151 | ], 152 | "dev": true, 153 | "optional": true, 154 | "os": [ 155 | "freebsd" 156 | ], 157 | "engines": { 158 | "node": ">=12" 159 | } 160 | }, 161 | "node_modules/@esbuild/linux-arm": { 162 | "version": "0.18.20", 163 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", 164 | "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", 165 | "cpu": [ 166 | "arm" 167 | ], 168 | "dev": true, 169 | "optional": true, 170 | "os": [ 171 | "linux" 172 | ], 173 | "engines": { 174 | "node": ">=12" 175 | } 176 | }, 177 | "node_modules/@esbuild/linux-arm64": { 178 | "version": "0.18.20", 179 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", 180 | "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", 181 | "cpu": [ 182 | "arm64" 183 | ], 184 | "dev": true, 185 | "optional": true, 186 | "os": [ 187 | "linux" 188 | ], 189 | "engines": { 190 | "node": ">=12" 191 | } 192 | }, 193 | "node_modules/@esbuild/linux-ia32": { 194 | "version": "0.18.20", 195 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", 196 | "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", 197 | "cpu": [ 198 | "ia32" 199 | ], 200 | "dev": true, 201 | "optional": true, 202 | "os": [ 203 | "linux" 204 | ], 205 | "engines": { 206 | "node": ">=12" 207 | } 208 | }, 209 | "node_modules/@esbuild/linux-loong64": { 210 | "version": "0.18.20", 211 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", 212 | "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", 213 | "cpu": [ 214 | "loong64" 215 | ], 216 | "dev": true, 217 | "optional": true, 218 | "os": [ 219 | "linux" 220 | ], 221 | "engines": { 222 | "node": ">=12" 223 | } 224 | }, 225 | "node_modules/@esbuild/linux-mips64el": { 226 | "version": "0.18.20", 227 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", 228 | "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", 229 | "cpu": [ 230 | "mips64el" 231 | ], 232 | "dev": true, 233 | "optional": true, 234 | "os": [ 235 | "linux" 236 | ], 237 | "engines": { 238 | "node": ">=12" 239 | } 240 | }, 241 | "node_modules/@esbuild/linux-ppc64": { 242 | "version": "0.18.20", 243 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", 244 | "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", 245 | "cpu": [ 246 | "ppc64" 247 | ], 248 | "dev": true, 249 | "optional": true, 250 | "os": [ 251 | "linux" 252 | ], 253 | "engines": { 254 | "node": ">=12" 255 | } 256 | }, 257 | "node_modules/@esbuild/linux-riscv64": { 258 | "version": "0.18.20", 259 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", 260 | "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", 261 | "cpu": [ 262 | "riscv64" 263 | ], 264 | "dev": true, 265 | "optional": true, 266 | "os": [ 267 | "linux" 268 | ], 269 | "engines": { 270 | "node": ">=12" 271 | } 272 | }, 273 | "node_modules/@esbuild/linux-s390x": { 274 | "version": "0.18.20", 275 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", 276 | "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", 277 | "cpu": [ 278 | "s390x" 279 | ], 280 | "dev": true, 281 | "optional": true, 282 | "os": [ 283 | "linux" 284 | ], 285 | "engines": { 286 | "node": ">=12" 287 | } 288 | }, 289 | "node_modules/@esbuild/linux-x64": { 290 | "version": "0.18.20", 291 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", 292 | "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", 293 | "cpu": [ 294 | "x64" 295 | ], 296 | "dev": true, 297 | "optional": true, 298 | "os": [ 299 | "linux" 300 | ], 301 | "engines": { 302 | "node": ">=12" 303 | } 304 | }, 305 | "node_modules/@esbuild/netbsd-x64": { 306 | "version": "0.18.20", 307 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", 308 | "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", 309 | "cpu": [ 310 | "x64" 311 | ], 312 | "dev": true, 313 | "optional": true, 314 | "os": [ 315 | "netbsd" 316 | ], 317 | "engines": { 318 | "node": ">=12" 319 | } 320 | }, 321 | "node_modules/@esbuild/openbsd-x64": { 322 | "version": "0.18.20", 323 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", 324 | "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", 325 | "cpu": [ 326 | "x64" 327 | ], 328 | "dev": true, 329 | "optional": true, 330 | "os": [ 331 | "openbsd" 332 | ], 333 | "engines": { 334 | "node": ">=12" 335 | } 336 | }, 337 | "node_modules/@esbuild/sunos-x64": { 338 | "version": "0.18.20", 339 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", 340 | "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", 341 | "cpu": [ 342 | "x64" 343 | ], 344 | "dev": true, 345 | "optional": true, 346 | "os": [ 347 | "sunos" 348 | ], 349 | "engines": { 350 | "node": ">=12" 351 | } 352 | }, 353 | "node_modules/@esbuild/win32-arm64": { 354 | "version": "0.18.20", 355 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", 356 | "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", 357 | "cpu": [ 358 | "arm64" 359 | ], 360 | "dev": true, 361 | "optional": true, 362 | "os": [ 363 | "win32" 364 | ], 365 | "engines": { 366 | "node": ">=12" 367 | } 368 | }, 369 | "node_modules/@esbuild/win32-ia32": { 370 | "version": "0.18.20", 371 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", 372 | "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", 373 | "cpu": [ 374 | "ia32" 375 | ], 376 | "dev": true, 377 | "optional": true, 378 | "os": [ 379 | "win32" 380 | ], 381 | "engines": { 382 | "node": ">=12" 383 | } 384 | }, 385 | "node_modules/@esbuild/win32-x64": { 386 | "version": "0.18.20", 387 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", 388 | "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", 389 | "cpu": [ 390 | "x64" 391 | ], 392 | "dev": true, 393 | "optional": true, 394 | "os": [ 395 | "win32" 396 | ], 397 | "engines": { 398 | "node": ">=12" 399 | } 400 | }, 401 | "node_modules/@types/node": { 402 | "version": "20.9.1", 403 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.1.tgz", 404 | "integrity": "sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==", 405 | "dependencies": { 406 | "undici-types": "~5.26.4" 407 | } 408 | }, 409 | "node_modules/@types/node-fetch": { 410 | "version": "2.6.9", 411 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.9.tgz", 412 | "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==", 413 | "dependencies": { 414 | "@types/node": "*", 415 | "form-data": "^4.0.0" 416 | } 417 | }, 418 | "node_modules/@types/prompts": { 419 | "version": "2.4.8", 420 | "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.8.tgz", 421 | "integrity": "sha512-fPOEzviubkEVCiLduO45h+zFHB0RZX8tFt3C783sO5cT7fUXf3EEECpD26djtYdh4Isa9Z9tasMQuZnYPtvYzw==", 422 | "dev": true, 423 | "dependencies": { 424 | "@types/node": "*", 425 | "kleur": "^3.0.3" 426 | } 427 | }, 428 | "node_modules/abort-controller": { 429 | "version": "3.0.0", 430 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 431 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 432 | "dependencies": { 433 | "event-target-shim": "^5.0.0" 434 | }, 435 | "engines": { 436 | "node": ">=6.5" 437 | } 438 | }, 439 | "node_modules/agentkeepalive": { 440 | "version": "4.5.0", 441 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", 442 | "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", 443 | "dependencies": { 444 | "humanize-ms": "^1.2.1" 445 | }, 446 | "engines": { 447 | "node": ">= 8.0.0" 448 | } 449 | }, 450 | "node_modules/ansi-regex": { 451 | "version": "6.0.1", 452 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", 453 | "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", 454 | "engines": { 455 | "node": ">=12" 456 | }, 457 | "funding": { 458 | "url": "https://github.com/chalk/ansi-regex?sponsor=1" 459 | } 460 | }, 461 | "node_modules/asynckit": { 462 | "version": "0.4.0", 463 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 464 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 465 | }, 466 | "node_modules/base-64": { 467 | "version": "0.1.0", 468 | "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", 469 | "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" 470 | }, 471 | "node_modules/base64-js": { 472 | "version": "1.5.1", 473 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 474 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 475 | "funding": [ 476 | { 477 | "type": "github", 478 | "url": "https://github.com/sponsors/feross" 479 | }, 480 | { 481 | "type": "patreon", 482 | "url": "https://www.patreon.com/feross" 483 | }, 484 | { 485 | "type": "consulting", 486 | "url": "https://feross.org/support" 487 | } 488 | ] 489 | }, 490 | "node_modules/bl": { 491 | "version": "5.1.0", 492 | "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", 493 | "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", 494 | "dependencies": { 495 | "buffer": "^6.0.3", 496 | "inherits": "^2.0.4", 497 | "readable-stream": "^3.4.0" 498 | } 499 | }, 500 | "node_modules/buffer": { 501 | "version": "6.0.3", 502 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 503 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 504 | "funding": [ 505 | { 506 | "type": "github", 507 | "url": "https://github.com/sponsors/feross" 508 | }, 509 | { 510 | "type": "patreon", 511 | "url": "https://www.patreon.com/feross" 512 | }, 513 | { 514 | "type": "consulting", 515 | "url": "https://feross.org/support" 516 | } 517 | ], 518 | "dependencies": { 519 | "base64-js": "^1.3.1", 520 | "ieee754": "^1.2.1" 521 | } 522 | }, 523 | "node_modules/buffer-from": { 524 | "version": "1.1.2", 525 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 526 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 527 | "dev": true 528 | }, 529 | "node_modules/chalk": { 530 | "version": "5.3.0", 531 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", 532 | "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", 533 | "engines": { 534 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 535 | }, 536 | "funding": { 537 | "url": "https://github.com/chalk/chalk?sponsor=1" 538 | } 539 | }, 540 | "node_modules/charenc": { 541 | "version": "0.0.2", 542 | "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", 543 | "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", 544 | "engines": { 545 | "node": "*" 546 | } 547 | }, 548 | "node_modules/cli-cursor": { 549 | "version": "4.0.0", 550 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", 551 | "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", 552 | "dependencies": { 553 | "restore-cursor": "^4.0.0" 554 | }, 555 | "engines": { 556 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 557 | }, 558 | "funding": { 559 | "url": "https://github.com/sponsors/sindresorhus" 560 | } 561 | }, 562 | "node_modules/cli-spinners": { 563 | "version": "2.9.1", 564 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", 565 | "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", 566 | "engines": { 567 | "node": ">=6" 568 | }, 569 | "funding": { 570 | "url": "https://github.com/sponsors/sindresorhus" 571 | } 572 | }, 573 | "node_modules/combined-stream": { 574 | "version": "1.0.8", 575 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 576 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 577 | "dependencies": { 578 | "delayed-stream": "~1.0.0" 579 | }, 580 | "engines": { 581 | "node": ">= 0.8" 582 | } 583 | }, 584 | "node_modules/crypt": { 585 | "version": "0.0.2", 586 | "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", 587 | "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", 588 | "engines": { 589 | "node": "*" 590 | } 591 | }, 592 | "node_modules/delayed-stream": { 593 | "version": "1.0.0", 594 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 595 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 596 | "engines": { 597 | "node": ">=0.4.0" 598 | } 599 | }, 600 | "node_modules/digest-fetch": { 601 | "version": "1.3.0", 602 | "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", 603 | "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", 604 | "dependencies": { 605 | "base-64": "^0.1.0", 606 | "md5": "^2.3.0" 607 | } 608 | }, 609 | "node_modules/dotenv": { 610 | "version": "16.3.1", 611 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", 612 | "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", 613 | "engines": { 614 | "node": ">=12" 615 | }, 616 | "funding": { 617 | "url": "https://github.com/motdotla/dotenv?sponsor=1" 618 | } 619 | }, 620 | "node_modules/eastasianwidth": { 621 | "version": "0.2.0", 622 | "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", 623 | "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" 624 | }, 625 | "node_modules/emoji-regex": { 626 | "version": "10.3.0", 627 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", 628 | "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" 629 | }, 630 | "node_modules/esbuild": { 631 | "version": "0.18.20", 632 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", 633 | "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", 634 | "dev": true, 635 | "hasInstallScript": true, 636 | "bin": { 637 | "esbuild": "bin/esbuild" 638 | }, 639 | "engines": { 640 | "node": ">=12" 641 | }, 642 | "optionalDependencies": { 643 | "@esbuild/android-arm": "0.18.20", 644 | "@esbuild/android-arm64": "0.18.20", 645 | "@esbuild/android-x64": "0.18.20", 646 | "@esbuild/darwin-arm64": "0.18.20", 647 | "@esbuild/darwin-x64": "0.18.20", 648 | "@esbuild/freebsd-arm64": "0.18.20", 649 | "@esbuild/freebsd-x64": "0.18.20", 650 | "@esbuild/linux-arm": "0.18.20", 651 | "@esbuild/linux-arm64": "0.18.20", 652 | "@esbuild/linux-ia32": "0.18.20", 653 | "@esbuild/linux-loong64": "0.18.20", 654 | "@esbuild/linux-mips64el": "0.18.20", 655 | "@esbuild/linux-ppc64": "0.18.20", 656 | "@esbuild/linux-riscv64": "0.18.20", 657 | "@esbuild/linux-s390x": "0.18.20", 658 | "@esbuild/linux-x64": "0.18.20", 659 | "@esbuild/netbsd-x64": "0.18.20", 660 | "@esbuild/openbsd-x64": "0.18.20", 661 | "@esbuild/sunos-x64": "0.18.20", 662 | "@esbuild/win32-arm64": "0.18.20", 663 | "@esbuild/win32-ia32": "0.18.20", 664 | "@esbuild/win32-x64": "0.18.20" 665 | } 666 | }, 667 | "node_modules/event-target-shim": { 668 | "version": "5.0.1", 669 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 670 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 671 | "engines": { 672 | "node": ">=6" 673 | } 674 | }, 675 | "node_modules/form-data": { 676 | "version": "4.0.0", 677 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 678 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 679 | "dependencies": { 680 | "asynckit": "^0.4.0", 681 | "combined-stream": "^1.0.8", 682 | "mime-types": "^2.1.12" 683 | }, 684 | "engines": { 685 | "node": ">= 6" 686 | } 687 | }, 688 | "node_modules/form-data-encoder": { 689 | "version": "1.7.2", 690 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 691 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" 692 | }, 693 | "node_modules/formdata-node": { 694 | "version": "4.4.1", 695 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 696 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 697 | "dependencies": { 698 | "node-domexception": "1.0.0", 699 | "web-streams-polyfill": "4.0.0-beta.3" 700 | }, 701 | "engines": { 702 | "node": ">= 12.20" 703 | } 704 | }, 705 | "node_modules/formdata-node/node_modules/web-streams-polyfill": { 706 | "version": "4.0.0-beta.3", 707 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 708 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", 709 | "engines": { 710 | "node": ">= 14" 711 | } 712 | }, 713 | "node_modules/fsevents": { 714 | "version": "2.3.3", 715 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 716 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 717 | "dev": true, 718 | "hasInstallScript": true, 719 | "optional": true, 720 | "os": [ 721 | "darwin" 722 | ], 723 | "engines": { 724 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 725 | } 726 | }, 727 | "node_modules/get-tsconfig": { 728 | "version": "4.7.2", 729 | "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", 730 | "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", 731 | "dev": true, 732 | "dependencies": { 733 | "resolve-pkg-maps": "^1.0.0" 734 | }, 735 | "funding": { 736 | "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" 737 | } 738 | }, 739 | "node_modules/humanize-ms": { 740 | "version": "1.2.1", 741 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 742 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 743 | "dependencies": { 744 | "ms": "^2.0.0" 745 | } 746 | }, 747 | "node_modules/ieee754": { 748 | "version": "1.2.1", 749 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 750 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 751 | "funding": [ 752 | { 753 | "type": "github", 754 | "url": "https://github.com/sponsors/feross" 755 | }, 756 | { 757 | "type": "patreon", 758 | "url": "https://www.patreon.com/feross" 759 | }, 760 | { 761 | "type": "consulting", 762 | "url": "https://feross.org/support" 763 | } 764 | ] 765 | }, 766 | "node_modules/inherits": { 767 | "version": "2.0.4", 768 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 769 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 770 | }, 771 | "node_modules/is-buffer": { 772 | "version": "1.1.6", 773 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 774 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 775 | }, 776 | "node_modules/is-interactive": { 777 | "version": "2.0.0", 778 | "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", 779 | "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", 780 | "engines": { 781 | "node": ">=12" 782 | }, 783 | "funding": { 784 | "url": "https://github.com/sponsors/sindresorhus" 785 | } 786 | }, 787 | "node_modules/is-unicode-supported": { 788 | "version": "1.3.0", 789 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", 790 | "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", 791 | "engines": { 792 | "node": ">=12" 793 | }, 794 | "funding": { 795 | "url": "https://github.com/sponsors/sindresorhus" 796 | } 797 | }, 798 | "node_modules/isomorphic-ws": { 799 | "version": "4.0.1", 800 | "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", 801 | "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", 802 | "peerDependencies": { 803 | "ws": "*" 804 | } 805 | }, 806 | "node_modules/kleur": { 807 | "version": "3.0.3", 808 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", 809 | "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", 810 | "engines": { 811 | "node": ">=6" 812 | } 813 | }, 814 | "node_modules/log-symbols": { 815 | "version": "5.1.0", 816 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", 817 | "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", 818 | "dependencies": { 819 | "chalk": "^5.0.0", 820 | "is-unicode-supported": "^1.1.0" 821 | }, 822 | "engines": { 823 | "node": ">=12" 824 | }, 825 | "funding": { 826 | "url": "https://github.com/sponsors/sindresorhus" 827 | } 828 | }, 829 | "node_modules/md5": { 830 | "version": "2.3.0", 831 | "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", 832 | "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", 833 | "dependencies": { 834 | "charenc": "0.0.2", 835 | "crypt": "0.0.2", 836 | "is-buffer": "~1.1.6" 837 | } 838 | }, 839 | "node_modules/mime-db": { 840 | "version": "1.52.0", 841 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 842 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 843 | "engines": { 844 | "node": ">= 0.6" 845 | } 846 | }, 847 | "node_modules/mime-types": { 848 | "version": "2.1.35", 849 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 850 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 851 | "dependencies": { 852 | "mime-db": "1.52.0" 853 | }, 854 | "engines": { 855 | "node": ">= 0.6" 856 | } 857 | }, 858 | "node_modules/mimic-fn": { 859 | "version": "2.1.0", 860 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 861 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", 862 | "engines": { 863 | "node": ">=6" 864 | } 865 | }, 866 | "node_modules/ms": { 867 | "version": "2.1.3", 868 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 869 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 870 | }, 871 | "node_modules/nanoid": { 872 | "version": "5.0.3", 873 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.3.tgz", 874 | "integrity": "sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA==", 875 | "funding": [ 876 | { 877 | "type": "github", 878 | "url": "https://github.com/sponsors/ai" 879 | } 880 | ], 881 | "bin": { 882 | "nanoid": "bin/nanoid.js" 883 | }, 884 | "engines": { 885 | "node": "^18 || >=20" 886 | } 887 | }, 888 | "node_modules/node-domexception": { 889 | "version": "1.0.0", 890 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 891 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 892 | "funding": [ 893 | { 894 | "type": "github", 895 | "url": "https://github.com/sponsors/jimmywarting" 896 | }, 897 | { 898 | "type": "github", 899 | "url": "https://paypal.me/jimmywarting" 900 | } 901 | ], 902 | "engines": { 903 | "node": ">=10.5.0" 904 | } 905 | }, 906 | "node_modules/node-fetch": { 907 | "version": "2.7.0", 908 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 909 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 910 | "dependencies": { 911 | "whatwg-url": "^5.0.0" 912 | }, 913 | "engines": { 914 | "node": "4.x || >=6.0.0" 915 | }, 916 | "peerDependencies": { 917 | "encoding": "^0.1.0" 918 | }, 919 | "peerDependenciesMeta": { 920 | "encoding": { 921 | "optional": true 922 | } 923 | } 924 | }, 925 | "node_modules/normalize-path": { 926 | "version": "3.0.0", 927 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 928 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 929 | "engines": { 930 | "node": ">=0.10.0" 931 | } 932 | }, 933 | "node_modules/onetime": { 934 | "version": "5.1.2", 935 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", 936 | "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", 937 | "dependencies": { 938 | "mimic-fn": "^2.1.0" 939 | }, 940 | "engines": { 941 | "node": ">=6" 942 | }, 943 | "funding": { 944 | "url": "https://github.com/sponsors/sindresorhus" 945 | } 946 | }, 947 | "node_modules/openai": { 948 | "version": "4.19.0", 949 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.19.0.tgz", 950 | "integrity": "sha512-cJbl0noZyAaXVKBTMMq6X5BAvP1pm2rWYDBnZes99NL+Zh5/4NmlAwyuhTZEru5SqGGZIoiYKeMPXy4bm9DI0w==", 951 | "dependencies": { 952 | "@types/node": "^18.11.18", 953 | "@types/node-fetch": "^2.6.4", 954 | "abort-controller": "^3.0.0", 955 | "agentkeepalive": "^4.2.1", 956 | "digest-fetch": "^1.3.0", 957 | "form-data-encoder": "1.7.2", 958 | "formdata-node": "^4.3.2", 959 | "node-fetch": "^2.6.7", 960 | "web-streams-polyfill": "^3.2.1" 961 | }, 962 | "bin": { 963 | "openai": "bin/cli" 964 | } 965 | }, 966 | "node_modules/openai/node_modules/@types/node": { 967 | "version": "18.18.9", 968 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz", 969 | "integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==", 970 | "dependencies": { 971 | "undici-types": "~5.26.4" 972 | } 973 | }, 974 | "node_modules/openapi-typescript-fetch": { 975 | "version": "1.1.3", 976 | "resolved": "https://registry.npmjs.org/openapi-typescript-fetch/-/openapi-typescript-fetch-1.1.3.tgz", 977 | "integrity": "sha512-smLZPck4OkKMNExcw8jMgrMOGgVGx2N/s6DbKL2ftNl77g5HfoGpZGFy79RBzU/EkaO0OZpwBnslfdBfh7ZcWg==", 978 | "engines": { 979 | "node": ">= 12.0.0", 980 | "npm": ">= 7.0.0" 981 | } 982 | }, 983 | "node_modules/ora": { 984 | "version": "7.0.1", 985 | "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", 986 | "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", 987 | "dependencies": { 988 | "chalk": "^5.3.0", 989 | "cli-cursor": "^4.0.0", 990 | "cli-spinners": "^2.9.0", 991 | "is-interactive": "^2.0.0", 992 | "is-unicode-supported": "^1.3.0", 993 | "log-symbols": "^5.1.0", 994 | "stdin-discarder": "^0.1.0", 995 | "string-width": "^6.1.0", 996 | "strip-ansi": "^7.1.0" 997 | }, 998 | "engines": { 999 | "node": ">=16" 1000 | }, 1001 | "funding": { 1002 | "url": "https://github.com/sponsors/sindresorhus" 1003 | } 1004 | }, 1005 | "node_modules/path-browserify": { 1006 | "version": "1.0.1", 1007 | "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", 1008 | "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" 1009 | }, 1010 | "node_modules/platform": { 1011 | "version": "1.3.6", 1012 | "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", 1013 | "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" 1014 | }, 1015 | "node_modules/prompts": { 1016 | "version": "2.4.2", 1017 | "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", 1018 | "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", 1019 | "dependencies": { 1020 | "kleur": "^3.0.3", 1021 | "sisteransi": "^1.0.5" 1022 | }, 1023 | "engines": { 1024 | "node": ">= 6" 1025 | } 1026 | }, 1027 | "node_modules/readable-stream": { 1028 | "version": "3.6.2", 1029 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 1030 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 1031 | "dependencies": { 1032 | "inherits": "^2.0.3", 1033 | "string_decoder": "^1.1.1", 1034 | "util-deprecate": "^1.0.1" 1035 | }, 1036 | "engines": { 1037 | "node": ">= 6" 1038 | } 1039 | }, 1040 | "node_modules/resolve-pkg-maps": { 1041 | "version": "1.0.0", 1042 | "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", 1043 | "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", 1044 | "dev": true, 1045 | "funding": { 1046 | "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" 1047 | } 1048 | }, 1049 | "node_modules/restore-cursor": { 1050 | "version": "4.0.0", 1051 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", 1052 | "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", 1053 | "dependencies": { 1054 | "onetime": "^5.1.0", 1055 | "signal-exit": "^3.0.2" 1056 | }, 1057 | "engines": { 1058 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1059 | }, 1060 | "funding": { 1061 | "url": "https://github.com/sponsors/sindresorhus" 1062 | } 1063 | }, 1064 | "node_modules/rpc-websocket-client": { 1065 | "version": "1.1.4", 1066 | "resolved": "https://registry.npmjs.org/rpc-websocket-client/-/rpc-websocket-client-1.1.4.tgz", 1067 | "integrity": "sha512-UDMVcsNDJ3WET+0EFdmseYX+60MOCA3BpgpZ5dbtiGsqHo+9uWfgNbPZ4iGDtNcX+EUhCt/Y7C0fTmUU3CkkWA==", 1068 | "dependencies": { 1069 | "isomorphic-ws": "^4.0.1", 1070 | "uuid": "^3.3.3", 1071 | "ws": "^7.1.2" 1072 | }, 1073 | "engines": { 1074 | "node": ">=6.0.0" 1075 | } 1076 | }, 1077 | "node_modules/safe-buffer": { 1078 | "version": "5.2.1", 1079 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1080 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1081 | "funding": [ 1082 | { 1083 | "type": "github", 1084 | "url": "https://github.com/sponsors/feross" 1085 | }, 1086 | { 1087 | "type": "patreon", 1088 | "url": "https://www.patreon.com/feross" 1089 | }, 1090 | { 1091 | "type": "consulting", 1092 | "url": "https://feross.org/support" 1093 | } 1094 | ] 1095 | }, 1096 | "node_modules/signal-exit": { 1097 | "version": "3.0.7", 1098 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 1099 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 1100 | }, 1101 | "node_modules/sisteransi": { 1102 | "version": "1.0.5", 1103 | "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", 1104 | "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" 1105 | }, 1106 | "node_modules/source-map": { 1107 | "version": "0.6.1", 1108 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1109 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1110 | "dev": true, 1111 | "engines": { 1112 | "node": ">=0.10.0" 1113 | } 1114 | }, 1115 | "node_modules/source-map-support": { 1116 | "version": "0.5.21", 1117 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1118 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1119 | "dev": true, 1120 | "dependencies": { 1121 | "buffer-from": "^1.0.0", 1122 | "source-map": "^0.6.0" 1123 | } 1124 | }, 1125 | "node_modules/stdin-discarder": { 1126 | "version": "0.1.0", 1127 | "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", 1128 | "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", 1129 | "dependencies": { 1130 | "bl": "^5.0.0" 1131 | }, 1132 | "engines": { 1133 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1134 | }, 1135 | "funding": { 1136 | "url": "https://github.com/sponsors/sindresorhus" 1137 | } 1138 | }, 1139 | "node_modules/string_decoder": { 1140 | "version": "1.3.0", 1141 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1142 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1143 | "dependencies": { 1144 | "safe-buffer": "~5.2.0" 1145 | } 1146 | }, 1147 | "node_modules/string-width": { 1148 | "version": "6.1.0", 1149 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", 1150 | "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", 1151 | "dependencies": { 1152 | "eastasianwidth": "^0.2.0", 1153 | "emoji-regex": "^10.2.1", 1154 | "strip-ansi": "^7.0.1" 1155 | }, 1156 | "engines": { 1157 | "node": ">=16" 1158 | }, 1159 | "funding": { 1160 | "url": "https://github.com/sponsors/sindresorhus" 1161 | } 1162 | }, 1163 | "node_modules/strip-ansi": { 1164 | "version": "7.1.0", 1165 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", 1166 | "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", 1167 | "dependencies": { 1168 | "ansi-regex": "^6.0.1" 1169 | }, 1170 | "engines": { 1171 | "node": ">=12" 1172 | }, 1173 | "funding": { 1174 | "url": "https://github.com/chalk/strip-ansi?sponsor=1" 1175 | } 1176 | }, 1177 | "node_modules/tr46": { 1178 | "version": "0.0.3", 1179 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1180 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1181 | }, 1182 | "node_modules/tsx": { 1183 | "version": "4.1.2", 1184 | "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.1.2.tgz", 1185 | "integrity": "sha512-1spM1bFV6MP2s4tO4tDC7g52fsaFdtEWdO4GfGdqi20qUgPbnAJqixOyIAvCSx1DDj3YIUB4CD06owTWUsOAuQ==", 1186 | "dev": true, 1187 | "dependencies": { 1188 | "esbuild": "~0.18.20", 1189 | "get-tsconfig": "^4.7.2", 1190 | "source-map-support": "^0.5.21" 1191 | }, 1192 | "bin": { 1193 | "tsx": "dist/cli.mjs" 1194 | }, 1195 | "engines": { 1196 | "node": ">=18.0.0" 1197 | }, 1198 | "optionalDependencies": { 1199 | "fsevents": "~2.3.3" 1200 | } 1201 | }, 1202 | "node_modules/typescript": { 1203 | "version": "5.2.2", 1204 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", 1205 | "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", 1206 | "dev": true, 1207 | "bin": { 1208 | "tsc": "bin/tsc", 1209 | "tsserver": "bin/tsserver" 1210 | }, 1211 | "engines": { 1212 | "node": ">=14.17" 1213 | } 1214 | }, 1215 | "node_modules/undici-types": { 1216 | "version": "5.26.5", 1217 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1218 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 1219 | }, 1220 | "node_modules/util-deprecate": { 1221 | "version": "1.0.2", 1222 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1223 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1224 | }, 1225 | "node_modules/uuid": { 1226 | "version": "3.4.0", 1227 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 1228 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", 1229 | "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", 1230 | "bin": { 1231 | "uuid": "bin/uuid" 1232 | } 1233 | }, 1234 | "node_modules/web-streams-polyfill": { 1235 | "version": "3.2.1", 1236 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", 1237 | "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", 1238 | "engines": { 1239 | "node": ">= 8" 1240 | } 1241 | }, 1242 | "node_modules/webidl-conversions": { 1243 | "version": "3.0.1", 1244 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1245 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1246 | }, 1247 | "node_modules/whatwg-url": { 1248 | "version": "5.0.0", 1249 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1250 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1251 | "dependencies": { 1252 | "tr46": "~0.0.3", 1253 | "webidl-conversions": "^3.0.0" 1254 | } 1255 | }, 1256 | "node_modules/ws": { 1257 | "version": "7.5.9", 1258 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", 1259 | "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", 1260 | "engines": { 1261 | "node": ">=8.3.0" 1262 | }, 1263 | "peerDependencies": { 1264 | "bufferutil": "^4.0.1", 1265 | "utf-8-validate": "^5.0.2" 1266 | }, 1267 | "peerDependenciesMeta": { 1268 | "bufferutil": { 1269 | "optional": true 1270 | }, 1271 | "utf-8-validate": { 1272 | "optional": true 1273 | } 1274 | } 1275 | } 1276 | } 1277 | } 1278 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ai-developer-with-sandbox", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "license": "MIT", 8 | "private": false, 9 | "engines": { 10 | "node": ">= 18.0.0", 11 | "npm": ">= 6.0.0" 12 | }, 13 | "contributors": [], 14 | "scripts": { 15 | "dev": "", 16 | "create-ai-assistant": "tsx aiAssistant-create.ts", 17 | "update-ai-assistant": "tsx aiAssistant-update.ts", 18 | "list-ai-assistant": "tsx aiAssistant-list.ts", 19 | "start": "tsx index.ts" 20 | 21 | }, 22 | "dependencies": { 23 | "@e2b/sdk": "^0.11.0", 24 | "chalk": "^5.3.0", 25 | "dotenv": "^16.3.1", 26 | "nanoid": "^5.0.3", 27 | "openai": "^4.17.3", 28 | "ora": "^7.0.1", 29 | "prompts": "^2.4.2" 30 | }, 31 | "devDependencies": { 32 | "@types/node": "^20.9.0", 33 | "@types/prompts": "^2.4.8", 34 | "tsx": "^4.1.2", 35 | "typescript": "^5.2.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # AI Developer powered by GPT-4-Turbo & OpenAI's AI Assistant API 2 |

3 | e2b logo 4 |

5 | 6 |

7 | AI Developer is an AI agent powered by OpenAI Assistant API that's using custom E2B Sandbox 8 |

9 | 10 | --- 11 | 12 | The AI developer is an AI agent that perform user's tasks in the user's GitHub repository including: 13 | - reading files 14 | - writing code 15 | - making pull requests 16 | - pulling GitHub repository 17 | - responding to the user's feedback to the agent's previous work. 18 | - running commands in the generated environment 19 | 20 | All agent's work is happening inside the [E2B sandbox](https://e2b.dev/docs). 21 | 22 | The E2B's sandboxes are isolated cloud environments made specifically for AI apps and agents. Inside the sandbox AI agents perform long-running tasks, run code in a secure cloud environment, and use the same tools as a human developer would use. 23 | 24 | - Pull GitHub repository 25 | - Read files 26 | - Make needed changes 27 | - Commit work 28 | - Create a pull request 29 | - Run commands in the generated environment 30 | 31 | The custom E2B sandbox environment is defined in the [`e2b.Dockerfile`](./e2b.Dockerfile) 32 | 33 | ## Getting started 34 | 1. Run `npm install` 35 | 1. Copy `.env.example` and save it as `.env` file 36 | 1. Add your OpenAI API key to `.env` 37 | 1. Get E2B API Key at [https://e2b.dev/docs/getting-started/api-key](https://e2b.dev/docs/getting-started/api-key) 38 | - Save it to `.env` 39 | 1. Run `npm run create-ai-assistant` to create the AI assistant using OpenAI's new Assistant API 40 | 1. Grab the assistant ID you just created here [https://platform.openai.com/assistants](https://platform.openai.com/assistants) 41 | - Save the assistant ID to `.env` 42 | 1. Create classic GitHub token [here](https://github.com/settings/tokens) and give it the `read:org` and `repo` permissions 43 | - Save the GitHub token to `.env` 44 | 1. Save your GitHub username to `.env` 45 | 46 | ## Run AI Developer 47 | 48 | Run the following command in terminal and follow instructions 49 | ```bash 50 | npm run start 51 | ``` 52 | ## Update AI Developer 53 | 54 | If you make changes to the description in `functions.ts`, you can update the AI by running the following command in the terminal: 55 | ```bash 56 | npm run update-ai-assistant 57 | ``` 58 | -------------------------------------------------------------------------------- /sleep.ts: -------------------------------------------------------------------------------- 1 | export function sleep(time: number) { 2 | return new Promise(resolve => setTimeout(resolve, time)) 3 | } 4 | --------------------------------------------------------------------------------