Repository files navigation

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:

defdepsdo[{:opencode_sdk,"~> 0.1.89"}]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}.

OptionTypeDescriptionDefault
:hostnameString.t()Server hostname"127.0.0.1"
:portinteger()Server port4096
:timeoutinteger()Startup timeout in ms5000
:configmap()Config passed via OPENCODE_CONFIG_CONTENT%{}

OpenCode.create_client/1 options

OptionTypeDescriptionDefault
:base_urlString.t()OpenCode server URL"http://127.0.0.1:4096"
:directoryString.t()Project directory sent via x-opencode-directorynil
:headersmap() | keyword()Extra HTTP headers[]
:timeoutinteger() | :infinityRequest 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

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

Sessions

FunctionDescriptionResponse
session_create(body, client)Create a new sessionSession
session_list(client)List all sessions[Session]
Session.session_get(id, client)Get a session by IDSession
Session.session_children(id, client)List child sessions[Session]
session_delete(id, client)Delete a sessionboolean
session_update(id, body, client)Update session propertiesSession
session_abort(id, client)Abort a running sessionboolean
session_share(id, client)Share a sessionSession
session_unshare(id, client)Unshare a sessionSession
session_summarize(id, body, client)Summarize a sessionboolean
# 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

FunctionDescriptionResponse
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 commandAssistantMessage
session_diff(id, client)Get file diffs from a session[FileDiff]
session_revert(id, body, client)Revert a messageSession
session_unrevert(id, client)Restore reverted messagesSession
# 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 responsefor%{"type"=>"text","text"=>text}<-result["parts"]doIO.puts(text)end# Access token usage from the response infoinfo=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 typeKey fieldsDescription
"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

FunctionDescriptionResponse
app_agents(client)List available agents[Agent]
app_log(body, client)Write a log entryboolean
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

FunctionDescriptionResponse
file_list(client)List files in a path[FileNode]
file_read(client)Read file contentFileContent
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 infoPath
# 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

FunctionDescriptionResponse
config_get(client)Get configConfig
config_update(body, client)Update configConfig
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

FunctionDescriptionResponse
auth_set(providerID, body, client)Set auth credentialsboolean
auth_remove(providerID, client)Remove auth credentialsboolean
Operations.auth_set("anthropic",%{type: "api",key: "sk-..."},client)

Events (SSE)

FunctionDescriptionResponse
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,fnevent->IO.inspect(event,label: "event")end)

Permissions and questions

FunctionDescriptionResponse
permission_list(client)List pending permissions[Permission]
permission_reply(id, body, client)Reply to a permission requestboolean
question_list(client)List pending questions[Question]
question_reply(id, body, client)Reply to a questionboolean
question_reject(id, client)Reject a questionboolean

MCP

FunctionDescriptionResponse
mcp_status(client)Get MCP server statusMcpStatus
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

FunctionDescriptionResponse
pty_list(client)List PTY sessions[Pty]
pty_create(body, client)Create a PTY sessionPty
pty_get(id, client)Get a PTY sessionPty
pty_remove(id, client)Remove a PTY sessionboolean

TUI

FunctionDescriptionResponse
tui_append_prompt(body, client)Append text to the promptboolean
tui_submit_prompt(client)Submit the current promptboolean
tui_clear_prompt(client)Clear the promptboolean
tui_execute_command(body, client)Execute a commandboolean
tui_show_toast(body, client)Show a toast notificationboolean
tui_open_help(client)Open help dialogboolean
tui_open_sessions(client)Open session selectorboolean
tui_open_models(client)Open model selectorboolean
tui_open_themes(client)Open theme selectorboolean
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:

caseOperations.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.

About

Unofficial Elixir SDK for OpenCode

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

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:

defdepsdo[{:opencode_sdk,"~> 0.1.89"}]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}.

OptionTypeDescriptionDefault
:hostnameString.t()Server hostname"127.0.0.1"
:portinteger()Server port4096
:timeoutinteger()Startup timeout in ms5000
:configmap()Config passed via OPENCODE_CONFIG_CONTENT%{}

OpenCode.create_client/1 options

