optio-opencode 0.1.4__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/PKG-INFO +1 -1
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/pyproject.toml +1 -1
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode/__init__.py +12 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode/host_actions.py +78 -51
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode/prompt.py +9 -5
- optio_opencode-0.1.6/src/optio_opencode/seed_manifest.py +61 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode/session.py +244 -1
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode/types.py +21 -7
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode.egg-info/PKG-INFO +1 -1
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode.egg-info/SOURCES.txt +5 -0
- optio_opencode-0.1.6/tests/test_host_actions.py +196 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_prompt.py +11 -0
- optio_opencode-0.1.6/tests/test_purge_seed.py +56 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_sanity.py +20 -0
- optio_opencode-0.1.6/tests/test_seed_config.py +76 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_session_hooks.py +1 -1
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_session_local.py +2 -1
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_session_resume.py +2 -1
- optio_opencode-0.1.6/tests/test_session_seed.py +491 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_smart_install.py +24 -12
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/README.md +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/setup.cfg +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode/snapshots.py +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode.egg-info/dependency_links.txt +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode.egg-info/requires.txt +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/src/optio_opencode.egg-info/top_level.txt +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_host_local.py +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_host_primitives_local.py +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_host_primitives_remote.py +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_host_remote_resume.py +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_host_resume.py +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_session_blob_hooks.py +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_session_remote.py +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_snapshots.py +0 -0
- {optio_opencode-0.1.4 → optio_opencode-0.1.6}/tests/test_types.py +0 -0
|
@@ -14,6 +14,13 @@ from optio_opencode.types import (
|
|
|
14
14
|
HookCallback,
|
|
15
15
|
OpencodeTaskConfig,
|
|
16
16
|
)
|
|
17
|
+
from optio_opencode.seed_manifest import (
|
|
18
|
+
OPENCODE_SEED_MANIFEST,
|
|
19
|
+
OPENCODE_SEED_SUFFIX,
|
|
20
|
+
delete_seed,
|
|
21
|
+
list_seeds,
|
|
22
|
+
purge_seed,
|
|
23
|
+
)
|
|
17
24
|
|
|
18
25
|
# asyncssh emits per-connection / per-channel INFO lines ("Opening SSH
|
|
19
26
|
# connection...", "Received channel close", etc.) that flood the worker's
|
|
@@ -34,4 +41,9 @@ __all__ = [
|
|
|
34
41
|
"HostCommandError",
|
|
35
42
|
"RunResult",
|
|
36
43
|
"HookCallback",
|
|
44
|
+
"OPENCODE_SEED_MANIFEST",
|
|
45
|
+
"OPENCODE_SEED_SUFFIX",
|
|
46
|
+
"delete_seed",
|
|
47
|
+
"list_seeds",
|
|
48
|
+
"purge_seed",
|
|
37
49
|
]
|
|
@@ -9,8 +9,10 @@ Install path is uniform: ``ensure_opencode_installed`` drives
|
|
|
9
9
|
csillag/opencode's ``smart-install.sh --check`` and, when needed, downloads
|
|
10
10
|
the release zip as an optio child task (`HookContext.download_file`),
|
|
11
11
|
unpacks it on the host, and places the binary at
|
|
12
|
-
``<install_dir>/opencode``. ``install_dir`` defaults to
|
|
13
|
-
|
|
12
|
+
``<install_dir>/opencode``. ``install_dir`` defaults to the optio-owned
|
|
13
|
+
binary cache on the worker
|
|
14
|
+
(``OPENCODE_CACHE_DIR`` / ``${XDG_CACHE_HOME:-$HOME/.cache}/optio-opencode/bin``,
|
|
15
|
+
resolved per host — never the host home bin dir) and is overridable via the
|
|
14
16
|
``install_dir`` keyword argument on the public entry points; consumers
|
|
15
17
|
expose this as ``OpencodeTaskConfig.opencode_install_dir``.
|
|
16
18
|
No isinstance branches.
|
|
@@ -36,23 +38,53 @@ _SMART_INSTALL_URL = (
|
|
|
36
38
|
"https://raw.githubusercontent.com/csillag/opencode/main/smart-install.sh"
|
|
37
39
|
)
|
|
38
40
|
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
# install
|
|
44
|
-
|
|
41
|
+
# The optio-owned opencode binary cache lives on the WORKER, never in the host
|
|
42
|
+
# user's home bin dir. Default cache:
|
|
43
|
+
# ``${XDG_CACHE_HOME:-$HOME/.cache}/optio-opencode/bin``, overridable via the
|
|
44
|
+
# ``OPENCODE_CACHE_DIR`` env var on the worker. Kept as a constant so the places
|
|
45
|
+
# that care about it (smart-install PATH augmentation, post-ok ``command -v``
|
|
46
|
+
# lookup, ``_install_opencode_from_zip`` install target) stay in agreement.
|
|
47
|
+
_OPENCODE_CACHE_SHELL_DEFAULT = (
|
|
48
|
+
'${OPENCODE_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/optio-opencode/bin}'
|
|
49
|
+
)
|
|
45
50
|
|
|
46
51
|
|
|
47
52
|
async def _resolve_install_dir(host: "Host", install_dir: str | None) -> str:
|
|
48
|
-
"""
|
|
53
|
+
"""Resolve the opencode binary-cache dir as an absolute path on the worker.
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
``install_dir`` (config.opencode_install_dir) overrides. Else the worker's
|
|
56
|
+
OPENCODE_CACHE_DIR / XDG_CACHE_HOME / $HOME decide it — resolved via a shell
|
|
57
|
+
echo so RemoteHost gets the remote cache. Resolved from the worker's REAL env
|
|
58
|
+
(this runs before per-task XDG isolation), so the cache stays shared and
|
|
59
|
+
outside any workdir → never snapshotted; evictable → smart-install re-downloads."""
|
|
52
60
|
if install_dir is not None:
|
|
53
|
-
return install_dir
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
return install_dir.rstrip("/")
|
|
62
|
+
r = await host.run_command(f'printf %s "{_OPENCODE_CACHE_SHELL_DEFAULT}"')
|
|
63
|
+
path = r.stdout.strip()
|
|
64
|
+
if r.exit_code != 0 or not path:
|
|
65
|
+
raise RuntimeError(
|
|
66
|
+
f"failed to resolve opencode cache dir on host (exit {r.exit_code}): "
|
|
67
|
+
f"{r.stderr.strip()[:200]}"
|
|
68
|
+
)
|
|
69
|
+
return path.rstrip("/")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _isolation_env(host: "Host") -> dict[str, str]:
|
|
73
|
+
"""Per-task HOME/XDG isolation env, derived from ``host.workdir``.
|
|
74
|
+
|
|
75
|
+
Merged into the launch env AND the export/import env so opencode's
|
|
76
|
+
auth/config/data go per-task under ``<workdir>/home`` (where the seed
|
|
77
|
+
manifest's ``home_subdir`` and merge/capture target). Distinct from the
|
|
78
|
+
binary cache (``_resolve_install_dir``), which is shared and resolved from
|
|
79
|
+
the worker's REAL env before this isolation applies.
|
|
80
|
+
"""
|
|
81
|
+
home = f"{host.workdir.rstrip('/')}/home"
|
|
82
|
+
return {
|
|
83
|
+
"HOME": home,
|
|
84
|
+
"XDG_CONFIG_HOME": f"{home}/.config",
|
|
85
|
+
"XDG_DATA_HOME": f"{home}/.local/share",
|
|
86
|
+
"XDG_CACHE_HOME": f"{home}/.cache",
|
|
87
|
+
}
|
|
56
88
|
|
|
57
89
|
|
|
58
90
|
def _path_augmented(cmd: str, install_dir: str) -> str:
|
|
@@ -61,9 +93,9 @@ def _path_augmented(cmd: str, install_dir: str) -> str:
|
|
|
61
93
|
Used so smart-install's internal ``command -v opencode`` and the
|
|
62
94
|
post-"ok" lookup find the binary at the install location even when
|
|
63
95
|
the calling shell's PATH doesn't already include it (common: the
|
|
64
|
-
python process inherits a slimmed-down PATH that doesn't add
|
|
65
|
-
|
|
66
|
-
and we'd reinstall on every task run).
|
|
96
|
+
python process inherits a slimmed-down PATH that doesn't add the
|
|
97
|
+
optio-owned opencode binary cache dir, so smart-install would falsely
|
|
98
|
+
report "download" and we'd reinstall on every task run).
|
|
67
99
|
"""
|
|
68
100
|
return f'export PATH={shlex.quote(install_dir)}:"$PATH"; {cmd}'
|
|
69
101
|
|
|
@@ -80,8 +112,9 @@ async def _smart_install_check(
|
|
|
80
112
|
|
|
81
113
|
``install_dir`` is prepended to PATH inside the remote shell so
|
|
82
114
|
smart-install's internal ``command -v opencode`` can see binaries
|
|
83
|
-
installed by a prior ``_install_opencode_from_zip``. Defaults to
|
|
84
|
-
|
|
115
|
+
installed by a prior ``_install_opencode_from_zip``. Defaults to the
|
|
116
|
+
optio-owned opencode binary cache on the worker (see
|
|
117
|
+
``_resolve_install_dir``).
|
|
85
118
|
|
|
86
119
|
Raises RuntimeError on non-zero exit or unparseable output.
|
|
87
120
|
"""
|
|
@@ -124,7 +157,8 @@ async def _install_opencode_from_zip(
|
|
|
124
157
|
4. mkdir -p ``install_dir``; move binary there; chmod +x.
|
|
125
158
|
5. Remove the tempdir.
|
|
126
159
|
|
|
127
|
-
``install_dir`` defaults to
|
|
160
|
+
``install_dir`` defaults to the optio-owned opencode binary cache on
|
|
161
|
+
the worker when None (see ``_resolve_install_dir``).
|
|
128
162
|
|
|
129
163
|
Returns the absolute install path on the host.
|
|
130
164
|
"""
|
|
@@ -194,7 +228,9 @@ async def ensure_opencode_installed(
|
|
|
194
228
|
|
|
195
229
|
``install_dir`` is the absolute path of the directory that holds (or
|
|
196
230
|
will hold) the ``opencode`` binary on the host. When None (default),
|
|
197
|
-
resolves to
|
|
231
|
+
resolves to the optio-owned binary cache on the worker
|
|
232
|
+
(``OPENCODE_CACHE_DIR`` / ``${XDG_CACHE_HOME:-$HOME/.cache}/optio-opencode/bin``;
|
|
233
|
+
see ``_resolve_install_dir``). Pass an explicit absolute path
|
|
198
234
|
to opt out of the default — the same dir is used for installation, for
|
|
199
235
|
smart-install's PATH lookup, and for the post-"ok" ``command -v``
|
|
200
236
|
resolution, so all three stay in agreement.
|
|
@@ -278,7 +314,7 @@ async def opencode_import(
|
|
|
278
314
|
try:
|
|
279
315
|
result = await host.run_command(
|
|
280
316
|
f"bash -lc {shlex.quote(opencode_executable + ' import ' + shlex.quote(scratch))}",
|
|
281
|
-
env={"OPENCODE_DB": opencode_db_path},
|
|
317
|
+
env={**_isolation_env(host), "OPENCODE_DB": opencode_db_path},
|
|
282
318
|
)
|
|
283
319
|
if result.exit_code != 0:
|
|
284
320
|
raise RuntimeError(
|
|
@@ -310,7 +346,7 @@ async def opencode_export(
|
|
|
310
346
|
result = await host.run_command(
|
|
311
347
|
f"bash -lc "
|
|
312
348
|
f"{shlex.quote(opencode_executable + ' export ' + shlex.quote(session_id) + ' > ' + shlex.quote(scratch))}",
|
|
313
|
-
env={"OPENCODE_DB": opencode_db_path},
|
|
349
|
+
env={**_isolation_env(host), "OPENCODE_DB": opencode_db_path},
|
|
314
350
|
)
|
|
315
351
|
if result.exit_code != 0:
|
|
316
352
|
raise RuntimeError(
|
|
@@ -329,6 +365,7 @@ async def launch_opencode(
|
|
|
329
365
|
ready_timeout_s: float = 30.0,
|
|
330
366
|
opencode_executable: str = "opencode",
|
|
331
367
|
hostname: str = "127.0.0.1",
|
|
368
|
+
extra_env: dict[str, str] | None = None,
|
|
332
369
|
) -> tuple[ProcessHandle, int]:
|
|
333
370
|
"""Launch ``opencode web`` on ``host``; wait for the listening URL.
|
|
334
371
|
|
|
@@ -336,10 +373,10 @@ async def launch_opencode(
|
|
|
336
373
|
and references it via ``$(cat ...)`` in the launch command so the
|
|
337
374
|
literal value never appears on the remote process's argv.
|
|
338
375
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
376
|
+
Browser-open suppression is handled by the optio-agents protocol
|
|
377
|
+
driver (``get_protocol(browser="suppress")``), which installs no-op
|
|
378
|
+
opener stubs under ``<workdir>/bin`` and returns the ``BROWSER`` /
|
|
379
|
+
``PATH`` env additions; the caller passes those in via ``extra_env``.
|
|
343
380
|
|
|
344
381
|
``hostname`` is passed to ``opencode web --hostname=`` so callers
|
|
345
382
|
can bind to a non-loopback interface when consumers reach the server
|
|
@@ -354,48 +391,38 @@ async def launch_opencode(
|
|
|
354
391
|
await host.write_text(pw_file, password)
|
|
355
392
|
await host.run_command(f"chmod 600 {host.workdir}/{pw_file}")
|
|
356
393
|
|
|
357
|
-
#
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if chmod_result.exit_code != 0:
|
|
362
|
-
# Non-fatal: the noop scripts may fail to be executable, but worst
|
|
363
|
-
# case opencode tries to open a browser and we just live with it.
|
|
364
|
-
pass
|
|
365
|
-
|
|
366
|
-
# Build cmd: read password from file via $(cat), set BROWSER=true,
|
|
367
|
-
# cd to workdir so opencode picks up opencode.json.
|
|
394
|
+
# Build cmd: read password from file via $(cat), cd to workdir so
|
|
395
|
+
# opencode picks up opencode.json. Browser suppression (the BROWSER
|
|
396
|
+
# env + the <workdir>/bin PATH prepend that shadows the openers) comes
|
|
397
|
+
# from the protocol driver's suppress shims, passed in via extra_env.
|
|
368
398
|
#
|
|
369
399
|
# NOTE: do NOT wrap in `bash -lc` / `bash -l`. A login shell sources
|
|
370
400
|
# the user's profile (~/.profile, ~/.bash_profile, /etc/profile),
|
|
371
401
|
# which on most Linux installs rewrites PATH from scratch and
|
|
372
|
-
# therefore wipes the workdir/bin prefix
|
|
373
|
-
# the prefix gone, the
|
|
374
|
-
#
|
|
375
|
-
#
|
|
376
|
-
#
|
|
377
|
-
# lookup is not needed to find the binary. Let LocalHost / RemoteHost
|
|
378
|
-
# launch_subprocess do the shell wrapping; we just need the env-var
|
|
379
|
-
# prefix and $(cat ...) substitution, which any POSIX sh handles.
|
|
402
|
+
# therefore wipes the workdir/bin prefix carried in `env` below. With
|
|
403
|
+
# the prefix gone, the suppress stubs stop hiding the real openers and
|
|
404
|
+
# opencode succeeds at opening a real browser window. opencode_executable
|
|
405
|
+
# is an absolute path (resolved by ensure_opencode_installed), so
|
|
406
|
+
# login-shell PATH lookup is not needed to find the binary.
|
|
380
407
|
cmd = (
|
|
381
408
|
f"exec env "
|
|
382
409
|
f"OPENCODE_SERVER_PASSWORD=\"$(cat {shlex.quote(host.workdir + '/' + pw_file)})\" "
|
|
383
|
-
f"BROWSER=true "
|
|
384
410
|
f"{opencode_executable} web --port=0 --hostname={shlex.quote(hostname)}"
|
|
385
411
|
)
|
|
386
412
|
|
|
387
|
-
# Prepend the noop-browsers bin dir to PATH via env on launch_subprocess.
|
|
388
|
-
workdir_bin = f"{host.workdir}/bin"
|
|
389
|
-
extra_path = workdir_bin + ":" + os.environ.get("PATH", "/usr/local/bin:/usr/bin:/bin")
|
|
390
413
|
# OPENCODE_DB must point at the same per-task db file used by the
|
|
391
414
|
# subsequent export/import CLI calls. Without this, the server falls
|
|
392
415
|
# back to opencode's global default db while export/import target the
|
|
393
416
|
# taskdir-local file — causing snapshot capture to "Session not found"
|
|
394
417
|
# against an empty file. Convention: opencode.db is a sibling of the
|
|
395
418
|
# workdir under taskdir (session.py: opencode_db = f"{taskdir}/opencode.db").
|
|
419
|
+
# The browser-suppression env (PATH prepend + BROWSER) comes from extra_env.
|
|
420
|
+
# The HOME/XDG isolation env (from _isolation_env) points opencode's
|
|
421
|
+
# auth/config/data at <workdir>/home so per-task seeding works.
|
|
396
422
|
env = {
|
|
397
|
-
|
|
423
|
+
**_isolation_env(host),
|
|
398
424
|
"OPENCODE_DB": f"{host.taskdir}/opencode.db",
|
|
425
|
+
**(extra_env or {}),
|
|
399
426
|
}
|
|
400
427
|
|
|
401
428
|
handle = await host.launch_subprocess(cmd, env=env, cwd=host.workdir)
|
|
@@ -7,7 +7,7 @@ addressed. The consumer's own task description is then appended verbatim.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
from optio_agents.protocol
|
|
10
|
+
from optio_agents.protocol import build_log_channel_prompt
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
_OPENCODE_INTRO = """# Coordination protocol with the host (optio-opencode)
|
|
@@ -18,9 +18,6 @@ throughout the session.
|
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
BASE_PROMPT_PRE = _OPENCODE_INTRO + LOG_CHANNEL_PROMPT
|
|
22
|
-
|
|
23
|
-
|
|
24
21
|
BASE_PROMPT_POST = """## Task
|
|
25
22
|
|
|
26
23
|
Here comes the description of your actual task to complete. Throughout
|
|
@@ -120,6 +117,7 @@ def compose_agents_md(
|
|
|
120
117
|
consumer_instructions: str,
|
|
121
118
|
*,
|
|
122
119
|
workdir_exclude: list[str] | None,
|
|
120
|
+
documentation: str | None = None,
|
|
123
121
|
supports_resume: bool = True,
|
|
124
122
|
) -> str:
|
|
125
123
|
"""Build the full AGENTS.md body.
|
|
@@ -130,12 +128,18 @@ def compose_agents_md(
|
|
|
130
128
|
callers must pass it explicitly to prevent silent desync between
|
|
131
129
|
archive.py defaults and the prompt's claims about what's preserved.
|
|
132
130
|
Pass None to render with the framework defaults.
|
|
131
|
+
documentation: the keyword-protocol block; the session passes
|
|
132
|
+
``get_protocol(browser="suppress").documentation``. Defaults (for
|
|
133
|
+
unit tests / standalone callers) to opencode's ``suppress`` docs.
|
|
133
134
|
supports_resume: when False, the resume-detection section is omitted
|
|
134
135
|
from the prompt. Default True.
|
|
135
136
|
"""
|
|
137
|
+
if documentation is None:
|
|
138
|
+
documentation = build_log_channel_prompt("suppress")
|
|
139
|
+
base_prompt_pre = _OPENCODE_INTRO + documentation
|
|
136
140
|
body = consumer_instructions.rstrip()
|
|
137
141
|
if supports_resume:
|
|
138
142
|
resume_block = _render_resume_section(workdir_exclude) + "\n"
|
|
139
143
|
else:
|
|
140
144
|
resume_block = ""
|
|
141
|
-
return f"{
|
|
145
|
+
return f"{base_prompt_pre}\n{resume_block}{BASE_PROMPT_POST}\n{body}\n"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""opencode adopter of the generic optio-agents seed engine.
|
|
2
|
+
|
|
3
|
+
Defines the opencode seed manifest (HOME layout + capture-time include
|
|
4
|
+
triage), the Mongo collection suffix, and ergonomic `delete_seed` /
|
|
5
|
+
`list_seeds` / `purge_seed` wrappers that bind the suffix for consuming
|
|
6
|
+
apps.
|
|
7
|
+
|
|
8
|
+
Unlike claudecode, opencode needs no consume-time rekey: its auth/config
|
|
9
|
+
are cwd-independent, so `consume_transform` is None.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from optio_agents import seeds
|
|
15
|
+
|
|
16
|
+
OPENCODE_SEED_SUFFIX = "_opencode_seeds"
|
|
17
|
+
OPENCODE_SEED_MANIFEST_VERSION = 1
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
OPENCODE_SEED_MANIFEST = seeds.SeedManifest(
|
|
21
|
+
home_subdir="home",
|
|
22
|
+
include=[
|
|
23
|
+
".local/share/opencode/auth.json",
|
|
24
|
+
".config/opencode/opencode.json",
|
|
25
|
+
".config/opencode/plugins",
|
|
26
|
+
],
|
|
27
|
+
version=OPENCODE_SEED_MANIFEST_VERSION,
|
|
28
|
+
consume_transform=None, # no cwd-rekey for opencode
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def delete_seed(store, seed_id: str):
|
|
33
|
+
"""Delete an opencode seed doc; returns its GridFS blobId (or None).
|
|
34
|
+
|
|
35
|
+
Takes an optio store binding (``optio.store`` — exposes ``db`` and
|
|
36
|
+
``prefix``) as-is, so consuming apps hand over the whole namespace handle
|
|
37
|
+
instead of threading db+prefix (or knowing the collection suffix). The
|
|
38
|
+
caller still removes the returned blob from GridFS.
|
|
39
|
+
"""
|
|
40
|
+
return await seeds.delete_seed(
|
|
41
|
+
store.db, prefix=store.prefix, suffix=OPENCODE_SEED_SUFFIX, seed_id=seed_id,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def list_seeds(store) -> list[dict]:
|
|
46
|
+
"""List opencode seeds as [{seedId, createdAt}, ...]. Takes an optio store
|
|
47
|
+
binding (``optio.store``) as-is."""
|
|
48
|
+
return await seeds.list_seeds(store.db, prefix=store.prefix, suffix=OPENCODE_SEED_SUFFIX)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def purge_seed(store, seed_id: str):
|
|
52
|
+
"""Purge an opencode seed (doc + its GridFS blob) in one call.
|
|
53
|
+
|
|
54
|
+
Takes an optio store binding (``optio.store``) as-is, per the Shared-
|
|
55
|
+
contracts surface. Mirrors `optio_claudecode.purge_seed`; both are thin
|
|
56
|
+
re-exports of the `optio_agents.seeds.purge_seed` engine, which expunges
|
|
57
|
+
the seed doc and its GridFS blob and raises KeyError if absent.
|
|
58
|
+
"""
|
|
59
|
+
return await seeds.purge_seed(
|
|
60
|
+
store.db, prefix=store.prefix, suffix=OPENCODE_SEED_SUFFIX, seed_id=seed_id,
|
|
61
|
+
)
|