Skip to content

Conversation

@rgarcia
Copy link
Contributor

@rgarcia rgarcia commented Dec 22, 2025

Summary

Add manually-maintained cp functionality to the TypeScript SDK for copying files to/from running VM instances.

Usage

import { cpToInstance, cpFromInstance } from '@anthropic-ai/hypeman/lib';

// Copy file to instance
await cpToInstance(client, instanceId, localPath, guestPath, {
  recursive: true,
  archive: true,  // preserve UID/GID
});

// Copy from instance
await cpFromInstance(client, instanceId, guestPath, localPath, {
  followLinks: true,
});

Features

  • cpToInstance: Copy local file/directory to instance
  • cpFromInstance: Copy file/directory from instance to local
  • WebSocket-based streaming with JSON control messages
  • Supports files and recursive directory operations
  • Handles symlinks, permissions, and modification times
  • Chunked binary data transfer for efficient streaming

Implementation

Located in src/lib/cp.ts following the SDK's convention for custom extensions that won't conflict with auto-generated code.

Dependencies


Note

Introduces file transfer helpers and a new instance filesystem stat API.

  • New lib: src/lib/cp.ts with cpToInstance and cpFromInstance for streaming file/directory copies to/from running instances (handles dirs, symlinks, perms, mtimes; path sanitization; uses ws)
  • API surface: Adds GET /instances/{id}/stat -> PathInfo and params InstanceStatParams; updates Instances client (instances.stat), types, and exports
  • Deps: Adds ws runtime dep and @types/ws dev dep; lockfile updated
  • Docs/metadata/tests: api.md updated; .stats.yml endpoint count bumped; skipped tests added for instances.stat

Written by Cursor Bugbot for commit ba6c287. This will update automatically on new commits. Configure here.

@rgarcia rgarcia requested a review from sjmiller609 December 23, 2025 00:34
Add manually-maintained cp functionality to the SDK for copying files
to/from running instances. This is placed in src/lib/ directory following
the SDK's convention for custom extensions.

Features:
- cpToInstance: Copy local file/directory to instance
- cpFromInstance: Copy file/directory from instance to local
- WebSocket-based streaming with JSON control messages
- Supports files and recursive directory operations
- Handles symlinks, permissions, and modification times
- Chunked binary data transfer for efficient streaming

Adds ws as a peer dependency for WebSocket support.
Cherry-picked from stainless/preview/feature/hypeman-cp

- Add client.instances.stat() method
- Add PathInfo response type
- Add InstanceStatParams query parameters
- Add WebSocket readyState check before ws.send() to prevent uncaught
  exceptions when connection closes unexpectedly during file streaming
- Add circular symlink protection using inode-based cycle detection
- Skip directory symlinks to prevent infinite recursion
- Follow file symlinks (copy target content) for consistent behavior
ws.close();
return;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symlink escape check bypassed for root destination

When the destination path is root (/ or path.sep), the symlink escape validation at line 479 is skipped entirely. This means a malicious server could send symlinks with relative targets like ../../etc/passwd that would be created pointing outside the destination directory. While copying to root is an edge case, when it occurs, the security check meant to prevent path traversal via symlinks is not applied.

Fix in Cursor Fix in Web

const targetPath = sanitizePath(opts.dstPath, currentHeader.path);

if (currentHeader.is_dir) {
fs.mkdirSync(targetPath, { recursive: true, mode: currentHeader.mode });
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directory and symlink mtime not preserved from headers

The CpFileHeader interface includes mtime for all entries (files, directories, symlinks), and the code at lines 524-528 correctly applies mtime for files after closing the write stream. However, for directories and symlinks, the mtime from the header is never applied. Directories are created at line 456 but no fs.utimesSync is called for them. The mtime preservation logic is inside the fileToClose.end() callback which only executes for regular files. This causes directories and symlinks to have the current time as mtime rather than the original timestamp from the source.

Additional Locations (1)

Fix in Cursor Fix in Web

@rgarcia rgarcia merged commit ac70855 into main Dec 23, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants