Skip to content

5.10a SDK Basics

TL;DR: Use the JavaScript/TypeScript SDK to control OpenCode programmatically for automated workflows and custom integrations.

Course Notes

Key takeaways from this lesson:

5.10a SDK Basics Notes


What You'll Learn

  • Install and configure the OpenCode SDK
  • Create server and client instances
  • Launch the TUI interface
  • Manage sessions and send messages
  • Listen to real-time events

Your Current Challenges

  • Want to call OpenCode from your own application
  • Want to batch process tasks programmatically
  • Want to build custom integrations (IDE plugins, CI/CD tools, etc.)
  • Want to automate OpenCode operations in scripts

SDK Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                    Your Application                       │
├─────────────────────────────────────────────────────────┤
│                   @opencode-ai/sdk                       │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │
│  │createOpencode│  │createOpencode│  │createOpencode│    │
│  │             │  │   Client    │  │    Tui      │      │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘      │
│         │                │                │              │
│  Server+Client     Client Only      Launch TUI          │
└─────────┼────────────────┼────────────────┼─────────────┘
          │                │                │
          ▼                ▼                ▼
┌─────────────────────────────────────────────────────────┐
│                  OpenCode Server                         │
│            HTTP API (default port 4096)                  │
└─────────────────────────────────────────────────────────┘

Install SDK

bash
npm install @opencode-ai/sdk

V1 and V2 Entry Points

The examples in this lesson use the V1 entry point, @opencode-ai/sdk. As of v1.18.22, V1 has not been removed. The same package also exports @opencode-ai/sdk/v2, which provides V2 extensions for sessions, questions, the current location, event streams, paginated history, runtime operations, and permission requests. The two entry points use different parameter structures—do not simply change the import and continue using V1-style { path, body } calls.

typescript
// V1: used by the remaining examples in this lesson
import { createOpencodeClient } from "@opencode-ai/sdk"

// V2: flat parameters; see Lesson 5.10c
import { createOpencodeClient as createV2Client } from "@opencode-ai/sdk/v2"

Source: packages/sdk/js/package.json:12-20, V1 sdk.gen.ts:431-700, V2 location:5038-5058, V2 session:5171-5793


Three Usage Modes

Start both server and client, ideal for standalone scripts and automation:

typescript
import { createOpencode } from "@opencode-ai/sdk"

const { client, server } = await createOpencode()

// Use client to call APIs
const sessions = await client.session.list()
console.log(`Currently ${sessions.data?.length} sessions`)

// Close server when done
server.close()

ServerOptions Parameters

ParameterTypeDescriptionDefault
hostnamestringServer hostname127.0.0.1
portnumberServer port4096
signalAbortSignalAbort signal for cancellationundefined
timeoutnumberServer startup timeout (ms)5000
configConfigConfig object to override opencode.json{}

Source: packages/sdk/js/src/server.ts:5-11

Configuration Override Example

typescript
import { createOpencode } from "@opencode-ai/sdk"

const opencode = await createOpencode({
  hostname: "127.0.0.1",
  port: 4097,  // Use different port to avoid conflicts
  timeout: 10000,
  config: {
    model: "anthropic/claude-opus-4-5-thinking",
    logLevel: "DEBUG",
  },
})

console.log(`Server running at ${opencode.server.url}`)

// Close when done
opencode.server.close()

2. Client-Only Mode

Connect to a running OpenCode instance, ideal for plugin development:

typescript
import { createOpencodeClient } from "@opencode-ai/sdk"

const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
})

// Use client directly
const sessions = await client.session.list()

ClientOptions Parameters

ParameterTypeDescriptionDefault
baseUrlstringServer URLhttp://localhost:4096
fetchfunctionCustom fetch implementationglobalThis.fetch
parseAsstringResponse parsing: auto, json, text, blob, arrayBuffer, stream, formDataauto
responseStyle"data" | "fields"Return style: data returns only data, fields returns full responsefields
throwOnErrorbooleanThrow on error instead of returningfalse
directorystringProject directory (passed via X-Opencode-Directory header)undefined

Source: packages/sdk/js/src/gen/client/types.gen.ts:10-52, packages/sdk/js/src/client.ts:33

Switching Project Directories

typescript
// Connect to different projects
const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
  directory: "/path/to/my-project",
})

Remote Connection (with Authentication)

When connecting to a remote OpenCode server with OPENCODE_SERVER_PASSWORD set, pass Basic Auth via headers:

typescript
import { createOpencodeClient } from "@opencode-ai/sdk"

// Remote connection (with auth)
const client = createOpencodeClient({
  baseUrl: "http://192.168.1.100:4096",
  headers: {
    // Basic Auth format: Base64(username:password)
    // Use btoa() in browser/Edge Runtime
    Authorization: `Basic ${btoa("opencode:your-password")}`
  },
  directory: "/projects/my-app"  // Specify remote project directory
})
Use Buffer in Node.js
typescript
// Node.js doesn't have btoa, use Buffer instead
Authorization: `Basic ${Buffer.from("opencode:password").toString("base64")}`
ScenarioUsernameDescription
DefaultopencodeServer default username
CustomValue of OPENCODE_SERVER_USERNAME env varIf server has custom username

Source: packages/opencode/src/server/auth.ts:36-42 (Basic Auth header generation), packages/opencode/src/server/routes/instance/httpapi/middleware/authorization.ts (request parsing)


3. Launch TUI Interface

Programmatically start OpenCode's terminal interface:

typescript
import { createOpencodeTui } from "@opencode-ai/sdk"

const tui = createOpencodeTui({
  project: "/path/to/my-project",
  model: "anthropic/claude-opus-4-5-thinking",
  session: "abc123",  // Resume specific session
  agent: "build",
})

// User can interact in TUI
// ...

// Close TUI
tui.close()

TuiOptions Parameters

ParameterTypeDescription
projectstringProject directory path
modelstringModel to use (format: provider/model)
sessionstringSession ID to resume
agentstringAgent to use (e.g., build, plan)
signalAbortSignalAbort signal for cancellation
configConfigConfiguration object

Source: packages/sdk/js/src/server.ts:13-20


Basic API Usage

Session Management

typescript
// Create new session
const session = await client.session.create({
  body: { title: "My Task" },
})
console.log(`Created session: ${session.data?.id}`)

// List all sessions
const sessions = await client.session.list()

// Get single session
const detail = await client.session.get({
  path: { id: session.data!.id },
})

// Delete session
await client.session.delete({
  path: { id: session.data!.id },
})

Send Messages

typescript
// Send prompt and wait for AI response
const result = await client.session.prompt({
  path: { id: sessionId },
  body: {
    model: { providerID: "anthropic", modelID: "claude-opus-4-5-thinking" },
    parts: [{ type: "text", text: "Please analyze the performance issues in this code" }],
  },
})

// Inject context (without triggering AI response)
await client.session.prompt({
  path: { id: sessionId },
  body: {
    noReply: true,
    parts: [{ type: "text", text: "You are a professional code review assistant." }],
  },
})

