opencode

package
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Mar 26, 2026 License: MIT Imports: 45 Imported by: 0

README

OpenCode Bridge

The OpenCode bridge connects Beeper to OpenCode.

It supports two modes:

  • remote: connect to an existing OpenCode server over HTTP
  • managed: let the bridge launch opencode locally and keep a default working directory

What it does

  • maps OpenCode sessions into Beeper rooms
  • streams replies and session updates into chat
  • keeps reconnect logic inside the bridge instead of requiring a separate UI

Login flow

Remote mode asks for:

  • server URL
  • optional basic-auth username
  • optional basic-auth password

Managed mode asks for:

  • path to the opencode binary
  • default working directory

Run

./tools/bridges run opencode

Or:

./run.sh opencode

Documentation

Index

Constants

View Source
const (
	FlowOpenCodeRemote  = "opencode_remote"
	FlowOpenCodeManaged = "opencode_managed"
)
View Source
const (
	OpenCodeModeRemote          = "remote"
	OpenCodeModeManagedLauncher = "managed_launcher"
	OpenCodeModeManaged         = "managed"
)
View Source
const ProviderOpenCode = "opencode"

Variables

View Source
var (
	ErrUnavailable = bridgeError("OpenCode integration is not available")
)

Functions

func BuildDataPartMap

func BuildDataPartMap(part api.Part) map[string]any

BuildDataPartMap builds a map representation of an opencode data part for streaming or backfill. Returns nil for unknown part types.

func OpenCodeInstanceID

func OpenCodeInstanceID(baseURL, username string) string

func OpenCodeManagedInstanceID

func OpenCodeManagedInstanceID(loginID, directory string) string

func OpenCodeManagedLauncherID

func OpenCodeManagedLauncherID(loginID string) string

func OpenCodePortalKey

func OpenCodePortalKey(loginID networkid.UserLoginID, instanceID, sessionID string) networkid.PortalKey

func OpenCodeUserID

func OpenCodeUserID(instanceID string) networkid.UserID

func ParseOpenCodeGhostID

func ParseOpenCodeGhostID(ghostID string) (string, bool)

func ParseOpenCodeIdentifier

func ParseOpenCodeIdentifier(identifier string) (string, bool)

Types

type Bridge

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

Bridge coordinates OpenCode sessions with Matrix rooms.

func NewBridge

func NewBridge(host Host) *Bridge

func (*Bridge) AbortSession

func (b *Bridge) AbortSession(ctx context.Context, instanceID, sessionID string) error

func (*Bridge) ApprovalHandler

func (b *Bridge) ApprovalHandler() agentremote.ApprovalReactionHandler

ApprovalHandler returns the manager's ApprovalFlow as an ApprovalReactionHandler, or nil if unavailable.

func (*Bridge) CreateSessionChat

func (b *Bridge) CreateSessionChat(ctx context.Context, instanceID, title string, pendingTitle bool) (*bridgev2.CreateChatResponse, error)

func (*Bridge) DisconnectAll

func (b *Bridge) DisconnectAll()

func (*Bridge) DisplayName

func (b *Bridge) DisplayName(instanceID string) string

func (*Bridge) EnsureGhostDisplayName

func (b *Bridge) EnsureGhostDisplayName(ctx context.Context, instanceID string)

func (*Bridge) FetchMessages

func (*Bridge) HandleMatrixDeleteChat

func (b *Bridge) HandleMatrixDeleteChat(ctx context.Context, msg *bridgev2.MatrixDeleteChat) error

HandleMatrixDeleteChat deletes the remote OpenCode session when a chat is deleted.

func (*Bridge) HandleMatrixMessage

func (b *Bridge) HandleMatrixMessage(ctx context.Context, msg *bridgev2.MatrixMessage, portal *bridgev2.Portal, meta *PortalMeta) (*bridgev2.MatrixMessageResponse, error)

func (*Bridge) InstanceConfig

func (b *Bridge) InstanceConfig(instanceID string) *OpenCodeInstance

func (*Bridge) ReIDPortalToSession

func (b *Bridge) ReIDPortalToSession(ctx context.Context, portal *bridgev2.Portal, instanceID, sessionID string) (*bridgev2.Portal, error)

func (*Bridge) RestoreConnections

func (b *Bridge) RestoreConnections(ctx context.Context) error

type Config

type Config struct {
	Bridge   bridgeconfig.BridgeConfig `yaml:"bridge"`
	OpenCode OpenCode                  `yaml:"opencode"`
}

