rde

package
v0.4.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Overview

Package rde holds the business-logic layer for Remote Dev Environments.

CLI-stable types (snake_case json tags) live here. The fromAPI mappers convert wire-format DTOs from bitriseapi/rde — they're the only place where backend renames affect `--output json`.

Index

Constants

View Source
const (
	ActionDownload = "download"
	ActionUpload   = "upload"
)

ActionDownload and ActionUpload move files between the session and the user's local machine. Unlike open-vnc they apply to every session (not just ones that expose a VNC endpoint). The path segment doubles as the route the in-session skill calls.

View Source
const (
	DiskStatusAvailable       = "available"
	DiskStatusUnavailableSoon = "unavailable_soon"
	DiskStatusUnavailable     = "unavailable"
)

Persistent-disk status values as produced by diskStatusFromAPI (the PERSISTENT_DISK_STATUS_ prefix stripped and lowercased). Only terminated sessions carry a disk status; running/active sessions report "". The disk is what a terminated session is restored from, so its status determines whether `rde session restore` can succeed.

View Source
const (
	LogStageWarmup  = "warmup"  // runs once at session creation
	LogStageStartup = "startup" // runs on every session start/restart
)

Friendly --stage values accepted by the CLI. The backend takes the numeric LogStage enum; these map to it in logStageToAPI. The backend calls the second stage "main" internally; the user-facing name is "startup" (it runs the session's startup script).

View Source
const ActionOpenVNC = "open-vnc"

ActionOpenVNC opens a VNC viewer on the user's local machine, pointed at this session's desktop. It is the first action the host bridge exposes; the path segment doubles as the route the in-session skill calls.

View Source
const DefaultExecuteTimeout = 10 * time.Minute

DefaultExecuteTimeout is the default cap on a single `rde session exec` invocation. exec runs client-side over SSH, so this ceiling is the CLI's own — not a backend limit — and callers override it (including disabling it with 0) via the --timeout flag. It's set generously so ordinary build steps (clone, LFS hydration, xcodegen, a warm xcodebuild) finish under one exec; the genuinely long cold builds pass a larger --timeout or 0 to uncap.

View Source
const DefaultMetadataInterval = time.Minute

DefaultMetadataInterval is how often ClaudeMetadataMonitor polls the session for the AI-generated title. One minute per the RDE plan: frequent enough that a freshly-named session shows a useful title soon, cheap enough to ignore.

Variables

View Source
var ErrConnectionLost = errors.New("connection to the session was lost")

ErrConnectionLost marks an interactive run that ended because the connection to the session dropped (network failure, dropped SSH channel) rather than the remote program exiting. Callers can match it with errors.Is to decide whether to reconnect. A program inside a survivable wrapper (e.g. tmux) keeps running on the session, so reattaching resumes it.

LogStages is the ordered set of valid --stage values, for validation and shell completion.

Functions

func FormatVNCURL added in v0.4.7

func FormatVNCURL(host string, port int, user, pass string) string

FormatVNCURL builds a `vnc://[user[:pass]@]host:port` URL with URL-escaped credentials — the same shape as VNCCredentials.URL. Exposed so a caller that forwards the endpoint to a local port (see ForwardVNC) can present a ready-to-use URL pointing at the local address.

func IsNotFound added in v0.3.0

func IsNotFound(err error) bool

IsNotFound reports whether err is an RDE API 404 — the resource was deleted or never existed. Callers use it to distinguish a gone session from a transient failure.

Types

type AutoMappedInput

type AutoMappedInput struct {
	SessionInputKey string `json:"session_input_key"`
	SavedInputID    string `json:"saved_input_id"`
}

AutoMappedInput records keys auto-filled from saved inputs during create.

type ClaudeMetadataMonitor added in v0.3.0

type ClaudeMetadataMonitor struct {
	Service         *Service
	WorkspaceID     string
	SessionID       string
	ClaudeSessionID string
	Interval        time.Duration

	// Record is the current local record; the monitor mutates and re-saves it
	// as the title/description evolve.
	Record localsession.Record

	// Describe returns the current session description (e.g.
	// "owner/repo @ branch" with the pull-request URL on its own line). Called
	// each tick because parts of it (the pull request) can appear after the
	// session starts. May be nil.
	Describe func(context.Context) string

	// Debug, if set, receives diagnostic messages about skipped/failed updates.
	Debug func(format string, args ...any)
}

ClaudeMetadataMonitor periodically reads the AI-generated title from the Claude Code transcript running inside an RDE session and, whenever the title or description changes, persists it to the local session store (so `rde claude --resume` has something descriptive to show) and pushes it to the API (so the session is recognizable in `rde session list` / the web UI).

Everything is best-effort: a failed SSH read or API call is logged via Debug (if set) and retried on the next tick. It never disrupts the foreground Claude session.

func (*ClaudeMetadataMonitor) Run added in v0.3.0

Run polls until ctx is cancelled. It checks once immediately (so the description is pushed promptly) and then on every Interval tick.

type CreateSavedInputRequest

type CreateSavedInputRequest struct {
	Key      string
	Value    string
	IsSecret bool
}

CreateSavedInputRequest is the CLI-side create payload.

type CreateSessionRequest

type CreateSessionRequest struct {
	Name                    string
	Description             string
	TemplateID              string
	StackID                 string
	MachineType             string
	SessionInputs           []SessionInputValue
	EnabledFeatureFlagNames []string
	Cluster                 string
	AIPrompt                string
	AutoTerminateMinutes    *int
	MapSavedToSessionInputs bool
	Labels                  map[string]string
}

CreateSessionRequest is the CLI-side request shape. AutoTerminateMinutes is a pointer so "not provided" stays distinguishable from "0 = disable". Labels is arbitrary key=value metadata attached to the session; the backend validates it (entry count, key and value charset/length, reserved "bitrise.io/" key prefix).

type CreateSessionResult

type CreateSessionResult struct {
	Session          Session           `json:"session"`
	AutoMappedInputs []AutoMappedInput `json:"auto_mapped_inputs,omitempty"`
}

CreateSessionResult is what the create endpoint returns: the new session plus any inputs that were auto-filled from saved inputs.

type EnvVar added in v0.4.8

type EnvVar struct {
	Name  string
	Value string
}

EnvVar is one NAME=VALUE pair destined for the remote command's environment.

func ResolveExecEnv added in v0.4.8

func ResolveExecEnv(fileEntries []string, filePath string, flagEntries []string, lookupEnv func(string) (string, bool)) (vars []EnvVar, skipped []string, err error)

ResolveExecEnv resolves the env-forwarding entries for an exec: fileEntries from the repo dotfile (filePath names it in errors) followed by flagEntries from --env. Each entry is either NAME (forward the local value via lookupEnv) or NAME=VALUE (a literal). The two sources differ on an unset NAME: a dotfile entry is skipped and its name returned in skipped (a shared file must not break a teammate who doesn't have the var), while a flag entry is a hard error (the caller asked for it explicitly). A flag entry overrides a same-named file entry in place, so the final order is first-mention order with last-mention values. Values never appear in errors — only names.

type ExecResult

type ExecResult struct {
	ExitCode int    `json:"exit_code"`
	Stdout   string `json:"stdout"`
	Stderr   string `json:"stderr"`
}

ExecResult is the captured result of a remote command execution. Field names match the JSON contract emitted by `rde session exec --output json`.

type FeatureFlag

type FeatureFlag struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
}

FeatureFlag is a feature flag defined by a template.

type FeatureFlagSpec

type FeatureFlagSpec struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
}

