skills/vm0-ai/vm0-skills/vm0-computer

vm0-computer

SKILL.md

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:

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 $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 -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:

  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.

Weekly Installs
21
GitHub Stars
45
First Seen
Feb 27, 2026
Installed on
opencode21
gemini-cli21
codebuddy21
github-copilot21
codex21
kimi-cli21