type GhostMetadata

type GhostMetadata struct{}

type Host

type Host interface {
	Log() *zerolog.Logger
	GetUserLogin() *bridgev2.UserLogin
	BackgroundContext(ctx context.Context) context.Context
	SendSystemNotice(ctx context.Context, portal *bridgev2.Portal, msg string)
	EmitOpenCodeStreamEvent(ctx context.Context, portal *bridgev2.Portal, turnID, agentID string, part map[string]any)
	FinishOpenCodeStream(turnID string)
	DownloadAndEncodeMedia(ctx context.Context, mediaURL string, file *event.EncryptedFileInfo, maxMB int) (string, string, error)
	SetRoomName(ctx context.Context, portal *bridgev2.Portal, name string) error
	SenderForOpenCode(instanceID string, fromMe bool) bridgev2.EventSender
	CleanupPortal(ctx context.Context, portal *bridgev2.Portal, reason string)
	PortalMeta(portal *bridgev2.Portal) *PortalMeta
	SetPortalMeta(portal *bridgev2.Portal, meta *PortalMeta)
	SavePortal(ctx context.Context, portal *bridgev2.Portal) error
	DefaultAgentID() string
	OpenCodeInstances() map[string]*OpenCodeInstance
	SaveOpenCodeInstances(ctx context.Context, instances map[string]*OpenCodeInstance) error
	HumanUserID(loginID networkid.UserLoginID) networkid.UserID
	// contains filtered or unexported methods
}

Host provides the minimal surface area the OpenCode bridge needs to integrate with the surrounding connector.

type MessageMetadata

type MessageMetadata struct {
	agentremote.BaseMessageMetadata
	SessionID       string  `json:"session_id,omitempty"`
	MessageID       string  `json:"message_id,omitempty"`
	ParentMessageID string  `json:"parent_message_id,omitempty"`
	Agent           string  `json:"agent,omitempty"`
	ModelID         string  `json:"model_id,omitempty"`
	ProviderID      string  `json:"provider_id,omitempty"`
	Mode            string  `json:"mode,omitempty"`
	ErrorText       string  `json:"error_text,omitempty"`
	Cost            float64 `json:"cost,omitempty"`
	TotalTokens     int64   `json:"total_tokens,omitempty"`
}

func (*MessageMetadata) CopyFrom

func (mm *MessageMetadata) CopyFrom(other any)

type MessageMetadataParams

type MessageMetadataParams struct {
	Role             string
	Body             string
	FinishReason     string
	PromptTokens     int64
	CompletionTokens int64
	ReasoningTokens  int64
	TurnID           string
	AgentID          string
	UIMessage        map[string]any
	StartedAtMs      int64
	CompletedAtMs    int64
	SessionID        string
	MessageID        string
	ParentMessageID  string
	Agent            string
	ModelID          string
	ProviderID       string
	Mode             string
	ErrorText        string
	Cost             float64
	TotalTokens      int64
}

MessageMetadataParams holds all fields needed to construct a MessageMetadata. Both streaming and backfill code paths populate this struct, then call buildMessageMetadataFromParams to produce the final value.

type OpenCode

type OpenCode struct {
	Enabled *bool `yaml:"enabled"`
}

type OpenCodeClient

type OpenCodeClient struct {
	agentremote.ClientBase
	UserLogin *bridgev2.UserLogin
	// contains filtered or unexported fields
}

func (*OpenCodeClient) CleanupPortal

func (oc *OpenCodeClient) CleanupPortal(ctx context.Context, portal *bridgev2.Portal, reason string)

func (*OpenCodeClient) Connect

func (oc *OpenCodeClient) Connect(ctx context.Context)

func (*OpenCodeClient) DefaultAgentID

func (oc *OpenCodeClient) DefaultAgentID() string

func (*OpenCodeClient) Disconnect

func (oc *OpenCodeClient) Disconnect()

func (*OpenCodeClient) DownloadAndEncodeMedia

func (oc *OpenCodeClient) DownloadAndEncodeMedia(ctx context.Context, mediaURL string, file *event.EncryptedFileInfo, maxMB int) (string, string, error)

func (*OpenCodeClient) EmitOpenCodeStreamEvent

func (oc *OpenCodeClient) EmitOpenCodeStreamEvent(ctx context.Context, portal *bridgev2.Portal, turnID, agentID string, part map[string]any)

func (*OpenCodeClient) FetchMessages

