Skip to content

feat: add hypeman cp for file copy to/from running VMs#45

Merged
rgarcia merged 8 commits intomainfrom
feature/hypeman-cp
Dec 23, 2025
Merged

feat: add hypeman cp for file copy to/from running VMs#45
rgarcia merged 8 commits intomainfrom
feature/hypeman-cp

Conversation

@rgarcia
Copy link
Contributor

@rgarcia rgarcia commented Dec 22, 2025

Summary

Add hypeman cp functionality similar to docker cp for copying files to/from running VM instances. This includes renaming the exec-agent to guest-agent to better reflect its expanding role.

Changes

Guest Agent Refactor

  • Renamed lib/exec to lib/guest and exec-agent to guest-agent
  • Added gRPC RPCs: CopyToGuest, CopyFromGuest, StatPath
  • Implemented file copy handlers with UID/GID preservation support

API Server

  • Added WebSocket endpoint /instances/{id}/cp for streaming file transfers
  • Added HTTP endpoint /instances/{id}/stat for guest path metadata queries

Features

  • Copy files and directories to/from running instances
  • Docker cp compatible path semantics (trailing slash, /. suffix)
  • STDIN/STDOUT streaming with - argument
  • Archive mode (-a) for UID/GID preservation
  • Symlink handling with --follow-links
  • Chunked binary streaming for efficient transfers

Testing

  • Added integration tests for file and directory copy operations in both directions

Note

Introduces streamed file transfer and consolidates guest operations under a new guest package and agent.

  • New WebSocket CP endpoint: GET /instances/{id}/cp supporting copy "to"/"from" with chunked binary streaming, headers, end markers, errors; OTEL tracing + metrics
  • Guest gRPC API: guest.proto with Exec, CopyToGuest, CopyFromGuest, StatPath; client in lib/guest (vsock pooling); replaces lib/exec (removed)
  • Guest agent rename: exec-agentguest-agent; Makefile/build/test flow updated and .gitignore entries added
  • New HTTP API: StatInstancePath to query guest FS metadata; router wired; metrics migrated to guest package
  • API changes: ExecHandler now uses guest.ExecIntoInstance
  • Tests: Integration tests for cp (file/dir, to/from), exec paths, GPU and volume tests updated to use guest client

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

@rgarcia rgarcia requested a review from sjmiller609 December 23, 2025 00:34
@github-actions
Copy link

github-actions bot commented Dec 23, 2025

✱ Stainless preview builds

This PR will update the hypeman SDKs with the following commit message.

feat: add hypeman cp for file copy to/from running VMs
hypeman-typescript studio · code

Your SDK built successfully.
generate ✅build ✅lint ✅test ✅

hypeman-go studio · code

Your SDK built successfully.
generate ✅lint ✅test ✅

go get github.com/stainless-sdks/hypeman-go@49ea89852eed5e0893febc4c68d295a0d1a8bfe5
hypeman-cli studio · conflict

There was a conflict between your custom code and your generated changes.
You don't need to resolve this conflict right now, but you will need to resolve it for your changes to be released to your users. Read more about why this happened here.


This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
Last updated: 2025-12-23 19:09:08 UTC

This commit renames the exec-agent to guest-agent to better reflect its
expanding role as a general-purpose guest interaction service. Along with
the rename, this adds gRPC support for file copy operations:

- Rename lib/exec to lib/guest
- Rename lib/system/exec_agent to lib/system/guest_agent
- Add CopyToGuest and CopyFromGuest RPCs
- Add StatPath RPC for querying guest filesystem metadata
- Implement cp handlers with uid/gid preservation support
- Update all imports and references

The guest-agent continues to listen on vsock port 2222 and handle
command execution, with new file copy capabilities.
Add /instances/{id}/cp WebSocket endpoint to enable file copy operations
between the local filesystem and running VM instances. Also adds
/instances/{id}/stat HTTP endpoint for querying guest path metadata.

Features:
- Copy files and directories to/from running instances
- Streaming file transfer with chunked data
- Support for symlinks and directory hierarchies
- UID/GID preservation for archive mode
- Follow symlinks option
Add a separate HTTP endpoint for querying filesystem path info in guests.
This replaces the overloaded 'direction: stat' option in the cp WebSocket
endpoint with a cleaner REST API.

- Add PathInfo schema with exists, is_dir, is_file, is_symlink, etc.
- Add GET /instances/{id}/stat endpoint with path and follow_links params
- Add stat method to stainless.yaml for SDK generation

The new endpoint:
- Uses simple HTTP GET instead of WebSocket overhead
- Enables autogenerated SDK methods (client.Instances.Stat())
- Mirrors the guest agent's separate StatPath RPC
- Is self-documenting via OpenAPI spec
Add comprehensive integration tests for file copy functionality:
- Test copying single files to instance
- Test copying directories recursively to instance
- Test copying files from instance
- Test copying directories from instance
- Verify file content, permissions, and metadata preservation

Also adds Reset() method to outputBuffer helper used in tests.
The mtime was only being applied when currentFile != nil, which is only
true for regular files. For directories, currentFile is never set since
they are created with os.MkdirAll without opening a file handle.

This fix moves the mtime logic outside of the currentFile != nil block
so it applies to both files and directories.
@rgarcia
Copy link
Contributor Author

rgarcia commented Dec 23, 2025

Fixed in bdad985 - moved the mtime logic outside of the currentFile != nil block so it now applies to both files and directories. The guest-agent was already correctly sending mtime in directory headers.

@@ -0,0 +1,733 @@
package main
Copy link
Collaborator

Choose a reason for hiding this comment

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

accidentally committed the binary for this file to lib/system/guest_agent/guest_agent need adjust .gitignore file

)

// Metrics holds the metrics instruments for exec operations.
// Metrics holds the metrics instruments for guest operations.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should add metrics for copy:

The exec path has metrics (hypeman_exec_sessions_total, _duration_seconds, bytes*), but cp has none.

Suggest adding similar counters in lib/guest/metrics.go, e.g.

hypeman_cp_sessions_total (labels: direction, status)
hypeman_cp_duration_seconds
hypeman_cp_bytes_total

but consider what other metrics might be good to include.

Also, WebSocket handlers bypass otelchi tracing—consider adding a manual span in CpHandler.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Image


log.Printf("[guest-agent] copy-from-guest complete: %d entries from %s", len(entries), rootPath)
return nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

is it worth making the guest agent more structured, similar to the lib/*/README.md pattern we have in the server? at this point 732 lines in a main.go is pretty big, might be worth doing since now it's 2 features instead of just 1

Updated .gitignore to cover both guest-agent and guest_agent naming
patterns and removed the tracked binary from the repository.
Added metrics for copy operations following the exec metrics pattern:
- hypeman_cp_sessions_total (labels: direction, status)
- hypeman_cp_duration_seconds
- hypeman_cp_bytes_total

Also added OTEL span in CpHandler since WebSocket connections bypass
the otelchi middleware for HTTP request tracing.
Reorganized the 732-line main.go into a cleaner structure:
- main.go: Server setup, listener, gRPC registration (minimal)
- exec.go: Exec-related methods (Exec, executeTTY, executeNoTTY, buildEnv)
- cp.go: Copy methods (CopyToGuest, CopyFromGuest, helpers)
- stat.go: StatPath method

This matches the lib/*/README.md pattern used in the server codebase
and makes the guest-agent more maintainable as it grows.
@rgarcia rgarcia merged commit e4b8399 into main Dec 23, 2025
4 checks passed
@rgarcia rgarcia deleted the feature/hypeman-cp branch December 23, 2025 19:07
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