orchestrator: restore opencode web launcher

This commit is contained in:
autonomic-bot
2026-06-12 15:45:09 +00:00
parent 03343ed3cf
commit a186f23b37
4 changed files with 61 additions and 15 deletions
+15
View File
@@ -543,3 +543,18 @@ session cc-ci-orchestrator-stale can be killed; recipe-mirrors org still private
(c) ghost PR debug: plan-ghostpr-debug-fix.md + memory [[ghost-pr-debug]]. (c) ghost PR debug: plan-ghostpr-debug-fix.md + memory [[ghost-pr-debug]].
- NOT switching the upgrade to sequential (operator: concurrency is fine; the leak is the issue). - NOT switching the upgrade to sequential (operator: concurrency is fine; the leak is the issue).
Duplicate ghost subagent from the interrupt churn — told the upgrader to TaskStop one. Duplicate ghost subagent from the interrupt churn — told the upgrader to TaskStop one.
## 2026-06-12 15:43 UTC — opencode web restored + OpenAI launcher added
- Re-enabled `opencode-web.service` in `nix/hosts/cc-ci-orchestrator-hetzner/configuration.nix`
(`wantedBy = [ "multi-user.target" ]`) and persisted the broader PATH that the old runtime
override had been providing. `nixos-rebuild switch --flake .#cc-ci-orchestrator-hetzner`
brought the service back and it now passes `curl http://127.0.0.1:4096/global/health`.
- Added `cc-ci-plan/launch-opencode.sh` as a separate entrypoint that keeps the default
Claude `launch-orchestrator.sh` untouched. It rebuilds the host flake if `opencode-web`
is not enabled, starts the service if needed, then launches the orchestrator with
`LOOP_BACKEND=opencode`, `LOOP_MODEL=openai/gpt-5.4`, and default session name
`cc-ci-orchestrator-oc`.
- Patched `launch-orchestrator.py` so opencode launches can force the requested model even
though `opencode attach` has no `--model` flag: it now injects `OPENCODE_CONFIG_CONTENT`
for the session. Verified live: `cc-ci-orchestrator-oc` tmux session running on
`backend=opencode model=openai/gpt-5.4`, visible through the shared web server.
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail
SELF_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
FLAKE_ROOT="$(readlink -f "$SELF_DIR/..")"
FLAKE_HOST="${FLAKE_HOST:-cc-ci-orchestrator-hetzner}"
DEFAULT_MODEL="${LOOP_MODEL:-openai/gpt-5.4}"
ensure_opencode_web() {
if ! systemctl is-enabled --quiet opencode-web.service; then
sudo nixos-rebuild switch --flake "$FLAKE_ROOT#$FLAKE_HOST"
fi
if ! systemctl is-active --quiet opencode-web.service; then
sudo systemctl start opencode-web.service
fi
}
ensure_opencode_web
export LOOP_BACKEND=opencode
export LOOP_MODEL="$DEFAULT_MODEL"
export ORCH_SESSION="${ORCH_SESSION:-cc-ci-orchestrator-oc}"
if [ "$#" -eq 0 ]; then
set -- fresh
fi
exec python3 "$SELF_DIR/launch-orchestrator.py" "$@"
+8 -2
View File
@@ -31,7 +31,7 @@ Env:
the session title in the web UI is the SESSION name) the session title in the web UI is the SESSION name)
""" """
import os, sys, subprocess import json, os, shlex, sys, subprocess
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
@@ -147,9 +147,15 @@ def start(mode="resume"):
die(f"opencode not found at {OPENCODE_BIN}") die(f"opencode not found at {OPENCODE_BIN}")
# Attach the orchestrator TUI to the shared opencode web server so it shows up in the # Attach the orchestrator TUI to the shared opencode web server so it shows up in the
# same project/session listing as browser-created sessions. # same project/session listing as browser-created sessions.
model_env = ""
if LOOP_MODEL:
# attach has no --model flag, so inject a one-off config overlay for this session.
model_env = (
f"OPENCODE_CONFIG_CONTENT={shlex.quote(json.dumps({'model': LOOP_MODEL}))} "
)
cmd = ( cmd = (
f"set -a; . /srv/cc-ci/.testenv; set +a; " f"set -a; . /srv/cc-ci/.testenv; set +a; "
f"NO_COLOR=1 {OPENCODE_BIN} attach {OPENCODE_SERVER} --dir {WORKDIR}" f"{model_env}NO_COLOR=1 {OPENCODE_BIN} attach {OPENCODE_SERVER} --dir {shlex.quote(WORKDIR)}"
) )
log(f"starting {SESSION} (backend=opencode, model={LOOP_MODEL or 'default'})") log(f"starting {SESSION} (backend=opencode, model={LOOP_MODEL or 'default'})")
log(f" visible at http://oc.commoninternet.net (tailnet only)") log(f" visible at http://oc.commoninternet.net (tailnet only)")
@@ -118,15 +118,12 @@ SSHCFG
''; '';
}; };
# opencode web server — one shared instance; all agent sessions attach to it. # opencode web server — one shared instance; agent sessions attach to it for web visibility.
# Serves the web UI at http://oc.commoninternet.net (via nginx below, tailscale-only). # Serves the web UI at http://oc.commoninternet.net (via nginx below, tailscale-only).
# TINFOIL_API_KEY and other creds are read from /srv/cc-ci/.testenv at startup. # Provider creds are read from /srv/cc-ci/.testenv at startup.
systemd.services.opencode-web = { systemd.services.opencode-web = {
description = "opencode web server for cc-ci agents (tinfoil/deepseek backend)"; description = "opencode web server for cc-ci agents";
# PARKED 2026-06-01: loops run on the claude backend now, so the opencode web server is not wantedBy = [ "multi-user.target" ];
# needed. Definition kept for easy re-enable — restore `wantedBy = [ "multi-user.target" ];`
# and rebuild to bring it back.
wantedBy = [ ];
after = [ "network-online.target" "tailscaled.service" ]; after = [ "network-online.target" "tailscaled.service" ];
wants = [ "network-online.target" ]; wants = [ "network-online.target" ];
serviceConfig = { serviceConfig = {
@@ -139,17 +136,16 @@ SSHCFG
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "5s"; RestartSec = "5s";
}; };
environment = { HOME = "/home/loops"; }; environment = {
HOME = "/home/loops";
PATH = lib.mkForce "/run/wrappers/bin:/home/loops/.local/bin:/run/current-system/sw/bin:/usr/bin:/bin:/home/loops/.nix-profile/bin:/nix/profile/bin:/home/loops/.local/state/nix/profile/bin:/etc/profiles/per-user/loops/bin:/nix/var/nix/profiles/default/bin";
};
path = [ pkgs.bash pkgs.coreutils pkgs.git pkgs.python3 pkgs.openssh pkgs.tmux pkgs.nettools ]; path = [ pkgs.bash pkgs.coreutils pkgs.git pkgs.python3 pkgs.openssh pkgs.tmux pkgs.nettools ];
}; };
# nginx — reverse-proxy oc.commoninternet.net → opencode web server. # nginx — reverse-proxy oc.commoninternet.net → opencode web server.
# Bound to the tailscale IP so it is only reachable on the tailnet. # Bound to the tailscale IP so it is only reachable on the tailnet.
# PARKED 2026-06-01 alongside opencode-web (loops are on claude now): this vhost just proxies to # DNS: add A record oc.commoninternet.net → 100.84.190.30 (operator step if hostname access is wanted).
# the now-stopped :4096 server, so it's harmless but dormant. Kept (not deleted) so re-enabling
# opencode-web restores the UI in one step. If opencode is dropped for good, remove this block
# and the DNS step below.
# DNS: add A record oc.commoninternet.net → 100.84.190.30 (operator step; only needed if re-enabled).
services.nginx = { services.nginx = {
enable = true; enable = true;
recommendedProxySettings = true; recommendedProxySettings = true;