func (*OpenCodeClient) FinishOpenCodeStream

func (oc *OpenCodeClient) FinishOpenCodeStream(turnID string)

func (*OpenCodeClient) GetApprovalHandler

func (oc *OpenCodeClient) GetApprovalHandler() agentremote.ApprovalReactionHandler

func (*OpenCodeClient) GetCapabilities

func (oc *OpenCodeClient) GetCapabilities(_ context.Context, _ *bridgev2.Portal) *event.RoomFeatures

func (*OpenCodeClient) GetChatInfo

func (oc *OpenCodeClient) GetChatInfo(_ context.Context, portal *bridgev2.Portal) (*bridgev2.ChatInfo, error)

func (*OpenCodeClient) GetContactList

func (oc *OpenCodeClient) GetContactList(ctx context.Context) ([]*bridgev2.ResolveIdentifierResponse, error)

func (*OpenCodeClient) GetUserInfo

func (oc *OpenCodeClient) GetUserInfo(_ context.Context, ghost *bridgev2.Ghost) (*bridgev2.UserInfo, error)

func (*OpenCodeClient) GetUserLogin

func (oc *OpenCodeClient) GetUserLogin() *bridgev2.UserLogin

func (*OpenCodeClient) HandleMatrixDeleteChat

func (oc *OpenCodeClient) HandleMatrixDeleteChat(ctx context.Context, msg *bridgev2.MatrixDeleteChat) error

func (*OpenCodeClient) HandleMatrixMessage

func (*OpenCodeClient) HumanUserID

func (oc *OpenCodeClient) HumanUserID(loginID networkid.UserLoginID) networkid.UserID

func (*OpenCodeClient) Log

func (oc *OpenCodeClient) Log() *zerolog.Logger

func (*OpenCodeClient) LogoutRemote

func (oc *OpenCodeClient) LogoutRemote(_ context.Context)

func (*OpenCodeClient) OpenCodeInstances

func (oc *OpenCodeClient) OpenCodeInstances() map[string]*OpenCodeInstance

func (*OpenCodeClient) PortalMeta

func (oc *OpenCodeClient) PortalMeta(portal *bridgev2.Portal) *PortalMeta

func (*OpenCodeClient) ResolveIdentifier

func (oc *OpenCodeClient) ResolveIdentifier(ctx context.Context, identifier string, createChat bool) (*bridgev2.ResolveIdentifierResponse, error)

func (*OpenCodeClient) SaveOpenCodeInstances

func (oc *OpenCodeClient) SaveOpenCodeInstances(ctx context.Context, instances map[string]*OpenCodeInstance) error

func (*OpenCodeClient) SavePortal

func (oc *OpenCodeClient) SavePortal(ctx context.Context, portal *bridgev2.Portal) error

func (*OpenCodeClient) SearchUsers

func (oc *OpenCodeClient) SearchUsers(ctx context.Context, query string) ([]*bridgev2.ResolveIdentifierResponse, error)

func (*OpenCodeClient) SendSystemNotice

func (oc *OpenCodeClient) SendSystemNotice(ctx context.Context, portal *bridgev2.Portal, msg string)

func (*OpenCodeClient) SenderForOpenCode

func (oc *OpenCodeClient) SenderForOpenCode(instanceID string, fromMe bool) bridgev2.EventSender

func (*OpenCodeClient) SetPortalMeta

func (oc *OpenCodeClient) SetPortalMeta(portal *bridgev2.Portal, meta *PortalMeta)

func (*OpenCodeClient) SetRoomName

func (oc *OpenCodeClient) SetRoomName(_ context.Context, _ *bridgev2.Portal, _ string) error

func (*OpenCodeClient) SetUserLogin

func (oc *OpenCodeClient) SetUserLogin(login *bridgev2.UserLogin)

type OpenCodeConnector

type OpenCodeConnector struct {
	*agentremote.ConnectorBase

	Config Config
	// contains filtered or unexported fields
}

func NewConnector

func NewConnector() *OpenCodeConnector

type OpenCodeInstance

type OpenCodeInstance struct {
	ID               string `json:"id,omitempty"`
	Mode             string `json:"mode,omitempty"`
	URL              string `json:"url,omitempty"`
	Username         string `json:"username,omitempty"`
	Password         string `json:"password,omitempty"`
	HasPassword      bool   `json:"has_password,omitempty"`
	BinaryPath       string `json:"binary_path,omitempty"`
	DefaultDirectory string `json:"default_directory,omitempty"`
	WorkingDirectory string `json:"working_directory,omitempty"`
	LauncherID       string `json:"launcher_id,omitempty"`
}