OptionTypeDescriptionDefault
:base_urlString.t()OpenCode server URL"http://127.0.0.1:4096"
:directoryString.t()Project directory sent via x-opencode-directorynil
:headersmap() | keyword()Extra HTTP headers[]
:timeoutinteger() | :infinityRequest 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

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

Sessions

FunctionDescriptionResponse
session_create(body, client)Create a new sessionSession
session_list(client)List all sessions[Session]
Session.session_get(id, client)Get a session by IDSession
Session.session_children(id, client)List child sessions[Session]
session_delete(id, client)Delete a sessionboolean
session_update(id, body, client)Update session propertiesSession
session_abort(id, client)Abort a running sessionboolean
session_share(id, client)Share a sessionSession
session_unshare(id, client)Unshare a sessionSession
session_summarize(id, body, client)Summarize a sessionboolean
# 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

FunctionDescriptionResponse
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 commandAssistantMessage
session_diff(id, client)Get file diffs from a session[FileDiff]
session_revert(id, body, client)Revert a messageSession
session_unrevert(id, client)Restore reverted messagesSession
# 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 responsefor%{"type"=>"text","text"=>text}<-result["parts"]doIO.puts(text)end# Access token usage from the response infoinfo=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 typeKey fieldsDescription
"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

FunctionDescriptionResponse
app_agents(client)List available agents[Agent]
app_log(body, client)Write a log entryboolean
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

FunctionDescriptionResponse
file_list(client)List files in a path[FileNode]
file_read(client)Read file contentFileContent
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 infoPath
# 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

FunctionDescriptionResponse
config_get(client)Get configConfig
config_update(body, client)Update configConfig
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

FunctionDescriptionResponse
auth_set(providerID, body, client)Set auth credentialsboolean
auth_remove(providerID, client)Remove auth credentialsboolean
Operations.auth_set("anthropic",%{type: "api",key: "sk-..."},client)

Events (SSE)

FunctionDescriptionResponse
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,fnevent->IO.inspect(event,label: "event")end)

Permissions and questions

FunctionDescriptionResponse
permission_list(client)List pending permissions[Permission]
permission_reply(id, body, client)Reply to a permission requestboolean
question_list(client)List pending questions[Question]
question_reply(id, body, client)Reply to a questionboolean
question_reject(id, client)Reject a questionboolean

MCP

FunctionDescriptionResponse
mcp_status(client)Get MCP server statusMcpStatus
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

FunctionDescriptionResponse
pty_list(client)List PTY sessions[Pty]
pty_create(body, client)Create a PTY sessionPty
pty_get(id, client)Get a PTY sessionPty
pty_remove(id, client)Remove a PTY sessionboolean

TUI

FunctionDescriptionResponse
tui_append_prompt(body, client)Append text to the promptboolean
tui_submit_prompt(client)Submit the current promptboolean
tui_clear_prompt(client)Clear the promptboolean
tui_execute_command(body, client)Execute a commandboolean
tui_show_toast(body, client)Show a toast notificationboolean
tui_open_help(client)Open help dialogboolean
tui_open_sessions(client)Open session selectorboolean
tui_open_models(client)Open model selectorboolean
tui_open_themes(client)Open theme selectorboolean
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:

caseOperations.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.

About

Unofficial Elixir SDK for OpenCode

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

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:

defdepsdo[{:opencode_sdk,"~> 0.1.89"}]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}.

OptionTypeDescriptionDefault
:hostnameString.t()Server hostname"127.0.0.1"
:portinteger()Server port4096
:timeoutinteger()Startup timeout in ms5000
:configmap()Config passed via OPENCODE_CONFIG_CONTENT%{}

OpenCode.create_client/1 options

OptionTypeDescriptionDefault
:base_urlString.t()OpenCode server URL"http://127.0.0.1:4096"
:directoryString.t()Project directory sent via x-opencode-directorynil
:headersmap() | keyword()Extra HTTP headers[]
:timeoutinteger() | :infinityRequest 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

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

Sessions

FunctionDescriptionResponse
session_create(body, client)Create a new sessionSession
session_list(client)List all sessions[Session]
Session.session_get(id, client)Get a session by IDSession
Session.session_children(id, client)List child sessions[Session]
session_delete(id, client)Delete a sessionboolean
session_update(id, body, client)Update session propertiesSession
session_abort(id, client)Abort a running sessionboolean
session_share(id, client)Share a sessionSession
session_unshare(id, client)Unshare a sessionSession
session_summarize(id, body, client)Summarize a sessionboolean
# 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

