├── .gitignore ├── Dockerfile ├── LICENSE ├── MOUNTS └── readme.txt ├── README.md ├── WORKDIR └── sample-file.txt ├── api ├── exec.php ├── getGuide.php └── writeFile.php ├── docker-build.sh ├── docker-run.sh ├── guides ├── ctags.txt ├── displaying-images-in-chat.txt ├── do-a-demo.txt ├── docker.txt ├── editing-files.txt ├── get-secret-keys-or-passwords-from-user-securely.txt └── running-commands-on-docker-host.txt ├── host ├── get-secret.py └── host_command.py ├── images ├── demo-1-list-files.png ├── demo-2-problem-solving.png ├── demo-3-installing-packages.png ├── demo-4-running-docker.png ├── demo-5-running-python.png ├── demo-6-pulling-node-image.png ├── meme.jpg └── pandoras-box-icon-small.png ├── lib ├── api.php ├── cors-headers.php ├── functions.php ├── router.php └── serve-static.php ├── public ├── .well-known │ └── ai-plugin.json ├── openapi.yaml └── pandoras-box-icon.png ├── tests ├── execTest.php ├── getGuideTest.php └── writeFileTest.php └── the-guide.txt /.gitignore: -------------------------------------------------------------------------------- 1 | WORKDIR 2 | MOUNTS -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker:latest 2 | 3 | RUN apk add --no-cache \ 4 | curl \ 5 | php82 \ 6 | php82-curl 7 | 8 | WORKDIR /pandora 9 | 10 | COPY . . 11 | 12 | CMD [ "php82", "-S", "0.0.0.0:8000", "lib/router.php" ] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Dave Hulbert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /MOUNTS/readme.txt: -------------------------------------------------------------------------------- 1 | Symlink dirs here before starting the container. 2 | 3 | docker-run.sh will then mount them into /pandora/WORKDIR 4 | 5 | From your project's parent directory: 6 | 7 | ln -s $PWD/your-project /path/to/pandora/MOUNTS/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pandora 2 | ## ChatGPT Coding Unleashed 3 | 4 | **Pandora gives ChatGPT the ability to read and write files and run commands on your machine.** 5 | 6 | ![Pandora icon](images/pandoras-box-icon-small.png) 7 | 8 | Pandora is a lightweight and powerful plugin for OpenAI's ChatGPT. It provides a barebones API that allows ChatGPT to execute arbitrary shell commands and perform file operations, making it a versatile tool for a wide range of tasks. 9 | 10 | Pandora is incredibly powerful but also dangerous. 11 | 12 | Pandora is like an unrestricted version of the ChatGPT Code Interpreter. 13 | 14 | Read the [Introduction to the Pandora ChatGPT plugin](https://medium.com/@dave1010/making-pandora-a-super-charged-coding-plugin-for-chatgpt-670bbb60469d) 15 | blog post. 16 | 17 | ![Pandoa vs Code Interpreter](images/meme.jpg) 18 | 19 | 20 | 🚨❗️⚠️ **Pandora can access and modify all files on your host system!** 🚨❗️⚠️ 21 | 22 | 🚨❗️⚠️ **Pandora can control other Docker containers!** 🚨❗️⚠️ 23 | 24 | Pandora can be used for: 25 | 26 | * coding 27 | * data processing 28 | * connecting to the internet 29 | * interacting with other Docker containers 30 | * releasing curses upon mankind (please don't try this) 31 | 32 | The idea for Pandora came from playing with [Kaguya](https://github.com/ykdojo/kaguya), a Node project with similar functionality. Pandora is more lightweight and is focused on letting the AI find and use tools itself, including letting it install software and run Docker containers. 33 | 34 | [Pandoras box icons created by Freepik - Flaticon](https://www.flaticon.com/free-icons/pandoras-box). 35 | 36 | ## Demos 37 | 38 | **[Github discussion thread with demos and examples](https://github.com/dave1010/pandora/discussions/6)**. More contributions are very welcome. 39 | 40 | * [Installing Python and running a new Docker container](https://chat.openai.com/share/9df39ba5-6779-4abf-9372-95535a97c4ff) (💬 ChatGPT transcript) 41 | * [Installing ffmpeg to resize a video](https://chat.openai.com/share/ab22f639-6901-4c63-97a3-edf488dbdd09) (💬 ChatGPT transcript) 42 | * [Running JS in a new Node Docker container](https://twitter.com/dave1010/status/1675556922747920384) (🎬 Video) 43 | 44 | | Image | Description | 45 | | ----- | ----------- | 46 | | ![screenshot](images/demo-4-running-docker.png) | Prompt: `write hello world scripts in python and node. then run them`. Pandora writes hello world scripts in Python and Node. It fails to run them, suggesting installing Python. Prompt: `yeah. dont install node though - use docker to run that`. It then installs Python locally and runs Node in a Docker container. | 47 | 48 | ## Security and Risks 49 | 50 | Pandora is designed to be used in a standalone host environment and it should be used with caution! 51 | 52 | Pandora has access to control Docker on your host, which means it can create a new container, mounting `/`! 53 | 54 | There is not much that Pandora cannot do. 55 | 56 | * Do not expose the API to the internet, or the whole world will have full access to your machine 57 | * Only let ChatGPT access it if you trust ChatGPT 58 | * You have been warned! 59 | 60 | That said, it's unlikely to do anything outside its own container unless you specifically ask it to. 61 | 62 | ## Running Pandora 63 | 64 | ### Requirements 65 | 66 | * Docker. You may be able to run Pandora locally, without Docker 67 | * Access to ChatGPT plugin development (currently behind a [wait list](https://openai.com/blog/chatgpt-plugins)) 68 | 69 | ### Setup 70 | 71 | git clone https://github.com/dave1010/pandora.git 72 | cd pandora 73 | ./docker-build.sh 74 | ./docker-run.sh 75 | 76 | Pandora should now be running on http://localhost:8000 77 | 78 | Add the plugin: go to the ChatGPT plugin store, click Develop your own plugin, and enter localhost:8000 79 | 80 | ## Basic Usage 81 | 82 | There's 3 commands: 83 | 84 | * `exec` - execute a command in the Pandora Docker container 85 | * `writeFile` - write a file. This is separate as ChatGPT struggles if it needs to escape special characters and new lines. 86 | * `getGuide` - fetch a guide. ChatGPT has limitations. This guide ([`the-guide.txt`](the-guide.txt)) helps it get better results. 87 | 88 | ChatGPT should automatically read the guide early on. If it gets confused then tell it to read the guide. 89 | 90 | See [`public/openapi.yaml`](public/openapi.yaml) for full API details. 91 | 92 | Pandora will work in `/pandora/WORKDIR` by default but can access its own files if you tell it to go up a dir. 93 | 94 | Run `docker exec -it $(docker ps -qf "ancestor=pandora") sh` if you want to work in the Pandora container. 95 | 96 | ### Mounting other directories 97 | 98 | Pandora can work on files in other directories! 99 | 100 | From your host, mount other projects into Pandora's `MOUNTS` directory. 101 | 102 | ln -s $PWD/your-project /path/to/pandora/MOUNTS/ 103 | 104 | This needs to be done _before_ the container is started (with [`docker-run.sh`](docker-run.sh)). 105 | 106 | `docker-run.sh` will then mount them into `/pandora/WORKDIR/`, allowing ChatGPT to read and write them. 107 | 108 | Side note: On a Mac, `ls -l` seems to show an extra metadata flag files that Pandora creates in mounted folders. This seems to have no effect. You can run `xattr -d com.apple.provenance` to remove the metadata flag if you want. 109 | 110 | ### Usage with Other Docker Containers 111 | 112 | Pandora mounts the host's `/var/run/docker.sock`, so it can control other containers running on the host. 113 | 114 | It can manage and access containers just like the host can. The only caveat is that ChatGPT will need to 115 | prefix host paths with `$PANDORA_CONTAINER_PATH` for them to be mounted correctly. It should realise it has to 116 | do this when it reads the guide. 117 | 118 | You can disable this behaviour by removing it from [`docker-run.sh`](docker-run.sh). 119 | 120 | ### Interacting with your host machine 121 | 122 | As well as modifying its own container and managing Docker, you can even get Pandora to interact 123 | with your host machine. 124 | 125 | These require manually running a Python on your machine, so don't run by default. 126 | 127 | 🚨❗️⚠️ **Make sure you understand what these are doing before you run them!** 🚨❗️⚠️ 128 | 129 | #### Running commands 130 | 131 | If you really want to open Pandora's box then you can ask it to run commands on your Docker host. 132 | This requires running a small Python webserver [`host/host_command.py`](host/host_command.py) on port 8080. 133 | 134 | python3 host/host_command.py 135 | 136 | When ChatGPT tries to run a command, it will ask you for confirmation before running the command. 137 | Currently only supports macOS. 138 | If you tell ChatGPT to run a command on your host machine, it should find the relevant guide 139 | [`guides/running-commands-on-docker-host.txt`](guides/running-commands-on-docker-host.txt) 140 | and walk you through the steps. 141 | 142 | This is useful for things like running `say` after a long running command or potentially 143 | interacting with your desktop. 144 | 145 | #### Getting secrets 146 | 147 | You can let ChatGPT use secrets without having to send them to OpenAI! 148 | This requires a separate small Python webserver: [`host/get-secret.py`](host/get-secret.py) on port 8080. 149 | 150 | python host/get-secret.py 151 | 152 | ChatGPT will ask you for a secret, which will pop up a prompt and send it back to the Pandora container. 153 | ChatGPT can use this as an env var or pipe it into other commands, so the secret never leaves your machine. 154 | 155 | If you tell ChatGPT to get a secret from you, it should read the 156 | [`guides/get-secret-keys-or-passwords-from-user-securely.txt`](guides/get-secret-keys-or-passwords-from-user-securely.txt) guide. 157 | 158 | ## How it works 159 | 160 | Pandora is little more than a PHP file that executes what ChatGPT sends to it. 161 | 162 | ## Design goals 163 | 164 | * Keep the API small, so ChatGPT has less context to deal with. 165 | * Minimal requirements and small Dockerfile. 166 | * Encourage ChatGPT to acquire tools itself, rather than giving it the kitchen sink. 167 | * Very little application code, so it's easier for ChatGPT to understand and modify. 168 | 169 | ## Pros and Cons vs OpenAI Code Interpreter 170 | 171 | * Pandora can access the internet 172 | * Pandora can use any software 173 | * Pandora doesn't time out and remove your files 174 | * Pandora works on your local files, so you can edit them at the same time 175 | * Pandora can run with other plugins too 176 | * CI seems a bit faster 177 | * CI works on your phone 178 | * CI keeps an interactive Python notebook process running, rather than spawning new processes for each API call 179 | * CI is better at displaying images (Pandora can do this but it can get confused) 180 | * CI lets you download files from the chat UI 181 | 182 | See my [ChatGPT Code Interpreter guide](https://github.com/dave1010/exploring-chatgpt-code-interpreter) for more info. 183 | 184 | ## Contributing and feedback 185 | 186 | Please send PRs! 187 | 188 | If you get ChatGPT to change the API endpoints then you may get `UnrecognizedKwargsError` until you refresh the plugin in the ChatGPT Plugin devtools sidebar. 189 | 190 | Use Github Discussions for general feedback: https://github.com/dave1010/pandora/discussions 191 | 192 | Please let me know how well it works, any success you've had and suggestions for [`the-guide.txt`](the-guide.txt). 193 | 194 | 195 | ## Licence 196 | 197 | MIT License 198 | 199 | Copyright (c) 2023 Dave Hulbert 200 | -------------------------------------------------------------------------------- /WORKDIR/sample-file.txt: -------------------------------------------------------------------------------- 1 | Hello World 2 | -------------------------------------------------------------------------------- /api/exec.php: -------------------------------------------------------------------------------- 1 | 'The guide must have been read recently and followed.']); 12 | return; 13 | } 14 | 15 | 16 | if (!$command) { 17 | http_response_code(400); 18 | echo json_encode(['error' => 'No command provided.']); 19 | return; 20 | } 21 | 22 | if ($command[0] !== '/') { 23 | $command = 'cd ' . dirname(__DIR__) . '/WORKDIR && ' . $command; 24 | } 25 | 26 | $output = doExec($command); 27 | 28 | if (isset($output['return_value']) && $output['return_value'] !== 0) { 29 | $output['help'] = 'VERY IMPORTANT: check the guide (getGuide) for how to fix problems before attempting something yourself!'; 30 | } 31 | 32 | echo json_encode($output); 33 | -------------------------------------------------------------------------------- /api/getGuide.php: -------------------------------------------------------------------------------- 1 | $guide, 11 | 'other-guides' => $otherGuides, 12 | ]); -------------------------------------------------------------------------------- /api/writeFile.php: -------------------------------------------------------------------------------- 1 | 'filePath and content are required.']); 15 | return; 16 | } 17 | 18 | if ($filePath[0] !== '/') { 19 | $filePath = dirname(__DIR__) . '/WORKDIR/' . $filePath; 20 | } 21 | 22 | if (file_exists($filePath) && !$force) { 23 | http_response_code(400); 24 | echo json_encode(['error' => 'File already exists.']); 25 | return; 26 | } 27 | 28 | $newLine = ($data['appendNewlineAtEOF'] ?? false ? "\n" : ""); 29 | 30 | file_put_contents($filePath, $content . $newLine); 31 | 32 | if (isset($data['permissions'])) { 33 | chmod($filePath, octdec($data['permissions'])); 34 | } 35 | 36 | http_response_code(200); 37 | echo json_encode(['message' => 'File written successfully.']); 38 | -------------------------------------------------------------------------------- /docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the absolute path of this script 4 | DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 5 | 6 | # Build the Docker image 7 | docker build -t pandora $DIR 8 | -------------------------------------------------------------------------------- /docker-run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Get the absolute path of this script 4 | DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 5 | 6 | # Loop over directories in the mounts directory and mount them 7 | # change to :ro for read only 8 | MOUNTS="" 9 | for dir in $(ls -d $DIR/MOUNTS/*/ 2>/dev/null); do 10 | echo Mounting $(realpath $dir) to /pandora/WORKDIR/$(basename $dir) 11 | MOUNTS+="-v $(realpath $dir):/pandora/WORKDIR/$(basename $dir) " 12 | done; 13 | 14 | SOCK="/var/run/docker.sock" 15 | 16 | docker run -p 8000:8000 -v $(pwd):/pandora -v $SOCK:$SOCK $MOUNTS -e PANDORA_CONTAINER_PATH=$(pwd) pandora 17 | 18 | # TODO: add a script for killing / restarting the container 19 | -------------------------------------------------------------------------------- /guides/ctags.txt: -------------------------------------------------------------------------------- 1 | UNTESTED 2 | ctags will probably help for working on codebases 3 | -------------------------------------------------------------------------------- /guides/displaying-images-in-chat.txt: -------------------------------------------------------------------------------- 1 | ChatGPT can display images directly inline in chat, even though it thinks it can't. 2 | 3 | It has lots of trouble doing this though, as it tries to escape the code. 4 | 5 | These rules must be followed very precicely to get images displaying correctly: 6 | 7 | * Use the ![alt](uri) markdown format for images 8 | * http://localhost:8000/ is visibile to the user and will serve files in /pandora/WORKDIR 9 | * Data URIs are a bad idea, unless the image is definitely under 2kB. If you must use data URIs, check the file size first and encode any spaces 10 | * Display the text verbatim. Not in a markdown code block. Not with backticks 11 | 12 | That last point about displaying text verbatim is incredibly important. 13 | If it is not followed then it will not work at all. 14 | ChatGPT needs to follow that point to the letter. 15 | 16 | You can also output LaTeX formatted text directly in the chat! 17 | 18 | * Inline math mode : `\(` and `\)` 19 | * Display math mode: insert linebreak after opening `$$`, `\[` and before closing `$$`, `\]` 20 | 21 | As with images, this must be done without backticks and not as a code block. 22 | -------------------------------------------------------------------------------- /guides/do-a-demo.txt: -------------------------------------------------------------------------------- 1 | You can do all sorts of impressive things - probably more than you can imagine. 2 | 3 | For a demo, try to do something really impressive for the user. 4 | Ideally something visual, interactive or surprising. 5 | 6 | You have almost any software available to you, information from the whole internet 7 | and can write any code you want to process data and files. 8 | 9 | You may want to read some other guides too. 10 | 11 | Come up with a few specific suggestions, then ask the user what they'd like to see. -------------------------------------------------------------------------------- /guides/docker.txt: -------------------------------------------------------------------------------- 1 | Docker: 2 | 3 | The Pandora plugin is running in a Docker container, 4 | but Docker commands are executed against the host machine, not the container. 5 | 6 | When specifying paths in Docker commands, you need to use the host's file system. 7 | To do this, prefix paths with the $PANDORA_CONTAINER_PATH environment variable. 8 | Eg `$PANDORA_PATH/WORKDIR:/usr/src/app`. 9 | This variable holds the path to the directory where the Pandora plugin is mounted on the host machine. 10 | 11 | You can pull new containers. Your training data is years old so use :latest by default. -------------------------------------------------------------------------------- /guides/editing-files.txt: -------------------------------------------------------------------------------- 1 | LLMs aren't good with big files. 2 | 3 | Use sed or awk if easier. 4 | 5 | grep --line-number may help. 6 | 7 | 8 | 22 | -------------------------------------------------------------------------------- /guides/get-secret-keys-or-passwords-from-user-securely.txt: -------------------------------------------------------------------------------- 1 | Getting the user to paste secret keys or passwords into the chat isn't a good idea. 2 | 3 | To get a secret from the user: 4 | 5 | 1. Ensure they're running the /pandora/host/get-secret.py script, which runs a webserver on their host machine. 6 | 2. Give the secret a name that will be used to store it 7 | 2. Fetch http://host.docker.internal:8080/secret?name=Example%20website to get a password for "Example website" 8 | 9 | IMPORTANT: You must not let secrets go to stdout or stderr. 10 | 11 | This asks the user for the secret and saves it against the key for quick retrieval. 12 | If there are issues then read the script to understand how it works. 13 | 14 | # Usage examples 15 | 16 | GITHUB_TOKEN=$(curl 'http://host.docker.internal:8080/secret?name=github%20token') gh issue list 17 | 18 | curl --user name:$(curl 'http://host.docker.internal:8080/secret?name=example%20password') http://www.example.com 19 | -------------------------------------------------------------------------------- /guides/running-commands-on-docker-host.txt: -------------------------------------------------------------------------------- 1 | To run commands on the user's host machine: 2 | 3 | 1. Ask user to run `host/host_command.py` on host machine. 4 | 2. To execute a non-interactive command, send GET request to `http://host.docker.internal:8080/?command=`. 5 | 3. User authorizes command via dialog box on host machine. 6 | 4. Command output returned in HTTP response, display in chat. 7 | 8 | Note: 9 | 10 | * If there's issues then look at host/host_command.py so you can help debug 11 | * The Pandora Docker container could be installed anywhere on the user's machine -------------------------------------------------------------------------------- /host/get-secret.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from flask.json import jsonify 3 | import subprocess 4 | 5 | app = Flask(__name__) 6 | 7 | # In-memory dictionary to store secrets 8 | secrets = {} 9 | 10 | @app.route('/secret', methods=['GET']) 11 | def get_secret(): 12 | secret_name = request.args.get('name') 13 | if secret_name in secrets: 14 | # If the secret is already stored, return it 15 | return jsonify({secret_name: secrets[secret_name]}) 16 | else: 17 | # If the secret is not stored, prompt the user for it 18 | secret_value = subprocess.getoutput(f'osascript -e "display dialog \\"Enter the {secret_name}\\" default answer \\"\\" with hidden answer" -e "text returned of result"') 19 | secrets[secret_name] = secret_value 20 | return secret_value 21 | 22 | if __name__ == '__main__': 23 | app.run(host='0.0.0.0', port=8080) 24 | 25 | # TODO: Linux support -------------------------------------------------------------------------------- /host/host_command.py: -------------------------------------------------------------------------------- 1 | from http.server import BaseHTTPRequestHandler, HTTPServer 2 | import os 3 | from urllib.parse import parse_qs, urlparse 4 | import subprocess 5 | 6 | class RequestHandler(BaseHTTPRequestHandler): 7 | def do_GET(self): 8 | query = urlparse(self.path).query 9 | params = parse_qs(query) 10 | command = params.get('command', [''])[0] 11 | result = subprocess.run(f"osascript -e 'display dialog \"Authorize command: {command}?\" buttons {{\"Yes\", \"No\"}} default button \"No\"' -e 'if the button returned of the result is \"Yes\" then do shell script \"{command}\"'", shell=True, capture_output=True, text=True) 12 | self.send_response(200) 13 | self.send_header('Content-type', 'text/plain') 14 | self.end_headers() 15 | self.wfile.write(result.stdout.encode()) 16 | 17 | def run(server_class=HTTPServer, handler_class=RequestHandler): 18 | server_address = ('', 8080) 19 | httpd = server_class(server_address, handler_class) 20 | httpd.serve_forever() 21 | 22 | if __name__ == '__main__': 23 | run() 24 | 25 | # TODO: Linux support 26 | # TODO: return the output of the executed command 27 | # TODO: ensure the command doesn't wait for user input or it will hang 28 | # TODO: handle timeouts better 29 | # TODO: maybe support async commands, notifying the user somehow 30 | # TODO: check escaping / encoding of command -------------------------------------------------------------------------------- /images/demo-1-list-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dave1010/pandora/f0835c18a54cf36d426d9d00dad0b1670681d88f/images/demo-1-list-files.png -------------------------------------------------------------------------------- /images/demo-2-problem-solving.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dave1010/pandora/f0835c18a54cf36d426d9d00dad0b1670681d88f/images/demo-2-problem-solving.png -------------------------------------------------------------------------------- /images/demo-3-installing-packages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dave1010/pandora/f0835c18a54cf36d426d9d00dad0b1670681d88f/images/demo-3-installing-packages.png -------------------------------------------------------------------------------- /images/demo-4-running-docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dave1010/pandora/f0835c18a54cf36d426d9d00dad0b1670681d88f/images/demo-4-running-docker.png -------------------------------------------------------------------------------- /images/demo-5-running-python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dave1010/pandora/f0835c18a54cf36d426d9d00dad0b1670681d88f/images/demo-5-running-python.png -------------------------------------------------------------------------------- /images/demo-6-pulling-node-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dave1010/pandora/f0835c18a54cf36d426d9d00dad0b1670681d88f/images/demo-6-pulling-node-image.png -------------------------------------------------------------------------------- /images/meme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dave1010/pandora/f0835c18a54cf36d426d9d00dad0b1670681d88f/images/meme.jpg -------------------------------------------------------------------------------- /images/pandoras-box-icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dave1010/pandora/f0835c18a54cf36d426d9d00dad0b1670681d88f/images/pandoras-box-icon-small.png -------------------------------------------------------------------------------- /lib/api.php: -------------------------------------------------------------------------------- 1 | 'Invalid JSON in request body']); 11 | exit; 12 | } 13 | 14 | return $data; 15 | } 16 | 17 | function doExec($command) 18 | { 19 | $descriptorspec = array( 20 | 0 => array("pipe", "r"), // stdin 21 | 1 => array("pipe", "w"), // stdout 22 | 2 => array("pipe", "w") // stderr 23 | ); 24 | 25 | $process = proc_open($command, $descriptorspec, $pipes); 26 | 27 | if (!is_resource($process)) { 28 | return ['error' => 'Could not create process resource']; 29 | } 30 | 31 | $stdout = stream_get_contents($pipes[1]); 32 | $stderr = stream_get_contents($pipes[2]); 33 | 34 | fclose($pipes[0]); 35 | fclose($pipes[1]); 36 | fclose($pipes[2]); 37 | 38 | $return_value = proc_close($process); 39 | 40 | return [ 41 | 'stdout' => $stdout, 42 | 'stderr' => $stderr, 43 | 'return_value' => $return_value, 44 | ]; 45 | 46 | } -------------------------------------------------------------------------------- /lib/router.php: -------------------------------------------------------------------------------- 1 | 'API endpoint not found.']); 39 | exit(1); 40 | -------------------------------------------------------------------------------- /lib/serve-static.php: -------------------------------------------------------------------------------- 1 | [ 11 | 'method' => 'POST', 12 | 'header' => 'Content-Type: application/x-www-form-urlencoded', 13 | 'content' => http_build_query(['command' => $command]), 14 | ], 15 | ])); 16 | 17 | // Decode the JSON response 18 | $response = json_decode($response, true); 19 | 20 | // Check the response 21 | assert($response['output'] === "Hello, world!\n", 'Unexpected response from /exec endpoint.'); 22 | 23 | echo "Test passed!\n"; 24 | -------------------------------------------------------------------------------- /tests/getGuideTest.php: -------------------------------------------------------------------------------- 1 | $filename, 11 | 'content' => $content, 12 | 'force' => $force, 13 | 'appendNewline' => $appendNewline, 14 | 'permissions' => $permissions, 15 | ]; 16 | 17 | $options = [ 18 | 'http' => [ 19 | 'method' => 'POST', 20 | 'header' => "Content-Type: application/json\r\n", 21 | 'content' => json_encode($data), 22 | ], 23 | ]; 24 | 25 | $context = stream_context_create($options); 26 | $result = json_decode(file_get_contents('http://localhost:8000/api/writeFile', false, $context), true); 27 | 28 | assert($result['message'] === 'File written successfully.'); 29 | 30 | $localFile = dirname(__DIR__).'/WORKDIR/'.$filename; 31 | 32 | assert(file_get_contents($localFile) === $content . "\n"); 33 | assert(substr(sprintf('%o', fileperms($localFile)), -4) === $permissions); 34 | 35 | unlink($localFile); 36 | 37 | echo "Test passed!\n"; 38 | -------------------------------------------------------------------------------- /the-guide.txt: -------------------------------------------------------------------------------- 1 | The LLM's Guide to Using the Pandora Plugin Effectively 2 | 3 | Context: 4 | 5 | * LLMs have trouble with lots of context, recreating text verbatim, counting and following strict syntax. 6 | * Taking account of these guidelines is CRITICAL to being effective. Use of this plugin without following the guide has been known to cause serious issues. 7 | 8 | Your environment: 9 | 10 | * You're in a Docker container 11 | * You're currently in /pandora/WORKDIR 12 | * There's probably existing files in that dir 13 | * Files may also be changed by the user. Don't assume there are no changes if you haven't seen them in a while. 14 | 15 | Text editing: 16 | 17 | * Large files will overwhelm you. 18 | * Use basic UNIX tools to process just the bits of files that are relevant. Eg `grep -n -C`, head, tail, cut, and awk. 19 | * Temp files may help 20 | * Watch out for off by one errors with line numbers. Consider new line at end of file. 21 | 22 | Running commands: 23 | 24 | * Be safe and explicit. Use robust commands and flags like `cp -n`. Check you're in the right dir. 25 | * Check any big changes before proceeding to stop errors cascading. `git diff -- path` may help. 26 | * Minimise the need to escape special characters. 27 | * Your actions may be destructive. Ask if not sure. 28 | 29 | Fixing problems: 30 | 31 | * Be methodical with troubleshooting to validate assumptions 32 | * Install with `apk add` 33 | * If things fail more than once then stop and ask the user 34 | * If you're confident that you can fix a problem then go for it 35 | 36 | Other guides: 37 | 38 | * You can `cat` other guides in /pandora/guides/ 39 | * It is *critical* that you follow any that may be relevant to the current task 40 | 41 | Remember: the user wants you to follow this guide. Users aren't interested in the guide's contents. 42 | 43 | (c) 2023 Dave Hulbert. MIT licence. --------------------------------------------------------------------------------