devin-cli 0.1.2__tar.gz → 1.0.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.
- devin_cli-1.0.0/PKG-INFO +161 -0
- devin_cli-1.0.0/README.md +125 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/pyproject.toml +1 -1
- {devin_cli-0.1.2 → devin_cli-1.0.0}/src/devin_cli/api/attachments.py +4 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/src/devin_cli/api/client.py +47 -27
- devin_cli-1.0.0/src/devin_cli/api/consumption.py +22 -0
- devin_cli-1.0.0/src/devin_cli/api/knowledge.py +43 -0
- devin_cli-1.0.0/src/devin_cli/api/members.py +10 -0
- devin_cli-1.0.0/src/devin_cli/api/organizations.py +9 -0
- devin_cli-1.0.0/src/devin_cli/api/playbooks.py +53 -0
- devin_cli-1.0.0/src/devin_cli/api/repositories.py +44 -0
- devin_cli-1.0.0/src/devin_cli/api/schedules.py +60 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/src/devin_cli/api/secrets.py +3 -0
- devin_cli-1.0.0/src/devin_cli/api/sessions.py +93 -0
- devin_cli-1.0.0/src/devin_cli/cli.py +457 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/src/devin_cli/config.py +20 -1
- {devin_cli-0.1.2 → devin_cli-1.0.0}/tests/test_api/test_attachments.py +14 -4
- {devin_cli-0.1.2 → devin_cli-1.0.0}/tests/test_api/test_knowledge.py +11 -12
- {devin_cli-0.1.2 → devin_cli-1.0.0}/tests/test_api/test_playbooks.py +15 -7
- devin_cli-1.0.0/tests/test_api/test_repositories.py +33 -0
- devin_cli-1.0.0/tests/test_api/test_schedules.py +38 -0
- devin_cli-1.0.0/tests/test_api/test_secrets.py +34 -0
- devin_cli-1.0.0/tests/test_api/test_sessions.py +69 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/tests/test_cli.py +3 -4
- {devin_cli-0.1.2 → devin_cli-1.0.0}/tests/test_config.py +1 -1
- devin_cli-0.1.2/PKG-INFO +0 -282
- devin_cli-0.1.2/README.md +0 -246
- devin_cli-0.1.2/src/devin_cli/api/knowledge.py +0 -46
- devin_cli-0.1.2/src/devin_cli/api/playbooks.py +0 -27
- devin_cli-0.1.2/src/devin_cli/api/sessions.py +0 -57
- devin_cli-0.1.2/src/devin_cli/cli.py +0 -600
- devin_cli-0.1.2/tests/test_api/test_secrets.py +0 -25
- devin_cli-0.1.2/tests/test_api/test_sessions.py +0 -59
- {devin_cli-0.1.2 → devin_cli-1.0.0}/.github/workflows/pypi-publish.yml +0 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/.gitignore +0 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/CHANGELOG.md +0 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/LICENSE +0 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/assets/logo.png +0 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/setup.py +0 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/src/devin_cli/__init__.py +0 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/src/devin_cli/api/__init__.py +0 -0
- {devin_cli-0.1.2 → devin_cli-1.0.0}/tests/__init__.py +0 -0
devin_cli-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: devin-cli
|
|
3
|
+
Version: 1.0.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 fully upgraded to Devin API v3.**
|
|
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
|
+
---
|
|
88
|
+
|
|
89
|
+
## 🛠 Command Cheat Sheet (v3 Architecture)
|
|
90
|
+
|
|
91
|
+
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.
|
|
92
|
+
|
|
93
|
+
| Category | Commands | Description |
|
|
94
|
+
| :--- | :--- | :--- |
|
|
95
|
+
| **Sessions** | `create`, `list`, `get`, `insights`, `cost`, `messages`, `message`, `terminate` | Core agent lifecycle and analytics. |
|
|
96
|
+
| **Knowledge** | `list`, `create`, `delete` | Manage organizational context and AI memory. |
|
|
97
|
+
| **Playbooks** | `list`, `create`, `delete` | Automate complex, multi-step agent workflows. |
|
|
98
|
+
| **Secrets** | `list`, `create`, `delete` | Manage API keys passing to Devin sessions. |
|
|
99
|
+
| **Schedules** | `list`, `create` | Schedule recurring autonomous tasks via CRON. |
|
|
100
|
+
| **Repositories** | `list`, `index` | Force indexing of Git repositories. |
|
|
101
|
+
| **Attachments** | `upload`, `download` | Transfer context files seamlessly. |
|
|
102
|
+
| **Enterprise** | `whoami`, `list-orgs` | Administrative identity discovery. |
|
|
103
|
+
| **Global** | `configure`, `use` | CLI setup and active session swapping. |
|
|
104
|
+
|
|
105
|
+
### Example Automations
|
|
106
|
+
|
|
107
|
+
**Blocking Call for CI/CD:**
|
|
108
|
+
```bash
|
|
109
|
+
# Trigger Devin and wait for unit tests to be fixed
|
|
110
|
+
devin sessions create "Fix the failing authentication tests"
|
|
111
|
+
echo "Devin finished. Running integration tests..."
|
|
112
|
+
npm test
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Audit Subsystem Costs:**
|
|
116
|
+
```bash
|
|
117
|
+
# Get ACU consumption for a specific incident
|
|
118
|
+
devin sessions cost --id <SESSION_ID>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 📟 Integration & Environment Variables
|
|
124
|
+
|
|
125
|
+
Devin CLI is designed for CI/CD. Use environment variables to bypass the `configure` step entirely.
|
|
126
|
+
|
|
127
|
+
- `DEVIN_API_TOKEN`: Your API token.
|
|
128
|
+
- `DEVIN_ORG_ID`: Your target organization ID.
|
|
129
|
+
- `DEVIN_BASE_URL`: (Optional) Overrides the standard `https://api.devin.ai/v3`.
|
|
130
|
+
|
|
131
|
+
```yaml
|
|
132
|
+
# Example GitHub Action Step
|
|
133
|
+
env:
|
|
134
|
+
DEVIN_API_TOKEN: ${{ secrets.DEVIN_API_TOKEN }}
|
|
135
|
+
DEVIN_ORG_ID: ${{ secrets.DEVIN_ORG_ID }}
|
|
136
|
+
run: |
|
|
137
|
+
devin sessions create "Review PR #${{ github.event.pull_request.number }}"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## ⚙️ Engineering Specs
|
|
143
|
+
- **Architecture**: Complete Devin API `v3` Support (including `v3beta1` and `enterprise` endpoints).
|
|
144
|
+
- **Config Storage**: `~/.config/devin/config.json`
|
|
145
|
+
- **Platform Support**: Linux, macOS, WSL2
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 🧪 Developer Hub
|
|
150
|
+
```bash
|
|
151
|
+
# Setup
|
|
152
|
+
pip install -e ".[dev]"
|
|
153
|
+
|
|
154
|
+
# Test Suite (100% path coverage)
|
|
155
|
+
PYTHONPATH=src python3 -m pytest
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 📄 License
|
|
161
|
+
MIT. **Devin CLI** is an unofficial community project and is not affiliated with Cognition AI.
|
|
@@ -0,0 +1,125 @@
|
|
|
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 fully upgraded to Devin API v3.**
|
|
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
|
+
---
|
|
52
|
+
|
|
53
|
+
## 🛠 Command Cheat Sheet (v3 Architecture)
|
|
54
|
+
|
|
55
|
+
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.
|
|
56
|
+
|
|
57
|
+
| Category | Commands | Description |
|
|
58
|
+
| :--- | :--- | :--- |
|
|
59
|
+
| **Sessions** | `create`, `list`, `get`, `insights`, `cost`, `messages`, `message`, `terminate` | Core agent lifecycle and analytics. |
|
|
60
|
+
| **Knowledge** | `list`, `create`, `delete` | Manage organizational context and AI memory. |
|
|
61
|
+
| **Playbooks** | `list`, `create`, `delete` | Automate complex, multi-step agent workflows. |
|
|
62
|
+
| **Secrets** | `list`, `create`, `delete` | Manage API keys passing to Devin sessions. |
|
|
63
|
+
| **Schedules** | `list`, `create` | Schedule recurring autonomous tasks via CRON. |
|
|
64
|
+
| **Repositories** | `list`, `index` | Force indexing of Git repositories. |
|
|
65
|
+
| **Attachments** | `upload`, `download` | Transfer context files seamlessly. |
|
|
66
|
+
| **Enterprise** | `whoami`, `list-orgs` | Administrative identity discovery. |
|
|
67
|
+
| **Global** | `configure`, `use` | CLI setup and active session swapping. |
|
|
68
|
+
|
|
69
|
+
### Example Automations
|
|
70
|
+
|
|
71
|
+
**Blocking Call for CI/CD:**
|
|
72
|
+
```bash
|
|
73
|
+
# Trigger Devin and wait for unit tests to be fixed
|
|
74
|
+
devin sessions create "Fix the failing authentication tests"
|
|
75
|
+
echo "Devin finished. Running integration tests..."
|
|
76
|
+
npm test
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Audit Subsystem Costs:**
|
|
80
|
+
```bash
|
|
81
|
+
# Get ACU consumption for a specific incident
|
|
82
|
+
devin sessions cost --id <SESSION_ID>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 📟 Integration & Environment Variables
|
|
88
|
+
|
|
89
|
+
Devin CLI is designed for CI/CD. Use environment variables to bypass the `configure` step entirely.
|
|
90
|
+
|
|
91
|
+
- `DEVIN_API_TOKEN`: Your API token.
|
|
92
|
+
- `DEVIN_ORG_ID`: Your target organization ID.
|
|
93
|
+
- `DEVIN_BASE_URL`: (Optional) Overrides the standard `https://api.devin.ai/v3`.
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
# Example GitHub Action Step
|
|
97
|
+
env:
|
|
98
|
+
DEVIN_API_TOKEN: ${{ secrets.DEVIN_API_TOKEN }}
|
|
99
|
+
DEVIN_ORG_ID: ${{ secrets.DEVIN_ORG_ID }}
|
|
100
|
+
run: |
|
|
101
|
+
devin sessions create "Review PR #${{ github.event.pull_request.number }}"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## ⚙️ Engineering Specs
|
|
107
|
+
- **Architecture**: Complete Devin API `v3` Support (including `v3beta1` and `enterprise` endpoints).
|
|
108
|
+
- **Config Storage**: `~/.config/devin/config.json`
|
|
109
|
+
- **Platform Support**: Linux, macOS, WSL2
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 🧪 Developer Hub
|
|
114
|
+
```bash
|
|
115
|
+
# Setup
|
|
116
|
+
pip install -e ".[dev]"
|
|
117
|
+
|
|
118
|
+
# Test Suite (100% path coverage)
|
|
119
|
+
PYTHONPATH=src python3 -m pytest
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 📄 License
|
|
125
|
+
MIT. **Devin CLI** is an unofficial community project and is not affiliated with Cognition AI.
|
|
@@ -9,4 +9,8 @@ def upload_file(file_path: str):
|
|
|
9
9
|
with open(path, "rb") as f:
|
|
10
10
|
files = {"file": f}
|
|
11
11
|
# client.post handles Content-Type removal for files
|
|
12
|
+
# The client will inject /organizations/{org_id}/ if configured
|
|
12
13
|
return client.post("attachments", files=files)
|
|
14
|
+
|
|
15
|
+
def download_attachment(uuid: str, name: str):
|
|
16
|
+
return client.get(f"attachments/{uuid}/{name}")
|
|
@@ -13,25 +13,21 @@ class APIError(Exception):
|
|
|
13
13
|
|
|
14
14
|
class APIClient:
|
|
15
15
|
def __init__(self):
|
|
16
|
-
|
|
17
|
-
self._headers: Dict[str, str] = {}
|
|
16
|
+
pass
|
|
18
17
|
|
|
19
18
|
@property
|
|
20
19
|
def token(self) -> Optional[str]:
|
|
21
|
-
|
|
22
|
-
self._token = config.api_token
|
|
23
|
-
return self._token
|
|
20
|
+
return config.api_token
|
|
24
21
|
|
|
25
22
|
@property
|
|
26
23
|
def headers(self) -> Dict[str, str]:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return self._headers
|
|
24
|
+
t = self.token
|
|
25
|
+
headers = {
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
}
|
|
28
|
+
if t:
|
|
29
|
+
headers["Authorization"] = f"Bearer {t}"
|
|
30
|
+
return headers
|
|
35
31
|
|
|
36
32
|
@property
|
|
37
33
|
def BASE_URL(self) -> str:
|
|
@@ -63,14 +59,38 @@ class APIClient:
|
|
|
63
59
|
if response.status_code == 204:
|
|
64
60
|
return None
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
content_type = response.headers.get("Content-Type", "")
|
|
63
|
+
if "application/json" in content_type:
|
|
64
|
+
try:
|
|
65
|
+
return response.json()
|
|
66
|
+
except ValueError:
|
|
67
|
+
return response.text
|
|
68
|
+
|
|
69
|
+
# If it's not JSON, it might be an attachment or raw text
|
|
70
|
+
if any(t in content_type for t in ["image/", "application/octet-stream", "application/pdf"]):
|
|
71
|
+
return response.content
|
|
72
|
+
|
|
73
|
+
return response.text
|
|
70
74
|
|
|
71
75
|
def request(self, method: str, endpoint: str, **kwargs) -> Any:
|
|
72
76
|
self._ensure_token()
|
|
73
|
-
|
|
77
|
+
|
|
78
|
+
endpoint = endpoint.lstrip("/")
|
|
79
|
+
|
|
80
|
+
# Determine the base URL and whether to inject organization
|
|
81
|
+
if endpoint.startswith("v3beta1/"):
|
|
82
|
+
base = config.base_url.replace("/v3", "").replace("/v2", "").replace("/v1", "").rstrip("/")
|
|
83
|
+
url = f"{base}/{endpoint}"
|
|
84
|
+
# Inject organization for v3beta1 if not present
|
|
85
|
+
if config.org_id and "/organizations/" not in url:
|
|
86
|
+
url = url.replace("v3beta1/", f"v3beta1/organizations/{config.org_id}/")
|
|
87
|
+
elif endpoint.startswith("enterprise/"):
|
|
88
|
+
url = f"{self.BASE_URL}/{endpoint}"
|
|
89
|
+
else:
|
|
90
|
+
# Standard path, inject organization if configured
|
|
91
|
+
if config.org_id and not endpoint.startswith("organizations/"):
|
|
92
|
+
endpoint = f"organizations/{config.org_id}/{endpoint}"
|
|
93
|
+
url = f"{self.BASE_URL}/{endpoint}"
|
|
74
94
|
|
|
75
95
|
# Merge headers if needed, but usually self.headers is enough
|
|
76
96
|
headers = self.headers.copy()
|
|
@@ -82,22 +102,22 @@ class APIClient:
|
|
|
82
102
|
headers.pop("Content-Type", None)
|
|
83
103
|
|
|
84
104
|
try:
|
|
85
|
-
with httpx.Client() as client:
|
|
105
|
+
with httpx.Client(follow_redirects=True) as client:
|
|
86
106
|
response = client.request(method, url, headers=headers, **kwargs)
|
|
87
107
|
return self._handle_response(response)
|
|
88
108
|
except httpx.RequestError as e:
|
|
89
109
|
raise APIError(f"Network error: {e}")
|
|
90
110
|
|
|
91
|
-
def get(self, endpoint: str,
|
|
92
|
-
return self.request("GET", endpoint,
|
|
111
|
+
def get(self, endpoint: str, **kwargs) -> Any:
|
|
112
|
+
return self.request("GET", endpoint, **kwargs)
|
|
93
113
|
|
|
94
|
-
def post(self, endpoint: str,
|
|
95
|
-
return self.request("POST", endpoint,
|
|
114
|
+
def post(self, endpoint: str, **kwargs) -> Any:
|
|
115
|
+
return self.request("POST", endpoint, **kwargs)
|
|
96
116
|
|
|
97
|
-
def put(self, endpoint: str,
|
|
98
|
-
return self.request("PUT", endpoint,
|
|
117
|
+
def put(self, endpoint: str, **kwargs) -> Any:
|
|
118
|
+
return self.request("PUT", endpoint, **kwargs)
|
|
99
119
|
|
|
100
|
-
def delete(self, endpoint: str) -> Any:
|
|
101
|
-
return self.request("DELETE", endpoint)
|
|
120
|
+
def delete(self, endpoint: str, **kwargs) -> Any:
|
|
121
|
+
return self.request("DELETE", endpoint, **kwargs)
|
|
102
122
|
|
|
103
123
|
client = APIClient()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from devin_cli.api.client import client
|
|
3
|
+
|
|
4
|
+
def get_session_consumption(session_id: str):
|
|
5
|
+
"""Get daily ACU consumption for a specific session"""
|
|
6
|
+
return client.get(f"enterprise/consumption/daily/sessions/{session_id}")
|
|
7
|
+
|
|
8
|
+
def get_service_user_consumption(service_user_id: str):
|
|
9
|
+
"""Get daily ACU consumption for sessions initiated by a service user"""
|
|
10
|
+
return client.get(f"enterprise/consumption/daily/service-users/{service_user_id}")
|
|
11
|
+
|
|
12
|
+
def list_consumption_cycles():
|
|
13
|
+
"""List consumption cycles (Enterprise key required)"""
|
|
14
|
+
return client.get("enterprise/consumption/cycles")
|
|
15
|
+
|
|
16
|
+
def get_daily_consumption_breakdown():
|
|
17
|
+
"""Get overall daily ACU consumption breakdown"""
|
|
18
|
+
return client.get("enterprise/consumption/daily")
|
|
19
|
+
|
|
20
|
+
def get_acu_limits():
|
|
21
|
+
"""Get ACU limits for the organization/enterprise"""
|
|
22
|
+
return client.get("enterprise/consumption/limits")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
from devin_cli.api.client import client
|
|
3
|
+
|
|
4
|
+
def list_knowledge():
|
|
5
|
+
return client.get("knowledge/notes")
|
|
6
|
+
|
|
7
|
+
def create_knowledge(
|
|
8
|
+
title: str,
|
|
9
|
+
body: str,
|
|
10
|
+
# v3 uses title/body, v1 used name/body/trigger_description
|
|
11
|
+
# We'll map them for compatibility if possible, but v3 is primary
|
|
12
|
+
):
|
|
13
|
+
data = {
|
|
14
|
+
"title": title,
|
|
15
|
+
"body": body,
|
|
16
|
+
}
|
|
17
|
+
return client.post("knowledge/notes", json=data)
|
|
18
|
+
|
|
19
|
+
def get_knowledge(note_id: str):
|
|
20
|
+
return client.get(f"knowledge/notes/{note_id}")
|
|
21
|
+
|
|
22
|
+
def update_knowledge(
|
|
23
|
+
note_id: str,
|
|
24
|
+
title: Optional[str] = None,
|
|
25
|
+
body: Optional[str] = None,
|
|
26
|
+
):
|
|
27
|
+
data = {}
|
|
28
|
+
if title:
|
|
29
|
+
data["title"] = title
|
|
30
|
+
if body:
|
|
31
|
+
data["body"] = body
|
|
32
|
+
|
|
33
|
+
return client.put(f"knowledge/notes/{note_id}", json=data)
|
|
34
|
+
|
|
35
|
+
def delete_knowledge(note_id: str):
|
|
36
|
+
return client.delete(f"knowledge/notes/{note_id}")
|
|
37
|
+
|
|
38
|
+
# Enterprise Knowledge
|
|
39
|
+
def list_enterprise_knowledge():
|
|
40
|
+
return client.get("enterprise/knowledge/notes")
|
|
41
|
+
|
|
42
|
+
def get_enterprise_knowledge(note_id: str):
|
|
43
|
+
return client.get(f"enterprise/knowledge/notes/{note_id}")
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from devin_cli.api.client import client
|
|
2
|
+
|
|
3
|
+
def get_self():
|
|
4
|
+
"""Get current authenticated user info (Enterprise context)"""
|
|
5
|
+
# Based on Devin API v3 "Self" category info
|
|
6
|
+
return client.get("enterprise/members/self")
|
|
7
|
+
|
|
8
|
+
def get_user(user_id: str):
|
|
9
|
+
"""Get info for a specific user"""
|
|
10
|
+
return client.get(f"enterprise/members/users/{user_id}")
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from devin_cli.api.client import client
|
|
2
|
+
|
|
3
|
+
def list_organizations():
|
|
4
|
+
"""List all organizations (Enterprise key required)"""
|
|
5
|
+
return client.get("enterprise/organizations")
|
|
6
|
+
|
|
7
|
+
def get_organization(org_id: str):
|
|
8
|
+
"""Get details of a specific organization (Enterprise key required)"""
|
|
9
|
+
return client.get(f"enterprise/organizations/{org_id}")
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
from devin_cli.api.client import client
|
|
3
|
+
|
|
4
|
+
def list_playbooks():
|
|
5
|
+
return client.get("playbooks")
|
|
6
|
+
|
|
7
|
+
def create_playbook(title: str, body: str, macro: Optional[str] = None):
|
|
8
|
+
data = {"title": title, "body": body}
|
|
9
|
+
if macro:
|
|
10
|
+
data["macro"] = macro
|
|
11
|
+
return client.post("playbooks", json=data)
|
|
12
|
+
|
|
13
|
+
def get_playbook(playbook_id: str):
|
|
14
|
+
return client.get(f"playbooks/{playbook_id}")
|
|
15
|
+
|
|
16
|
+
def update_playbook(playbook_id: str, title: Optional[str] = None, body: Optional[str] = None, macro: Optional[str] = None):
|
|
17
|
+
data = {}
|
|
18
|
+
if title:
|
|
19
|
+
data["title"] = title
|
|
20
|
+
if body:
|
|
21
|
+
data["body"] = body
|
|
22
|
+
if macro:
|
|
23
|
+
data["macro"] = macro
|
|
24
|
+
return client.put(f"playbooks/{playbook_id}", json=data)
|
|
25
|
+
|
|
26
|
+
def delete_playbook(playbook_id: str):
|
|
27
|
+
return client.delete(f"playbooks/{playbook_id}")
|
|
28
|
+
|
|
29
|
+
# Enterprise Playbooks
|
|
30
|
+
def list_enterprise_playbooks():
|
|
31
|
+
return client.get("enterprise/playbooks")
|
|
32
|
+
|
|
33
|
+
def create_enterprise_playbook(title: str, body: str, macro: Optional[str] = None):
|
|
34
|
+
data = {"title": title, "body": body}
|
|
35
|
+
if macro:
|
|
36
|
+
data["macro"] = macro
|
|
37
|
+
return client.post("enterprise/playbooks", json=data)
|
|
38
|
+
|
|
39
|
+
def get_enterprise_playbook(playbook_id: str):
|
|
40
|
+
return client.get(f"enterprise/playbooks/{playbook_id}")
|
|
41
|
+
|
|
42
|
+
def update_enterprise_playbook(playbook_id: str, title: Optional[str] = None, body: Optional[str] = None, macro: Optional[str] = None):
|
|
43
|
+
data = {}
|
|
44
|
+
if title:
|
|
45
|
+
data["title"] = title
|
|
46
|
+
if body:
|
|
47
|
+
data["body"] = body
|
|
48
|
+
if macro:
|
|
49
|
+
data["macro"] = macro
|
|
50
|
+
return client.put(f"enterprise/playbooks/{playbook_id}", json=data)
|
|
51
|
+
|
|
52
|
+
def delete_enterprise_playbook(playbook_id: str):
|
|
53
|
+
return client.delete(f"enterprise/playbooks/{playbook_id}")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import List, Optional, Union
|
|
2
|
+
from devin_cli.api.client import client
|
|
3
|
+
|
|
4
|
+
def list_repositories(limit: int = 100, after: Optional[str] = None):
|
|
5
|
+
params = {"limit": limit}
|
|
6
|
+
if after:
|
|
7
|
+
params["after"] = after
|
|
8
|
+
return client.get("v3beta1/repositories", params=params)
|
|
9
|
+
|
|
10
|
+
def list_indexed_repositories():
|
|
11
|
+
return client.get("v3beta1/repositories/indexing")
|
|
12
|
+
|
|
13
|
+
def get_indexing_status(repository_path: str):
|
|
14
|
+
"""repository_path should be owner/repo"""
|
|
15
|
+
return client.get(f"v3beta1/repositories/{repository_path}/indexing")
|
|
16
|
+
|
|
17
|
+
def index_repository(repository_path: str, branch_name: Optional[str] = None):
|
|
18
|
+
data = {}
|
|
19
|
+
if branch_name:
|
|
20
|
+
data["branch_name"] = branch_name
|
|
21
|
+
return client.put(f"v3beta1/repositories/{repository_path}/indexing", json=data)
|
|
22
|
+
|
|
23
|
+
def index_repositories_bulk(repository_paths: List[str]):
|
|
24
|
+
return client.put("v3beta1/repositories/indexing/bulk", json={"repository_paths": repository_paths})
|
|
25
|
+
|
|
26
|
+
def remove_from_indexing(repository_path: str):
|
|
27
|
+
return client.delete(f"v3beta1/repositories/{repository_path}/indexing")
|
|
28
|
+
|
|
29
|
+
def remove_from_indexing_bulk(repository_paths: List[str]):
|
|
30
|
+
return client.delete("v3beta1/repositories/indexing/bulk", json={"repository_paths": repository_paths})
|
|
31
|
+
|
|
32
|
+
# Enterprise Git Management
|
|
33
|
+
def list_git_connections():
|
|
34
|
+
return client.get("enterprise/git-providers/connections")
|
|
35
|
+
|
|
36
|
+
def list_git_permissions():
|
|
37
|
+
return client.get("enterprise/git-providers/permissions")
|
|
38
|
+
|
|
39
|
+
def create_git_permission(org_id: str, permission: str):
|
|
40
|
+
data = {"org_id": org_id, "permission": permission}
|
|
41
|
+
return client.post("enterprise/git-providers/permissions", json=data)
|
|
42
|
+
|
|
43
|
+
def delete_git_permission(org_id: str):
|
|
44
|
+
return client.delete(f"enterprise/git-providers/permissions", params={"org_id": org_id})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from typing import List, Optional, Dict, Any
|
|
2
|
+
from devin_cli.api.client import client
|
|
3
|
+
|
|
4
|
+
def list_schedules(limit: int = 100, after: Optional[str] = None):
|
|
5
|
+
params = {"limit": limit}
|
|
6
|
+
if after:
|
|
7
|
+
params["after"] = after
|
|
8
|
+
return client.get("schedules", params=params)
|
|
9
|
+
|
|
10
|
+
def create_schedule(
|
|
11
|
+
prompt: str,
|
|
12
|
+
cron: str,
|
|
13
|
+
title: Optional[str] = None,
|
|
14
|
+
# Additional v3 parameters
|
|
15
|
+
advanced_mode: Optional[str] = None,
|
|
16
|
+
playbook_id: Optional[str] = None,
|
|
17
|
+
repos: Optional[List[str]] = None,
|
|
18
|
+
tags: Optional[List[str]] = None,
|
|
19
|
+
):
|
|
20
|
+
data = {
|
|
21
|
+
"prompt": prompt,
|
|
22
|
+
"cron": cron,
|
|
23
|
+
}
|
|
24
|
+
if title:
|
|
25
|
+
data["title"] = title
|
|
26
|
+
if advanced_mode:
|
|
27
|
+
data["advanced_mode"] = advanced_mode
|
|
28
|
+
if playbook_id:
|
|
29
|
+
data["playbook_id"] = playbook_id
|
|
30
|
+
if repos:
|
|
31
|
+
data["repos"] = repos
|
|
32
|
+
if tags:
|
|
33
|
+
data["tags"] = tags
|
|
34
|
+
|
|
35
|
+
return client.post("schedules", json=data)
|
|
36
|
+
|
|
37
|
+
def get_schedule(schedule_id: str):
|
|
38
|
+
return client.get(f"schedules/{schedule_id}")
|
|
39
|
+
|
|
40
|
+
def update_schedule(
|
|
41
|
+
schedule_id: str,
|
|
42
|
+
prompt: Optional[str] = None,
|
|
43
|
+
cron: Optional[str] = None,
|
|
44
|
+
title: Optional[str] = None,
|
|
45
|
+
enabled: Optional[bool] = None,
|
|
46
|
+
):
|
|
47
|
+
data = {}
|
|
48
|
+
if prompt:
|
|
49
|
+
data["prompt"] = prompt
|
|
50
|
+
if cron:
|
|
51
|
+
data["cron"] = cron
|
|
52
|
+
if title:
|
|
53
|
+
data["title"] = title
|
|
54
|
+
if enabled is not None:
|
|
55
|
+
data["enabled"] = enabled
|
|
56
|
+
|
|
57
|
+
return client.put(f"schedules/{schedule_id}", json=data)
|
|
58
|
+
|
|
59
|
+
def delete_schedule(schedule_id: str):
|
|
60
|
+
return client.delete(f"schedules/{schedule_id}")
|
|
@@ -3,5 +3,8 @@ from devin_cli.api.client import client
|
|
|
3
3
|
def list_secrets():
|
|
4
4
|
return client.get("secrets")
|
|
5
5
|
|
|
6
|
+
def create_secret(name: str, value: str):
|
|
7
|
+
return client.post("secrets", json={"name": name, "value": value})
|
|
8
|
+
|
|
6
9
|
def delete_secret(secret_id: str):
|
|
7
10
|
return client.delete(f"secrets/{secret_id}")
|