FunctionDescriptionResponse
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 commandAssistantMessage
session_diff(id, client)Get file diffs from a session[FileDiff]
session_revert(id, body, client)Revert a messageSession
session_unrevert(id, client)Restore reverted messagesSession
# 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 responsefor%{"type"=>"text","text"=>text}<-result["parts"]doIO.puts(text)end# Access token usage from the response infoinfo=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 typeKey fieldsDescription
"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

FunctionDescriptionResponse
app_agents(client)List available agents[Agent]
app_log(body, client)Write a log entryboolean
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

FunctionDescriptionResponse
file_list(client)List files in a path[FileNode]
file_read(client)Read file contentFileContent
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 infoPath
# 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

FunctionDescriptionResponse
config_get(client)Get configConfig
config_update(body, client)Update configConfig
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

FunctionDescriptionResponse
auth_set(providerID, body, client)Set auth credentialsboolean
auth_remove(providerID, client)Remove auth credentialsboolean
Operations.auth_set("anthropic",%{type: "api",key: "sk-..."},client)

Events (SSE)

FunctionDescriptionResponse
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,fnevent->IO.inspect(event,label: "event")end)

Permissions and questions

FunctionDescriptionResponse
permission_list(client)List pending permissions[Permission]
permission_reply(id, body, client)Reply to a permission requestboolean
question_list(client)List pending questions[Question]
question_reply(id, body, client)Reply to a questionboolean
question_reject(id, client)Reject a questionboolean

MCP

FunctionDescriptionResponse
mcp_status(client)Get MCP server statusMcpStatus
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

FunctionDescriptionResponse
pty_list(client)List PTY sessions[Pty]
pty_create(body, client)Create a PTY sessionPty
pty_get(id, client)Get a PTY sessionPty
pty_remove(id, client)Remove a PTY sessionboolean

TUI

FunctionDescriptionResponse
tui_append_prompt(body, client)Append text to the promptboolean
tui_submit_prompt(client)Submit the current promptboolean
tui_clear_prompt(client)Clear the promptboolean
tui_execute_command(body, client)Execute a commandboolean
tui_show_toast(body, client)Show a toast notificationboolean
tui_open_help(client)Open help dialogboolean
tui_open_sessions(client)Open session selectorboolean
tui_open_models(client)Open model selectorboolean
tui_open_themes(client)Open theme selectorboolean
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:

caseOperations.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.

About

Unofficial Elixir SDK for OpenCode

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

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:

defdepsdo[{:opencode_sdk,"~> 0.1.89"}]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}.

OptionTypeDescriptionDefault
:hostnameString.t()Server hostname"127.0.0.1"
:portinteger()Server port4096
:timeoutinteger()Startup timeout in ms5000
:configmap()Config passed via OPENCODE_CONFIG_CONTENT%{}

OpenCode.create_client/1 options

OptionTypeDescriptionDefault
:base_urlString.t()OpenCode server URL"http://127.0.0.1:4096"
:directoryString.t()Project directory sent via x-opencode-directorynil
:headersmap() | keyword()Extra HTTP headers[]
:timeoutinteger() | :infinityRequest 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

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

Sessions

FunctionDescriptionResponse
session_create(body, client)Create a new sessionSession
session_list(client)List all sessions[Session]
Session.session_get(id, client)Get a session by IDSession
Session.session_children(id, client)List child sessions[Session]
session_delete(id, client)Delete a sessionboolean
session_update(id, body, client)Update session propertiesSession
session_abort(id, client)Abort a running sessionboolean
session_share(id, client)Share a sessionSession
session_unshare(id, client)Unshare a sessionSession
session_summarize(id, body, client)Summarize a sessionboolean
# 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

FunctionDescriptionResponse
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 commandAssistantMessage
session_diff(id, client)Get file diffs from a session[FileDiff]
session_revert(id, body, client)Revert a messageSession
session_unrevert(id, client)Restore reverted messagesSession
# 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 responsefor%{"type"=>"text","text"=>text}<-result["parts"]doIO.puts(text)end# Access token usage from the response infoinfo=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 typeKey fieldsDescription
"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

