opencode-python-sdk 0.2.0__py3-none-any.whl → 0.2.3__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.
Files changed (45) hide show
  1. opencode/__init__.py +14 -0
  2. opencode/_aiohttp.py +44 -0
  3. opencode/_base_client.py +237 -11
  4. opencode/_client.py +115 -3
  5. opencode/_logs.py +29 -0
  6. opencode/_pagination.py +14 -0
  7. opencode/_response.py +316 -0
  8. opencode/_service.py +1018 -0
  9. opencode/resources/agent.py +63 -0
  10. opencode/resources/command.py +59 -0
  11. opencode/resources/config.py +93 -20
  12. opencode/resources/credential.py +149 -56
  13. opencode/resources/debug.py +156 -20
  14. opencode/resources/event.py +59 -0
  15. opencode/resources/experimental.py +210 -36
  16. opencode/resources/file.py +71 -0
  17. opencode/resources/form.py +59 -0
  18. opencode/resources/generate.py +59 -0
  19. opencode/resources/integration.py +538 -128
  20. opencode/resources/location.py +87 -18
  21. opencode/resources/mcp.py +252 -82
  22. opencode/resources/message.py +59 -0
  23. opencode/resources/migration.py +126 -0
  24. opencode/resources/model.py +63 -0
  25. opencode/resources/permission.py +272 -38
  26. opencode/resources/plugin.py +95 -22
  27. opencode/resources/project.py +63 -0
  28. opencode/resources/provider.py +63 -0
  29. opencode/resources/pty.py +172 -20
  30. opencode/resources/reference.py +59 -0
  31. opencode/resources/rpc.py +59 -0
  32. opencode/resources/server.py +67 -0
  33. opencode/resources/session.py +1159 -405
  34. opencode/resources/shell.py +101 -20
  35. opencode/resources/skill.py +59 -0
  36. opencode/resources/vcs.py +172 -20
  37. opencode/resources/voice.py +59 -0
  38. opencode/resources/websearch.py +63 -0
  39. opencode/resources/worktree.py +123 -40
  40. opencode/service.py +50 -0
  41. {opencode_python_sdk-0.2.0.dist-info → opencode_python_sdk-0.2.3.dist-info}/METADATA +111 -2
  42. opencode_python_sdk-0.2.3.dist-info/RECORD +67 -0
  43. opencode_python_sdk-0.2.0.dist-info/RECORD +0 -62
  44. {opencode_python_sdk-0.2.0.dist-info → opencode_python_sdk-0.2.3.dist-info}/WHEEL +0 -0
  45. {opencode_python_sdk-0.2.0.dist-info → opencode_python_sdk-0.2.3.dist-info}/licenses/LICENSE +0 -0
opencode/_response.py ADDED
@@ -0,0 +1,316 @@
1
+ """Raw HTTP response access for generated methods.
2
+
3
+ ``client.session.with_raw_response.get(...)`` calls the same method, but returns an :class:`APIResponse` with the
4
+ status, headers and request instead of the parsed value; :meth:`APIResponse.parse` gives the value the plain call
5
+ would have returned. ``with_streaming_response`` does the same without reading the body, inside a context manager::
6
+
7
+ with client.file.with_streaming_response.content(...) as response:
8
+ for chunk in response.iter_bytes():
9
+ ...
10
+
11
+ The generated methods are unchanged: a context variable tells the transport to hand back the response instead of
12
+ parsing it.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import contextvars
18
+ import functools
19
+ import json
20
+ from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, cast, overload
21
+
22
+ from typing_extensions import ParamSpec, override
23
+
24
+ if TYPE_CHECKING:
25
+ from collections.abc import AsyncIterator, Awaitable, Callable, Iterator
26
+ from types import TracebackType
27
+
28
+ import httpx
29
+
30
+ from ._pagination import AsyncCursorPage, AsyncPaginator
31
+
32
+ __all__ = [
33
+ "APIResponse",
34
+ "AsyncAPIResponse",
35
+ "AsyncResponseContextManager",
36
+ "ResponseContextManager",
37
+ ]
38
+
39
+ _P = ParamSpec("_P")
40
+ _R = TypeVar("_R")
41
+ _T = TypeVar("_T")
42
+
43
+ RawMode = Literal["raw", "stream"]
44
+
45
+ _raw_mode: contextvars.ContextVar[RawMode | None] = contextvars.ContextVar("opencode_raw_mode", default=None)
46
+
47
+
48
+ def raw_mode() -> RawMode | None:
49
+ """The raw-response mode of the current call, or ``None`` for a normal call."""
50
+ return _raw_mode.get()
51
+
52
+
53
+ class _BaseAPIResponse(Generic[_R]):
54
+ http_response: httpx.Response
55
+ """The underlying ``httpx.Response``."""
56
+
57
+ def __init__(
58
+ self, response: httpx.Response, parser: Callable[[httpx.Response], Any], *, event_stream: bool
59
+ ) -> None:
60
+ self.http_response = response
61
+ self._parser = parser
62
+ self._event_stream = event_stream
63
+ self._parsed: Any = _UNSET
64
+
65
+ @property
66
+ def headers(self) -> httpx.Headers:
67
+ """Response headers."""
68
+ return self.http_response.headers
69
+
70
+ @property
71
+ def status_code(self) -> int:
72
+ """HTTP status code."""
73
+ return self.http_response.status_code
74
+
75
+ @property
76
+ def request(self) -> httpx.Request:
77
+ """The request that was sent (the last attempt, when it was retried)."""
78
+ return self.http_response.request
79
+
80
+ @property
81
+ def url(self) -> httpx.URL:
82
+ """The request URL."""
83
+ return self.http_response.url
84
+
85
+ @property
86
+ def method(self) -> str:
87
+ """The request method."""
88
+ return self.http_response.request.method
89
+
90
+ @property
91
+ def elapsed(self) -> Any:
92
+ """Time from sending the request to receiving the response headers."""
93
+ return self.http_response.elapsed
94
+
95
+ @property
96
+ def is_closed(self) -> bool:
97
+ """Whether the connection has been released."""
98
+ return self.http_response.is_closed
99
+
100
+ @override
101
+ def __repr__(self) -> str:
102
+ return f"<{type(self).__name__} [{self.status_code} {self.http_response.reason_phrase}]>"
103
+
104
+
105
+ class _Unset:
106
+ pass
107
+
108
+
109
+ _UNSET = _Unset()
110
+
111
+
112
+ class APIResponse(_BaseAPIResponse[_R]):
113
+ """An HTTP response from a sync ``with_raw_response`` / ``with_streaming_response`` call."""
114
+
115
+ def parse(self) -> _R:
116
+ """Returns the value the plain method call would have returned, reading the body first if needed.
117
+
118
+ For event-stream endpoints this is the :class:`~opencode.Stream`, which consumes the body as you iterate.
119
+ """
120
+ if isinstance(self._parsed, _Unset):
121
+ if not self._event_stream:
122
+ self.read()
123
+ self._parsed = self._parser(self.http_response)
124
+ return cast("_R", self._parsed)
125
+
126
+ def read(self) -> bytes:
127
+ """Reads and returns the whole body."""
128
+ return self.http_response.read()
129
+
130
+ @property
131
+ def content(self) -> bytes:
132
+ """The whole body, read on first access."""
133
+ return self.read()
134
+
135
+ @property
136
+ def text(self) -> str:
137
+ """The body decoded as text."""
138
+ self.read()
139
+ return self.http_response.text
140
+
141
+ def json(self) -> Any:
142
+ """The body decoded as JSON."""
143
+ return json.loads(self.read())
144
+
145
+ def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]:
146
+ """Iterates over the body in byte chunks, decoding any content encoding."""
147
+ yield from self.http_response.iter_bytes(chunk_size)
148
+
149
+ def iter_text(self, chunk_size: int | None = None) -> Iterator[str]:
150
+ """Iterates over the body as decoded text chunks."""
151
+ yield from self.http_response.iter_text(chunk_size)
152
+
153
+ def iter_lines(self) -> Iterator[str]:
154
+ """Iterates over the body line by line."""
155
+ yield from self.http_response.iter_lines()
156
+
157
+ def close(self) -> None:
158
+ """Releases the connection without reading the rest of the body."""
159
+ self.http_response.close()
160
+
161
+
162
+ class AsyncAPIResponse(_BaseAPIResponse[_R]):
163
+ """An HTTP response from an async ``with_raw_response`` / ``with_streaming_response`` call."""
164
+
165
+ async def parse(self) -> _R:
166
+ """Returns the value the plain method call would have returned, reading the body first if needed.
167
+
168
+ For event-stream endpoints this is the :class:`~opencode.AsyncStream`, which consumes the body as you
169
+ iterate.
170
+ """
171
+ if isinstance(self._parsed, _Unset):
172
+ if not self._event_stream:
173
+ await self.read()
174
+ self._parsed = self._parser(self.http_response)
175
+ return cast("_R", self._parsed)
176
+
177
+ async def read(self) -> bytes:
178
+ """Reads and returns the whole body."""
179
+ return await self.http_response.aread()
180
+
181
+ async def text(self) -> str:
182
+ """The body decoded as text."""
183
+ await self.read()
184
+ return self.http_response.text
185
+
186
+ async def json(self) -> Any:
187
+ """The body decoded as JSON."""
188
+ return json.loads(await self.read())
189
+
190
+ async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]:
191
+ """Iterates over the body in byte chunks, decoding any content encoding."""
192
+ async for chunk in self.http_response.aiter_bytes(chunk_size):
193
+ yield chunk
194
+
195
+ async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]:
196
+ """Iterates over the body as decoded text chunks."""
197
+ async for chunk in self.http_response.aiter_text(chunk_size):
198
+ yield chunk
199
+
200
+ async def iter_lines(self) -> AsyncIterator[str]:
201
+ """Iterates over the body line by line."""
202
+ async for line in self.http_response.aiter_lines():
203
+ yield line
204
+
205
+ async def close(self) -> None:
206
+ """Releases the connection without reading the rest of the body."""
207
+ await self.http_response.aclose()
208
+
209
+
210
+ class ResponseContextManager(Generic[_R]):
211
+ """Sends the request on ``__enter__`` and closes the response on ``__exit__``."""
212
+
213
+ def __init__(self, send: Callable[[], APIResponse[_R]]) -> None:
214
+ self._send = send
215
+ self._response: APIResponse[_R] | None = None
216
+
217
+ def __enter__(self) -> APIResponse[_R]:
218
+ self._response = self._send()
219
+ return self._response
220
+
221
+ def __exit__(
222
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None
223
+ ) -> None:
224
+ if self._response is not None:
225
+ self._response.close()
226
+
227
+
228
+ class AsyncResponseContextManager(Generic[_R]):
229
+ """Sends the request on ``__aenter__`` and closes the response on ``__aexit__``."""
230
+
231
+ def __init__(self, send: Callable[[], Awaitable[AsyncAPIResponse[_R]]]) -> None:
232
+ self._send = send
233
+ self._response: AsyncAPIResponse[_R] | None = None
234
+
235
+ async def __aenter__(self) -> AsyncAPIResponse[_R]:
236
+ self._response = await self._send()
237
+ return self._response
238
+
239
+ async def __aexit__(
240
+ self, exc_type: type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None
241
+ ) -> None:
242
+ if self._response is not None:
243
+ await self._response.close()
244
+
245
+
246
+ def _call_sync(mode: RawMode, func: Callable[..., Any], args: Any, kwargs: Any) -> Any:
247
+ token = _raw_mode.set(mode)
248
+ try:
249
+ return func(*args, **kwargs)
250
+ finally:
251
+ _raw_mode.reset(token)
252
+
253
+
254
+ async def _call_async(mode: RawMode, func: Callable[..., Any], args: Any, kwargs: Any) -> Any:
255
+ token = _raw_mode.set(mode)
256
+ try:
257
+ # Async list methods are plain functions; in raw mode they return a coroutine for the first page.
258
+ return await func(*args, **kwargs)
259
+ finally:
260
+ _raw_mode.reset(token)
261
+
262
+
263
+ def to_raw_response_wrapper(func: Callable[_P, _R]) -> Callable[_P, APIResponse[_R]]:
264
+ """Wraps a sync generated method so it returns an :class:`APIResponse`."""
265
+
266
+ @functools.wraps(func)
267
+ def wrapped(*args: _P.args, **kwargs: _P.kwargs) -> APIResponse[_R]:
268
+ return cast("APIResponse[_R]", _call_sync("raw", func, args, kwargs))
269
+
270
+ return wrapped
271
+
272
+
273
+ def to_streamed_response_wrapper(func: Callable[_P, _R]) -> Callable[_P, ResponseContextManager[_R]]:
274
+ """Wraps a sync generated method so it returns a context manager over an unread :class:`APIResponse`."""
275
+
276
+ @functools.wraps(func)
277
+ def wrapped(*args: _P.args, **kwargs: _P.kwargs) -> ResponseContextManager[_R]:
278
+ return ResponseContextManager(lambda: cast("APIResponse[_R]", _call_sync("stream", func, args, kwargs)))
279
+
280
+ return wrapped
281
+
282
+
283
+ @overload
284
+ def async_to_raw_response_wrapper(
285
+ func: Callable[_P, AsyncPaginator[_T]],
286
+ ) -> Callable[_P, Awaitable[AsyncAPIResponse[AsyncCursorPage[_T]]]]: ...
287
+ @overload
288
+ def async_to_raw_response_wrapper(
289
+ func: Callable[_P, Awaitable[_R]],
290
+ ) -> Callable[_P, Awaitable[AsyncAPIResponse[_R]]]: ...
291
+ def async_to_raw_response_wrapper(func: Callable[_P, Any]) -> Callable[_P, Awaitable[AsyncAPIResponse[Any]]]:
292
+ """Wraps an async generated method so it returns an :class:`AsyncAPIResponse`."""
293
+
294
+ @functools.wraps(func)
295
+ async def wrapped(*args: _P.args, **kwargs: _P.kwargs) -> AsyncAPIResponse[Any]:
296
+ return cast("AsyncAPIResponse[Any]", await _call_async("raw", func, args, kwargs))
297
+
298
+ return wrapped
299
+
300
+
301
+ @overload
302
+ def async_to_streamed_response_wrapper(
303
+ func: Callable[_P, AsyncPaginator[_T]],
304
+ ) -> Callable[_P, AsyncResponseContextManager[AsyncCursorPage[_T]]]: ...
305
+ @overload
306
+ def async_to_streamed_response_wrapper(
307
+ func: Callable[_P, Awaitable[_R]],
308
+ ) -> Callable[_P, AsyncResponseContextManager[_R]]: ...
309
+ def async_to_streamed_response_wrapper(func: Callable[_P, Any]) -> Callable[_P, AsyncResponseContextManager[Any]]:
310
+ """Wraps an async generated method so it returns an async context manager over an unread response."""
311
+
312
+ @functools.wraps(func)
313
+ def wrapped(*args: _P.args, **kwargs: _P.kwargs) -> AsyncResponseContextManager[Any]:
314
+ return AsyncResponseContextManager(lambda: _call_async("stream", func, args, kwargs))
315
+
316
+ return wrapped