opencode serve: lifecycle logging + configurable launcher (Ollama)#17
Merged
Conversation
The shared `opencode serve` process started and stopped silently: the raw `spawn:` line shows `--port 0`, not the bound port, and there was no teardown line. Log the resolved listening URL on start and a stopping line at scope teardown (DEBUG → /tmp/orca-*.log), so the long-lived server is visible in the per-run trace. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the `opencode serve` launch command configurable, so a flow can start the server via `ollama launch opencode` — which injects Ollama's generated provider config (OPENCODE_CONFIG_CONTENT) — instead of the hardcoded `opencode` binary. This is the zero-config path for local Ollama models (issue #10). - OpencodeLauncher: `default`, `ollama(model)`, and `apply(Seq)` for any wrapper. Threaded flow → DefaultFlowContext → OpencodeBackend → OpencodeServer → OpencodeArgs.serve (which appends `serve --port 0 --log-level WARN`). Exposed as `flow(opencodeLauncher = …)`. - `ollama(model)` needs the model: `ollama launch` falls back to interactive selection headless otherwise. It pins that one model as the server default, so bare `opencode` routes to it (no `withModel`). - Teardown SIGINTs the process tree (CliProcess.sendSigIntTree, used only by the server) so a forking launcher can't orphan the real serve process. - README documents both Ollama paths; ADR 0017 records the decision. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A serve that exited without binding (e.g. `ollama launch --model X` for a model that isn't pulled) threw a generic "did not report a listening URL" while the actionable reason went to inherited stderr. Now pipe and drain stderr (tracing each line, keeping a bounded tail) and include it in the start-failure error, so the message reads e.g. `opencode serve did not start: … model "X" not found; run 'ollama pull X' …`. Also keeps serve's stderr out of the console on the success path (it goes to the trace at DEBUG instead). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Member
Author
|
@JD557 good question :) so in case the model was missing, the flow was failing (it's a non-interactive terminal), but with a cryptic error message. So now you need to start You can't really use |
|
Thanks for confirming, looks good to me, then. Thanks for the fix 👌 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Closes #10 - @JD557 let me know if this would work for you
Two related improvements to how orca runs the shared
opencode serveprocess.1. Log server start/stop to the run trace
The shared server started and stopped silently — the raw
spawn:line shows--port 0(not the bound port) and there was no teardown line, so a long-livedprocess was invisible in the trace. Now logs the resolved listening URL on start
and a stopping line at teardown (DEBUG →
/tmp/orca-*.log).This came out of issue #10, where a user was confused that orca had silently
started its own opencode server.
2. Configurable serve launcher (
OpencodeLauncher)The
opencodebinary was hardcoded, so there was no way to start the server viaollama launch opencode— which injects Ollama's generated provider config(
OPENCODE_CONFIG_CONTENT) and is the zero-config path for local Ollama models(issue #10). Hand-writing that provider config (npm package, baseURL, exact model
ids, raised
num_ctx) is fiddly, so the launcher is the genuine easy path.OpencodeLauncher:default(plainopencode),ollama(model),and
apply(Seq)for any wrapper. Threadedflow(opencodeLauncher = …) → DefaultFlowContext → OpencodeBackend → OpencodeServer → OpencodeArgs.serve(orca appends
serve --port 0 --log-level WARN).ollama(model)→ollama launch opencode --model <model> --.--modelisrequired (headless
ollama launchotherwise falls back to interactiveselection). It pins that one model as the server default, so a bare
opencodeturn routes to it — nowithModel. Other Ollama ids are rejected,so switching models means relaunching.
CliProcess.sendSigIntTree, used onlyby the server) so a forking launcher can't orphan the real
opencode serve.Docs
README documents both Ollama paths (launcher vs. manual opencode.json +
withModel); ADR 0017 records the decision.
Testing
65 opencode unit tests pass, including one asserting orca spawns the
launcher-wrapped argv. Not verified end-to-end against a live model here
(CPU-only + Ollama's model registry firewalled): validated by inspecting a
launcher-started server's injected /config (the ollama provider and default
model are present); a real agentic turn was too slow to finish on a 0.5B model.
A real-model run on capable hardware is recommended before relying on it.