FunctionDescriptionResponse
app_agents(client)List available agents[Agent]
app_log(body, client)Write a log entryboolean
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

FunctionDescriptionResponse
file_list(client)List files in a path[FileNode]
file_read(client)Read file contentFileContent
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 infoPath
# 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

FunctionDescriptionResponse
config_get(client)Get configConfig
config_update(body, client)Update configConfig
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

FunctionDescriptionResponse
auth_set(providerID, body, client)Set auth credentialsboolean
auth_remove(providerID, client)Remove auth credentialsboolean
Operations.auth_set("anthropic",%{type: "api",key: "sk-..."},client)

Events (SSE)

FunctionDescriptionResponse
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,fnevent->IO.inspect(event,label: "event")end)

Permissions and questions

FunctionDescriptionResponse
permission_list(client)List pending permissions[Permission]
permission_reply(id, body, client)Reply to a permission requestboolean
question_list(client)List pending questions[Question]
question_reply(id, body, client)Reply to a questionboolean
question_reject(id, client)Reject a questionboolean

MCP

FunctionDescriptionResponse
mcp_status(client)Get MCP server statusMcpStatus
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

FunctionDescriptionResponse
pty_list(client)List PTY sessions[Pty]
pty_create(body, client)Create a PTY sessionPty
pty_get(id, client)Get a PTY sessionPty
pty_remove(id, client)Remove a PTY sessionboolean

TUI

FunctionDescriptionResponse
tui_append_prompt(body, client)Append text to the promptboolean
tui_submit_prompt(client)Submit the current promptboolean
tui_clear_prompt(client)Clear the promptboolean
tui_execute_command(body, client)Execute a commandboolean
tui_show_toast(body, client)Show a toast notificationboolean
tui_open_help(client)Open help dialogboolean
tui_open_sessions(client)Open session selectorboolean
tui_open_models(client)Open model selectorboolean
tui_open_themes(client)Open theme selectorboolean
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:

caseOperations.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.

About

Unofficial Elixir SDK for OpenCode

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

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:

defdepsdo[{:opencode_sdk,"~> 0.1.89"}]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}.

OptionTypeDescriptionDefault
:hostnameString.t()Server hostname"127.0.0.1"
:portinteger()Server port4096
:timeoutinteger()Startup timeout in ms5000
:configmap()Config passed via OPENCODE_CONFIG_CONTENT%{}

OpenCode.create_client/1 options

OptionTypeDescriptionDefault
:base_urlString.t()OpenCode server URL"http://127.0.0.1:4096"
:directoryString.t()Project directory sent via x-opencode-directorynil
:headersmap() | keyword()Extra HTTP headers[]
:timeoutinteger() | :infinityRequest 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

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

Sessions

FunctionDescriptionResponse
session_create(body, client)Create a new sessionSession
session_list(client)List all sessions[Session]
Session.session_get(id, client)Get a session by IDSession
Session.session_children(id, client)List child sessions[Session]
session_delete(id, client)Delete a sessionboolean
session_update(id, body, client)Update session propertiesSession
session_abort(id, client)Abort a running sessionboolean
session_share(id, client)Share a sessionSession
session_unshare(id, client)Unshare a sessionSession
session_summarize(id, body, client)Summarize a sessionboolean
# 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

FunctionDescriptionResponse
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 commandAssistantMessage
session_diff(id, client)Get file diffs from a session[FileDiff]
session_revert(id, body, client)Revert a messageSession
session_unrevert(id, client)Restore reverted messagesSession
# 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 responsefor%{"type"=>"text","text"=>text}<-result["parts"]doIO.puts(text)end# Access token usage from the response infoinfo=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 typeKey fieldsDescription
"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

FunctionDescriptionResponse
app_agents(client)List available agents[Agent]
app_log(body, client)Write a log entryboolean
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

FunctionDescriptionResponse
file_list(client)List files in a path[FileNode]
file_read(client)Read file contentFileContent
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 infoPath
# 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

FunctionDescriptionResponse
config_get(client)Get configConfig
config_update(body, client)Update configConfig
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

