py-opencode-wrapper 0.2.1__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
- opencode_wrapper/__init__.py +2 -0
- opencode_wrapper/client.py +17 -6
- opencode_wrapper/config.py +4 -0
- opencode_wrapper/events.py +13 -0
- opencode_wrapper/session.py +90 -0
- {py_opencode_wrapper-0.2.1.dist-info → py_opencode_wrapper-0.3.0.dist-info}/METADATA +64 -18
- py_opencode_wrapper-0.3.0.dist-info/RECORD +11 -0
- py_opencode_wrapper-0.3.0.dist-info/licenses/LICENSE +21 -0
- py_opencode_wrapper-0.2.1.dist-info/RECORD +0 -9
- {py_opencode_wrapper-0.2.1.dist-info → py_opencode_wrapper-0.3.0.dist-info}/WHEEL +0 -0
- {py_opencode_wrapper-0.2.1.dist-info → py_opencode_wrapper-0.3.0.dist-info}/top_level.txt +0 -0
opencode_wrapper/__init__.py
CHANGED
|
@@ -16,9 +16,11 @@ from opencode_wrapper.events import (
|
|
|
16
16
|
parse_event_line,
|
|
17
17
|
run_result_fuzzy_text,
|
|
18
18
|
)
|
|
19
|
+
from opencode_wrapper.session import OpenCodeSession
|
|
19
20
|
|
|
20
21
|
__all__ = [
|
|
21
22
|
"AsyncOpenCodeClient",
|
|
23
|
+
"OpenCodeSession",
|
|
22
24
|
"RunConfig",
|
|
23
25
|
"RunResult",
|
|
24
26
|
"TokenUsage",
|
opencode_wrapper/client.py
CHANGED
|
@@ -73,7 +73,7 @@ def build_argv(
|
|
|
73
73
|
cmd.extend(["--port", str(run_cfg.port)])
|
|
74
74
|
if run_cfg.variant:
|
|
75
75
|
cmd.extend(["--variant", run_cfg.variant])
|
|
76
|
-
if run_cfg.thinking is True:
|
|
76
|
+
if run_cfg.record_thinking is True or run_cfg.thinking is True:
|
|
77
77
|
cmd.append("--thinking")
|
|
78
78
|
|
|
79
79
|
if prompt:
|
|
@@ -313,15 +313,23 @@ class AsyncOpenCodeClient:
|
|
|
313
313
|
cwd: str,
|
|
314
314
|
env: dict[str, str],
|
|
315
315
|
run_cfg: RunConfig,
|
|
316
|
+
data_home: str | None = None,
|
|
316
317
|
) -> AsyncIterator[tuple[asyncio.subprocess.Process, list[str]]]:
|
|
317
318
|
stderr_lines: list[str] = []
|
|
318
319
|
cleanup_tmpdirs: list[str] = []
|
|
319
320
|
# Give each process its own XDG_DATA_HOME so opencode.db is isolated.
|
|
320
321
|
# Without this, all concurrent processes share ~/.local/share/opencode/opencode.db
|
|
321
322
|
# and SQLite write locks during tool execution serialize the runs (37–46s delays).
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
323
|
+
# When *data_home* is provided the caller owns a persistent dir (e.g. an
|
|
324
|
+
# OpenCodeSession reusing one DB across turns), so it is NOT added to
|
|
325
|
+
# cleanup_tmpdirs — the caller deletes it when done.
|
|
326
|
+
managed = data_home is not None
|
|
327
|
+
if self._isolate_db or managed:
|
|
328
|
+
if managed:
|
|
329
|
+
xdg_tmpdir = data_home # type: ignore[assignment]
|
|
330
|
+
else:
|
|
331
|
+
xdg_tmpdir = tempfile.mkdtemp(prefix="oc_xdg_")
|
|
332
|
+
cleanup_tmpdirs.append(xdg_tmpdir)
|
|
325
333
|
# Symlink auth.json so provider API keys (stored by `opencode auth`)
|
|
326
334
|
# are visible in the isolated data dir. Without this, providers
|
|
327
335
|
# that rely on auth.json (rather than env-var keys) fail with
|
|
@@ -331,7 +339,9 @@ class AsyncOpenCodeClient:
|
|
|
331
339
|
if real_auth.is_file():
|
|
332
340
|
iso_oc_dir = Path(xdg_tmpdir) / "opencode"
|
|
333
341
|
iso_oc_dir.mkdir(parents=True, exist_ok=True)
|
|
334
|
-
|
|
342
|
+
link = iso_oc_dir / "auth.json"
|
|
343
|
+
if not link.exists(): # reused across turns — guard re-symlink
|
|
344
|
+
link.symlink_to(real_auth)
|
|
335
345
|
env = {**env, "XDG_DATA_HOME": xdg_tmpdir}
|
|
336
346
|
# When the caller has not opted into host-config inheritance (the
|
|
337
347
|
# default), redirect XDG_CONFIG_HOME / OPENCODE_TEST_HOME at a sanitized
|
|
@@ -425,6 +435,7 @@ class AsyncOpenCodeClient:
|
|
|
425
435
|
log_file: str | Path | None = None,
|
|
426
436
|
max_retries: int = 2,
|
|
427
437
|
retry_delay_s: float = 1.0,
|
|
438
|
+
data_home: str | None = None,
|
|
428
439
|
) -> RunResult:
|
|
429
440
|
"""
|
|
430
441
|
Run to completion and return a :class:`RunResult`.
|
|
@@ -457,7 +468,7 @@ class AsyncOpenCodeClient:
|
|
|
457
468
|
|
|
458
469
|
log_fh = open(log_file, "w") if log_file is not None else None
|
|
459
470
|
try:
|
|
460
|
-
async with self._managed_process(argv, cwd, env, run_cfg) as (proc, stderr_lines):
|
|
471
|
+
async with self._managed_process(argv, cwd, env, run_cfg, data_home=data_home) as (proc, stderr_lines):
|
|
461
472
|
async for line, ev in _stdout_line_event_iter(proc):
|
|
462
473
|
raw_acc.append(line)
|
|
463
474
|
events_acc.append(ev)
|
opencode_wrapper/config.py
CHANGED
|
@@ -147,6 +147,10 @@ class RunConfig:
|
|
|
147
147
|
remote_dir: str | None = None
|
|
148
148
|
port: int | None = None
|
|
149
149
|
variant: str | None = None
|
|
150
|
+
# Include OpenCode reasoning/thinking parts in the JSON event stream.
|
|
151
|
+
# This maps to `opencode run --thinking`; it does not set model reasoning effort.
|
|
152
|
+
record_thinking: bool | None = None
|
|
153
|
+
# Backward-compatible alias for record_thinking.
|
|
150
154
|
thinking: bool | None = None
|
|
151
155
|
print_logs: bool | None = None
|
|
152
156
|
log_level: str | None = None
|
opencode_wrapper/events.py
CHANGED
|
@@ -70,6 +70,16 @@ def _text_from_event(ev: dict[str, Any]) -> str | None:
|
|
|
70
70
|
return None
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
def _session_id_from_event(ev: dict[str, Any]) -> str | None:
|
|
74
|
+
sid = ev.get("sessionID")
|
|
75
|
+
if isinstance(sid, str):
|
|
76
|
+
return sid
|
|
77
|
+
part = ev.get("part")
|
|
78
|
+
if isinstance(part, dict) and isinstance(part.get("sessionID"), str):
|
|
79
|
+
return part["sessionID"]
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
|
|
73
83
|
def run_result_fuzzy_text(result: "RunResult") -> str:
|
|
74
84
|
"""
|
|
75
85
|
Best-effort extract human-visible model output across varying ``--format json`` shapes.
|
|
@@ -136,9 +146,12 @@ class RunResult:
|
|
|
136
146
|
token_usage: TokenUsage = field(default_factory=TokenUsage)
|
|
137
147
|
total_cost: float = 0.0
|
|
138
148
|
turns: int = 0
|
|
149
|
+
session_id: str | None = None
|
|
139
150
|
|
|
140
151
|
def append_event(self, ev: dict[str, Any]) -> None:
|
|
141
152
|
self.events.append(ev)
|
|
153
|
+
if self.session_id is None:
|
|
154
|
+
self.session_id = _session_id_from_event(ev)
|
|
142
155
|
chunk = _text_from_event(ev)
|
|
143
156
|
if chunk:
|
|
144
157
|
self.final_text += chunk
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Stateful multi-turn conversation over a single opencode session."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import dataclasses
|
|
6
|
+
import shutil
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from opencode_wrapper.config import RunConfig
|
|
12
|
+
from opencode_wrapper.events import RunResult
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from opencode_wrapper.client import AsyncOpenCodeClient
|
|
16
|
+
|
|
17
|
+
_UNSET: object = object()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OpenCodeSession:
|
|
21
|
+
"""Multi-turn conversation backed by one persistent opencode session.
|
|
22
|
+
|
|
23
|
+
On ``__aenter__`` the session allocates a private ``XDG_DATA_HOME`` tmpdir.
|
|
24
|
+
Every :meth:`send` reuses it, so opencode's SQLite session DB survives across
|
|
25
|
+
turns — the first turn creates the session, later turns continue it via
|
|
26
|
+
``--session <id>``. The dir is a per-session island (no shared global DB, so
|
|
27
|
+
no cross-session lock contention) and is removed on ``__aexit__``.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
client:
|
|
32
|
+
The :class:`AsyncOpenCodeClient` used to spawn each turn.
|
|
33
|
+
workspace:
|
|
34
|
+
Project directory passed to every ``opencode run``.
|
|
35
|
+
run_cfg:
|
|
36
|
+
Base config applied to each turn; ``session_id`` is injected automatically
|
|
37
|
+
after the first turn. A per-call override may be passed to :meth:`send`.
|
|
38
|
+
timeout_s:
|
|
39
|
+
Default per-turn timeout; overridable per :meth:`send` call.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
client: "AsyncOpenCodeClient",
|
|
45
|
+
workspace: str | Path,
|
|
46
|
+
*,
|
|
47
|
+
run_cfg: RunConfig | None = None,
|
|
48
|
+
timeout_s: float | None = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
self._client = client
|
|
51
|
+
self._workspace = workspace
|
|
52
|
+
self._base_cfg = run_cfg or RunConfig()
|
|
53
|
+
self._timeout_s = timeout_s
|
|
54
|
+
self._data_home: str | None = None
|
|
55
|
+
self.session_id: str | None = None
|
|
56
|
+
|
|
57
|
+
async def __aenter__(self) -> "OpenCodeSession":
|
|
58
|
+
self._data_home = tempfile.mkdtemp(prefix="oc_session_")
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
async def __aexit__(self, *exc: object) -> None:
|
|
62
|
+
if self._data_home is not None:
|
|
63
|
+
shutil.rmtree(self._data_home, ignore_errors=True)
|
|
64
|
+
self._data_home = None
|
|
65
|
+
|
|
66
|
+
async def send(
|
|
67
|
+
self,
|
|
68
|
+
prompt: str,
|
|
69
|
+
*,
|
|
70
|
+
run_cfg: RunConfig | None = None,
|
|
71
|
+
timeout_s: float | object = _UNSET,
|
|
72
|
+
) -> RunResult:
|
|
73
|
+
"""Run one turn and return its :class:`RunResult`, continuing the session."""
|
|
74
|
+
if self._data_home is None:
|
|
75
|
+
raise RuntimeError(
|
|
76
|
+
"OpenCodeSession.send() must be called inside 'async with'"
|
|
77
|
+
)
|
|
78
|
+
cfg = run_cfg or self._base_cfg
|
|
79
|
+
if self.session_id:
|
|
80
|
+
cfg = dataclasses.replace(cfg, session_id=self.session_id)
|
|
81
|
+
result = await self._client.async_run(
|
|
82
|
+
prompt,
|
|
83
|
+
self._workspace,
|
|
84
|
+
run_cfg=cfg,
|
|
85
|
+
timeout_s=self._timeout_s if timeout_s is _UNSET else timeout_s, # type: ignore[arg-type]
|
|
86
|
+
data_home=self._data_home,
|
|
87
|
+
)
|
|
88
|
+
if self.session_id is None and result.session_id:
|
|
89
|
+
self.session_id = result.session_id
|
|
90
|
+
return result
|
|
@@ -1,26 +1,42 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-opencode-wrapper
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Async Python wrapper for OpenCode CLI (opencode run --format json)
|
|
5
5
|
Project-URL: Homepage, https://github.com/idailylife/oc_py_wrapper
|
|
6
6
|
Project-URL: Repository, https://github.com/idailylife/oc_py_wrapper
|
|
7
7
|
Project-URL: Issues, https://github.com/idailylife/oc_py_wrapper/issues
|
|
8
8
|
Requires-Python: >=3.8
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
10
11
|
Provides-Extra: dev
|
|
11
12
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
12
13
|
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
|
|
14
|
+
Dynamic: license-file
|
|
13
15
|
|
|
14
|
-
#
|
|
16
|
+
# py-opencode-wrapper
|
|
15
17
|
|
|
16
18
|
Python **async** wrapper around the [OpenCode](https://opencode.ai/docs/) CLI (`opencode run --format json`). Intended as a subprocess-based executor for **multi-agent workflow** orchestration.
|
|
17
19
|
|
|
18
20
|
## Requirements
|
|
19
21
|
|
|
20
|
-
- Python 3.
|
|
22
|
+
- Python 3.8+
|
|
21
23
|
- `opencode` on `PATH` (or pass an absolute path to the binary)
|
|
22
24
|
|
|
23
|
-
## Install
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
From PyPI (most users):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install py-opencode-wrapper
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The distribution name on PyPI is `py-opencode-wrapper`; import it as `opencode_wrapper`:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from opencode_wrapper import AsyncOpenCodeClient, RunConfig
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For local development (editable install with test deps):
|
|
24
40
|
|
|
25
41
|
```bash
|
|
26
42
|
pip install -e ".[dev]"
|
|
@@ -39,7 +55,7 @@ from opencode_wrapper import AsyncOpenCodeClient, RunConfig
|
|
|
39
55
|
async def main():
|
|
40
56
|
client = AsyncOpenCodeClient("opencode")
|
|
41
57
|
cfg = RunConfig(
|
|
42
|
-
model="
|
|
58
|
+
model="opencode/big-pickle",
|
|
43
59
|
agent="plan",
|
|
44
60
|
permission={"bash": "deny", "edit": "deny"},
|
|
45
61
|
mcp={
|
|
@@ -61,6 +77,40 @@ async def main():
|
|
|
61
77
|
asyncio.run(main())
|
|
62
78
|
```
|
|
63
79
|
|
|
80
|
+
Set `RunConfig(record_thinking=True)` when you want OpenCode reasoning/thinking
|
|
81
|
+
parts included in `result.events` and `log_file` JSON lines. This only maps to
|
|
82
|
+
OpenCode's display/output flag `--thinking`; it does not change model reasoning
|
|
83
|
+
effort. Use `variant` separately if you intentionally want a provider-specific
|
|
84
|
+
reasoning effort.
|
|
85
|
+
|
|
86
|
+
### Multi-turn conversation (`OpenCodeSession`)
|
|
87
|
+
|
|
88
|
+
For a stateful, multi-turn chat over a single opencode session, use
|
|
89
|
+
`OpenCodeSession` as an async context manager. Each `send()` continues the same
|
|
90
|
+
session, so the model retains context across turns:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import asyncio
|
|
94
|
+
from opencode_wrapper import AsyncOpenCodeClient, OpenCodeSession, RunConfig
|
|
95
|
+
|
|
96
|
+
async def chat():
|
|
97
|
+
client = AsyncOpenCodeClient()
|
|
98
|
+
async with OpenCodeSession(client, ".", run_cfg=RunConfig(model="opencode/big-pickle")) as s:
|
|
99
|
+
r1 = await s.send("My name is Bob.")
|
|
100
|
+
r2 = await s.send("What is my name?") # auto-continues → "Bob"
|
|
101
|
+
print(s.session_id, r2.final_text)
|
|
102
|
+
|
|
103
|
+
asyncio.run(chat())
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
On enter, the session allocates a private, persistent `XDG_DATA_HOME` tmpdir that
|
|
107
|
+
every turn reuses, so opencode's SQLite session DB survives across turns. The
|
|
108
|
+
first `send()` creates the session (its id is captured on `RunResult.session_id`);
|
|
109
|
+
later turns continue it via `--session <id>`. Each session is an isolated island —
|
|
110
|
+
no shared global DB, so no cross-session lock contention — and the tmpdir is
|
|
111
|
+
removed when the `async with` block exits. `send()` accepts per-turn `run_cfg` and
|
|
112
|
+
`timeout_s` overrides.
|
|
113
|
+
|
|
64
114
|
### Stream structured JSON events
|
|
65
115
|
|
|
66
116
|
```python
|
|
@@ -75,11 +125,7 @@ async def stream_example():
|
|
|
75
125
|
|
|
76
126
|
```python
|
|
77
127
|
async def multi():
|
|
78
|
-
|
|
79
|
-
# WAL-pragma race in opencode when many instances start simultaneously.
|
|
80
|
-
# startup_delay_s controls how long each slot is held before the next
|
|
81
|
-
# process is allowed to start (default 0.3 s).
|
|
82
|
-
client = AsyncOpenCodeClient(startup_concurrency=1, startup_delay_s=0.3)
|
|
128
|
+
client = AsyncOpenCodeClient()
|
|
83
129
|
ws = Path("/path/to/monorepo")
|
|
84
130
|
results = await asyncio.gather(*[
|
|
85
131
|
client.async_run(
|
|
@@ -87,14 +133,14 @@ async def multi():
|
|
|
87
133
|
ws / "services" / svc,
|
|
88
134
|
run_cfg=RunConfig(agent="explore"),
|
|
89
135
|
timeout_s=600,
|
|
90
|
-
# max_retries=2 (default): retry automatically if opencode crashes
|
|
91
|
-
# during SQLite startup before giving up.
|
|
92
136
|
)
|
|
93
137
|
for svc in ["api", "worker", "gateway"]
|
|
94
138
|
])
|
|
95
139
|
return results
|
|
96
140
|
```
|
|
97
141
|
|
|
142
|
+
Safe defaults for parallel runs (startup serialisation, private SQLite DB per run, and automatic retry on SQLite-startup crashes) are enabled out of the box — most users don't need to tune them. See *Concurrency notes* below if you want to.
|
|
143
|
+
|
|
98
144
|
## Configuration injection
|
|
99
145
|
|
|
100
146
|
Per-call JSON is merged and passed as `OPENCODE_CONFIG_CONTENT` (see [OpenCode config](https://opencode.ai/docs/config/)). Use `RunConfig` fields:
|
|
@@ -166,15 +212,15 @@ Default `pytest -q` runs **all** tests; use `-m "not integration"` in CI without
|
|
|
166
212
|
|
|
167
213
|
## Concurrency notes
|
|
168
214
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
**Startup serialisation** — `startup_concurrency=1` and `startup_delay_s=0.3` limit how many processes enter SQLite startup at once, reducing WAL-initialisation race crashes.
|
|
215
|
+
The defaults already handle the common pitfalls when running many `async_run` calls in parallel — you usually don't need to touch any of these.
|
|
172
216
|
|
|
173
|
-
**
|
|
217
|
+
- **Startup serialisation** (`startup_concurrency=1`, `startup_delay_s=0.3`) — spaces out SQLite WAL initialisation across processes to avoid a startup race in `opencode`.
|
|
218
|
+
- **DB isolation** (`isolate_db=True`) — each run gets its own `XDG_DATA_HOME`, so concurrent runs don't share `opencode.db` and serialise on SQLite write locks during tool execution.
|
|
219
|
+
- **Automatic retry** (`async_run(max_retries=2, retry_delay_s=1.0)`) — retries known SQLite-startup crashes with short backoff. Non-SQLite failures still fail fast.
|
|
174
220
|
|
|
175
|
-
|
|
221
|
+
To opt out: pass `startup_delay_s=0` (and a large `startup_concurrency`) to drop the startup pacing, `isolate_db=False` to share session history across runs, and `max_retries=0` to disable retries.
|
|
176
222
|
|
|
177
|
-
|
|
223
|
+
> For **multi-turn conversations** you don't need `isolate_db=False`. Use [`OpenCodeSession`](#multi-turn-conversation-opencodesession) instead — it keeps one session's DB alive across turns in a private dir, so context is preserved without sharing the global DB.
|
|
178
224
|
|
|
179
225
|
## Notes
|
|
180
226
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
opencode_wrapper/__init__.py,sha256=zX72bGTjtcDDm-lL3JZHj-IiVAHjsFO7uldQ6Up7w3U,1089
|
|
2
|
+
opencode_wrapper/client.py,sha256=T-L9Ubz1FtIfueix9fbg_G7cTs4tWhvLd6xKP58JHrk,20180
|
|
3
|
+
opencode_wrapper/config.py,sha256=TGQ85vd06qQPbP02G6YG9re-rESpYHfhAJ8PaAUqHRg,8076
|
|
4
|
+
opencode_wrapper/errors.py,sha256=zaXzzFb6ObdrNlm-PJE_7tbgvMEhoZcbeQVWNHNTkUQ,1168
|
|
5
|
+
opencode_wrapper/events.py,sha256=x6KcHXB8vLHB5mesMkGxx5QxiaPQOfI-ponwAufcnlg,6896
|
|
6
|
+
opencode_wrapper/session.py,sha256=_weqUbbs2ul5568b086JEBqK7Ni2ehwc6p6-nQFuz1g,3096
|
|
7
|
+
py_opencode_wrapper-0.3.0.dist-info/licenses/LICENSE,sha256=W9SvvGfo_x1O5jNp-vV1NzRrMGB5C6sjHnQDnGm73qg,1067
|
|
8
|
+
py_opencode_wrapper-0.3.0.dist-info/METADATA,sha256=AQoxbJpt1ghHtsxmjXhsQwD3VX7v9hext_uy5dLflDQ,9001
|
|
9
|
+
py_opencode_wrapper-0.3.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
10
|
+
py_opencode_wrapper-0.3.0.dist-info/top_level.txt,sha256=8LETj5bPgl1YnB83iOiueuQvGryj3RzaeEQecPVS9Q8,17
|
|
11
|
+
py_opencode_wrapper-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 0x0000ffff
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
opencode_wrapper/__init__.py,sha256=iCkMcrh7P35jHFq8gH-GKjaKqwnvmOkGCLRfgnb0moE,1013
|
|
2
|
-
opencode_wrapper/client.py,sha256=Ny4pDBV6cToIOn2W7BgCFL1w12KdlVQVCInhddf_Df4,19546
|
|
3
|
-
opencode_wrapper/config.py,sha256=JraBPkX95GXFoOvn181BRv_8YPvsUDXiZMN1hZWhSf0,7823
|
|
4
|
-
opencode_wrapper/errors.py,sha256=zaXzzFb6ObdrNlm-PJE_7tbgvMEhoZcbeQVWNHNTkUQ,1168
|
|
5
|
-
opencode_wrapper/events.py,sha256=PHz04DcB0K0JfIRxgV8GQ3psl7VjQXZ25gIrDCOgAHQ,6478
|
|
6
|
-
py_opencode_wrapper-0.2.1.dist-info/METADATA,sha256=W56IyS4yoBlsSFIIrtsPx0-Ck6AoCCuhmdHiZslRgnE,6884
|
|
7
|
-
py_opencode_wrapper-0.2.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
8
|
-
py_opencode_wrapper-0.2.1.dist-info/top_level.txt,sha256=8LETj5bPgl1YnB83iOiueuQvGryj3RzaeEQecPVS9Q8,17
|
|
9
|
-
py_opencode_wrapper-0.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|