Async Send (Don't Wait for Response)

typescript
// Returns immediately, ideal for long tasks
await client.session.promptAsync({
  path: { id: sessionId },
  body: {
    parts: [{ type: "text", text: "Please refactor the entire module" }],
  },
})

// Get response via event listener

File Operations

typescript
// Search text content
const textResults = await client.find.text({
  query: { pattern: "function.*opencode" },
})

// Find files (supports glob patterns)
const files = await client.find.files({
  query: { query: "*.ts" },
})

// Find directories only
const dirs = await client.find.files({
  query: { query: "src", dirs: "true" },
})

// Read file content
const content = await client.file.read({
  query: { path: "src/index.ts" },
})

// Get file status (git changes)
const status = await client.file.status()

TUI Control

typescript
// Append text to input box
await client.tui.appendPrompt({
  body: { text: "Please check this file" },
})

// Submit current input
await client.tui.submitPrompt()

// Clear input
await client.tui.clearPrompt()

// Show notification
await client.tui.showToast({
  body: { 
    message: "Task completed!", 
    variant: "success",
    duration: 3000,  // Show for 3 seconds
  },
})

// Open dialogs
await client.tui.openHelp()
await client.tui.openSessions()
await client.tui.openThemes()
await client.tui.openModels()

// Execute TUI commands
await client.tui.executeCommand({
  body: { command: "agent_cycle" },
})

Real-time Event Listening

Subscribe to Event Stream

typescript
const events = await client.event.subscribe()

for await (const event of events.stream) {
  console.log(`Event type: ${event.type}`)
  console.log(`Event data:`, event.properties)
  
  // Handle by event type
  switch (event.type) {
    case "message.updated":
      console.log("Message updated:", event.properties.info)
      break
    case "session.idle":
      console.log("Session idle:", event.properties.sessionID)
      break
    case "permission.updated":
      console.log("Permission request:", event.properties)
      break
  }
}

Common Event Types

Event TypeDescription
message.updatedMessage content updated
message.part.updatedMessage part updated (includes delta)
session.statusSession status changed (idle/busy/retry)
session.idleSession entered idle state
permission.updatedPermission request pending
file.editedFile was edited
todo.updatedTodo list updated

For complete event types, see 5.10b API Reference


Type Imports

SDK provides complete TypeScript type definitions:

typescript
import type { 
  // Core types
  Session,
  Message,
  Part,
  
  // Event types
  Event,
  EventMessageUpdated,
  EventSessionIdle,
  
  // Config types
  Config,
  AgentConfig,
  ProviderConfig,
  
  // Others
  Todo,
  Permission,
  Agent,
  Provider,
  Model,
} from "@opencode-ai/sdk"

Error Handling

Standard Error Handling

typescript
try {
  const session = await client.session.get({ 
    path: { id: "invalid-id" } 
  })
} catch (error) {
  console.error("Failed to get session:", (error as Error).message)
}

Using throwOnError Option

typescript
// Global configuration
const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
  throwOnError: true,  // Throw on all request errors
})

// Or use in single request
const result = await client.session.get({
  path: { id: sessionId },
  throwOnError: true,
})

Check Return Value

typescript
const result = await client.session.get({
  path: { id: sessionId },
})

if (result.error) {
  console.error("Error:", result.error)
} else {
  console.log("Session:", result.data)
}

Practical Example: Batch Code Review

typescript
import { createOpencode } from "@opencode-ai/sdk"
import { readdir } from "fs/promises"

async function batchCodeReview(directory: string) {
  const { client, server } = await createOpencode({
    config: {
      model: "anthropic/claude-opus-4-5-thinking",
    },
  })

  try {
    // Create session
    const session = await client.session.create({
      body: { title: `Batch Code Review - ${directory}` },
    })
    const sessionId = session.data!.id

    // Find all TypeScript files
    const files = await client.find.files({
      query: { query: "*.ts", directory },
    })

    console.log(`Found ${files.data?.length} files`)

    // Review each file
    for (const file of files.data ?? []) {
      console.log(`Reviewing: ${file}`)
      
      await client.session.prompt({
        path: { id: sessionId },
        body: {
          parts: [{ 
            type: "text", 
            text: `Please review file ${file} for potential issues and improvement suggestions.` 
          }],
        },
      })
    }

    console.log("Review complete!")
  } finally {
    server.close()
  }
}

batchCodeReview("./src")

Common Pitfalls

IssueCauseSolution
SDK connection failedServer not runningRun opencode serve first or use createOpencode()
Port conflictDefault port 4096 in useSpecify different port port: 4097
Type errorsSDK version mismatchUpdate to latest npm update @opencode-ai/sdk
Timeout errorSlow server startup or network issuesIncrease timeout value
Event stream interruptedConnection droppedImplement reconnection logic
Response format confusionresponseStyle configDefault is fields, returns { data, error, request, response }

Lesson Summary

You learned:

  1. Install SDK: npm install @opencode-ai/sdk
  2. Three usage modes:
    • createOpencode() - Server + Client
    • createOpencodeClient() - Client only
    • createOpencodeTui() - Launch TUI
  3. Basic APIs: Session management, message sending, file operations, TUI control
  4. Event listening: Real-time status change notifications


Next Lesson Preview

5.10b API Reference covers all 20 API modules plus one permission-response method, complete type definitions, and 32 event types in detail.