vm0-computer
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.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
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.mjsfile will be cleared between sessions. This is expected behavior - you'll need to reinstallwsand recreate the proxy each time you start a new sandbox session.
Write the proxy script to /tmp/proxy.mjs:
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
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
$VARinbash -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 -c 'curl -s -X PROPFIND "http://127.0.0.1:8080"'
List a subdirectory:
curl -s -X PROPFIND "http://127.0.0.1:8080/reports/" --header "Depth: 1"
2. Download a File from the Local Machine
curl -s -o /tmp/myfile.pdf "http://127.0.0.1:8080/myfile.pdf"
Read a text file directly:
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:
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):
curl -s -X PUT "http://127.0.0.1:8080/result.json" --header "Content-Type: application/json" -d '{"status": "done"}'
4. Delete a File
curl -s -X DELETE "http://127.0.0.1:8080/old-file.txt"
5. Create a Directory
curl -s -X MKCOL "http://127.0.0.1:8080/new-folder/"
6. Move (Rename) a File
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:
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:
# List all files recursively
curl -s -X PROPFIND "http://127.0.0.1:8080/" --header "Depth: infinity" | grep -o '<D:href>[^<]*</D:href>' | sed 's/<[^>]*>//g'
Search for a keyword across all text files:
for f in $(curl -s -X PROPFIND "http://127.0.0.1:8080/" --header "Depth: infinity" | grep -o '<D:href>[^<]*</D:href>' | 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:
- Wait 2-3 minutes after starting the Computer Connector for SSL certificates to be fully provisioned
- The proxy will automatically work once certificates are ready - no code changes needed
- 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.
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-03-11
- Index updated
- 2026-03-11
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!