--- name: vm0-computer description: Access the user's local computer files via WebDAV through the VM0 Computer Connector. Use when you need to read, write, or list files on the user's local machine from within a VM0 sandbox. vm0_secrets: - COMPUTER_CONNECTOR_BRIDGE_TOKEN - COMPUTER_CONNECTOR_DOMAIN --- # VM0 Computer Connector Access the user's local filesystem from a VM0 sandbox over a secure WebDAV tunnel established by the VM0 Computer Connector. --- ## When to Use Use this skill when you need to: - List files and directories on the user's local machine - Read files from the user's computer into the sandbox - Write or update files on the user's local machine - Transfer outputs back to the user's local filesystem --- ## How to Use ### Setup > **Note on Proxy Persistence:** VM0 sandboxes are stateless environments. The proxy process and `/tmp/proxy.mjs` file will be cleared between sessions. This is expected behavior - you'll need to reinstall `ws` and recreate the proxy each time you start a new sandbox session. Write the proxy script to `/tmp/proxy.mjs`: ```javascript import { WebSocket, WebSocketServer } from "ws"; import http from "http"; import https from "https"; const TOKEN = process.env.COMPUTER_CONNECTOR_BRIDGE_TOKEN; const DOMAIN = process.env.COMPUTER_CONNECTOR_DOMAIN; // Chrome CDP WebSocket proxy: ws://127.0.0.1:9222 → wss://chrome.{DOMAIN} const wss = new WebSocketServer({ host: "127.0.0.1", port: 9222 }); wss.on("connection", (local) => { const remote = new WebSocket(`wss://chrome.${DOMAIN}`, { headers: { "x-vm0-token": TOKEN }, }); local.on("message", (msg) => remote.send(msg)); remote.on("message", (msg) => local.send(msg)); local.on("close", () => remote.close()); remote.on("close", () => local.close()); }); // WebDAV HTTP proxy: http://127.0.0.1:8080 → https://webdav.{DOMAIN} http.createServer((req, res) => { const upstream = https.request( `https://webdav.${DOMAIN}${req.url}`, { method: req.method, headers: { ...req.headers, "x-vm0-token": TOKEN, host: `webdav.${DOMAIN}` } }, (proxyRes) => { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); } ); req.pipe(upstream); }).listen(8080, "127.0.0.1"); console.log("WebDAV proxy: http://127.0.0.1:8080"); console.log("CDP proxy: ws://127.0.0.1:9222"); ``` ### Run ```bash cd /tmp && npm install ws && node /tmp/proxy.mjs & ``` The proxy runs in the background. Tools can now connect to `http://127.0.0.1:8080` (WebDAV) and `ws://127.0.0.1:9222` (Chrome CDP) without any additional headers. > **Important:** Wrap curl commands that use `$VAR` in `bash -c '...'`. Due to a Claude Code bug, environment variables are silently cleared when pipes are used directly. ### 1. List Files in a Directory Use `PROPFIND` to list the contents of a directory (depth 1): ```bash bash -c 'curl -s -X PROPFIND "http://127.0.0.1:8080"' ``` List a subdirectory: ```bash curl -s -X PROPFIND "http://127.0.0.1:8080/reports/" --header "Depth: 1" ``` ### 2. Download a File from the Local Machine ```bash curl -s -o /tmp/myfile.pdf "http://127.0.0.1:8080/myfile.pdf" ``` Read a text file directly: ```bash curl -s "http://127.0.0.1:8080/notes.txt" ``` ### 3. Upload a File to the Local Machine Write a local sandbox file to remote: ```bash curl -s -X PUT "http://127.0.0.1:8080/output-report.txt" --header "Content-Type: text/plain" --data-binary @/home/user/output-report.txt ``` Upload from stdin (inline content): ```bash curl -s -X PUT "http://127.0.0.1:8080/result.json" --header "Content-Type: application/json" -d '{"status": "done"}' ``` ### 4. Delete a File ```bash curl -s -X DELETE "http://127.0.0.1:8080/old-file.txt" ``` ### 5. Create a Directory ```bash curl -s -X MKCOL "http://127.0.0.1:8080/new-folder/" ``` ### 6. Move (Rename) a File ```bash curl -s -X MOVE "http://127.0.0.1:8080/old-name.txt" --header "Destination: http://127.0.0.1:8080/new-name.txt" ``` Move to a subdirectory: ```bash curl -s -X MOVE "http://127.0.0.1:8080/report.pdf" --header "Destination: http://127.0.0.1:8080/archive/report.pdf" ``` ### 7. Search File Contents WebDAV does not support full-text search natively. Use PROPFIND with `Depth: infinity` to list all files recursively, then download and search: ```bash # List all files recursively curl -s -X PROPFIND "http://127.0.0.1:8080/" --header "Depth: infinity" | grep -o '[^<]*' | sed 's/<[^>]*>//g' ``` Search for a keyword across all text files: ```bash for f in $(curl -s -X PROPFIND "http://127.0.0.1:8080/" --header "Depth: infinity" | grep -o '[^<]*' | sed 's/<[^>]*>//g' | grep '\.txt$'); do content=$(curl -s "http://127.0.0.1:8080${f}") echo "$content" | grep -q "keyword" && echo "Found in: $f" done ``` --- ## Troubleshooting ### SSL Certificate Error (`ERR_TLS_CERT_ALTNAME_INVALID`) **Issue:** The first connection attempt may fail with `ERR_TLS_CERT_ALTNAME_INVALID` or similar SSL certificate errors. **Cause:** The ngrok tunnel needs a few minutes to provision and propagate SSL certificates after the Computer Connector starts. **Solution:** 1. **Wait 2-3 minutes** after starting the Computer Connector for SSL certificates to be fully provisioned 2. The proxy will automatically work once certificates are ready - no code changes needed 3. Test with a simple curl command: `curl -s "http://127.0.0.1:8080/"` **Note:** Do NOT add `rejectUnauthorized: false` to the proxy code as a workaround. This weakens security and is unnecessary - simply wait for proper certificate provisioning.