FeatureFlagSpec is the editable shape of a feature flag.

type HostAction added in v0.3.0

type HostAction struct {
	// Handle runs the action and returns a JSON-marshalable result. ctx is
	// bounded by bridgeActionTimeout; r exposes the request for actions that take
	// parameters (open-vnc takes none).
	Handle func(ctx context.Context, r *http.Request) (any, error)

	// SkillSection is the Markdown section appended to the skill header to tell
	// Claude when and how to use this action. Only sections for registered
	// actions are written, so the skill never advertises a capability the
	// session lacks. (Reserved for future metadata too, e.g. a confirmation
	// policy for side-effecting actions like a file download.)
	SkillSection string

	// Timeout caps how long this action's Handle may run. Zero means use
	// bridgeActionTimeout (the 30s default suited to quick actions like
	// open-vnc); file transfers set a much larger value since a single archive
	// can take minutes to move through cloud storage.
	Timeout time.Duration
}

HostAction is one entry in the bridge's allowlist. It is a struct, not a bare func, so an action can carry metadata alongside its handler without reshaping the allowlist or every existing action.

type HostBridge added in v0.3.0

type HostBridge struct {
	Service     *Service
	WorkspaceID string
	SessionID   string

	// Actions is the allowlist, keyed by the path segment a request targets
	// (e.g. "open-vnc"). It is built by the caller so this layer never depends on
	// the cmd packages.
	Actions map[string]HostAction

	// Debug, if set, receives diagnostics about degraded or failed bridge
	// activity. The bridge never disrupts the foreground session, so problems
	// surface only here.
	Debug func(format string, args ...any)
	// contains filtered or unexported fields
}

