An async-first Swift port of OpenAI's
openai-codex Python SDK.
It communicates with the Codex app server over newline-delimited JSON-RPC and
is pinned to
openai/codex@aea26af.
This is an independent community project, not an official OpenAI Swift package.
Add the package in Xcode, or declare it in Package.swift:
dependencies: [
.package(url: "https://github.com/jimmystridh/codex-sdk-swift.git", from: "0.1.0")
]Then add .product(name: "CodexSDK", package: "codex-sdk-swift") to your
target dependencies.
- Swift 6.0 or newer
- macOS 13 or newer, or a recent Swift toolchain on Linux
- A compatible
codexexecutable onPATH, orCodexConfiguration.codexBinary
The upstream Python wheel pins Codex CLI 0.144.4. SwiftPM cannot bundle that
platform-specific executable, so applications should install and pin it
separately.
import CodexSDK
let codex = try await Codex.connect()
let thread = try await codex.threadStart()
let result = try await thread.run("Explain this repository")
print(result.finalResponse ?? "")
await codex.close()The package also includes a small executable:
swift run codex-quickstart "Reply with one sentence"Codex is an actor and initializes lazily on the first API call. Use
Codex.connect when startup should fail immediately:
let configuration = CodexConfiguration(
codexBinary: URL(fileURLWithPath: "/opt/codex/bin/codex"),
configOverrides: ["model_reasoning_effort=\"high\""],
workingDirectory: URL(fileURLWithPath: "/work/project"),
environment: ["RUST_LOG": "warn"]
)
let codex = try await Codex.connect(configuration: configuration)
defer { Task { await codex.close() } }let thread = try await codex.threadStart(options: .init(
approvalMode: .denyAll,
model: "gpt-5.4",
sandbox: .readOnly
))
let input: Input = [
.text("Describe this image"),
.localImage("/tmp/example.png"),
]
let result = try await thread.run(input, options: .init(
effort: .high,
summary: .concise
))Start a controllable turn and consume its routed stream:
let turn = try await thread.turn("Draft a plan")
for try await notification in try await turn.stream() {
if case let .agentMessageDelta(delta) = notification.payload {
print(delta.delta, terminator: "")
}
}A turn stream is single-consumer. Early notifications are buffered, and active turns on one client are routed independently.
try await codex.loginAPIKey(apiKey)
let login = try await codex.loginChatGPTDeviceCode()
print("Open \(login.verificationUrl) and enter \(login.userCode)")
let completed = try await login.wait()let result = try await retryOnOverload {
try await thread.run("Continue")
}
do {
_ = try await codex.models()
} catch let CodexError.jsonRPC(error) {
print(error.kind, error.message)
}Unknown enum strings, thread-item variants, and notifications are retained so routine upstream protocol additions do not immediately break decoding.
make check
make live-smoke
make integration-test # authenticated real model requestThe deterministic suite launches a real child app-server process and exercises
the JSON-RPC boundary. Source line coverage is above 90% and enforced by
scripts/check_coverage.py.
- Compatibility and deliberate Swift adaptations
- Python-to-Swift migration guide
- Testing tiers
- Weekly upstream update process
- Machine-readable upstream contract
- Release process
Agent-assisted updates can invoke the project-local $sync-codex-upstream
skill.