devin-cli 0.1.2__tar.gz → 1.1.0__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 (61) hide show
  1. devin_cli-1.1.0/PKG-INFO +170 -0
  2. devin_cli-1.1.0/README.md +134 -0
  3. devin_cli-1.1.0/generate_facades.py +21 -0
  4. {devin_cli-0.1.2 → devin_cli-1.1.0}/pyproject.toml +1 -1
  5. devin_cli-1.1.0/src/devin_cli/api/attachments.py +13 -0
  6. devin_cli-1.1.0/src/devin_cli/api/client.py +165 -0
  7. devin_cli-1.1.0/src/devin_cli/api/consumption.py +13 -0
  8. devin_cli-1.1.0/src/devin_cli/api/knowledge.py +13 -0
  9. devin_cli-1.1.0/src/devin_cli/api/members.py +13 -0
  10. devin_cli-1.1.0/src/devin_cli/api/organizations.py +13 -0
  11. devin_cli-1.1.0/src/devin_cli/api/playbooks.py +13 -0
  12. devin_cli-1.1.0/src/devin_cli/api/repositories.py +13 -0
  13. devin_cli-1.1.0/src/devin_cli/api/schedules.py +13 -0
  14. devin_cli-1.1.0/src/devin_cli/api/secrets.py +13 -0
  15. devin_cli-1.1.0/src/devin_cli/api/sessions.py +13 -0
  16. devin_cli-1.1.0/src/devin_cli/api/v1/__init__.py +0 -0
  17. devin_cli-1.1.0/src/devin_cli/api/v1/attachments.py +16 -0
  18. {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v1}/knowledge.py +12 -9
  19. {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v1}/playbooks.py +4 -4
  20. {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v1}/sessions.py +2 -1
  21. devin_cli-1.1.0/src/devin_cli/api/v3/__init__.py +0 -0
  22. {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v3}/attachments.py +4 -0
  23. devin_cli-1.1.0/src/devin_cli/api/v3/consumption.py +22 -0
  24. devin_cli-1.1.0/src/devin_cli/api/v3/knowledge.py +43 -0
  25. devin_cli-1.1.0/src/devin_cli/api/v3/members.py +10 -0
  26. devin_cli-1.1.0/src/devin_cli/api/v3/organizations.py +9 -0
  27. devin_cli-1.1.0/src/devin_cli/api/v3/playbooks.py +49 -0
  28. devin_cli-1.1.0/src/devin_cli/api/v3/repositories.py +44 -0
  29. devin_cli-1.1.0/src/devin_cli/api/v3/schedules.py +60 -0
  30. devin_cli-1.1.0/src/devin_cli/api/v3/secrets.py +10 -0
  31. devin_cli-1.1.0/src/devin_cli/api/v3/sessions.py +93 -0
  32. devin_cli-1.1.0/src/devin_cli/cli.py +552 -0
  33. devin_cli-1.1.0/src/devin_cli/config.py +153 -0
  34. devin_cli-1.1.0/test_duplicate.py +19 -0
  35. devin_cli-1.1.0/tests/conftest.py +8 -0
  36. {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/test_api/test_attachments.py +14 -4
  37. {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/test_api/test_knowledge.py +11 -12
  38. {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/test_api/test_playbooks.py +16 -8
  39. devin_cli-1.1.0/tests/test_api/test_repositories.py +33 -0
  40. devin_cli-1.1.0/tests/test_api/test_schedules.py +38 -0
  41. devin_cli-1.1.0/tests/test_api/test_secrets.py +34 -0
  42. devin_cli-1.1.0/tests/test_api/test_sessions.py +69 -0
  43. {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/test_cli.py +3 -4
  44. {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/test_config.py +1 -1
  45. devin_cli-0.1.2/PKG-INFO +0 -282
  46. devin_cli-0.1.2/README.md +0 -246
  47. devin_cli-0.1.2/src/devin_cli/api/client.py +0 -103
  48. devin_cli-0.1.2/src/devin_cli/cli.py +0 -600
  49. devin_cli-0.1.2/src/devin_cli/config.py +0 -67
  50. devin_cli-0.1.2/tests/test_api/test_secrets.py +0 -25
  51. devin_cli-0.1.2/tests/test_api/test_sessions.py +0 -59
  52. {devin_cli-0.1.2 → devin_cli-1.1.0}/.github/workflows/pypi-publish.yml +0 -0
  53. {devin_cli-0.1.2 → devin_cli-1.1.0}/.gitignore +0 -0
  54. {devin_cli-0.1.2 → devin_cli-1.1.0}/CHANGELOG.md +0 -0
  55. {devin_cli-0.1.2 → devin_cli-1.1.0}/LICENSE +0 -0
  56. {devin_cli-0.1.2 → devin_cli-1.1.0}/assets/logo.png +0 -0
  57. {devin_cli-0.1.2 → devin_cli-1.1.0}/setup.py +0 -0
  58. {devin_cli-0.1.2 → devin_cli-1.1.0}/src/devin_cli/__init__.py +0 -0
  59. {devin_cli-0.1.2 → devin_cli-1.1.0}/src/devin_cli/api/__init__.py +0 -0
  60. {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v1}/secrets.py +0 -0
  61. {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/__init__.py +0 -0
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: devin-cli
3
+ Version: 1.1.0
4
+ Summary: Unofficial CLI for Devin AI - The first AI Software Engineer
5
+ Project-URL: Homepage, https://github.com/revanthpobala/devin-cli
6
+ Project-URL: Repository, https://github.com/revanthpobala/devin-cli.git
7
+ Project-URL: Issues, https://github.com/revanthpobala/devin-cli/issues
8
+ Author-email: revanth <revanth@example.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,ai,automation,autonomous,cli,cognition,devin,software-engineer,terminal
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Build Tools
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: httpx>=0.27.0
26
+ Requires-Dist: pyyaml>=6.0
27
+ Requires-Dist: rich>=13.0.0
28
+ Requires-Dist: typer[all]>=0.9.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: build; extra == 'dev'
31
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
32
+ Requires-Dist: pytest>=7.0; extra == 'dev'
33
+ Requires-Dist: respx>=0.20.0; extra == 'dev'
34
+ Requires-Dist: twine; extra == 'dev'
35
+ Description-Content-Type: text/markdown
36
+
37
+ <p align="center">
38
+ <img src="https://raw.githubusercontent.com/revanthpobala/devin-cli/main/assets/logo.png" alt="Devin CLI Logo" width="300">
39
+ </p>
40
+
41
+ # Devin CLI (Unofficial) — The Professional Terminal Interface for Devin AI
42
+
43
+ <p align="center">
44
+ <a href="https://pypi.org/project/devin-cli/"><img src="https://img.shields.io/pypi/v/devin-cli.svg?style=for-the-badge&color=0294DE" alt="PyPI version"></a>
45
+ <a href="https://github.com/revanthpobala/homebrew-tap"><img src="https://img.shields.io/badge/Homebrew-Tap-orange?style=for-the-badge&logo=homebrew" alt="Homebrew Tap"></a>
46
+ <a href="https://github.com/revanthpobala/devin-cli/actions/workflows/pypi-publish.yml"><img src="https://github.com/revanthpobala/devin-cli/actions/workflows/pypi-publish.yml/badge.svg" alt="Build Status"></a>
47
+ </p>
48
+
49
+ > **The first unofficial CLI for the world's first AI Software Engineer. Now upgraded to v1.1.0 with Multi-Profile support and legacy v1 backward-compatibility.**
50
+
51
+ Devin CLI is designed for high-velocity engineering teams. It strips away the friction of the web UI, allowing you to orchestrate autonomous agents, manage complex contexts, and automate multi-step development workflows through a robust, terminal-first interface. Built for performance, SEO, and developer productivity.
52
+
53
+ ---
54
+
55
+ ## ⚡ Quick Start
56
+
57
+ ### 1. Installation
58
+
59
+ **Recommended: Via Homebrew (macOS)**
60
+ ```bash
61
+ brew tap revanthpobala/tap
62
+ brew install devin-cli
63
+ ```
64
+
65
+ **Via pipx (Isolated environment)**
66
+ ```bash
67
+ pipx install devin-cli
68
+ ```
69
+
70
+ **Via pip**
71
+ ```bash
72
+ pip install devin-cli
73
+ ```
74
+
75
+ ### 2. Configuration
76
+ ```bash
77
+ devin configure
78
+ # Paste your v3 API token (apk_... or cog_...) from https://preview.devin.ai/settings
79
+ # Optionally configure your Organization ID here.
80
+ ```
81
+
82
+ ### 3. Your First Session
83
+ ```bash
84
+ devin sessions create -t "Identify and fix the race condition in our Redis cache layer"
85
+ ```
86
+
87
+ ## 🌟 What's New in v1.1.0
88
+
89
+ - **Dual-Token Profiles:** Seamlessly switch between personal (`apk_user_`) and Service Account (`cog_`) tokens on the fly using `--profile`.
90
+ - `devin configure --profile service`
91
+ - `devin --profile service sessions list`
92
+ - **Legacy v1 Backward Compatibility:** Need to run an older integration that depends on the original Devin API shapes? The CLI now acts as a dynamic proxy. Simply configure a profile to use `api_version: v1`, and the CLI will route authentic payloads directly to the deprecated v1 endpoints without breaking your v3 integrations.
93
+ - **Session Deduplication (Anti-Spam):** Avoid burning ACUs on accidental retries. The CLI now actively caches a SHA-256 hash of your last 50 prompts per profile, immediately halting and alerting you if you attempt to launch a duplicate session.
94
+ - **Advanced Mode Auth:** Automatically traps `advanced_mode_url` requirements and securely prompts you to finish the connection handshake directly in your web browser.
95
+
96
+ ---
97
+
98
+ ## 🛠 Command Cheat Sheet (v3 Architecture)
99
+
100
+ The v3 architecture introduces a modular, hierarchical CLI structure focusing on enterprise features, secrets, and organizational management. Every sub-command supports the `--org` flag to override your active organization on the fly.
101
+
102
+ | Category | Commands | Description |
103
+ | :--- | :--- | :--- |
104
+ | **Sessions** | `create`, `list`, `get`, `insights`, `cost`, `messages`, `message`, `terminate` | Core agent lifecycle and analytics. |
105
+ | **Knowledge** | `list`, `create`, `delete` | Manage organizational context and AI memory. |
106
+ | **Playbooks** | `list`, `create`, `delete` | Automate complex, multi-step agent workflows. |
107
+ | **Secrets** | `list`, `create`, `delete` | Manage API keys passing to Devin sessions. |
108
+ | **Schedules** | `list`, `create` | Schedule recurring autonomous tasks via CRON. |
109
+ | **Repositories** | `list`, `index` | Force indexing of Git repositories. |
110
+ | **Attachments** | `upload`, `download` | Transfer context files seamlessly. |
111
+ | **Enterprise** | `whoami`, `list-orgs` | Administrative identity discovery. |
112
+ | **Global** | `configure`, `use` | CLI setup and active session swapping. |
113
+
114
+ ### Example Automations
115
+
116
+ **Blocking Call for CI/CD:**
117
+ ```bash
118
+ # Trigger Devin and wait for unit tests to be fixed
119
+ devin sessions create "Fix the failing authentication tests"
120
+ echo "Devin finished. Running integration tests..."
121
+ npm test
122
+ ```
123
+
124
+ **Audit Subsystem Costs:**
125
+ ```bash
126
+ # Get ACU consumption for a specific incident
127
+ devin sessions cost --id <SESSION_ID>
128
+ ```
129
+
130
+ ---
131
+
132
+ ## 📟 Integration & Environment Variables
133
+
134
+ Devin CLI is designed for CI/CD. Use environment variables to bypass the `configure` step entirely.
135
+
136
+ - `DEVIN_API_TOKEN`: Your API token.
137
+ - `DEVIN_ORG_ID`: Your target organization ID.
138
+ - `DEVIN_BASE_URL`: (Optional) Overrides the standard `https://api.devin.ai/v3`.
139
+
140
+ ```yaml
141
+ # Example GitHub Action Step
142
+ env:
143
+ DEVIN_API_TOKEN: ${{ secrets.DEVIN_API_TOKEN }}
144
+ DEVIN_ORG_ID: ${{ secrets.DEVIN_ORG_ID }}
145
+ run: |
146
+ devin sessions create "Review PR #${{ github.event.pull_request.number }}"
147
+ ```
148
+
149
+ ---
150
+
151
+ ## ⚙️ Engineering Specs
152
+ - **Architecture**: Complete Devin API `v3` Support (including `v3beta1` and `enterprise` endpoints).
153
+ - **Config Storage**: `~/.config/devin/config.json`
154
+ - **Platform Support**: Linux, macOS, WSL2
155
+
156
+ ---
157
+
158
+ ## 🧪 Developer Hub
159
+ ```bash
160
+ # Setup
161
+ pip install -e ".[dev]"
162
+
163
+ # Test Suite (100% path coverage)
164
+ PYTHONPATH=src python3 -m pytest
165
+ ```
166
+
167
+ ---
168
+
169
+ ## 📄 License
170
+ MIT. **Devin CLI** is an unofficial community project and is not affiliated with Cognition AI.
@@ -0,0 +1,134 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/revanthpobala/devin-cli/main/assets/logo.png" alt="Devin CLI Logo" width="300">
3
+ </p>
4
+
5
+ # Devin CLI (Unofficial) — The Professional Terminal Interface for Devin AI
6
+
7
+ <p align="center">
8
+ <a href="https://pypi.org/project/devin-cli/"><img src="https://img.shields.io/pypi/v/devin-cli.svg?style=for-the-badge&color=0294DE" alt="PyPI version"></a>
9
+ <a href="https://github.com/revanthpobala/homebrew-tap"><img src="https://img.shields.io/badge/Homebrew-Tap-orange?style=for-the-badge&logo=homebrew" alt="Homebrew Tap"></a>
10
+ <a href="https://github.com/revanthpobala/devin-cli/actions/workflows/pypi-publish.yml"><img src="https://github.com/revanthpobala/devin-cli/actions/workflows/pypi-publish.yml/badge.svg" alt="Build Status"></a>
11
+ </p>
12
+
13
+ > **The first unofficial CLI for the world's first AI Software Engineer. Now upgraded to v1.1.0 with Multi-Profile support and legacy v1 backward-compatibility.**
14
+
15
+ Devin CLI is designed for high-velocity engineering teams. It strips away the friction of the web UI, allowing you to orchestrate autonomous agents, manage complex contexts, and automate multi-step development workflows through a robust, terminal-first interface. Built for performance, SEO, and developer productivity.
16
+
17
+ ---
18
+
19
+ ## ⚡ Quick Start
20
+
21
+ ### 1. Installation
22
+
23
+ **Recommended: Via Homebrew (macOS)**
24
+ ```bash
25
+ brew tap revanthpobala/tap
26
+ brew install devin-cli
27
+ ```
28
+
29
+ **Via pipx (Isolated environment)**
30
+ ```bash
31
+ pipx install devin-cli
32
+ ```
33
+
34
+ **Via pip**
35
+ ```bash
36
+ pip install devin-cli
37
+ ```
38
+
39
+ ### 2. Configuration
40
+ ```bash
41
+ devin configure
42
+ # Paste your v3 API token (apk_... or cog_...) from https://preview.devin.ai/settings
43
+ # Optionally configure your Organization ID here.
44
+ ```
45
+
46
+ ### 3. Your First Session
47
+ ```bash
48
+ devin sessions create -t "Identify and fix the race condition in our Redis cache layer"
49
+ ```
50
+
51
+ ## 🌟 What's New in v1.1.0
52
+
53
+ - **Dual-Token Profiles:** Seamlessly switch between personal (`apk_user_`) and Service Account (`cog_`) tokens on the fly using `--profile`.
54
+ - `devin configure --profile service`
55
+ - `devin --profile service sessions list`
56
+ - **Legacy v1 Backward Compatibility:** Need to run an older integration that depends on the original Devin API shapes? The CLI now acts as a dynamic proxy. Simply configure a profile to use `api_version: v1`, and the CLI will route authentic payloads directly to the deprecated v1 endpoints without breaking your v3 integrations.
57
+ - **Session Deduplication (Anti-Spam):** Avoid burning ACUs on accidental retries. The CLI now actively caches a SHA-256 hash of your last 50 prompts per profile, immediately halting and alerting you if you attempt to launch a duplicate session.
58
+ - **Advanced Mode Auth:** Automatically traps `advanced_mode_url` requirements and securely prompts you to finish the connection handshake directly in your web browser.
59
+
60
+ ---
61
+
62
+ ## 🛠 Command Cheat Sheet (v3 Architecture)
63
+
64
+ The v3 architecture introduces a modular, hierarchical CLI structure focusing on enterprise features, secrets, and organizational management. Every sub-command supports the `--org` flag to override your active organization on the fly.
65
+
66
+ | Category | Commands | Description |
67
+ | :--- | :--- | :--- |
68
+ | **Sessions** | `create`, `list`, `get`, `insights`, `cost`, `messages`, `message`, `terminate` | Core agent lifecycle and analytics. |
69
+ | **Knowledge** | `list`, `create`, `delete` | Manage organizational context and AI memory. |
70
+ | **Playbooks** | `list`, `create`, `delete` | Automate complex, multi-step agent workflows. |
71
+ | **Secrets** | `list`, `create`, `delete` | Manage API keys passing to Devin sessions. |
72
+ | **Schedules** | `list`, `create` | Schedule recurring autonomous tasks via CRON. |
73
+ | **Repositories** | `list`, `index` | Force indexing of Git repositories. |
74
+ | **Attachments** | `upload`, `download` | Transfer context files seamlessly. |
75
+ | **Enterprise** | `whoami`, `list-orgs` | Administrative identity discovery. |
76
+ | **Global** | `configure`, `use` | CLI setup and active session swapping. |
77
+
78
+ ### Example Automations
79
+
80
+ **Blocking Call for CI/CD:**
81
+ ```bash
82
+ # Trigger Devin and wait for unit tests to be fixed
83
+ devin sessions create "Fix the failing authentication tests"
84
+ echo "Devin finished. Running integration tests..."
85
+ npm test
86
+ ```
87
+
88
+ **Audit Subsystem Costs:**
89
+ ```bash
90
+ # Get ACU consumption for a specific incident
91
+ devin sessions cost --id <SESSION_ID>
92
+ ```
93
+
94
+ ---
95
+
96
+ ## 📟 Integration & Environment Variables
97
+
98
+ Devin CLI is designed for CI/CD. Use environment variables to bypass the `configure` step entirely.
99
+
100
+ - `DEVIN_API_TOKEN`: Your API token.
101
+ - `DEVIN_ORG_ID`: Your target organization ID.
102
+ - `DEVIN_BASE_URL`: (Optional) Overrides the standard `https://api.devin.ai/v3`.
103
+
104
+ ```yaml
105
+ # Example GitHub Action Step
106
+ env:
107
+ DEVIN_API_TOKEN: ${{ secrets.DEVIN_API_TOKEN }}
108
+ DEVIN_ORG_ID: ${{ secrets.DEVIN_ORG_ID }}
109
+ run: |
110
+ devin sessions create "Review PR #${{ github.event.pull_request.number }}"
111
+ ```
112
+
113
+ ---
114
+
115
+ ## ⚙️ Engineering Specs
116
+ - **Architecture**: Complete Devin API `v3` Support (including `v3beta1` and `enterprise` endpoints).
117
+ - **Config Storage**: `~/.config/devin/config.json`
118
+ - **Platform Support**: Linux, macOS, WSL2
119
+
120
+ ---
121
+
122
+ ## 🧪 Developer Hub
123
+ ```bash
124
+ # Setup
125
+ pip install -e ".[dev]"
126
+
127
+ # Test Suite (100% path coverage)
128
+ PYTHONPATH=src python3 -m pytest
129
+ ```
130
+
131
+ ---
132
+
133
+ ## 📄 License
134
+ MIT. **Devin CLI** is an unofficial community project and is not affiliated with Cognition AI.
@@ -0,0 +1,21 @@
1
+ import os
2
+
3
+ modules = ["attachments", "consumption", "knowledge", "members", "organizations", "playbooks", "repositories", "schedules", "secrets", "sessions"]
4
+
5
+ for mod in modules:
6
+ content = f"""from devin_cli.config import config
7
+ import importlib
8
+
9
+ def _get_impl():
10
+ if config.api_version == "v1":
11
+ try:
12
+ return importlib.import_module(f"devin_cli.api.v1.{mod}")
13
+ except ImportError:
14
+ raise NotImplementedError(f"'{mod}' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
15
+ return importlib.import_module(f"devin_cli.api.v3.{mod}")
16
+
17
+ def __getattr__(name):
18
+ return getattr(_get_impl(), name)
19
+ """
20
+ with open(f"src/devin_cli/api/{mod}.py", "w") as f:
21
+ f.write(content)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "devin-cli"
3
- version = "0.1.2"
3
+ version = "1.1.0"
4
4
  description = "Unofficial CLI for Devin AI - The first AI Software Engineer"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.attachments")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'attachments' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.attachments")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
@@ -0,0 +1,165 @@
1
+ import httpx
2
+ from devin_cli.config import config
3
+ from rich.console import Console
4
+ import sys
5
+ from typing import Optional, Any, Dict
6
+
7
+ console = Console()
8
+
9
+ class APIError(Exception):
10
+ def __init__(self, message: str, status_code: Optional[int] = None):
11
+ super().__init__(message)
12
+ self.status_code = status_code
13
+
14
+ class BaseClient:
15
+ def __init__(self):
16
+ pass
17
+
18
+ @property
19
+ def token(self) -> Optional[str]:
20
+ return config.api_token
21
+
22
+ @property
23
+ def headers(self) -> Dict[str, str]:
24
+ t = self.token
25
+ headers = {
26
+ "Content-Type": "application/json",
27
+ }
28
+ if t:
29
+ headers["Authorization"] = f"Bearer {t}"
30
+ return headers
31
+
32
+ @property
33
+ def BASE_URL(self) -> str:
34
+ return config.base_url.rstrip("/")
35
+
36
+ def _ensure_token(self):
37
+ if not self.token:
38
+ # Only raise error if meaningful operation is attempted
39
+ raise APIError("API token not found. Run 'devin configure' to set your API token.")
40
+
41
+ def _handle_response(self, response: httpx.Response) -> Any:
42
+ try:
43
+ response.raise_for_status()
44
+ except httpx.HTTPStatusError as e:
45
+ status = e.response.status_code
46
+ path = str(e.request.url.path)
47
+
48
+ error_detail = ""
49
+ try:
50
+ error_body = e.response.json()
51
+ error_detail = error_body.get("detail") or error_body.get("message") or error_body.get("error") or str(error_body)
52
+ except Exception:
53
+ error_detail = e.response.text.strip()
54
+
55
+ msg_suffix = f"\n Path: {path}"
56
+ if error_detail:
57
+ msg_suffix += f"\n Detail: {error_detail}"
58
+
59
+ if status == 401:
60
+ raise APIError(f"Invalid or expired API token (401).{msg_suffix}", status)
61
+ elif status == 403:
62
+ raise APIError(f"Insufficient permissions (403).{msg_suffix}", status)
63
+ elif status == 404:
64
+ raise APIError(f"Resource not found (404).{msg_suffix}", status)
65
+ elif status == 422:
66
+ raise APIError(f"Validation Error (422).{msg_suffix}", status)
67
+ elif status == 429:
68
+ raise APIError(f"Rate limit exceeded (429). Please try again later.", status)
69
+ elif status >= 500:
70
+ raise APIError(f"Server error ({status}).{msg_suffix}", status)
71
+ else:
72
+ raise APIError(f"HTTP Error {status}.{msg_suffix}", status)
73
+ if response.status_code == 204:
74
+ return None
75
+
76
+ content_type = response.headers.get("Content-Type", "")
77
+ if "application/json" in content_type:
78
+ try:
79
+ return response.json()
80
+ except ValueError:
81
+ return response.text
82
+
83
+ # If it's not JSON, it might be an attachment or raw text
84
+ if any(t in content_type for t in ["image/", "application/octet-stream", "application/pdf"]):
85
+ return response.content
86
+
87
+ return response.text
88
+
89
+ def _format_url(self, endpoint: str) -> str:
90
+ raise NotImplementedError()
91
+
92
+ def request(self, method: str, endpoint: str, **kwargs) -> Any:
93
+ self._ensure_token()
94
+ url = self._format_url(endpoint)
95
+
96
+ # Merge headers if needed, but usually self.headers is enough
97
+ headers = self.headers.copy()
98
+ if "headers" in kwargs:
99
+ headers.update(kwargs.pop("headers"))
100
+
101
+ # Handle file uploads (remove Content-Type)
102
+ if "files" in kwargs:
103
+ headers.pop("Content-Type", None)
104
+
105
+ try:
106
+ with httpx.Client(follow_redirects=True, verify=False, timeout=httpx.Timeout(120.0)) as client:
107
+ response = client.request(method, url, headers=headers, **kwargs)
108
+ return self._handle_response(response)
109
+ except httpx.RequestError as e:
110
+ raise APIError(f"Network error: {e}")
111
+
112
+ def get(self, endpoint: str, **kwargs) -> Any:
113
+ return self.request("GET", endpoint, **kwargs)
114
+
115
+ def post(self, endpoint: str, **kwargs) -> Any:
116
+ return self.request("POST", endpoint, **kwargs)
117
+
118
+ def put(self, endpoint: str, **kwargs) -> Any:
119
+ return self.request("PUT", endpoint, **kwargs)
120
+
121
+ def delete(self, endpoint: str, **kwargs) -> Any:
122
+ return self.request("DELETE", endpoint, **kwargs)
123
+
124
+ class V3Client(BaseClient):
125
+ def _format_url(self, endpoint: str) -> str:
126
+ endpoint = endpoint.lstrip("/")
127
+ if endpoint.startswith("v3beta1/"):
128
+ base = config.base_url.replace("/v3", "").replace("/v2", "").replace("/v1", "").rstrip("/")
129
+ url = f"{base}/{endpoint}"
130
+ if config.org_id and "/organizations/" not in url:
131
+ url = url.replace("v3beta1/", f"v3beta1/organizations/{config.org_id}/")
132
+ return url
133
+ elif endpoint.startswith("enterprise/"):
134
+ return f"{self.BASE_URL}/{endpoint}"
135
+ else:
136
+ if config.org_id and not endpoint.startswith("organizations/"):
137
+ endpoint = f"organizations/{config.org_id}/{endpoint}"
138
+ return f"{self.BASE_URL}/{endpoint}"
139
+
140
+ class V1Client(BaseClient):
141
+ def _format_url(self, endpoint: str) -> str:
142
+ return f"{self.BASE_URL}/{endpoint.lstrip('/')}"
143
+
144
+ class ClientProxy:
145
+ def __init__(self):
146
+ self._v1 = V1Client()
147
+ self._v3 = V3Client()
148
+
149
+ @property
150
+ def _client(self) -> BaseClient:
151
+ return self._v1 if config.api_version == "v1" else self._v3
152
+
153
+ def get(self, endpoint: str, **kwargs) -> Any:
154
+ return self._client.get(endpoint, **kwargs)
155
+
156
+ def post(self, endpoint: str, **kwargs) -> Any:
157
+ return self._client.post(endpoint, **kwargs)
158
+
159
+ def put(self, endpoint: str, **kwargs) -> Any:
160
+ return self._client.put(endpoint, **kwargs)
161
+
162
+ def delete(self, endpoint: str, **kwargs) -> Any:
163
+ return self._client.delete(endpoint, **kwargs)
164
+
165
+ client = ClientProxy()
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.consumption")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'consumption' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.consumption")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.knowledge")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'knowledge' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.knowledge")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.members")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'members' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.members")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.organizations")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'organizations' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.organizations")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.playbooks")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'playbooks' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.playbooks")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.repositories")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'repositories' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.repositories")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.schedules")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'schedules' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.schedules")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.secrets")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'secrets' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.secrets")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
@@ -0,0 +1,13 @@
1
+ from devin_cli.config import config
2
+ import importlib
3
+
4
+ def _get_impl():
5
+ if config.api_version == "v1":
6
+ try:
7
+ return importlib.import_module(f"devin_cli.api.v1.sessions")
8
+ except ImportError:
9
+ raise NotImplementedError(f"'sessions' feature is only available in Devin API v3, or is not implemented for v1. Run 'devin configure' to switch your API version to v3.")
10
+ return importlib.import_module(f"devin_cli.api.v3.sessions")
11
+
12
+ def __getattr__(name):
13
+ return getattr(_get_impl(), name)
File without changes
@@ -0,0 +1,16 @@
1
+ import mimetypes
2
+ from pathlib import Path
3
+ from devin_cli.api.client import client
4
+
5
+ def upload_file(file_path: str, **kwargs):
6
+ path = Path(file_path)
7
+ mime_type, _ = mimetypes.guess_type(path)
8
+ mime_type = mime_type or "application/octet-stream"
9
+
10
+ with open(path, "rb") as f:
11
+ # V1 used multipart/form-data with 'file' argument
12
+ return client.post("attachments", files={"file": (path.name, f, mime_type)})
13
+
14
+ def download_attachment(attachment_id: str, file_name: str, **kwargs):
15
+ # Depending on exactly what v1 required, it might just be the direct path or stream
16
+ return client.get(f"attachments/{attachment_id}")