refactor: split opencode runtime and tests
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
"""Isolated opencode process runtime."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
_ENV_ALLOW = frozenset({
|
||||
"PATH", "LANG", "LANGUAGE", "LC_ALL", "LC_CTYPE", "TZ", "TERM",
|
||||
"SSL_CERT_FILE", "SSL_CERT_DIR", "NODE_EXTRA_CA_CERTS",
|
||||
"NO_PROXY", "no_proxy",
|
||||
})
|
||||
|
||||
|
||||
def shared_home(work_root):
|
||||
home = os.path.join(work_root, ".opencode-home")
|
||||
os.makedirs(home, exist_ok=True)
|
||||
return home
|
||||
|
||||
|
||||
def ensure_global_config(home, factory_dir, install_config):
|
||||
dst_dir = os.path.join(home, ".config", "opencode")
|
||||
os.makedirs(dst_dir, exist_ok=True)
|
||||
dst = os.path.join(dst_dir, "opencode.json")
|
||||
src = os.path.join(factory_dir, "opencode.json")
|
||||
if not os.path.isfile(src):
|
||||
return
|
||||
if not os.path.isfile(dst) or os.path.getmtime(src) > os.path.getmtime(dst):
|
||||
install_config(src, dst)
|
||||
|
||||
|
||||
def build_env(home, rtk_dir, source_env=None):
|
||||
source = os.environ if source_env is None else source_env
|
||||
env = {key: value for key, value in source.items() if key in _ENV_ALLOW}
|
||||
env["HOME"] = home
|
||||
path = env.get("PATH", "/usr/local/bin:/usr/bin:/bin")
|
||||
env["PATH"] = (rtk_dir + os.pathsep + path) if rtk_dir else path
|
||||
env["OPENCODE_EXPERIMENTAL_LSP_TOOL"] = source.get(
|
||||
"OPENCODE_EXPERIMENTAL_LSP_TOOL", "true"
|
||||
)
|
||||
return env
|
||||
|
||||
|
||||
def warm_opencode(
|
||||
home, model, *, opencode_bin, ensure_config, build_environment,
|
||||
runner=subprocess.run,
|
||||
):
|
||||
marker = os.path.join(home, ".pragent.warmed")
|
||||
if os.path.exists(marker):
|
||||
return
|
||||
ensure_config(home)
|
||||
env = build_environment(home)
|
||||
try:
|
||||
runner(
|
||||
[opencode_bin, "run", "--pure", "--model", model, "ok"],
|
||||
cwd=home, env=env, capture_output=True, text=True,
|
||||
stdin=subprocess.DEVNULL, timeout=240,
|
||||
)
|
||||
except (subprocess.TimeoutExpired, Exception):
|
||||
pass
|
||||
try:
|
||||
open(marker, "w").close()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def run_opencode(
|
||||
workdir, model, *, opencode_bin, shared_home_fn, warm_fn,
|
||||
build_environment, parse_events, prompt, timeout,
|
||||
runner=subprocess.run,
|
||||
):
|
||||
home = shared_home_fn()
|
||||
warm_fn(home, model)
|
||||
env = build_environment(home)
|
||||
cmd = [
|
||||
opencode_bin, "run", "--pure", "--format", "json",
|
||||
"--agent", "pragent", "--dir", workdir, "--model", model, prompt,
|
||||
]
|
||||
last_err = ""
|
||||
for _ in range(2):
|
||||
try:
|
||||
proc = runner(
|
||||
cmd, cwd=workdir, env=env, capture_output=True, text=True,
|
||||
stdin=subprocess.DEVNULL, timeout=timeout,
|
||||
)
|
||||
except subprocess.TimeoutExpired as exc:
|
||||
last_err = f"opencode timed out after {exc.timeout}s"
|
||||
continue
|
||||
text, usage = parse_events(proc.stdout or "")
|
||||
if text.strip():
|
||||
return text, usage
|
||||
last_err = (
|
||||
f"opencode empty text (rc={proc.returncode}); "
|
||||
f"stderr: {(proc.stderr or '')[-1500:]}"
|
||||
)
|
||||
raise RuntimeError(last_err or "opencode produced no output")
|
||||
|
||||
Reference in New Issue
Block a user