fix(opencode): the Linux check fixes for opencode 2.x (Issue #60) #62
No files matched your search
@@ -76,7 +76,8 @@ class OpenCodeAgentAdapter(BaseAgentAdapter):
|
||||
def input_placeholder_pattern(self) -> Optional[str]:
|
||||
# 2.x home screen: `Ask anything… "<example>"`, the example picked per
|
||||
# launch and cut without its closing quote in a narrow box (Issue #60).
|
||||
return r'Ask anything(?:…|\.\.\.)(?: "[^"]*"?)?'
|
||||
# The example is required: a bare `Ask anything…` is a typed draft.
|
||||
return r'Ask anything(?:…|\.\.\.) "[^"]*"?'
|
||||
|
||||
@property
|
||||
def input_rule_pattern(self) -> Optional[str]:
|
||||
@@ -318,7 +319,8 @@ class OpenCodeAgentAdapter(BaseAgentAdapter):
|
||||
# opencode 2.x needs no login: a fresh HOME runs a free default model
|
||||
# and local providers have no key, so the binary is the whole check.
|
||||
home = os.environ.get("HOME_DIR") or os.environ.get("HOME") or os.path.expanduser("~")
|
||||
return bool(shutil.which("opencode")) or os.path.exists(f"{home}/.opencode/bin/opencode")
|
||||
local = f"{home}/.opencode/bin/opencode"
|
||||
return bool(shutil.which("opencode")) or (os.path.isfile(local) and os.access(local, os.X_OK))
|
||||
|
||||
def discover(self, ctx: DiscoveryContext) -> list:
|
||||
db = self._resolve_db(ctx)
|
||||
|
||||
@@ -950,7 +950,9 @@ for s in d.get('herdr_sessions', []):
|
||||
|
||||
valid_candidates = []
|
||||
try:
|
||||
conn = sqlite3.connect(odb)
|
||||
# read-only: nothing is created or removed beside the agent's db (Issue #60)
|
||||
from lib_py.agents.adapters.opencode import _ro_connect
|
||||
conn = _ro_connect(odb)
|
||||
cursor = conn.cursor()
|
||||
# opencode 2.x keeps sessions in session_v2 (a migrated 1.x `session`
|
||||
# table is no longer written).
|
||||
|
||||
@@ -112,8 +112,9 @@ def resume_on_disk(s):
|
||||
if not odb or not os.path.exists(odb):
|
||||
return 'MISSING'
|
||||
try:
|
||||
import sqlite3
|
||||
conn = sqlite3.connect(odb)
|
||||
# read-only: nothing is created or removed beside the agent's db (Issue #60)
|
||||
from lib_py.agents.adapters.opencode import _ro_connect
|
||||
conn = _ro_connect(odb)
|
||||
r = conn.execute("SELECT 1 FROM session_v2 WHERE id=?", (u,)).fetchone() # opencode 2.x
|
||||
conn.close()
|
||||
return 'yes' if r else 'MISSING'
|
||||
@@ -312,8 +313,9 @@ def resume_on_disk(s):
|
||||
if not odb or not os.path.exists(odb):
|
||||
return 'MISSING'
|
||||
try:
|
||||
import sqlite3
|
||||
conn = sqlite3.connect(odb)
|
||||
# read-only: nothing is created or removed beside the agent's db (Issue #60)
|
||||
from lib_py.agents.adapters.opencode import _ro_connect
|
||||
conn = _ro_connect(odb)
|
||||
r = conn.execute("SELECT 1 FROM session_v2 WHERE id=?", (u,)).fetchone() # opencode 2.x
|
||||
conn.close()
|
||||
return 'yes' if r else 'MISSING'
|
||||
|
||||
@@ -81,6 +81,8 @@ def test_the_narrow_screen_stops_at_its_status_line_and_reads_ready():
|
||||
'Ask anything… "x" and fix it',
|
||||
"Ask anything about the build",
|
||||
'Ask anything… "a"b',
|
||||
"Ask anything…",
|
||||
"Ask anything...",
|
||||
])
|
||||
def test_typed_text_is_read_as_typed(name, typed):
|
||||
text, n = PLACEHOLDER_LINE.subn(lambda m: m.group(1) + typed, _pane(name), count=1)
|
||||
@@ -153,6 +155,11 @@ def wal_home(tmp_path, monkeypatch):
|
||||
def make(state):
|
||||
if state == "clean":
|
||||
_wal_writer(db, ws).close()
|
||||
# macOS SQLite keeps both sidecars after the last close (persistent
|
||||
# WAL); the close checkpointed them, so the Linux state is the same rows.
|
||||
for suffix in ("-wal", "-shm"):
|
||||
if os.path.exists(str(db) + suffix):
|
||||
os.remove(str(db) + suffix)
|
||||
elif state in ("leftover", "walonly"):
|
||||
src = tmp_path / "src" / "opencode.db"
|
||||
src.parent.mkdir()
|
||||
@@ -184,6 +191,16 @@ def test_a_query_reads_right_and_leaves_the_sidecars_alone(wal_home, state, list
|
||||
assert sorted(os.listdir(os.path.dirname(db))) == listing
|
||||
|
||||
|
||||
@pytest.mark.parametrize("script,count", [
|
||||
("multi-agent-mux-status/scripts/status.sh", 2),
|
||||
("multi-agent-mux-monitor/scripts/reconcile.sh", 1),
|
||||
])
|
||||
def test_status_and_reconcile_open_the_database_read_only(script, count):
|
||||
text = (SKILLS / script).read_text()
|
||||
assert text.count("_ro_connect(odb)") == count
|
||||
assert "sqlite3.connect(odb)" not in text
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. `debug paths` once per (binary, HOME, XDG_DATA_HOME)
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -314,6 +331,16 @@ def test_keys_and_auth_json_without_the_binary_are_not_enough(bare_home, monkeyp
|
||||
assert opencode.auth_ok() is False
|
||||
|
||||
|
||||
def test_a_home_install_that_cannot_run_is_not_enough(bare_home):
|
||||
local = bare_home / "home" / ".opencode" / "bin" / "opencode"
|
||||
_executable(local).chmod(0o644)
|
||||
assert get_adapter("opencode").auth_ok() is False
|
||||
local.unlink()
|
||||
local.mkdir()
|
||||
local.chmod(0o755)
|
||||
assert get_adapter("opencode").auth_ok() is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize("where", ["path", "home"])
|
||||
def test_the_binary_alone_is_enough(bare_home, monkeypatch, where):
|
||||
if where == "path":
|
||||
|
||||
Reference in new issue
Block a user