HostBridge exposes a fixed allowlist of "host actions" to the Claude Code instance running inside an RDE session. It opens a loopback listener on the session over an SSH reverse forward and serves HTTP on it; the in-session Claude reaches it by reading a control file (URL + token) and calling the endpoint. The reverse forward is only a control channel — actions execute locally (open-vnc launches the viewer on the user's machine and the VNC password never leaves the local side).

Everything is best-effort and isolated from the foreground Claude session: if the session sshd denies remote forwarding, Start returns an error and the caller simply runs without the bridge.

func (*HostBridge) Close added in v0.3.0

func (b *HostBridge) Close()

Close tears the bridge down. Safe to call repeatedly and even if Start failed.

func (*HostBridge) Serve added in v0.3.0

func (b *HostBridge) Serve(ctx context.Context)

Serve runs the bridge until ctx is cancelled. On a dropped connection it re-dials, re-listens, and rewrites the control file with the new port, so the bridge survives the same network blips the interactive attach reconnects through. It is best-effort and never returns an error: failures are logged via Debug and retried. Serve is a no-op if Start was not called or failed.

func (*HostBridge) Start added in v0.3.0

func (b *HostBridge) Start(ctx context.Context) error

Start dials the session, opens the reverse forward, writes the control file and the skill, and prepares the HTTP server. It runs under a short bounded context so a denied/slow forward fails fast. On any error the caller should degrade unconditionally (continue without the bridge) — the forward-denied error is not a typed sentinel. The skill is written only on success, so a failed forward never advertises a capability that cannot work.

The skill must be in place before Claude launches: Claude only watches ~/.claude/skills if that directory exists at startup, and on a fresh VM it does not — Start creates it.

type ListSessionNotificationsOptions

type ListSessionNotificationsOptions struct {
	CreatedBefore string // RFC3339
	CreatedAfter  string // RFC3339
	Limit         int
	Order         string // "asc" | "desc" | ""
}

ListSessionNotificationsOptions paginates and filters the notifications list.

type MachineType

type MachineType struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	ClusterName string `json:"cluster_name,omitempty"`
	// IsDefault is set by the backend on the deployment's default machine type.
	IsDefault bool   `json:"is_default,omitempty"`
	Title     string `json:"title,omitempty"`
	CPU       string `json:"cpu,omitempty"`
	RAM       string `json:"ram,omitempty"`
	OS        string `json:"os,omitempty"`
}

MachineType is a machine size available in the workspace. Name is the contract (what templates/sessions store); Title/CPU/RAM are human-friendly display metadata and may be empty when the backend has none.

type RepoConfig added in v0.4.8

type RepoConfig struct {
	Exec RepoExecConfig `yaml:"exec"`
}

RepoConfig is the parsed .bitrise/rde.yml — the repo-level RDE dotfile. This is the initial schema; new sections are additive, and unknown keys are ignored so an older CLI keeps working against a newer file.

func LoadRepoConfig added in v0.4.8

func LoadRepoConfig() (RepoConfig, string, error)