FunctionDescriptionResponse
auth_set(providerID, body, client)Set auth credentialsboolean
auth_remove(providerID, client)Remove auth credentialsboolean
Operations.auth_set("anthropic",%{type: "api",key: "sk-..."},client)

Events (SSE)

FunctionDescriptionResponse
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,fnevent->IO.inspect(event,label: "event")end)

Permissions and questions

FunctionDescriptionResponse
permission_list(client)List pending permissions[Permission]
permission_reply(id, body, client)Reply to a permission requestboolean
question_list(client)List pending questions[Question]
question_reply(id, body, client)Reply to a questionboolean
question_reject(id, client)Reject a questionboolean

MCP

FunctionDescriptionResponse
mcp_status(client)Get MCP server statusMcpStatus
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

FunctionDescriptionResponse
pty_list(client)List PTY sessions[Pty]
pty_create(body, client)Create a PTY sessionPty
pty_get(id, client)Get a PTY sessionPty
pty_remove(id, client)Remove a PTY sessionboolean

TUI

FunctionDescriptionResponse
tui_append_prompt(body, client)Append text to the promptboolean
tui_submit_prompt(client)Submit the current promptboolean
tui_clear_prompt(client)Clear the promptboolean
tui_execute_command(body, client)Execute a commandboolean
tui_show_toast(body, client)Show a toast notificationboolean
tui_open_help(client)Open help dialogboolean
tui_open_sessions(client)Open session selectorboolean
tui_open_models(client)Open model selectorboolean
tui_open_themes(client)Open theme selectorboolean
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:

caseOperations.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.

About

Unofficial Elixir SDK for OpenCode

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

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:

defdepsdo[{:opencode_sdk,"~> 0.1.89"}]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}.

OptionTypeDescriptionDefault
:hostnameString.t()Server hostname"127.0.0.1"
:portinteger()Server port4096
:timeoutinteger()Startup timeout in ms5000
:configmap()Config passed via OPENCODE_CONFIG_CONTENT%{}

OpenCode.create_client/1 options

OptionTypeDescriptionDefault
:base_urlString.t()OpenCode server URL"http://127.0.0.1:4096"
:directoryString.t()Project directory sent via x-opencode-directorynil
:headersmap() | keyword()Extra HTTP headers[]
:timeoutinteger() | :infinityRequest 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

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

Sessions

FunctionDescriptionResponse
session_create(body, client)Create a new sessionSession
session_list(client)List all sessions[Session]
Session.session_get(id, client)Get a session by IDSession
Session.session_children(id, client)List child sessions[Session]
session_delete(id, client)Delete a sessionboolean
session_update(id, body, client)Update session propertiesSession
session_abort(id, client)Abort a running sessionboolean
session_share(id, client)Share a sessionSession
session_unshare(id, client)Unshare a sessionSession
session_summarize(id, body, client)Summarize a sessionboolean
# 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

FunctionDescriptionResponse
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 commandAssistantMessage
session_diff(id, client)Get file diffs from a session[FileDiff]
session_revert(id, body, client)Revert a messageSession
session_unrevert(id, client)Restore reverted messagesSession
# 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 responsefor%{"type"=>"text","text"=>text}<-result["parts"]doIO.puts(text)end# Access token usage from the response infoinfo=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 typeKey fieldsDescription
"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

FunctionDescriptionResponse
app_agents(client)List available agents[Agent]
app_log(body, client)Write a log entryboolean
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

FunctionDescriptionResponse
file_list(client)List files in a path[FileNode]
file_read(client)Read file contentFileContent
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 infoPath
# 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

FunctionDescriptionResponse
config_get(client)Get configConfig
config_update(body, client)Update configConfig
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

FunctionDescriptionResponse
auth_set(providerID, body, client)Set auth credentialsboolean
auth_remove(providerID, client)Remove auth credentialsboolean
Operations.auth_set("anthropic",%{type: "api",key: "sk-..."},client)

Events (SSE)

FunctionDescriptionResponse
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,fnevent->IO.inspect(event,label: "event")end)

Permissions and questions

FunctionDescriptionResponse
permission_list(client)List pending permissions[Permission]
permission_reply(id, body, client)Reply to a permission requestboolean
question_list(client)List pending questions[Question]
question_reply(id, body, client)Reply to a questionboolean
question_reject(id, client)Reject a questionboolean

