Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,5 +156,5 @@ func init() {
initCmd.Flags().StringVar(&rootfsName, "rootfs", "", "Name of the rootfs image (defaults to project name)")
initCmd.Flags().StringVar(&kernelName, "kernel", "default.bin", "Name of the kernel image")
initCmd.Flags().StringVar(&dockerfileName, "dockerfile", "Dockerfile", "Name of the Docker file")
initCmd.Flags().Int64Var(&fsSizeVmMib, "fs-size-vm", 512, "VM filesystem size in MiB")
initCmd.Flags().Int64Var(&fsSizeVmMib, "fs-size-vm", 1024, "VM filesystem size in MiB")
}
2 changes: 1 addition & 1 deletion internal/handlers/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func HandleRun(ctx context.Context, a *app.App, r RunReq) (presenters.RunView, e
func validateAndNormalize(r *RunReq) error {
// Set default VM filesystem size if not specified
if r.FsSizeVmMib == 0 {
r.FsSizeVmMib = 512
r.FsSizeVmMib = 1024
}
return nil
}
2 changes: 1 addition & 1 deletion internal/mcp/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestValidateRun(t *testing.T) {
if err := validateRun(RunInput{FsSizeVmMib: -1}); err == nil {
t.Fatalf("expected error for negative filesystem size")
}
if err := validateRun(RunInput{FsSizeVmMib: 512}); err != nil {
if err := validateRun(RunInput{FsSizeVmMib: 1024}); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/runconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func Default() *Config {
Machine: MachineConfig{
MemSizeMib: 512,
VcpuCount: 1,
FsSizeVmMib: 512,
FsSizeVmMib: 1024,
},
Rootfs: RootfsConfig{Name: "default"},
Builder: BuilderConfig{
Expand Down
73 changes: 73 additions & 0 deletions internal/ssh/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func (c *Client) hostname() string {
return fmt.Sprintf("%s.vm.vers.sh", c.host)
}

// Host returns the host identifier (VM ID).
func (c *Client) Host() string {
return c.host
}

// Connect establishes an SSH connection over TLS.
func (c *Client) Connect(ctx context.Context) (*ssh.Client, error) {
hostname := c.hostname()
Expand Down Expand Up @@ -181,6 +186,74 @@ func (c *Client) Interactive(ctx context.Context, stdin io.Reader, stdout, stder
}
}

// InteractiveCommand runs a command interactively with PTY support.
func (c *Client) InteractiveCommand(ctx context.Context, cmd string, stdin io.Reader, stdout, stderr io.Writer) error {
client, err := c.Connect(ctx)
if err != nil {
return err
}
defer client.Close()

session, err := client.NewSession()
if err != nil {
return fmt.Errorf("new session: %w", err)
}
defer session.Close()

// Get terminal size
width, height := 80, 24
var fd int
var isTerm bool
if f, ok := stdin.(*os.File); ok {
fd = int(f.Fd())
if term.IsTerminal(fd) {
isTerm = true
if w, h, err := term.GetSize(fd); err == nil {
width, height = w, h
}
}
}

// Request PTY
modes := ssh.TerminalModes{
ssh.ECHO: 1,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
if err := session.RequestPty("xterm-256color", height, width, modes); err != nil {
return fmt.Errorf("request PTY: %w", err)
}

// Wire up IO
session.Stdin = stdin
session.Stdout = stdout
session.Stderr = stderr

// Start command
if err := session.Start(cmd); err != nil {
return fmt.Errorf("start command: %w", err)
}

// Handle terminal resize if we have a real terminal
if isTerm {
go c.watchResize(ctx, fd, session)
}

// Wait for session to end or context cancellation
done := make(chan error, 1)
go func() {
done <- session.Wait()
}()

select {
case <-ctx.Done():
session.Close()
return ctx.Err()
case err := <-done:
return err
}
}

// watchResize monitors terminal size changes and updates the remote PTY.
func (c *Client) watchResize(ctx context.Context, fd int, session *ssh.Session) {
// Use SIGWINCH on Unix systems
Expand Down