LoadRepoConfig searches the current working directory and its ancestors for the repo-level RDE dotfile (.bitrise/rde.yml). Returns the parsed config, the path of the file that was used (empty if none found), and any read/parse error. A missing file at all levels is not an error. (Mirrors config.LoadDir's discovery for .bitrise-cli.yml: first hit wins, the walk goes to the filesystem root.)

type RepoExecConfig added in v0.4.8

type RepoExecConfig struct {
	// Env lists environment variables forwarded to every exec: NAME
	// (forward the local value; skipped with a warning when unset locally)
	// or NAME=VALUE (a literal).
	Env []string `yaml:"env"`
}

RepoExecConfig configures `rde session exec` for a repo.

type SavedInput

type SavedInput struct {
	ID        string     `json:"id"`
	Key       string     `json:"key"`
	Value     string     `json:"value,omitempty"`
	IsSecret  bool       `json:"is_secret,omitempty"`
	CreatedAt *time.Time `json:"created_at,omitempty"`
	UpdatedAt *time.Time `json:"updated_at,omitempty"`
}

SavedInput is the CLI-facing saved-input record. `Value` is masked by savedInputFromAPI when IsSecret=true — the backend omits secret values on reads unless include_secrets=true is requested, which the CLI never does, and the CLI blanks any value the backend does return before any renderer sees it.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service exposes RDE operations to the cmd layer.

func NewService

func NewService(client *rdeapi.Client) *Service

NewService returns a Service backed by the given RDE client. The client must be non-nil — every method makes a network call.

func (*Service) CreateSavedInput

func (s *Service) CreateSavedInput(ctx context.Context, req CreateSavedInputRequest) (SavedInput, error)

CreateSavedInput creates a saved input.

func (*Service) CreateSession

func (s *Service) CreateSession(ctx context.Context, workspaceID string, req CreateSessionRequest) (CreateSessionResult, error)

CreateSession creates a session. Provide either a TemplateID or, for a templateless session, a StackID + MachineType.

func (*Service) CreateTemplate

func (s *Service) CreateTemplate(ctx context.Context, workspaceID string, spec TemplateSpec) (Template, error)

CreateTemplate creates a new template from spec.

func (*Service) DeleteSavedInput

func (s *Service) DeleteSavedInput(ctx context.Context, id string) error

DeleteSavedInput removes a saved input.

func (*Service) DeleteSession

func (s *Service) DeleteSession(ctx context.Context, workspaceID, sessionID string) error

DeleteSession permanently removes a session.

func (*Service) DeleteTemplate

func (s *Service) DeleteTemplate(ctx context.Context, workspaceID, templateID string) error

DeleteTemplate removes a template.

func (*Service) DeleteTerminatedSessions

func (s *Service) DeleteTerminatedSessions(ctx context.Context, workspaceID string) (int, error)

DeleteTerminatedSessions removes every terminated session and returns the count of sessions actually deleted.

func (*Service) DiffSessionTemplate

func (s *Service) DiffSessionTemplate(ctx context.Context, workspaceID, sessionID string) (SessionTemplateDiff, error)

DiffSessionTemplate returns the snapshot-vs-current template diff for a session. Current is nil when the template was deleted.

func (*Service) DownloadFile

func (s *Service) DownloadFile(ctx context.Context, workspaceID, sessionID, sourcePath, localDest string, onlyContents bool) error

DownloadFile downloads remote sourcePath from the session into localDest. When onlyContents is true and the remote path is a directory, only the directory's contents are extracted (not the directory itself).

func (*Service) Execute

func (s *Service) Execute(ctx context.Context, workspaceID, sessionID, command string, env []EnvVar, timeout time.Duration) (ExecResult, error)

Execute runs command on the session via SSH and returns its captured stdout/stderr/exit_code. Mirrors the MCP's `bitrise_devenv_execute` behavior: forced-interactive login bash (`bash -i -l -c`), local SSH agent forwarded so git-over-SSH uses the caller's keys.

env vars are exported inside the login shell before the command runs — after profile sourcing, so they override profile-set values. The exports ride in the remote command line, so values are visible in `ps` on the session while the command runs.

timeout caps the whole dial+run. A non-positive timeout disables the cap, leaving the run bounded only by ctx and the SSH keepalive that tears the connection down within ~10s if it drops (so a genuinely dead connection still fails fast even when uncapped).

Errors fall into three categories:

  • "session not running" / "ssh not ready" — surfaced before the dial
  • dial/handshake/network failures — surfaced as errors
  • command exited non-zero — returned in ExecResult with a nil error; callers decide how to surface that to the user

func (*Service) ExecuteInteractive added in v0.3.0

func (s *Service) ExecuteInteractive(ctx context.Context, workspaceID, sessionID, command string, stdin io.Reader, stdout, stderr io.Writer) (int, error)

ExecuteInteractive attaches the caller's terminal to command running on the session over SSH and blocks until it exits, returning its exit code. Unlike Execute, it allocates a PTY (when stdin is a terminal), runs in raw mode, and is NOT capped by Execute's timeout — interactive programs are long-lived.

It is the interactive sibling of Execute: same pre-flight checks, same SSH dial and agent-forwarding posture, but stdin/stdout/stderr are streamed live instead of captured.

func (*Service) ForwardVNC added in v0.4.7

func (s *Service) ForwardVNC(ctx context.Context, workspaceID, sessionID string, localPort int, onReady func(localAddr string, creds VNCCredentials)) error

ForwardVNC opens an SSH tunnel to the session and forwards the VM's VNC server to a local TCP port, blocking until ctx is cancelled. localPort 0 auto-picks a free port; the chosen "127.0.0.1:port" is reported via onReady once the listener is accepting, so a caller can print connection details.

The tunnel targets the session VM's loopback Screen Sharing port — the standard `ssh -L LOCAL:localhost:5900` recipe. The SSH connection terminates on the VM, so dialing 127.0.0.1:5900 there reaches the raw RFB server directly, bypassing the external relay. That yields a plain-RFB localhost endpoint any VNC client (or a websockify/noVNC bridge) can consume, with no credentials embedded in a handed-off URL and no direct route to the relay required.

func (*Service) GetSavedInput

func (s *Service) GetSavedInput(ctx context.Context, id string) (SavedInput, error)

GetSavedInput returns a saved input by ID.

func (*Service) GetSession

func (s *Service) GetSession(ctx context.Context, workspaceID, sessionID string) (Session, error)

GetSession returns a session by ID.

func (*Service) GetSessionVNC

func (s *Service) GetSessionVNC(ctx context.Context, workspaceID, sessionID string) (VNCCredentials, error)

GetSessionVNC fetches the session and returns its VNC credentials, erroring clearly when the session has no VNC endpoint yet (still provisioning, terminated, or a Linux template that doesn't expose VNC).

func (*Service) GetTemplate

func (s *Service) GetTemplate(ctx context.Context, workspaceID, templateID string) (Template, error)

GetTemplate returns a single template by ID.

func (*Service) ListMachineTypes

func (s *Service) ListMachineTypes(ctx context.Context, workspaceID string) ([]MachineType, error)

ListMachineTypes returns every machine type available in the workspace.

func (*Service) ListSavedInputs

func (s *Service) ListSavedInputs(ctx context.Context) ([]SavedInput, error)

ListSavedInputs returns every saved input for the caller. Saved inputs are user-scoped, not workspace-scoped — no workspace ID needed.

func (*Service) ListSessionNotifications

func (s *Service) ListSessionNotifications(ctx context.Context, workspaceID, sessionID string, opts ListSessionNotificationsOptions) ([]SessionNotification, error)

ListSessionNotifications returns notifications for a session.

func (*Service) ListSessions

func (s *Service) ListSessions(ctx context.Context, workspaceID string, labelSelectors []string) ([]Session, error)

ListSessions returns the caller's sessions in the workspace, optionally filtered by label selectors ("key=value" exact matches, ANDed). Pass nil for the full list.

func (*Service) ListStacks added in v0.4.0

func (s *Service) ListStacks(ctx context.Context, workspaceID string) ([]Stack, error)

ListStacks returns every machine stack available in the workspace.

func (*Service) ListTemplates

func (s *Service) ListTemplates(ctx context.Context, workspaceID string) ([]Template, error)

ListTemplates returns every template visible to the caller in the workspace.

func (*Service) MachineTypesForStack added in v0.4.0

func (s *Service) MachineTypesForStack(ctx context.Context, workspaceID, stackID string) ([]MachineType, error)

MachineTypesForStack returns the machine types whose cluster overlaps with the clusters offering the stack with the given ID. A stack is provisionable in one or more clusters; a machine type is compatible when it's offered by at least one of those same clusters. Mirrors the FE's client-side join.

It errors if stackID isn't available in the workspace.

func (*Service) ResolveSessionID

func (s *Service) ResolveSessionID(ctx context.Context, workspaceID, value string) (string, error)

ResolveSessionID maps `value` to a session ID. UUID-shaped inputs short-circuit (no network call); names trigger a ListSessions call and an exact case-insensitive match. Errors clearly when zero or multiple sessions match the name, so callers can surface ambiguity to the user.

Session names aren't unique (unlike a UUID), so an ambiguous match is an expected outcome — the error lists the candidate IDs so the user can re-run with the exact one. Mirrors ResolveTemplateID.

func (*Service) ResolveTemplateID

func (s *Service) ResolveTemplateID(ctx context.Context, workspaceID, value string) (string, error)

ResolveTemplateID maps `value` to a template ID. UUID-shaped inputs short-circuit (no network call); names trigger a ListTemplates call and an exact case-insensitive match. Errors clearly when zero or multiple templates match the name, so callers can surface ambiguity to the user.

func (*Service) RestoreSession

func (s *Service) RestoreSession(ctx context.Context, workspaceID, sessionID string) (Session, error)

RestoreSession restores a terminated session by re-provisioning its VM from the persistent disk. The session re-enters the STARTING state and (assuming no failures) reaches RUNNING again.

func (*Service) SessionExposesVNC added in v0.3.0

func (s *Service) SessionExposesVNC(ctx context.Context, workspaceID, sessionID string) (bool, error)

SessionExposesVNC reports whether the session currently has a VNC endpoint. VNC is exposed by macOS sessions once they are running; Linux sessions have none. Callers use it to decide whether to offer VNC-related features for a session at all, rather than letting GetSessionVNC fail later.

func (*Service) StreamSessionLogs added in v0.2.0

func (s *Service) StreamSessionLogs(ctx context.Context, workspaceID, sessionID, stage string, idleTimeout time.Duration, fn func(string) error) error

StreamSessionLogs streams one stage's log for a session, invoking fn for each content chunk's text in order. stage is a friendly name (warmup/startup).

idleTimeout controls when to stop: 0 follows live until ctx is cancelled (Ctrl-C); a positive value returns once no new content has arrived for that long, which delivers the replayed log-so-far and then exits.

A pre-stream "logs not ready" condition surfaces as a 404 *rdeapi.APIError, which the cmd layer distinguishes to decide between a friendly exit and a follow-mode retry.

func (*Service) TerminateSession

func (s *Service) TerminateSession(ctx context.Context, workspaceID, sessionID string) (Session, error)

TerminateSession terminates a running session (preserves the session for later restart; the VM goes away).

func (*Service) UpdateSavedInput

func (s *Service) UpdateSavedInput(ctx context.Context, id string, req UpdateSavedInputRequest) (SavedInput, error)

UpdateSavedInput patches a saved input.

func (*Service) UpdateSession

func (s *Service) UpdateSession(ctx context.Context, workspaceID, sessionID string, req UpdateSessionRequest) (Session, error)

UpdateSession patches name, description, auto-terminate minutes, or labels (upserts via Labels, removals via RemoveLabels).

func (*Service) UpdateTemplate

func (s *Service) UpdateTemplate(ctx context.Context, workspaceID, templateID string, spec TemplateSpec) (Template, error)

UpdateTemplate patches an existing template. Pointer scalars are sent only when non-nil; arrays trigger their corresponding updateXxx flag when non-nil (even when empty — which clears the existing list).

func (*Service) UploadFile

func (s *Service) UploadFile(ctx context.Context, workspaceID, sessionID, sourcePath, destFolder string) error

UploadFile uploads a local file or directory to a session: tars the source, gzips it, PUTs it to the signed URL the backend returns, then calls complete-file-upload to trigger extraction at destFolder.

func (*Service) WaitForReady

func (s *Service) WaitForReady(ctx context.Context, workspaceID, sessionID string, interval time.Duration, onPoll func(status string)) (Session, error)

WaitForReady polls GetSession until the session leaves the provisioning states ("" / "pending" / "starting" / "unknown") and returns the resulting Session. "unknown" means the backend can't currently determine the machine state; it's expected to settle, so it's treated as still provisioning. The caller decides whether the returned status counts as success. Returns context.Canceled when ctx is cancelled.

onPoll, when non-nil, is called with the session's status on every poll, so a caller can surface the live provisioning state (e.g. in a progress spinner) without polling separately. It must not block.

func (*Service) WaitForSSHReady added in v0.3.0

func (s *Service) WaitForSSHReady(ctx context.Context, workspaceID, sessionID string, interval time.Duration) (Session, error)

WaitForSSHReady polls GetSession until the session's SSH endpoint is usable — connection open and address + password populated — and returns the resulting Session. A "running" status (what WaitForReady waits for) does not guarantee SSH is up: the backend issues credentials a few seconds later, so callers that want to dial in must wait on this too.

If the session leaves the "running" state while waiting (e.g. it fails or is terminated), it returns an error rather than spinning forever. Returns context.Canceled when ctx is cancelled.

func (*Service) WaitForTerminated

func (s *Service) WaitForTerminated(ctx context.Context, workspaceID, sessionID string, interval time.Duration) (Session, error)

WaitForTerminated polls GetSession until the session leaves the transitional teardown states ("terminating" / "draining") and returns the resulting Session — normally "terminated" (or "failed"). The caller decides whether the final status is acceptable. Returns context.Canceled when ctx is cancelled.

This is the teardown companion to WaitForReady: a bare TerminateSession returns while the session is still "terminating", so a 'terminate && delete' pipeline races the backend — delete rejects any session that isn't yet "terminated" or "failed". Waiting here closes that gap.

type Session

type Session struct {
	ID                          string                   `json:"id"`
	Name                        string                   `json:"name"`
	Description                 string                   `json:"description,omitempty"`
	Status                      string                   `json:"status,omitempty"`
	TemplateID                  string                   `json:"template_id,omitempty"`
	TemplateName                string                   `json:"template_name,omitempty"`
	TemplateDeleted             bool                     `json:"template_deleted,omitempty"`
	TemplateOutdated            bool                     `json:"template_outdated,omitempty"`
	TemplateSnapshot            *SessionTemplateSnapshot `json:"template_snapshot,omitempty"`
	AgentSessionStatus          string                   `json:"agent_session_status,omitempty"`
	AgentSessionStatusUpdatedAt *time.Time               `json:"agent_session_status_updated_at,omitempty"`
	AIEnabled                   bool                     `json:"ai_enabled,omitempty"`
	AIConfigured                bool                     `json:"ai_configured,omitempty"`
	AIPrompt                    string                   `json:"ai_prompt,omitempty"`
	AutoTerminateMinutes        int                      `json:"auto_terminate_minutes,omitempty"`
	AutoTerminateAt             *time.Time               `json:"auto_terminate_at,omitempty"`
	SSHAddress                  string                   `json:"ssh_address,omitempty"`
	// SSHPassword is the ephemeral SSH password issued for this session.
	// Excluded from --output json with json:"-" — secrets shouldn't leak
	// into the stable contract. The field is consumed internally by
	// `rde session exec` for the SSH dial.
	SSHPassword       string `json:"-"`
	SSHConnectionOpen bool   `json:"ssh_connection_open,omitempty"`
	VNCAddress        string `json:"vnc_address,omitempty"`
	VNCUsername       string `json:"vnc_username,omitempty"`
	// VNCPassword is the ephemeral VNC password issued for this session.
	// Same handling as SSHPassword: excluded from --output json so the
	// stable contract doesn't leak secrets. Surfaced only through the
	// opt-in `rde session vnc` and `rde session open-vnc` commands.
	VNCPassword          string            `json:"-"`
	PersistentDiskStatus string            `json:"persistent_disk_status,omitempty"`
	Labels               map[string]string `json:"labels,omitempty"`
	CreatedAt            *time.Time        `json:"created_at,omitempty"`
	UpdatedAt            *time.Time        `json:"updated_at,omitempty"`
}

Session is the CLI-facing session record. JSON tags define the stable `--output json` shape. Field set kept minimal per the RDE plan — expand additively as users ask for more.

func (Session) Resumable added in v0.3.0

func (s Session) Resumable() bool

Resumable reports whether `rde claude` can resume this session: a running session can be reattached, and a terminated/stopped/failed one can be restored as long as its persistent disk is still available. Any other (transitional) state is reported resumable optimistically — it's an in-flight status that should settle into one of the above. Whether the session can actually be resumed at that moment is decided by the resume flow, which reattaches a running session, restores a terminated/stopped/failed one, and asks the user to retry shortly for anything still in flight.

type SessionInputDef

type SessionInputDef struct {
	Key            string `json:"key"`
	Description    string `json:"description,omitempty"`
	Required       bool   `json:"required,omitempty"`
	DefaultValue   string `json:"default_value,omitempty"`
	ExposeAsEnvVar bool   `json:"expose_as_env_var,omitempty"`
}

SessionInputDef is an input definition on a template.

type SessionInputSpec

type SessionInputSpec struct {
	Key            string `json:"key"`
	Description    string `json:"description,omitempty"`
	Required       bool   `json:"required,omitempty"`
	DefaultValue   string `json:"default_value,omitempty"`
	ExposeAsEnvVar bool   `json:"expose_as_env_var,omitempty"`
}

SessionInputSpec is the editable shape of a session input definition.

type SessionInputValue

type SessionInputValue struct {
	Key          string
	Value        string
	IsSecret     bool
	SavedInputID string
}

SessionInputValue mirrors the wire type for create-session.

type SessionNotification

type SessionNotification struct {
	ID        string     `json:"id"`
	SessionID string     `json:"session_id,omitempty"`
	Title     string     `json:"title,omitempty"`
	Body      string     `json:"body,omitempty"`
	Type      string     `json:"type,omitempty"`
	CreatedAt *time.Time `json:"created_at,omitempty"`
}

SessionNotification is the CLI shape of a session notification.

type SessionTemplateDiff

type SessionTemplateDiff struct {
	Snapshot            *TemplateConfig `json:"snapshot,omitempty"`
	Current             *TemplateConfig `json:"current,omitempty"`
	ChangedVariableKeys []string        `json:"changed_variable_keys,omitempty"`
}

SessionTemplateDiff is the CLI shape of /template-diff.

type SessionTemplateSnapshot

type SessionTemplateSnapshot struct {
	TemplateName     string          `json:"template_name,omitempty"`
	StackID          string          `json:"stack_id,omitempty"`
	MachineType      string          `json:"machine_type,omitempty"`
	WorkingDirectory string          `json:"working_directory,omitempty"`
	HasStartupScript bool            `json:"has_startup_script,omitempty"`
	HasWarmupScript  bool            `json:"has_warmup_script,omitempty"`
	SessionInputs    []SnapshotInput `json:"session_inputs,omitempty"`
	FeatureFlags     []SnapshotFlag  `json:"feature_flags,omitempty"`
	WorkspaceLinks   []SnapshotLink  `json:"workspace_links,omitempty"`
	UpdatedAt        *time.Time      `json:"updated_at,omitempty"`
}

SessionTemplateSnapshot is the CLI shape of the template config captured at session creation. Mirrors the wire type but with snake_case tags and without the masked secret bag.

type SnapshotFlag

type SnapshotFlag struct {
	Name    string `json:"name"`
	Enabled bool   `json:"enabled,omitempty"`
}

SnapshotFlag is a captured feature-flag state.

type SnapshotInput

type SnapshotInput struct {
	Key            string `json:"key"`
	Value          string `json:"value,omitempty"`
	IsSecret       bool   `json:"is_secret,omitempty"`
	ExposeAsEnvVar bool   `json:"expose_as_env_var,omitempty"`
}

SnapshotInput is a captured session-input value.

type SnapshotLink struct {
	Label      string `json:"label,omitempty"`
	FolderPath string `json:"folder_path,omitempty"`
	SortOrder  int    `json:"sort_order,omitempty"`
}

SnapshotLink is a captured workspace link.

type Stack added in v0.4.0

type Stack struct {
	ID           string `json:"id"`
	Title        string `json:"title,omitempty"`
	Description  string `json:"description,omitempty"`
	OS           string `json:"os,omitempty"`
	OSVersion    int32  `json:"os_version,omitempty"`
	Status       string `json:"status,omitempty"`
	XcodeVersion string `json:"xcode_version,omitempty"`
	// IsDefault is set by the backend on the deployment's default stack.
	IsDefault bool `json:"is_default,omitempty"`
	// ClusterNames are the clusters where this stack can be provisioned.
	ClusterNames []string `json:"cluster_names,omitempty"`
	// DescriptionLink points at the stack's pre-installed tools / system report.
	DescriptionLink string `json:"description_link,omitempty"`
}

Stack is a machine stack available in the workspace. The ID is the stable contract stored on a template/session; the rest is human-friendly metadata.

type Template

type Template struct {
	ID                string             `json:"id"`
	Name              string             `json:"name"`
	Description       string             `json:"description,omitempty"`
	StackID           string             `json:"stack_id,omitempty"`
	MachineType       string             `json:"machine_type,omitempty"`
	WorkingDirectory  string             `json:"working_directory,omitempty"`
	StartupScript     string             `json:"startup_script,omitempty"`
	WarmupScript      string             `json:"warmup_script,omitempty"`
	CreatedByEmail    string             `json:"created_by_email,omitempty"`
	WorkspaceID       string             `json:"workspace_id,omitempty"`
	TemplateVariables []TemplateVariable `json:"template_variables,omitempty"`
	SessionInputs     []SessionInputDef  `json:"session_inputs,omitempty"`
	FeatureFlags      []FeatureFlag      `json:"feature_flags,omitempty"`
	WorkspaceLinks    []WorkspaceLink    `json:"workspace_links,omitempty"`
	CreatedAt         *time.Time         `json:"created_at,omitempty"`
	UpdatedAt         *time.Time         `json:"updated_at,omitempty"`
}

Template is the CLI-facing template record.

type TemplateConfig

type TemplateConfig struct {
	TemplateName      string                   `json:"template_name,omitempty"`
	StackID           string                   `json:"stack_id,omitempty"`
	MachineType       string                   `json:"machine_type,omitempty"`
	WorkingDirectory  string                   `json:"working_directory,omitempty"`
	StartupScript     string                   `json:"startup_script,omitempty"`
	WarmupScript      string                   `json:"warmup_script,omitempty"`
	SessionInputs     []TemplateConfigInput    `json:"session_inputs,omitempty"`
	FeatureFlags      []TemplateConfigFlag     `json:"feature_flags,omitempty"`
	TemplateVariables []TemplateConfigVariable `json:"template_variables,omitempty"`
	WorkspaceLinks    []SnapshotLink           `json:"workspace_links,omitempty"`
	UpdatedAt         *time.Time               `json:"updated_at,omitempty"`
}

TemplateConfig is the CLI shape of the template config on either side of a session's template diff.

type TemplateConfigFlag

type TemplateConfigFlag struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Enabled     bool   `json:"enabled,omitempty"`
}

TemplateConfigFlag carries default-enabled state alongside name/description.

type TemplateConfigInput

type TemplateConfigInput struct {
	Key            string `json:"key"`
	Description    string `json:"description,omitempty"`
	Required       bool   `json:"required,omitempty"`
	DefaultValue   string `json:"default_value,omitempty"`
	ExposeAsEnvVar bool   `json:"expose_as_env_var,omitempty"`
	IsSecret       bool   `json:"is_secret,omitempty"`
}

TemplateConfigInput mirrors the diff-endpoint session-input definition.

type TemplateConfigVariable

type TemplateConfigVariable struct {
	Key            string `json:"key"`
	IsSecret       bool   `json:"is_secret,omitempty"`
	ExposeAsEnvVar bool   `json:"expose_as_env_var,omitempty"`
}

TemplateConfigVariable is variable metadata (values stripped server-side).

type TemplateSpec

type TemplateSpec struct {
	Name             *string `json:"name,omitempty"`
	Description      *string `json:"description,omitempty"`
	StackID          *string `json:"stack_id,omitempty"`
	MachineType      *string `json:"machine_type,omitempty"`
	WorkingDirectory *string `json:"working_directory,omitempty"`
	StartupScript    *string `json:"startup_script,omitempty"`
	WarmupScript     *string `json:"warmup_script,omitempty"`

	// Nil = don't touch on update; non-nil = replace the server's list with
	// these values (even if empty). On create, nil/empty is equivalent.
	TemplateVariables *[]TemplateVariableSpec `json:"template_variables,omitempty"`
	SessionInputs     *[]SessionInputSpec     `json:"session_inputs,omitempty"`
	FeatureFlags      *[]FeatureFlagSpec      `json:"feature_flags,omitempty"`
	WorkspaceLinks    *[]WorkspaceLinkSpec    `json:"workspace_links,omitempty"`
}

TemplateSpec is the editable shape of a template — used for create and update payloads. Pointer fields preserve "unset, leave alone" semantics on update; slices replace the existing list when non-nil. The JSON tags match the snake_case shape `template view --output json` emits, so the canonical workflow is view → edit → update.

type TemplateVariable

type TemplateVariable struct {
	Key            string `json:"key"`
	Value          string `json:"value,omitempty"`
	IsSecret       bool   `json:"is_secret,omitempty"`
	ExposeAsEnvVar bool   `json:"expose_as_env_var,omitempty"`
}

TemplateVariable is a baked-in template variable.

type TemplateVariableSpec

type TemplateVariableSpec struct {
	Key            string `json:"key"`
	Value          string `json:"value,omitempty"`
	IsSecret       bool   `json:"is_secret,omitempty"`
	ExposeAsEnvVar bool   `json:"expose_as_env_var,omitempty"`
}

TemplateVariableSpec is the editable shape of a template variable.

type UpdateSavedInputRequest

type UpdateSavedInputRequest struct {
	Value    *string
	IsSecret *bool
}

UpdateSavedInputRequest is the CLI-side patch payload. Pointer fields preserve "unset, leave alone" semantics.

type UpdateSessionRequest

type UpdateSessionRequest struct {
	Name                 *string
	Description          *string
	AutoTerminateMinutes *int
	Labels               map[string]string
	RemoveLabels         []string
}

UpdateSessionRequest carries optional patch fields. Pointer fields preserve unset semantics. Labels upserts into the session's existing labels; RemoveLabels deletes keys (a key in both is removed — the backend gives removal precedence).

type VNCCredentials

type VNCCredentials struct {
	Address  string `json:"address"`
	Host     string `json:"host"`
	Port     int    `json:"port"`
	Username string `json:"username,omitempty"`
	Password string `json:"password,omitempty"`
	URL      string `json:"url"`
}

VNCCredentials is the credential bundle a session exposes for VNC. The JSON tags define the stable shape used by `rde session vnc --output json`. The fields mirror what the backend returns (address, username, password) plus a pre-built `vnc://` URL ready to hand to an OS handler.

Host and Port are the address decomposed into discrete fields, so callers that need to build their own connection (a bridge, a native client) never have to parse `address` or the URL — the endpoint is always fully qualified.

func VNCCredentialsFromSession

func VNCCredentialsFromSession(sess Session) (VNCCredentials, error)

VNCCredentialsFromSession assembles a credentials bundle from an already loaded Session. Split from GetSessionVNC so callers that already hold a Session (e.g. `session create --wait`) can reuse it without a second GET.

type WorkspaceLink struct {
	Label      string `json:"label,omitempty"`
	FolderPath string `json:"folder_path,omitempty"`
	SortOrder  int    `json:"sort_order,omitempty"`
}

WorkspaceLink is an IDE folder shortcut bundled with a template.

type WorkspaceLinkSpec

type WorkspaceLinkSpec struct {
	Label           string `json:"label,omitempty"`
	FolderPath      string `json:"folder_path,omitempty"`
	FeatureFlagName string `json:"feature_flag_name,omitempty"`
}

WorkspaceLinkSpec is the editable shape of a workspace link.

Directories

Path Synopsis
Package localsession persists `rde claude` session records locally so they can be resumed later (`rde claude --resume` / `--continue`).
Package localsession persists `rde claude` session records locally so they can be resumed later (`rde claude --resume` / `--continue`).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL