Skip to content

Latest commit

 

History

History
389 lines (294 loc) · 12.4 KB

File metadata and controls

389 lines (294 loc) · 12.4 KB

OpenCode SDK for Elixir

An unofficial Elixir SDK for OpenCode that mirrors the JS SDK (@opencode-ai/sdk). The client and types are generated from the OpenCode OpenAPI spec.

hex.pm link: https://hex.pm/packages/opencode_sdk/

Installation

Add opencode_sdk to your dependencies in mix.exs:

def deps do
  [
    {:opencode_sdk, "~> 0.1.18"}
  ]
end

Quickstart

Start an OpenCode server and get a connected client:

{:ok, %{client: client, server: server}} = OpenCode.create()

{:ok, health} = OpenCode.Generated.Operations.global_health(client)
IO.inspect(health, label: "health")
# => %{"healthy" => true, "version" => "1.1.53"}

OpenCode.close(%{server: server})

Connect to an existing server:

client = OpenCode.create_client(base_url: "http://127.0.0.1:4096")
{:ok, projects} = OpenCode.Generated.Operations.project_list(client)

Create API

OpenCode.create/1 options

OpenCode.create/1 forwards to OpenCode.create_server/1 and returns %{client, server}.

Option Type Description Default
:hostname String.t() Server hostname "127.0.0.1"
:port integer() Server port 4096
:timeout integer() Startup timeout in ms 5000
:config map() Config passed via OPENCODE_CONFIG_CONTENT %{}

OpenCode.create_client/1 options

Option Type Description Default
:base_url String.t() OpenCode server URL "http://127.0.0.1:4096"
:directory String.t() Project directory sent via x-opencode-directory nil
:headers map() | keyword() Extra HTTP headers []
:timeout integer() | :infinity Request timeout :infinity

Configuration

Pass a :config map to override settings. The server still reads your opencode.json, but inline config takes precedence:

{:ok, %{client: client, server: server}} =
  OpenCode.create(config: %{model: "opencode/big-pickle"})

API Reference

All operations live in OpenCode.Generated.Operations. The client keyword list is always the last argument.

Global

Function Description Response
global_health(client) Check server health and version %{"healthy" => true, "version" => "..."}
global_dispose(client) Shut down the server boolean
global_config_get(client) Get global config Config
global_config_update(body, client) Update global config Config
{:ok, health} = Operations.global_health(client)
IO.puts(health["version"])

Sessions

Function Description Response
session_create(body, client) Create a new session Session
session_list(client) List all sessions [Session]
Session.session_get(id, client) Get a session by ID Session
Session.session_children(id, client) List child sessions [Session]
session_delete(id, client) Delete a session boolean
session_update(id, body, client) Update session properties Session
session_abort(id, client) Abort a running session boolean
session_share(id, client) Share a session Session
session_unshare(id, client) Unshare a session Session
session_summarize(id, body, client) Summarize a session boolean
# Create a session
{:ok, session} = Operations.session_create(%{title: "My session"}, client)

# List recent sessions (with optional filters)
{:ok, sessions} = Operations.session_list(Keyword.merge(client, limit: 10, search: "my"))

# Get a specific session
{:ok, session} = OpenCode.Generated.Session.session_get("session-id", client)

# Delete a session
{:ok, true} = Operations.session_delete("session-id", client)

Messages and prompts

Function Description Response
session_prompt(id, body, client) Send a prompt, get AI response %{"info" => AssistantMessage, "parts" => [Part]}
session_prompt_async(id, body, client) Send a prompt asynchronously :ok
session_messages(id, client) List messages in a session [%{"info" => Message, "parts" => [Part]}]
session_message(id, msg_id, client) Get a specific message %{"info" => Message, "parts" => [Part]}
session_command(id, body, client) Send a command to a session %{"info" => AssistantMessage, "parts" => [Part]}
session_shell(id, body, client) Run a shell command AssistantMessage
session_diff(id, client) Get file diffs from a session [FileDiff]
session_revert(id, body, client) Revert a message Session
session_unrevert(id, client) Restore reverted messages Session
# Send a prompt
{:ok, result} =
  Operations.session_prompt(
    session["id"],
    %{parts: [%{type: "text", text: "Summarize this project in 3 bullets."}]},
    client
  )

# Extract text from the response
for %{"type" => "text", "text" => text} <- result["parts"] do
  IO.puts(text)
end

# Access token usage from the response info
info = result["info"]
IO.inspect(info["tokens"])
# => %{"input" => 1234, "output" => 567, ...}

# Specify a model in the prompt
{:ok, result} =
  Operations.session_prompt(
    session["id"],
    %{
      model: %{providerID: "opencode", modelID: "big-pickle"},
      parts: [%{type: "text", text: "Hello!"}]
    },
    client
  )

# Inject context without triggering AI response
{:ok, _} =
  Operations.session_prompt(
    session["id"],
    %{
      noReply: true,
      parts: [%{type: "text", text: "You are a helpful assistant."}]
    },
    client
  )

Response structure

session_prompt/3 returns {:ok, %{"info" => info, "parts" => parts}}:

  • info — assistant message metadata: "id", "role", "model_id", "provider_id", "cost", "tokens", "time".
  • parts — list of part maps, each with a "type" field:
Part type Key fields Description
"text" "text" The assistant's text response
"tool-invocation" "name", "args", "state", "result" A tool call and its result
"reasoning" "text" Model reasoning/thinking
"step-start" Start of a multi-step sequence
"step-finish" End of a multi-step sequence
"file" "filename", "url", "mime", "source" File attachment
"patch" "files", "hash" File diff/patch

App

Function Description Response
app_agents(client) List available agents [Agent]
app_log(body, client) Write a log entry boolean
app_skills(client) List available skills [Skill]
{:ok, agents} = Operations.app_agents(client)

Operations.app_log(
  %{service: "my-app", level: "info", message: "Operation completed"},
  client
)

Files and search

Function Description Response
file_list(client) List files in a path [FileNode]
file_read(client) Read file content FileContent
file_status(client) Get git status of files [File]
find_files(client) Search files by name/pattern [String]
find_text(client) Search text with ripgrep [Match]
find_symbols(client) Search workspace symbols (LSP) [Symbol]
path_get(client) Get current path info Path
# Search for text across the project
{:ok, results} = Operations.find_text(Keyword.merge(client, pattern: "defmodule"))

# Find files by pattern
{:ok, files} = Operations.find_files(Keyword.merge(client, query: "*.ex", type: "file"))

# Read a specific file
{:ok, content} = Operations.file_read(Keyword.merge(client, path: "lib/my_app.ex"))

# Get git status
{:ok, status} = Operations.file_status(client)

Config and providers

Function Description Response
config_get(client) Get config Config
config_update(body, client) Update config Config
config_providers(client) List providers and default models %{"providers" => [...], "default" => %{...}}
provider_list(client) List providers [Provider]
provider_auth(client) Get provider auth status
{:ok, config} = Operations.config_get(client)
{:ok, %{"providers" => providers, "default" => defaults}} = Operations.config_providers(client)

Auth

Function Description Response
auth_set(providerID, body, client) Set auth credentials boolean
auth_remove(providerID, client) Remove auth credentials boolean
Operations.auth_set("anthropic", %{type: "api", key: "sk-..."}, client)

Events (SSE)

Function Description Response
event_subscribe(client) Subscribe to real-time events %{stream: Stream}
global_event(client) Subscribe to global events %{stream: Stream}
{:ok, %{stream: stream}} = Operations.event_subscribe(client)

Enum.each(stream, fn event ->
  IO.inspect(event, label: "event")
end)

Permissions and questions

Function Description Response
permission_list(client) List pending permissions [Permission]
permission_reply(id, body, client) Reply to a permission request boolean
question_list(client) List pending questions [Question]
question_reply(id, body, client) Reply to a question boolean
question_reject(id, client) Reject a question boolean

MCP

Function Description Response
mcp_status(client) Get MCP server status McpStatus
mcp_add(body, client) Add an MCP server
mcp_connect(name, client) Connect to an MCP server
mcp_disconnect(name, client) Disconnect from an MCP server
{:ok, mcp} = Operations.mcp_status(client)

PTY

Function Description Response
pty_list(client) List PTY sessions [Pty]
pty_create(body, client) Create a PTY session Pty
pty_get(id, client) Get a PTY session Pty
pty_remove(id, client) Remove a PTY session boolean

TUI

Function Description Response
tui_append_prompt(body, client) Append text to the prompt boolean
tui_submit_prompt(client) Submit the current prompt boolean
tui_clear_prompt(client) Clear the prompt boolean
tui_execute_command(body, client) Execute a command boolean
tui_show_toast(body, client) Show a toast notification boolean
tui_open_help(client) Open help dialog boolean
tui_open_sessions(client) Open session selector boolean
tui_open_models(client) Open model selector boolean
tui_open_themes(client) Open theme selector boolean
Operations.tui_append_prompt(%{text: "Add this to prompt"}, client)
Operations.tui_show_toast(%{message: "Done!", variant: "success"}, client)

TUI process lifecycle

{:ok, tui} = OpenCode.create_tui(project: "/path/to/project")
OpenCode.Tui.close(tui)

Error handling

All operations return {:ok, result} on success. Failures return {:error, {status, body}} for HTTP errors or {:error, reason} for connection issues:

case Operations.session_prompt(session_id, body, client) do
  {:ok, result} ->
    result

  {:error, {404, _body}} ->
    IO.puts("Session not found")

  {:error, {400, body}} ->
    IO.puts("Bad request: #{inspect(body)}")

  {:error, %Req.TransportError{reason: :econnrefused}} ->
    IO.puts("Cannot connect to server")
end

Examples

See the examples/ directory:

  • hello.exs — minimal example: start server, create session, send one prompt, print response.
  • chat.exs — interactive CLI chat REPL with session management, slash commands, and token usage display.

Run an example:

mix run examples/hello.exs
mix run examples/chat.exs

Types and Docs

All OpenAPI types are generated under OpenCode.Generated.* (for example, OpenCode.Generated.Session).

API functions and types are documented in generated module docs, primarily:

  • OpenCode
  • OpenCode.Generated.Operations
  • OpenCode.Generated.* type modules

Regenerating

The OpenAPI spec is at priv/opencode_openapi.json. Regenerate the client with:

mix opencode.gen.client --spec priv/opencode_openapi.json

Note

This project is unofficial and is not affiliated with the OpenCode team.