MCP

FunctionDescriptionResponse
mcp_status(client)Get MCP server statusMcpStatus
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

FunctionDescriptionResponse
pty_list(client)List PTY sessions[Pty]
pty_create(body, client)Create a PTY sessionPty
pty_get(id, client)Get a PTY sessionPty
pty_remove(id, client)Remove a PTY sessionboolean

TUI

FunctionDescriptionResponse
tui_append_prompt(body, client)Append text to the promptboolean
tui_submit_prompt(client)Submit the current promptboolean
tui_clear_prompt(client)Clear the promptboolean
tui_execute_command(body, client)Execute a commandboolean
tui_show_toast(body, client)Show a toast notificationboolean
tui_open_help(client)Open help dialogboolean
tui_open_sessions(client)Open session selectorboolean
tui_open_models(client)Open model selectorboolean
tui_open_themes(client)Open theme selectorboolean
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:

caseOperations.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.

About

Unofficial Elixir SDK for OpenCode

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

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:

defdepsdo[{:opencode_sdk,"~> 0.1.89"}]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}.

OptionTypeDescriptionDefault
:hostnameString.t()Server hostname"127.0.0.1"
:portinteger()Server port4096
:timeoutinteger()Startup timeout in ms5000
:configmap()Config passed via OPENCODE_CONFIG_CONTENT%{}

OpenCode.create_client/1 options

OptionTypeDescriptionDefault
:base_urlString.t()OpenCode server URL"http://127.0.0.1:4096"
:directoryString.t()Project directory sent via x-opencode-directorynil
:headersmap() | keyword()Extra HTTP headers[]
:timeoutinteger() | :infinityRequest 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

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

Sessions

FunctionDescriptionResponse
session_create(body, client)Create a new sessionSession
session_list(client)List all sessions[Session]
Session.session_get(id, client)Get a session by IDSession
Session.session_children(id, client)List child sessions[Session]
session_delete(id, client)Delete a sessionboolean
session_update(id, body, client)Update session propertiesSession
session_abort(id, client)Abort a running sessionboolean
session_share(id, client)Share a sessionSession
session_unshare(id, client)Unshare a sessionSession
session_summarize(id, body, client)Summarize a sessionboolean
# 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

FunctionDescriptionResponse
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 commandAssistantMessage
session_diff(id, client)Get file diffs from a session[FileDiff]
session_revert(id, body, client)Revert a messageSession
session_unrevert(id, client)Restore reverted messagesSession
# 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 responsefor%{"type"=>"text","text"=>text}<-result["parts"]doIO.puts(text)end# Access token usage from the response infoinfo=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 typeKey fieldsDescription
"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

FunctionDescriptionResponse
app_agents(client)List available agents[Agent]
app_log(body, client)Write a log entryboolean
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

FunctionDescriptionResponse
file_list(client)List files in a path[FileNode]
file_read(client)Read file contentFileContent
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 infoPath
# 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

FunctionDescriptionResponse
config_get(client)Get configConfig
config_update(body, client)Update configConfig
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

FunctionDescriptionResponse
auth_set(providerID, body, client)Set auth credentialsboolean
auth_remove(providerID, client)Remove auth credentialsboolean
Operations.auth_set("anthropic",%{type: "api",key: "sk-..."},client)

Events (SSE)

FunctionDescriptionResponse
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,fnevent->IO.inspect(event,label: "event")end)

Permissions and questions

FunctionDescriptionResponse
permission_list(client)List pending permissions[Permission]
permission_reply(id, body, client)Reply to a permission requestboolean
question_list(client)List pending questions[Question]
question_reply(id, body, client)Reply to a questionboolean
question_reject(id, client)Reject a questionboolean

MCP

FunctionDescriptionResponse
mcp_status(client)Get MCP server statusMcpStatus
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

FunctionDescriptionResponse
pty_list(client)List PTY sessions[Pty]
pty_create(body, client)Create a PTY sessionPty
pty_get(id, client)Get a PTY sessionPty
pty_remove(id, client)Remove a PTY sessionboolean

TUI

