Search

PythonReactSEOPDF
GitHub Skill

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.

vm0-ai☆ 41 Repository stars4 Repository forks

Installation

Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.

View source ↗Guides

Skill 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.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.

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)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!