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.
- devin_cli-1.1.0/PKG-INFO +170 -0
- devin_cli-1.1.0/README.md +134 -0
- devin_cli-1.1.0/generate_facades.py +21 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/pyproject.toml +1 -1
- devin_cli-1.1.0/src/devin_cli/api/attachments.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/client.py +165 -0
- devin_cli-1.1.0/src/devin_cli/api/consumption.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/knowledge.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/members.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/organizations.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/playbooks.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/repositories.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/schedules.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/secrets.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/sessions.py +13 -0
- devin_cli-1.1.0/src/devin_cli/api/v1/__init__.py +0 -0
- devin_cli-1.1.0/src/devin_cli/api/v1/attachments.py +16 -0
- {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v1}/knowledge.py +12 -9
- {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v1}/playbooks.py +4 -4
- {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v1}/sessions.py +2 -1
- devin_cli-1.1.0/src/devin_cli/api/v3/__init__.py +0 -0
- {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v3}/attachments.py +4 -0
- devin_cli-1.1.0/src/devin_cli/api/v3/consumption.py +22 -0
- devin_cli-1.1.0/src/devin_cli/api/v3/knowledge.py +43 -0
- devin_cli-1.1.0/src/devin_cli/api/v3/members.py +10 -0
- devin_cli-1.1.0/src/devin_cli/api/v3/organizations.py +9 -0
- devin_cli-1.1.0/src/devin_cli/api/v3/playbooks.py +49 -0
- devin_cli-1.1.0/src/devin_cli/api/v3/repositories.py +44 -0
- devin_cli-1.1.0/src/devin_cli/api/v3/schedules.py +60 -0
- devin_cli-1.1.0/src/devin_cli/api/v3/secrets.py +10 -0
- devin_cli-1.1.0/src/devin_cli/api/v3/sessions.py +93 -0
- devin_cli-1.1.0/src/devin_cli/cli.py +552 -0
- devin_cli-1.1.0/src/devin_cli/config.py +153 -0
- devin_cli-1.1.0/test_duplicate.py +19 -0
- devin_cli-1.1.0/tests/conftest.py +8 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/test_api/test_attachments.py +14 -4
- {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/test_api/test_knowledge.py +11 -12
- {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/test_api/test_playbooks.py +16 -8
- devin_cli-1.1.0/tests/test_api/test_repositories.py +33 -0
- devin_cli-1.1.0/tests/test_api/test_schedules.py +38 -0
- devin_cli-1.1.0/tests/test_api/test_secrets.py +34 -0
- devin_cli-1.1.0/tests/test_api/test_sessions.py +69 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/test_cli.py +3 -4
- {devin_cli-0.1.2 → devin_cli-1.1.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/client.py +0 -103
- devin_cli-0.1.2/src/devin_cli/cli.py +0 -600
- devin_cli-0.1.2/src/devin_cli/config.py +0 -67
- 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.1.0}/.github/workflows/pypi-publish.yml +0 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/.gitignore +0 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/CHANGELOG.md +0 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/LICENSE +0 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/assets/logo.png +0 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/setup.py +0 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/src/devin_cli/__init__.py +0 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/src/devin_cli/api/__init__.py +0 -0
- {devin_cli-0.1.2/src/devin_cli/api → devin_cli-1.1.0/src/devin_cli/api/v1}/secrets.py +0 -0
- {devin_cli-0.1.2 → devin_cli-1.1.0}/tests/__init__.py +0 -0
devin_cli-1.1.0/PKG-INFO
ADDED
|
@@ -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)
|
|
@@ -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}")
|