OpenCodeInstance stores connection details for an OpenCode server.

type OpenCodeLogin

type OpenCodeLogin struct {
	agentremote.BaseLoginProcess
	User      *bridgev2.User
	Connector *OpenCodeConnector
	FlowID    string
}

func (*OpenCodeLogin) Start

func (*OpenCodeLogin) SubmitUserInput

func (ol *OpenCodeLogin) SubmitUserInput(ctx context.Context, input map[string]string) (*bridgev2.LoginStep, error)

type OpenCodeManager

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

OpenCodeManager coordinates connections to OpenCode server instances, dispatches SSE events, and manages session lifecycle.

func NewOpenCodeManager

func NewOpenCodeManager(bridge *Bridge) *OpenCodeManager

func (*OpenCodeManager) AbortSession

func (m *OpenCodeManager) AbortSession(ctx context.Context, instanceID, sessionID string) error

func (*OpenCodeManager) Connect

func (m *OpenCodeManager) Connect(ctx context.Context, baseURL, password, username string) (*openCodeInstance, int, error)

func (*OpenCodeManager) CreateSession

func (m *OpenCodeManager) CreateSession(ctx context.Context, instanceID, title, directory string) (*api.Session, error)

func (*OpenCodeManager) DeleteSession

func (m *OpenCodeManager) DeleteSession(ctx context.Context, instanceID, sessionID string) error

func (*OpenCodeManager) DisconnectAll

func (m *OpenCodeManager) DisconnectAll()

DisconnectAll stops all in-memory OpenCode connections/event loops without modifying persisted instance metadata.

func (*OpenCodeManager) EnsureManagedInstance

func (m *OpenCodeManager) EnsureManagedInstance(ctx context.Context, launcherID, workingDir string) (*openCodeInstance, error)

func (*OpenCodeManager) IsConnected

func (m *OpenCodeManager) IsConnected(instanceID string) bool

func (*OpenCodeManager) RestoreConnections

func (m *OpenCodeManager) RestoreConnections(ctx context.Context) error

func (*OpenCodeManager) SendMessage

func (m *OpenCodeManager) SendMessage(ctx context.Context, instanceID, sessionID string, parts []api.PartInput, eventID id.EventID) error

func (*OpenCodeManager) UpdateSessionTitle

func (m *OpenCodeManager) UpdateSessionTitle(ctx context.Context, instanceID, sessionID, title string) (*api.Session, error)

type PortalMeta

type PortalMeta struct {
	IsOpenCodeRoom bool
	InstanceID     string
	SessionID      string
	ReadOnly       bool
	TitlePending   bool
	Title          string
	TitleGenerated bool
	AgentID        string
	VerboseLevel   string
	AwaitingPath   bool
}

PortalMeta is the OpenCode-specific view of portal metadata.

type PortalMetadata

type PortalMetadata struct {
	Title                string                      `json:"title,omitempty"`
	TitleGenerated       bool                        `json:"title_generated,omitempty"`
	IsOpenCodeRoom       bool                        `json:"is_opencode_room,omitempty"`
	OpenCodeInstanceID   string                      `json:"opencode_instance_id,omitempty"`
	OpenCodeSessionID    string                      `json:"opencode_session_id,omitempty"`
	OpenCodeReadOnly     bool                        `json:"opencode_read_only,omitempty"`
	OpenCodeTitlePending bool                        `json:"opencode_title_pending,omitempty"`
	OpenCodeAwaitingPath bool                        `json:"opencode_awaiting_path,omitempty"`
	AgentID              string                      `json:"agent_id,omitempty"`
	VerboseLevel         string                      `json:"verbose_level,omitempty"`
	SDK                  bridgesdk.SDKPortalMetadata `json:"sdk,omitempty"`
}

func (*PortalMetadata) GetSDKPortalMetadata

func (pm *PortalMetadata) GetSDKPortalMetadata() *bridgesdk.SDKPortalMetadata

func (*PortalMetadata) SetSDKPortalMetadata

func (pm *PortalMetadata) SetSDKPortalMetadata(meta *bridgesdk.SDKPortalMetadata)

type UserLoginMetadata

type UserLoginMetadata struct {
	Provider          string                       `json:"provider,omitempty"`
	OpenCodeInstances map[string]*OpenCodeInstance `json:"opencode_instances,omitempty"`
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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