opencode-py 0.2.0__tar.gz → 0.2.1__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.
Files changed (53) hide show
  1. {opencode_py-0.2.0 → opencode_py-0.2.1}/PKG-INFO +1 -1
  2. {opencode_py-0.2.0 → opencode_py-0.2.1}/pyproject.toml +2 -2
  3. {opencode_py-0.2.0 → opencode_py-0.2.1}/scripts/check-release.py +95 -95
  4. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/__main__.py +3 -2
  5. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_errors.py +94 -94
  6. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_response_models.py +242 -242
  7. {opencode_py-0.2.0 → opencode_py-0.2.1}/web/index.html +199 -199
  8. {opencode_py-0.2.0 → opencode_py-0.2.1}/.editorconfig +0 -0
  9. {opencode_py-0.2.0 → opencode_py-0.2.1}/.gitattributes +0 -0
  10. {opencode_py-0.2.0 → opencode_py-0.2.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  11. {opencode_py-0.2.0 → opencode_py-0.2.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  12. {opencode_py-0.2.0 → opencode_py-0.2.1}/.github/workflows/publish.yml +0 -0
  13. {opencode_py-0.2.0 → opencode_py-0.2.1}/.github/workflows/test.yml +0 -0
  14. {opencode_py-0.2.0 → opencode_py-0.2.1}/.gitignore +0 -0
  15. {opencode_py-0.2.0 → opencode_py-0.2.1}/.pre-commit-config.yaml +0 -0
  16. {opencode_py-0.2.0 → opencode_py-0.2.1}/AGENTS.md +0 -0
  17. {opencode_py-0.2.0 → opencode_py-0.2.1}/CHANGELOG.md +0 -0
  18. {opencode_py-0.2.0 → opencode_py-0.2.1}/CODE_OF_CONDUCT.md +0 -0
  19. {opencode_py-0.2.0 → opencode_py-0.2.1}/CONTRIBUTING.md +0 -0
  20. {opencode_py-0.2.0 → opencode_py-0.2.1}/LICENSE +0 -0
  21. {opencode_py-0.2.0 → opencode_py-0.2.1}/README.md +0 -0
  22. {opencode_py-0.2.0 → opencode_py-0.2.1}/README.ru.md +0 -0
  23. {opencode_py-0.2.0 → opencode_py-0.2.1}/RELEASE.md +0 -0
  24. {opencode_py-0.2.0 → opencode_py-0.2.1}/SECURITY.md +0 -0
  25. {opencode_py-0.2.0 → opencode_py-0.2.1}/demo.py +0 -0
  26. {opencode_py-0.2.0 → opencode_py-0.2.1}/docs/opencode-docs-ru.md +0 -0
  27. {opencode_py-0.2.0 → opencode_py-0.2.1}/live.py +0 -0
  28. {opencode_py-0.2.0 → opencode_py-0.2.1}/live_async.py +0 -0
  29. {opencode_py-0.2.0 → opencode_py-0.2.1}/live_streaming.py +0 -0
  30. {opencode_py-0.2.0 → opencode_py-0.2.1}/scripts/check-upstream.py +0 -0
  31. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/__init__.py +0 -0
  32. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_async_client.py +0 -0
  33. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_async_opencode.py +0 -0
  34. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_async_session.py +0 -0
  35. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_binary.py +0 -0
  36. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_client.py +0 -0
  37. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_logs.py +0 -0
  38. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_models.py +0 -0
  39. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_opencode.py +0 -0
  40. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_process.py +0 -0
  41. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_server.py +0 -0
  42. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_session.py +0 -0
  43. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_tools.py +0 -0
  44. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/_types.py +0 -0
  45. {opencode_py-0.2.0 → opencode_py-0.2.1}/src/opencode/py.typed +0 -0
  46. {opencode_py-0.2.0 → opencode_py-0.2.1}/test_all.py +0 -0
  47. {opencode_py-0.2.0 → opencode_py-0.2.1}/test_live.py +0 -0
  48. {opencode_py-0.2.0 → opencode_py-0.2.1}/tests/docker/Dockerfile +0 -0
  49. {opencode_py-0.2.0 → opencode_py-0.2.1}/tests/docker/test_smoke.py +0 -0
  50. {opencode_py-0.2.0 → opencode_py-0.2.1}/tests/test_async_client.py +0 -0
  51. {opencode_py-0.2.0 → opencode_py-0.2.1}/tests/test_client.py +0 -0
  52. {opencode_py-0.2.0 → opencode_py-0.2.1}/tests/test_opencode.py +0 -0
  53. {opencode_py-0.2.0 → opencode_py-0.2.1}/web/server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencode-py
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Python SDK for Opencode — the open source AI coding agent
5
5
  Project-URL: Homepage, https://github.com/skislyakow/opencode-py
6
6
  Project-URL: Repository, https://github.com/skislyakow/opencode-py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "opencode-py"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Python SDK for Opencode — the open source AI coding agent"
5
5
  readme = "README.md"
6
6
  long_description_content_type = "text/markdown"
@@ -45,7 +45,7 @@ Documentation = "https://github.com/skislyakow/opencode-py"
45
45
  Changelog = "https://github.com/skislyakow/opencode-py/blob/main/CHANGELOG.md"
46
46
 
47
47
  [project.scripts]
48
- opencode = "opencode.__main__:main"
48
+ opencode-py = "opencode.__main__:main"
49
49
 
50
50
  [build-system]
51
51
  requires = ["hatchling"]
@@ -1,95 +1,95 @@
1
- """Check if a new PyPI release is needed.
2
-
3
- Compares the version in pyproject.toml with the latest git tag.
4
- Exits with code 0 if no release needed, 1 if a release is recommended.
5
-
6
- Usage:
7
- python scripts/check-release.py
8
- python scripts/check-release.py --verbose
9
- """
10
-
11
- from __future__ import annotations
12
-
13
- import re
14
- import subprocess
15
- import sys
16
- from pathlib import Path
17
-
18
-
19
- def get_current_version() -> str:
20
- pyproject = Path(__file__).parent.parent / "pyproject.toml"
21
- text = pyproject.read_text()
22
- m = re.search(r'^version\s*=\s*"([^"]+)"', text, re.MULTILINE)
23
- if m:
24
- return m.group(1)
25
- raise RuntimeError("Could not find version in pyproject.toml")
26
-
27
-
28
- def get_latest_tag() -> str | None:
29
- result = subprocess.run(
30
- ["git", "tag", "--list", "v*", "--sort=-version:refname"],
31
- capture_output=True, text=True, cwd=Path(__file__).parent.parent,
32
- )
33
- tags = [t.strip() for t in result.stdout.splitlines() if t.strip()]
34
- return tags[0] if tags else None
35
-
36
-
37
- def get_commit_count_since_tag(tag: str) -> int:
38
- result = subprocess.run(
39
- ["git", "rev-list", f"{tag}..HEAD", "--count"],
40
- capture_output=True, text=True, cwd=Path(__file__).parent.parent,
41
- )
42
- return int(result.stdout.strip() or "0")
43
-
44
-
45
- def main() -> int:
46
- verbose = "--verbose" in sys.argv or "-v" in sys.argv
47
-
48
- current = get_current_version()
49
- latest_tag = get_latest_tag()
50
-
51
- print(f"Current version (pyproject.toml): {current}")
52
-
53
- if latest_tag is None:
54
- print("No git tags found — this would be the first release.")
55
- return 0
56
-
57
- tag_version = latest_tag.lstrip("v")
58
- print(f"Latest git tag: {latest_tag}")
59
-
60
- commits_since = get_commit_count_since_tag(latest_tag)
61
-
62
- if current == tag_version:
63
- if commits_since > 0:
64
- print(f"\n==> {commits_since} commit(s) since {latest_tag}, "
65
- f"but version hasn't been bumped ({current}).")
66
- if verbose:
67
- result = subprocess.run(
68
- ["git", "log", f"{latest_tag}..HEAD", "--oneline"],
69
- capture_output=True, text=True,
70
- cwd=Path(__file__).parent.parent,
71
- )
72
- print("\nUnreleased commits:")
73
- for line in result.stdout.splitlines():
74
- print(f" {line}")
75
- print("\n=> Run `python -m build` and publish when ready:")
76
- print(" python -m build")
77
- print(" twine upload dist/*")
78
- return 1
79
- else:
80
- print("\n[ok] Everything is up to date. No release needed.")
81
- return 0
82
- else:
83
- print(f"\n=> Version in pyproject.toml ({current}) differs from "
84
- f"latest tag ({tag_version}).")
85
- print(" A new release may be ready to publish.")
86
- if verbose:
87
- print("\n To publish:")
88
- print(" git tag v" + current)
89
- print(" git push origin v" + current)
90
- print(" python -m build && twine upload dist/*")
91
- return 0
92
-
93
-
94
- if __name__ == "__main__":
95
- sys.exit(main())
1
+ """Check if a new PyPI release is needed.
2
+
3
+ Compares the version in pyproject.toml with the latest git tag.
4
+ Exits with code 0 if no release needed, 1 if a release is recommended.
5
+
6
+ Usage:
7
+ python scripts/check-release.py
8
+ python scripts/check-release.py --verbose
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ import subprocess
15
+ import sys
16
+ from pathlib import Path
17
+
18
+
19
+ def get_current_version() -> str:
20
+ pyproject = Path(__file__).parent.parent / "pyproject.toml"
21
+ text = pyproject.read_text()
22
+ m = re.search(r'^version\s*=\s*"([^"]+)"', text, re.MULTILINE)
23
+ if m:
24
+ return m.group(1)
25
+ raise RuntimeError("Could not find version in pyproject.toml")
26
+
27
+
28
+ def get_latest_tag() -> str | None:
29
+ result = subprocess.run(
30
+ ["git", "tag", "--list", "v*", "--sort=-version:refname"],
31
+ capture_output=True, text=True, cwd=Path(__file__).parent.parent,
32
+ )
33
+ tags = [t.strip() for t in result.stdout.splitlines() if t.strip()]
34
+ return tags[0] if tags else None
35
+
36
+
37
+ def get_commit_count_since_tag(tag: str) -> int:
38
+ result = subprocess.run(
39
+ ["git", "rev-list", f"{tag}..HEAD", "--count"],
40
+ capture_output=True, text=True, cwd=Path(__file__).parent.parent,
41
+ )
42
+ return int(result.stdout.strip() or "0")
43
+
44
+
45
+ def main() -> int:
46
+ verbose = "--verbose" in sys.argv or "-v" in sys.argv
47
+
48
+ current = get_current_version()
49
+ latest_tag = get_latest_tag()
50
+
51
+ print(f"Current version (pyproject.toml): {current}")
52
+
53
+ if latest_tag is None:
54
+ print("No git tags found — this would be the first release.")
55
+ return 0
56
+
57
+ tag_version = latest_tag.lstrip("v")
58
+ print(f"Latest git tag: {latest_tag}")
59
+
60
+ commits_since = get_commit_count_since_tag(latest_tag)
61
+
62
+ if current == tag_version:
63
+ if commits_since > 0:
64
+ print(f"\n==> {commits_since} commit(s) since {latest_tag}, "
65
+ f"but version hasn't been bumped ({current}).")
66
+ if verbose:
67
+ result = subprocess.run(
68
+ ["git", "log", f"{latest_tag}..HEAD", "--oneline"],
69
+ capture_output=True, text=True,
70
+ cwd=Path(__file__).parent.parent,
71
+ )
72
+ print("\nUnreleased commits:")
73
+ for line in result.stdout.splitlines():
74
+ print(f" {line}")
75
+ print("\n=> Run `python -m build` and publish when ready:")
76
+ print(" python -m build")
77
+ print(" twine upload dist/*")
78
+ return 1
79
+ else:
80
+ print("\n[ok] Everything is up to date. No release needed.")
81
+ return 0
82
+ else:
83
+ print(f"\n=> Version in pyproject.toml ({current}) differs from "
84
+ f"latest tag ({tag_version}).")
85
+ print(" A new release may be ready to publish.")
86
+ if verbose:
87
+ print("\n To publish:")
88
+ print(" git tag v" + current)
89
+ print(" git push origin v" + current)
90
+ print(" python -m build && twine upload dist/*")
91
+ return 0
92
+
93
+
94
+ if __name__ == "__main__":
95
+ sys.exit(main())
@@ -8,8 +8,9 @@ from opencode import opencode
8
8
  def main() -> None:
9
9
  args = sys.argv[1:]
10
10
  if not args:
11
- print("Usage: python -m opencode <prompt>")
12
- print(" or: echo 'question' | python -m opencode")
11
+ print("Usage: opencode-py <prompt>")
12
+ print(" or: python -m opencode <prompt>")
13
+ print(" or: echo 'question' | opencode-py")
13
14
  sys.exit(1)
14
15
  prompt = " ".join(args)
15
16
  result = opencode(prompt)
@@ -1,94 +1,94 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
-
6
- class OpencodeError(Exception):
7
- """Base exception for all opencode SDK errors."""
8
-
9
- def __init__(
10
- self,
11
- message: str = "",
12
- *,
13
- status: int | None = None,
14
- body: object = None,
15
- ):
16
- self.status = status
17
- self.body = body
18
- super().__init__(message)
19
-
20
-
21
- class APIError(OpencodeError):
22
- """Base class for all API-related errors."""
23
-
24
- def __init__(
25
- self,
26
- message: str = "",
27
- *,
28
- request: Any = None,
29
- response: Any = None,
30
- body: object = None,
31
- status_code: int | None = None,
32
- ):
33
- self.request = request
34
- self.response = response
35
- super().__init__(message, status=status_code, body=body)
36
-
37
-
38
- class APIResponseValidationError(APIError):
39
- """Response data did not match the expected schema."""
40
-
41
- def __init__(self, message: str = "", *, response: Any = None, body: object = None):
42
- super().__init__(message, response=response, body=body)
43
-
44
-
45
- class APIStatusError(APIError):
46
- """Base class for errors with an HTTP status code."""
47
-
48
-
49
- class BadRequestError(APIStatusError):
50
- """HTTP 400"""
51
-
52
-
53
- class AuthenticationError(APIStatusError):
54
- """HTTP 401"""
55
-
56
-
57
- class PermissionDeniedError(APIStatusError):
58
- """HTTP 403"""
59
-
60
-
61
- class NotFoundError(APIStatusError):
62
- """HTTP 404"""
63
-
64
-
65
- class ConflictError(APIStatusError):
66
- """HTTP 409"""
67
-
68
-
69
- class UnprocessableEntityError(APIStatusError):
70
- """HTTP 422"""
71
-
72
-
73
- class RateLimitError(APIStatusError):
74
- """HTTP 429"""
75
-
76
-
77
- class InternalServerError(APIStatusError):
78
- """HTTP 5xx"""
79
-
80
-
81
- class APIConnectionError(APIError):
82
- """Connection or transport error."""
83
-
84
-
85
- class APITimeoutError(APIConnectionError):
86
- """Request timed out."""
87
-
88
-
89
- class BinaryNotFoundError(OpencodeError):
90
- """Opencode binary not found on the system."""
91
-
92
-
93
- class ServerStartupTimeoutError(OpencodeError):
94
- """Server did not start within the expected timeout."""
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ class OpencodeError(Exception):
7
+ """Base exception for all opencode SDK errors."""
8
+
9
+ def __init__(
10
+ self,
11
+ message: str = "",
12
+ *,
13
+ status: int | None = None,
14
+ body: object = None,
15
+ ):
16
+ self.status = status
17
+ self.body = body
18
+ super().__init__(message)
19
+
20
+
21
+ class APIError(OpencodeError):
22
+ """Base class for all API-related errors."""
23
+
24
+ def __init__(
25
+ self,
26
+ message: str = "",
27
+ *,
28
+ request: Any = None,
29
+ response: Any = None,
30
+ body: object = None,
31
+ status_code: int | None = None,
32
+ ):
33
+ self.request = request
34
+ self.response = response
35
+ super().__init__(message, status=status_code, body=body)
36
+
37
+
38
+ class APIResponseValidationError(APIError):
39
+ """Response data did not match the expected schema."""
40
+
41
+ def __init__(self, message: str = "", *, response: Any = None, body: object = None):
42
+ super().__init__(message, response=response, body=body)
43
+
44
+
45
+ class APIStatusError(APIError):
46
+ """Base class for errors with an HTTP status code."""
47
+
48
+
49
+ class BadRequestError(APIStatusError):
50
+ """HTTP 400"""
51
+
52
+
53
+ class AuthenticationError(APIStatusError):
54
+ """HTTP 401"""
55
+
56
+
57
+ class PermissionDeniedError(APIStatusError):
58
+ """HTTP 403"""
59
+
60
+
61
+ class NotFoundError(APIStatusError):
62
+ """HTTP 404"""
63
+
64
+
65
+ class ConflictError(APIStatusError):
66
+ """HTTP 409"""
67
+
68
+
69
+ class UnprocessableEntityError(APIStatusError):
70
+ """HTTP 422"""
71
+
72
+
73
+ class RateLimitError(APIStatusError):
74
+ """HTTP 429"""
75
+
76
+
77
+ class InternalServerError(APIStatusError):
78
+ """HTTP 5xx"""
79
+
80
+
81
+ class APIConnectionError(APIError):
82
+ """Connection or transport error."""
83
+
84
+
85
+ class APITimeoutError(APIConnectionError):
86
+ """Request timed out."""
87
+
88
+
89
+ class BinaryNotFoundError(OpencodeError):
90
+ """Opencode binary not found on the system."""
91
+
92
+
93
+ class ServerStartupTimeoutError(OpencodeError):
94
+ """Server did not start within the expected timeout."""