opencode-py 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/_async_client.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import random
5
- from typing import Any, TypeVar, cast
5
+ from typing import Any, TypeVar, cast, get_args, get_origin
6
6
 
7
7
  import httpx
8
8
 
@@ -21,10 +21,40 @@ from opencode._errors import (
21
21
  )
22
22
  from opencode._logs import logger
23
23
  from opencode._response_models import (
24
+ AgentResponse,
25
+ CommandResponse,
26
+ ConfigProviderResponse,
27
+ ConfigResponse,
24
28
  FileContentResponse,
29
+ FileNode,
30
+ FileStatus,
31
+ FindMatch,
32
+ FormatterStatusResponse,
25
33
  HealthResponse,
34
+ LSPStatusResponse,
35
+ MCPStatusResponse,
36
+ ModelV2Info,
37
+ PathResponse,
38
+ PermissionRequestResponse,
39
+ ProjectResponse,
40
+ ProviderAuthListResponse,
41
+ ProviderResponse,
42
+ ProviderV2Info,
43
+ PtyResponse,
44
+ PtyShell,
45
+ QuestionRequestResponse,
26
46
  SessionResponse,
47
+ SnapshotFileDiffResponse,
48
+ Symbol,
49
+ TodoResponse,
50
+ ToolIDs,
51
+ ToolList,
27
52
  V1SessionResponse,
53
+ VcsFileDiff,
54
+ VcsFileStatus,
55
+ VcsInfo,
56
+ WorkspaceResponse,
57
+ WorktreeResponse,
28
58
  )
29
59
  from opencode._types import NOT_GIVEN, NotGiven, is_given
30
60
 
@@ -52,9 +82,7 @@ class AsyncOpendcodeClient:
52
82
  self.workspace = workspace
53
83
  self._timeout = timeout
54
84
  self._max_retries = max_retries
55
- self._client = httpx_client or httpx.AsyncClient(
56
- timeout=httpx.Timeout(timeout)
57
- )
85
+ self._client = httpx_client or httpx.AsyncClient(timeout=httpx.Timeout(timeout))
58
86
 
59
87
  # ------------------------------------------------------------------
60
88
  # Internal helpers
@@ -75,34 +103,23 @@ class AsyncOpendcodeClient:
75
103
  return params
76
104
 
77
105
  @staticmethod
78
- def _should_retry(
79
- response: httpx.Response | None = None, exc: Exception | None = None
80
- ) -> bool:
106
+ def _should_retry(response: httpx.Response | None = None, exc: Exception | None = None) -> bool:
81
107
  if response is not None:
82
- if (
83
- response.status_code in RETRYABLE_STATUS_CODES
84
- or response.status_code >= 500
85
- ):
108
+ if response.status_code in RETRYABLE_STATUS_CODES or response.status_code >= 500:
86
109
  return True
87
110
  if isinstance(exc, httpx.TimeoutException):
88
111
  return True
89
112
  return False
90
113
 
91
114
  @staticmethod
92
- def _retry_interval(
93
- attempt: int, response: httpx.Response | None = None
94
- ) -> float:
115
+ def _retry_interval(attempt: int, response: httpx.Response | None = None) -> float:
95
116
  if response is not None:
96
- retry_after = response.headers.get(
97
- "Retry-After"
98
- ) or response.headers.get("retry-after-ms")
117
+ retry_after = response.headers.get("Retry-After") or response.headers.get(
118
+ "retry-after-ms"
119
+ )
99
120
  if retry_after:
100
121
  try:
101
- return (
102
- float(retry_after) / 1000
103
- if "ms" in retry_after
104
- else float(retry_after)
105
- )
122
+ return float(retry_after) / 1000 if "ms" in retry_after else float(retry_after)
106
123
  except ValueError:
107
124
  pass
108
125
  delay = min(INITIAL_RETRY_DELAY * pow(2.0, attempt), MAX_RETRY_DELAY)
@@ -172,19 +189,26 @@ class AsyncOpendcodeClient:
172
189
  response=response,
173
190
  status_code=status,
174
191
  )
175
- return APIStatusError(
176
- message=message, body=body, response=response, status_code=status
177
- )
192
+ return APIStatusError(message=message, body=body, response=response, status_code=status)
178
193
 
179
- def _construct_type(
180
- self, model_class: type[_T] | None, data: Any
181
- ) -> _T | Any:
194
+ def _construct_type(self, model_class: type[_T] | None, data: Any) -> _T | Any:
182
195
  if model_class is None:
183
196
  return data
184
- if isinstance(data, list):
185
- return [self._construct_type(model_class, item) for item in data]
197
+ origin = get_origin(model_class)
198
+ args = get_args(model_class)
199
+ if origin is list and args:
200
+ inner = args[0]
201
+ if isinstance(data, list):
202
+ return [self._construct_type(inner, item) for item in data]
203
+ if isinstance(data, dict) and "data" in data:
204
+ inner_list = data["data"]
205
+ if isinstance(inner_list, list):
206
+ return [self._construct_type(inner, item) for item in inner_list]
207
+ return cast(_T, data)
186
208
  if isinstance(data, dict):
187
209
  return cast(_T, cast(Any, model_class).model_validate(data))
210
+ if isinstance(data, list):
211
+ return [self._construct_type(model_class, item) for item in data]
188
212
  if data is None:
189
213
  return None
190
214
  return data
@@ -222,8 +246,7 @@ class AsyncOpendcodeClient:
222
246
  elif isinstance(body, str):
223
247
  message = body
224
248
  raise self._make_status_error(
225
- message
226
- or f"HTTP {response.status_code}: {response.reason_phrase}",
249
+ message or f"HTTP {response.status_code}: {response.reason_phrase}",
227
250
  body=body,
228
251
  response=response,
229
252
  )
@@ -328,25 +351,13 @@ class AsyncOpendcodeClient:
328
351
  httpx_client: httpx.AsyncClient | None | NotGiven = NOT_GIVEN,
329
352
  ) -> AsyncOpendcodeClient:
330
353
  return AsyncOpendcodeClient(
331
- base_url=self.base_url
332
- if is_given(base_url)
333
- else cast(str, base_url),
334
- timeout=self._timeout
335
- if is_given(timeout)
336
- else cast(float, timeout),
337
- max_retries=self._max_retries
338
- if is_given(max_retries)
339
- else cast(int, max_retries),
340
- directory=self.directory
341
- if is_given(directory)
342
- else cast(str, directory),
343
- workspace=self.workspace
344
- if is_given(workspace)
345
- else cast(str, workspace),
354
+ base_url=self.base_url if is_given(base_url) else cast(str, base_url),
355
+ timeout=self._timeout if is_given(timeout) else cast(float, timeout),
356
+ max_retries=self._max_retries if is_given(max_retries) else cast(int, max_retries),
357
+ directory=self.directory if is_given(directory) else cast(str, directory),
358
+ workspace=self.workspace if is_given(workspace) else cast(str, workspace),
346
359
  httpx_client=(
347
- self._client
348
- if is_given(httpx_client)
349
- else cast(httpx.AsyncClient, httpx_client)
360
+ self._client if is_given(httpx_client) else cast(httpx.AsyncClient, httpx_client)
350
361
  ),
351
362
  )
352
363
 
@@ -360,9 +371,7 @@ class AsyncOpendcodeClient:
360
371
  async def health(self) -> HealthResponse:
361
372
  return cast(
362
373
  HealthResponse,
363
- await self._request(
364
- "GET", "/global/health", cast_to=HealthResponse
365
- ),
374
+ await self._request("GET", "/global/health", cast_to=HealthResponse),
366
375
  )
367
376
 
368
377
  async def global_event(self) -> httpx.Response:
@@ -372,16 +381,14 @@ class AsyncOpendcodeClient:
372
381
  return await self._request("POST", "/global/dispose")
373
382
 
374
383
  async def global_upgrade(self, target: str | None = None) -> Any:
375
- return await self._request(
376
- "POST", "/global/upgrade", json_body={"target": target}
377
- )
384
+ return await self._request("POST", "/global/upgrade", json_body={"target": target})
378
385
 
379
386
  async def global_config_get(self) -> Any:
380
- return await self._request("GET", "/global/config")
387
+ return await self._request("GET", "/global/config", cast_to=ConfigResponse)
381
388
 
382
389
  async def global_config_update(self, config: Any) -> Any:
383
390
  return await self._request(
384
- "PATCH", "/global/config", json_body={"config": config}
391
+ "PATCH", "/global/config", json_body={"config": config}, cast_to=ConfigResponse
385
392
  )
386
393
 
387
394
  # ------------------------------------------------------------------
@@ -389,15 +396,17 @@ class AsyncOpendcodeClient:
389
396
  # ------------------------------------------------------------------
390
397
 
391
398
  async def config_get(self, **kwargs: Any) -> Any:
392
- return await self._request("GET", "/config", params=kwargs)
399
+ return await self._request("GET", "/config", params=kwargs, cast_to=ConfigResponse)
393
400
 
394
401
  async def config_update(self, config: Any, **kwargs: Any) -> Any:
395
402
  return await self._request(
396
- "PATCH", "/config", json_body={"config": config}, params=kwargs
403
+ "PATCH", "/config", json_body={"config": config}, params=kwargs, cast_to=ConfigResponse
397
404
  )
398
405
 
399
406
  async def config_providers(self, **kwargs: Any) -> Any:
400
- return await self._request("GET", "/config/providers", params=kwargs)
407
+ return await self._request(
408
+ "GET", "/config/providers", params=kwargs, cast_to=ConfigProviderResponse
409
+ )
401
410
 
402
411
  # ------------------------------------------------------------------
403
412
  # Session
@@ -417,9 +426,7 @@ class AsyncOpendcodeClient:
417
426
  async def session_get(self, session_id: str) -> SessionResponse:
418
427
  return cast(
419
428
  SessionResponse,
420
- await self._request(
421
- "GET", f"/session/{session_id}", cast_to=SessionResponse
422
- ),
429
+ await self._request("GET", f"/session/{session_id}", cast_to=SessionResponse),
423
430
  )
424
431
 
425
432
  async def session_list(self, **kwargs: Any) -> Any:
@@ -429,44 +436,40 @@ class AsyncOpendcodeClient:
429
436
  return await self._request("DELETE", f"/session/{session_id}")
430
437
 
431
438
  async def session_update(self, session_id: str, **kwargs: Any) -> Any:
432
- return await self._request(
433
- "PUT", f"/session/{session_id}", json_body=kwargs or None
434
- )
439
+ return await self._request("PUT", f"/session/{session_id}", json_body=kwargs or None)
435
440
 
436
441
  async def session_messages(self, session_id: str, **kwargs: Any) -> Any:
437
- return await self._request(
438
- "GET", f"/session/{session_id}/message", params=kwargs
439
- )
442
+ return await self._request("GET", f"/session/{session_id}/message", params=kwargs)
440
443
 
441
444
  async def session_message(self, session_id: str, message_id: str) -> Any:
442
- return await self._request(
443
- "GET", f"/session/{session_id}/message/{message_id}"
444
- )
445
+ return await self._request("GET", f"/session/{session_id}/message/{message_id}")
445
446
 
446
447
  async def session_fork(self, session_id: str, **kwargs: Any) -> Any:
447
448
  return await self._request(
448
- "POST", f"/session/{session_id}/fork", json_body=kwargs or None
449
+ "POST", f"/session/{session_id}/fork", json_body=kwargs or None, cast_to=SessionResponse
449
450
  )
450
451
 
451
452
  async def session_abort(self, session_id: str) -> Any:
452
453
  return await self._request("POST", f"/session/{session_id}/abort")
453
454
 
454
455
  async def session_init(self, session_id: str, **kwargs: Any) -> Any:
455
- return await self._request(
456
- "POST", f"/session/{session_id}/init", json_body=kwargs or None
457
- )
456
+ return await self._request("POST", f"/session/{session_id}/init", json_body=kwargs or None)
458
457
 
459
458
  async def session_summarize(self, session_id: str) -> Any:
460
459
  return await self._request("POST", f"/session/{session_id}/summarize")
461
460
 
462
461
  async def session_todo(self, session_id: str) -> Any:
463
- return await self._request("GET", f"/session/{session_id}/todo")
462
+ return await self._request("GET", f"/session/{session_id}/todo", cast_to=list[TodoResponse])
464
463
 
465
464
  async def session_children(self, session_id: str) -> Any:
466
- return await self._request("GET", f"/session/{session_id}/child")
465
+ return await self._request(
466
+ "GET", f"/session/{session_id}/child", cast_to=list[SessionResponse]
467
+ )
467
468
 
468
469
  async def session_diff(self, session_id: str) -> Any:
469
- return await self._request("GET", f"/session/{session_id}/diff")
470
+ return await self._request(
471
+ "GET", f"/session/{session_id}/diff", cast_to=list[SnapshotFileDiffResponse]
472
+ )
470
473
 
471
474
  async def session_share(self, session_id: str) -> Any:
472
475
  return await self._request("POST", f"/session/{session_id}/share")
@@ -480,9 +483,7 @@ class AsyncOpendcodeClient:
480
483
  async def session_unrevert(self, session_id: str) -> Any:
481
484
  return await self._request("POST", f"/session/{session_id}/unrevert")
482
485
 
483
- async def session_command(
484
- self, session_id: str, command: str, **kwargs: Any
485
- ) -> Any:
486
+ async def session_command(self, session_id: str, command: str, **kwargs: Any) -> Any:
486
487
  return await self._request(
487
488
  "POST",
488
489
  f"/session/{session_id}/command",
@@ -503,9 +504,7 @@ class AsyncOpendcodeClient:
503
504
  async def v2_session_list(self, **kwargs: Any) -> Any:
504
505
  return await self._request("GET", "/api/session", params=kwargs)
505
506
 
506
- async def session_send(
507
- self, session_id: str, body: Any
508
- ) -> V1SessionResponse:
507
+ async def session_send(self, session_id: str, body: Any) -> V1SessionResponse:
509
508
  return cast(
510
509
  V1SessionResponse,
511
510
  await self._request(
@@ -530,44 +529,38 @@ class AsyncOpendcodeClient:
530
529
  **kwargs,
531
530
  }
532
531
  return await self._request(
533
- "POST", f"/api/session/{session_id}/prompt", json_body=body
532
+ "POST", f"/api/session/{session_id}/prompt", json_body=body, cast_to=V1SessionResponse
534
533
  )
535
534
 
536
535
  async def v2_session_wait(self, session_id: str) -> Any:
537
536
  return await self._request("POST", f"/api/session/{session_id}/wait")
538
537
 
539
538
  async def v2_session_context(self, session_id: str, **kwargs: Any) -> Any:
540
- return await self._request(
541
- "GET", f"/api/session/{session_id}/context", params=kwargs
542
- )
539
+ return await self._request("GET", f"/api/session/{session_id}/context", params=kwargs)
543
540
 
544
541
  async def v2_session_messages(self, session_id: str, **kwargs: Any) -> Any:
545
- return await self._request(
546
- "GET", f"/api/session/{session_id}/message", params=kwargs
547
- )
542
+ return await self._request("GET", f"/api/session/{session_id}/message", params=kwargs)
548
543
 
549
544
  async def v2_session_compact(self, session_id: str) -> Any:
550
- return await self._request(
551
- "POST", f"/api/session/{session_id}/compact"
552
- )
545
+ return await self._request("POST", f"/api/session/{session_id}/compact")
553
546
 
554
547
  async def v2_model_list(self, **kwargs: Any) -> Any:
555
- return await self._request("GET", "/api/model", params=kwargs)
548
+ return await self._request("GET", "/api/model", params=kwargs, cast_to=list[ModelV2Info])
556
549
 
557
550
  async def v2_provider_list(self, **kwargs: Any) -> Any:
558
- return await self._request("GET", "/api/provider", params=kwargs)
551
+ return await self._request(
552
+ "GET", "/api/provider", params=kwargs, cast_to=list[ProviderV2Info]
553
+ )
559
554
 
560
555
  async def v2_provider_get(self, provider_id: str) -> Any:
561
- return await self._request("GET", f"/api/provider/{provider_id}")
556
+ return await self._request("GET", f"/api/provider/{provider_id}", cast_to=ProviderV2Info)
562
557
 
563
558
  # ------------------------------------------------------------------
564
559
  # Auth
565
560
  # ------------------------------------------------------------------
566
561
 
567
562
  async def auth_set(self, provider_id: str, auth: Any) -> Any:
568
- return await self._request(
569
- "PUT", f"/auth/{provider_id}", json_body={"auth": auth}
570
- )
563
+ return await self._request("PUT", f"/auth/{provider_id}", json_body={"auth": auth})
571
564
 
572
565
  async def auth_remove(self, provider_id: str) -> Any:
573
566
  return await self._request("DELETE", f"/auth/{provider_id}")
@@ -580,7 +573,7 @@ class AsyncOpendcodeClient:
580
573
  return await self._request("POST", "/log", json_body=kwargs or None)
581
574
 
582
575
  async def app_agents(self, **kwargs: Any) -> Any:
583
- return await self._request("GET", "/agent", params=kwargs)
576
+ return await self._request("GET", "/agent", params=kwargs, cast_to=list[AgentResponse])
584
577
 
585
578
  # ------------------------------------------------------------------
586
579
  # File
@@ -599,11 +592,11 @@ class AsyncOpendcodeClient:
599
592
 
600
593
  async def file_list(self, path: str, **kwargs: Any) -> Any:
601
594
  return await self._request(
602
- "GET", "/file", params={"path": path, **kwargs}
595
+ "GET", "/file", params={"path": path, **kwargs}, cast_to=list[FileNode]
603
596
  )
604
597
 
605
598
  async def file_status(self, **kwargs: Any) -> Any:
606
- return await self._request("GET", "/file/status", params=kwargs)
599
+ return await self._request("GET", "/file/status", params=kwargs, cast_to=list[FileStatus])
607
600
 
608
601
  # ------------------------------------------------------------------
609
602
  # Find
@@ -611,17 +604,17 @@ class AsyncOpendcodeClient:
611
604
 
612
605
  async def find_text(self, pattern: str, **kwargs: Any) -> Any:
613
606
  return await self._request(
614
- "GET", "/find", params={"pattern": pattern, **kwargs}
607
+ "GET", "/find", params={"pattern": pattern, **kwargs}, cast_to=list[FindMatch]
615
608
  )
616
609
 
617
610
  async def find_files(self, query: str, **kwargs: Any) -> Any:
618
611
  return await self._request(
619
- "GET", "/find/file", params={"query": query, **kwargs}
612
+ "GET", "/find/file", params={"query": query, **kwargs}, cast_to=list[str]
620
613
  )
621
614
 
622
615
  async def find_symbols(self, query: str, **kwargs: Any) -> Any:
623
616
  return await self._request(
624
- "GET", "/find/symbol", params={"query": query, **kwargs}
617
+ "GET", "/find/symbol", params={"query": query, **kwargs}, cast_to=list[Symbol]
625
618
  )
626
619
 
627
620
  # ------------------------------------------------------------------
@@ -629,44 +622,44 @@ class AsyncOpendcodeClient:
629
622
  # ------------------------------------------------------------------
630
623
 
631
624
  async def vcs_get(self, **kwargs: Any) -> Any:
632
- return await self._request("GET", "/vcs", params=kwargs)
625
+ return await self._request("GET", "/vcs", params=kwargs, cast_to=VcsInfo)
633
626
 
634
627
  async def vcs_status(self, **kwargs: Any) -> Any:
635
- return await self._request("GET", "/vcs/status", params=kwargs)
628
+ return await self._request("GET", "/vcs/status", params=kwargs, cast_to=list[VcsFileStatus])
636
629
 
637
630
  async def vcs_diff(self, mode: str = "git", **kwargs: Any) -> Any:
638
631
  return await self._request(
639
- "GET", "/vcs/diff", params={"mode": mode, **kwargs}
632
+ "GET", "/vcs/diff", params={"mode": mode, **kwargs}, cast_to=list[VcsFileDiff]
640
633
  )
641
634
 
642
635
  async def vcs_diff_raw(self, **kwargs: Any) -> Any:
643
636
  return await self._request("GET", "/vcs/diff/raw", params=kwargs)
644
637
 
645
638
  async def vcs_apply(self, patch: str, **kwargs: Any) -> Any:
646
- return await self._request(
647
- "POST", "/vcs/apply", json_body={"patch": patch, **kwargs}
648
- )
639
+ return await self._request("POST", "/vcs/apply", json_body={"patch": patch, **kwargs})
649
640
 
650
641
  # ------------------------------------------------------------------
651
642
  # LSP / Formatter
652
643
  # ------------------------------------------------------------------
653
644
 
654
645
  async def lsp_status(self, **kwargs: Any) -> Any:
655
- return await self._request("GET", "/lsp", params=kwargs)
646
+ return await self._request("GET", "/lsp", params=kwargs, cast_to=list[LSPStatusResponse])
656
647
 
657
648
  async def formatter_status(self, **kwargs: Any) -> Any:
658
- return await self._request("GET", "/formatter", params=kwargs)
649
+ return await self._request(
650
+ "GET", "/formatter", params=kwargs, cast_to=list[FormatterStatusResponse]
651
+ )
659
652
 
660
653
  # ------------------------------------------------------------------
661
654
  # Provider
662
655
  # ------------------------------------------------------------------
663
656
 
664
657
  async def provider_list(self, **kwargs: Any) -> Any:
665
- return await self._request("GET", "/provider", params=kwargs)
658
+ return await self._request("GET", "/provider", params=kwargs, cast_to=ProviderResponse)
666
659
 
667
660
  async def provider_auth(self, provider_id: str, **kwargs: Any) -> Any:
668
661
  return await self._request(
669
- "GET", f"/provider/{provider_id}/auth", params=kwargs
662
+ "GET", f"/provider/{provider_id}/auth", params=kwargs, cast_to=ProviderAuthListResponse
670
663
  )
671
664
 
672
665
  # ------------------------------------------------------------------
@@ -674,19 +667,19 @@ class AsyncOpendcodeClient:
674
667
  # ------------------------------------------------------------------
675
668
 
676
669
  async def mcp_list(self, **kwargs: Any) -> Any:
677
- return await self._request("GET", "/mcp", params=kwargs)
670
+ return await self._request("GET", "/mcp", params=kwargs, cast_to=MCPStatusResponse)
678
671
 
679
672
  async def mcp_status(self, **kwargs: Any) -> Any:
680
- return await self._request("GET", "/mcp/status", params=kwargs)
673
+ return await self._request("GET", "/mcp/status", params=kwargs, cast_to=MCPStatusResponse)
681
674
 
682
675
  async def mcp_add(self, config: Any) -> Any:
683
- return await self._request("PUT", "/mcp", json_body={"config": config})
684
-
685
- async def mcp_connect(self, name: str, **kwargs: Any) -> Any:
686
676
  return await self._request(
687
- "POST", f"/mcp/{name}/connect", json_body=kwargs or None
677
+ "PUT", "/mcp", json_body={"config": config}, cast_to=MCPStatusResponse
688
678
  )
689
679
 
680
+ async def mcp_connect(self, name: str, **kwargs: Any) -> Any:
681
+ return await self._request("POST", f"/mcp/{name}/connect", json_body=kwargs or None)
682
+
690
683
  async def mcp_disconnect(self, name: str) -> Any:
691
684
  return await self._request("DELETE", f"/mcp/{name}/connect")
692
685
 
@@ -695,37 +688,35 @@ class AsyncOpendcodeClient:
695
688
  # ------------------------------------------------------------------
696
689
 
697
690
  async def tool_list(self, **kwargs: Any) -> Any:
698
- return await self._request("GET", "/experimental/tool", params=kwargs)
691
+ return await self._request("GET", "/experimental/tool", params=kwargs, cast_to=ToolList)
699
692
 
700
693
  async def tool_ids(self, **kwargs: Any) -> Any:
701
- return await self._request(
702
- "GET", "/experimental/tool/ids", params=kwargs
703
- )
694
+ return await self._request("GET", "/experimental/tool/ids", params=kwargs, cast_to=ToolIDs)
704
695
 
705
696
  # ------------------------------------------------------------------
706
697
  # Permission
707
698
  # ------------------------------------------------------------------
708
699
 
709
700
  async def permission_list(self, **kwargs: Any) -> Any:
710
- return await self._request("GET", "/permission", params=kwargs)
711
-
712
- async def permission_reply(self, permission_id: str, **kwargs: Any) -> Any:
713
701
  return await self._request(
714
- "POST", f"/permission/{permission_id}", json_body=kwargs or None
702
+ "GET", "/permission", params=kwargs, cast_to=list[PermissionRequestResponse]
715
703
  )
716
704
 
705
+ async def permission_reply(self, permission_id: str, **kwargs: Any) -> Any:
706
+ return await self._request("POST", f"/permission/{permission_id}", json_body=kwargs or None)
707
+
717
708
  # ------------------------------------------------------------------
718
709
  # Question
719
710
  # ------------------------------------------------------------------
720
711
 
721
712
  async def question_list(self, **kwargs: Any) -> Any:
722
- return await self._request("GET", "/question", params=kwargs)
723
-
724
- async def question_reply(self, question_id: str, answer: Any) -> Any:
725
713
  return await self._request(
726
- "POST", f"/question/{question_id}", json_body={"answer": answer}
714
+ "GET", "/question", params=kwargs, cast_to=list[QuestionRequestResponse]
727
715
  )
728
716
 
717
+ async def question_reply(self, question_id: str, answer: Any) -> Any:
718
+ return await self._request("POST", f"/question/{question_id}", json_body={"answer": answer})
719
+
729
720
  async def question_reject(self, question_id: str) -> Any:
730
721
  return await self._request("DELETE", f"/question/{question_id}")
731
722
 
@@ -741,31 +732,31 @@ class AsyncOpendcodeClient:
741
732
  # ------------------------------------------------------------------
742
733
 
743
734
  async def pty_list(self, **kwargs: Any) -> Any:
744
- return await self._request("GET", "/pty", params=kwargs)
735
+ return await self._request("GET", "/pty", params=kwargs, cast_to=list[PtyResponse])
745
736
 
746
737
  async def pty_create(self, **kwargs: Any) -> Any:
747
- return await self._request("POST", "/pty", json_body=kwargs or None)
738
+ return await self._request("POST", "/pty", json_body=kwargs or None, cast_to=PtyResponse)
748
739
 
749
740
  async def pty_get(self, pty_id: str) -> Any:
750
- return await self._request("GET", f"/pty/{pty_id}")
741
+ return await self._request("GET", f"/pty/{pty_id}", cast_to=PtyResponse)
751
742
 
752
743
  async def pty_remove(self, pty_id: str) -> Any:
753
744
  return await self._request("DELETE", f"/pty/{pty_id}")
754
745
 
755
746
  async def pty_update(self, pty_id: str, **kwargs: Any) -> Any:
756
747
  return await self._request(
757
- "PATCH", f"/pty/{pty_id}", json_body=kwargs or None
748
+ "PATCH", f"/pty/{pty_id}", json_body=kwargs or None, cast_to=PtyResponse
758
749
  )
759
750
 
760
751
  async def pty_shells(self, **kwargs: Any) -> Any:
761
- return await self._request("GET", "/pty/shells", params=kwargs)
752
+ return await self._request("GET", "/pty/shells", params=kwargs, cast_to=list[PtyShell])
762
753
 
763
754
  # ------------------------------------------------------------------
764
755
  # Path
765
756
  # ------------------------------------------------------------------
766
757
 
767
758
  async def path_get(self, **kwargs: Any) -> Any:
768
- return await self._request("GET", "/path", params=kwargs)
759
+ return await self._request("GET", "/path", params=kwargs, cast_to=PathResponse)
769
760
 
770
761
  # ------------------------------------------------------------------
771
762
  # Instance
@@ -779,26 +770,28 @@ class AsyncOpendcodeClient:
779
770
  # ------------------------------------------------------------------
780
771
 
781
772
  async def command_list(self, **kwargs: Any) -> Any:
782
- return await self._request("GET", "/command", params=kwargs)
773
+ return await self._request("GET", "/command", params=kwargs, cast_to=list[CommandResponse])
783
774
 
784
775
  # ------------------------------------------------------------------
785
776
  # Project
786
777
  # ------------------------------------------------------------------
787
778
 
788
779
  async def project_current(self, **kwargs: Any) -> Any:
789
- return await self._request("GET", "/project/current", params=kwargs)
780
+ return await self._request(
781
+ "GET", "/project/current", params=kwargs, cast_to=ProjectResponse
782
+ )
790
783
 
791
784
  async def project_list(self, **kwargs: Any) -> Any:
792
- return await self._request("GET", "/project", params=kwargs)
785
+ return await self._request("GET", "/project", params=kwargs, cast_to=list[ProjectResponse])
793
786
 
794
787
  async def project_update(self, **kwargs: Any) -> Any:
795
788
  return await self._request(
796
- "PATCH", "/project", json_body=kwargs or None
789
+ "PATCH", "/project", json_body=kwargs or None, cast_to=ProjectResponse
797
790
  )
798
791
 
799
792
  async def project_init_git(self, **kwargs: Any) -> Any:
800
793
  return await self._request(
801
- "POST", "/project/init-git", json_body=kwargs or None
794
+ "POST", "/project/init-git", json_body=kwargs or None, cast_to=ProjectResponse
802
795
  )
803
796
 
804
797
  # ------------------------------------------------------------------
@@ -807,23 +800,19 @@ class AsyncOpendcodeClient:
807
800
 
808
801
  async def worktree_list(self, **kwargs: Any) -> Any:
809
802
  return await self._request(
810
- "GET", "/experimental/worktree", params=kwargs
803
+ "GET", "/experimental/worktree", params=kwargs, cast_to=list[str]
811
804
  )
812
805
 
813
806
  async def worktree_create(self, **kwargs: Any) -> Any:
814
807
  return await self._request(
815
- "POST", "/experimental/worktree", json_body=kwargs or None
808
+ "POST", "/experimental/worktree", json_body=kwargs or None, cast_to=WorktreeResponse
816
809
  )
817
810
 
818
811
  async def worktree_remove(self, **kwargs: Any) -> Any:
819
- return await self._request(
820
- "DELETE", "/experimental/worktree", params=kwargs
821
- )
812
+ return await self._request("DELETE", "/experimental/worktree", params=kwargs)
822
813
 
823
814
  async def worktree_reset(self, **kwargs: Any) -> Any:
824
- return await self._request(
825
- "POST", "/experimental/worktree/reset", json_body=kwargs or None
826
- )
815
+ return await self._request("POST", "/experimental/worktree/reset", json_body=kwargs or None)
827
816
 
828
817
  # ------------------------------------------------------------------
829
818
  # Workspace (experimental)
@@ -831,79 +820,59 @@ class AsyncOpendcodeClient:
831
820
 
832
821
  async def workspace_list(self, **kwargs: Any) -> Any:
833
822
  return await self._request(
834
- "GET", "/experimental/workspace", params=kwargs
823
+ "GET", "/experimental/workspace", params=kwargs, cast_to=list[WorkspaceResponse]
835
824
  )
836
825
 
837
826
  async def workspace_create(self, **kwargs: Any) -> Any:
838
827
  return await self._request(
839
- "POST", "/experimental/workspace", json_body=kwargs or None
828
+ "POST", "/experimental/workspace", json_body=kwargs or None, cast_to=WorkspaceResponse
840
829
  )
841
830
 
842
831
  async def workspace_status(self, **kwargs: Any) -> Any:
843
- return await self._request(
844
- "GET", "/experimental/workspace/status", params=kwargs
845
- )
832
+ return await self._request("GET", "/experimental/workspace/status", params=kwargs)
846
833
 
847
834
  async def workspace_remove(self, workspace_id: str) -> Any:
848
835
  return await self._request(
849
- "DELETE", f"/experimental/workspace/{workspace_id}"
836
+ "DELETE", f"/experimental/workspace/{workspace_id}", cast_to=WorkspaceResponse
850
837
  )
851
838
 
852
839
  async def workspace_warp(self, **kwargs: Any) -> Any:
853
- return await self._request(
854
- "POST", "/experimental/workspace/warp", json_body=kwargs or None
855
- )
840
+ return await self._request("POST", "/experimental/workspace/warp", json_body=kwargs or None)
856
841
 
857
842
  # ------------------------------------------------------------------
858
843
  # Sync (experimental)
859
844
  # ------------------------------------------------------------------
860
845
 
861
846
  async def sync_start(self, **kwargs: Any) -> Any:
862
- return await self._request(
863
- "POST", "/experimental/sync/start", json_body=kwargs or None
864
- )
847
+ return await self._request("POST", "/experimental/sync/start", json_body=kwargs or None)
865
848
 
866
849
  async def sync_steal(self, **kwargs: Any) -> Any:
867
- return await self._request(
868
- "POST", "/experimental/sync/steal", json_body=kwargs or None
869
- )
850
+ return await self._request("POST", "/experimental/sync/steal", json_body=kwargs or None)
870
851
 
871
852
  async def sync_replay(self, session_id: str) -> Any:
872
- return await self._request(
873
- "POST", f"/experimental/sync/replay/{session_id}"
874
- )
853
+ return await self._request("POST", f"/experimental/sync/replay/{session_id}")
875
854
 
876
855
  async def sync_history(self, session_id: str) -> Any:
877
- return await self._request(
878
- "GET", f"/experimental/sync/history/{session_id}"
879
- )
856
+ return await self._request("GET", f"/experimental/sync/history/{session_id}")
880
857
 
881
858
  # ------------------------------------------------------------------
882
859
  # TUI
883
860
  # ------------------------------------------------------------------
884
861
 
885
862
  async def tui_submit_prompt(self, **kwargs: Any) -> Any:
886
- return await self._request(
887
- "POST", "/tui/submit", json_body=kwargs or None
888
- )
863
+ return await self._request("POST", "/tui/submit", json_body=kwargs or None)
889
864
 
890
865
  async def tui_append_prompt(self, **kwargs: Any) -> Any:
891
- return await self._request(
892
- "POST", "/tui/append", json_body=kwargs or None
893
- )
866
+ return await self._request("POST", "/tui/append", json_body=kwargs or None)
894
867
 
895
868
  async def tui_clear_prompt(self) -> Any:
896
869
  return await self._request("POST", "/tui/clear")
897
870
 
898
871
  async def tui_execute_command(self, **kwargs: Any) -> Any:
899
- return await self._request(
900
- "POST", "/tui/command", json_body=kwargs or None
901
- )
872
+ return await self._request("POST", "/tui/command", json_body=kwargs or None)
902
873
 
903
874
  async def tui_show_toast(self, **kwargs: Any) -> Any:
904
- return await self._request(
905
- "POST", "/tui/toast", json_body=kwargs or None
906
- )
875
+ return await self._request("POST", "/tui/toast", json_body=kwargs or None)
907
876
 
908
877
  async def tui_open_sessions(self) -> Any:
909
878
  return await self._request("POST", "/tui/sessions")
@@ -918,14 +887,10 @@ class AsyncOpendcodeClient:
918
887
  return await self._request("POST", "/tui/help")
919
888
 
920
889
  async def tui_publish(self, **kwargs: Any) -> Any:
921
- return await self._request(
922
- "POST", "/tui/publish", json_body=kwargs or None
923
- )
890
+ return await self._request("POST", "/tui/publish", json_body=kwargs or None)
924
891
 
925
892
  async def tui_control_response(self, **kwargs: Any) -> Any:
926
- return await self._request(
927
- "POST", "/tui/control/response", json_body=kwargs or None
928
- )
893
+ return await self._request("POST", "/tui/control/response", json_body=kwargs or None)
929
894
 
930
895
  async def tui_control_next(self, session_id: str) -> Any:
931
896
  return await self._request("POST", f"/tui/control/next/{session_id}")