FunctionDescriptionResponse
tui_append_prompt(body, client)Append text to the promptboolean
tui_submit_prompt(client)Submit the current promptboolean
tui_clear_prompt(client)Clear the promptboolean
tui_execute_command(body, client)Execute a commandboolean
tui_show_toast(body, client)Show a toast notificationboolean
tui_open_help(client)Open help dialogboolean
tui_open_sessions(client)Open session selectorboolean
tui_open_models(client)Open model selectorboolean
tui_open_themes(client)Open theme selectorboolean
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:

caseOperations.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.

About

Unofficial Elixir SDK for OpenCode

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

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:

defdepsdo[{:opencode_sdk,"~> 0.1.89"}]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}.

OptionTypeDescriptionDefault
:hostnameString.t()Server hostname"127.0.0.1"
:portinteger()Server port4096
:timeoutinteger()Startup timeout in ms5000
:configmap()Config passed via OPENCODE_CONFIG_CONTENT%{}

OpenCode.create_client/1 options

OptionTypeDescriptionDefault
:base_urlString.t()OpenCode server URL"http://127.0.0.1:4096"
:directoryString.t()Project directory sent via x-opencode-directorynil
:headersmap() | keyword()Extra HTTP headers[]
:timeoutinteger() | :infinityRequest 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

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

Sessions

FunctionDescriptionResponse
session_create(body, client)Create a new sessionSession
session_list(client)List all sessions[Session]
Session.session_get(id, client)Get a session by IDSession
Session.session_children(id, client)List child sessions[Session]
session_delete(id, client)Delete a sessionboolean
session_update(id, body, client)Update session propertiesSession
session_abort(id, client)Abort a running sessionboolean
session_share(id, client)Share a sessionSession
session_unshare(id, client)Unshare a sessionSession
session_summarize(id, body, client)Summarize a sessionboolean
# 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

FunctionDescriptionResponse
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 commandAssistantMessage
session_diff(id, client)Get file diffs from a session[FileDiff]
session_revert(id, body, client)Revert a messageSession
session_unrevert(id, client)Restore reverted messagesSession
# 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 responsefor%{"type"=>"text","text"=>text}<-result["parts"]doIO.puts(text)end# Access token usage from the response infoinfo=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 typeKey fieldsDescription
"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

FunctionDescriptionResponse
app_agents(client)List available agents[Agent]
app_log(body, client)Write a log entryboolean
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

FunctionDescriptionResponse
file_list(client)List files in a path[FileNode]
file_read(client)Read file contentFileContent
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 infoPath
# 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

FunctionDescriptionResponse
config_get(client)Get configConfig
config_update(body, client)Update configConfig
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

FunctionDescriptionResponse
auth_set(providerID, body, client)Set auth credentialsboolean
auth_remove(providerID, client)Remove auth credentialsboolean
Operations.auth_set("anthropic",%{type: "api",key: "sk-..."},client)

Events (SSE)

FunctionDescriptionResponse
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,fnevent->IO.inspect(event,label: "event")end)

Permissions and questions

FunctionDescriptionResponse
permission_list(client)List pending permissions[Permission]
permission_reply(id, body, client)Reply to a permission requestboolean
question_list(client)List pending questions[Question]
question_reply(id, body, client)Reply to a questionboolean
question_reject(id, client)Reject a questionboolean

MCP

FunctionDescriptionResponse
mcp_status(client)Get MCP server statusMcpStatus
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

FunctionDescriptionResponse
pty_list(client)List PTY sessions[Pty]
pty_create(body, client)Create a PTY sessionPty
pty_get(id, client)Get a PTY sessionPty
pty_remove(id, client)Remove a PTY sessionboolean

TUI

FunctionDescriptionResponse
tui_append_prompt(body, client)Append text to the promptboolean
tui_submit_prompt(client)Submit the current promptboolean
tui_clear_prompt(client)Clear the promptboolean
tui_execute_command(body, client)Execute a commandboolean
tui_show_toast(body, client)Show a toast notificationboolean
tui_open_help(client)Open help dialogboolean
tui_open_sessions(client)Open session selectorboolean
tui_open_models(client)Open model selectorboolean
tui_open_themes(client)Open theme selectorboolean
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:

caseOperations.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.

About

Unofficial Elixir SDK for OpenCode

Resources

Stars

14 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages