devin-cli 1.4.0__tar.gz → 1.5.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.4.0 → devin_cli-1.5.0}/.gitignore +1 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/CHANGELOG.md +18 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/PKG-INFO +52 -1
- {devin_cli-1.4.0 → devin_cli-1.5.0}/README.md +51 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/pyproject.toml +1 -1
- devin_cli-1.5.0/src/devin_cli/__init__.py +1 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/client.py +6 -9
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/cli.py +111 -31
- devin_cli-1.5.0/src/devin_cli/config.py +260 -0
- devin_cli-1.5.0/tests/conftest.py +15 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_cli.py +7 -0
- devin_cli-1.5.0/tests/test_cli_global_flags.py +112 -0
- devin_cli-1.5.0/tests/test_config.py +194 -0
- devin_cli-1.5.0/tests/test_configure.py +102 -0
- devin_cli-1.4.0/src/devin_cli/__init__.py +0 -1
- devin_cli-1.4.0/src/devin_cli/config.py +0 -153
- devin_cli-1.4.0/tests/conftest.py +0 -8
- devin_cli-1.4.0/tests/test_config.py +0 -27
- {devin_cli-1.4.0 → devin_cli-1.5.0}/.github/workflows/pypi-publish.yml +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/LICENSE +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/assets/logo.png +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/generate_facades.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/setup.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/__init__.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/attachments.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/audit_logs.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/blueprints.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/code_scans.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/consumption.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/guardrail_violations.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/ip_access_list.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/knowledge.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/members.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/organizations.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/playbooks.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/pr_reviews.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/queue.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/repositories.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/schedules.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/secrets.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/service_users.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/sessions.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/tags.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v1/__init__.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v1/attachments.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v1/knowledge.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v1/playbooks.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v1/secrets.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v1/sessions.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/__init__.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/attachments.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/audit_logs.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/blueprints.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/code_scans.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/consumption.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/guardrail_violations.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/ip_access_list.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/knowledge.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/members.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/organizations.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/playbooks.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/pr_reviews.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/queue.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/repositories.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/schedules.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/secrets.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/service_users.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/sessions.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/src/devin_cli/api/v3/tags.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/__init__.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_api/test_attachments.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_api/test_enterprise_endpoints.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_api/test_knowledge.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_api/test_playbooks.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_api/test_pr_reviews.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_api/test_repositories.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_api/test_schedules.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_api/test_secrets.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_api/test_sessions.py +0 -0
- {devin_cli-1.4.0 → devin_cli-1.5.0}/tests/test_cli_extended.py +0 -0
|
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.5.0] - 2026-09-04
|
|
9
|
+
### Added
|
|
10
|
+
- **On-the-Fly Configuration & Global CLI Flags**: Root command now supports `--token`, `--org`, `--base-url`, `--api-version`, and `--config-file` flags, allowing complete credential configuration on the fly without writing to disk or modifying saved configs.
|
|
11
|
+
- **Predictable Precedence Resolution**: Formalized resolution order across all authentication paths:
|
|
12
|
+
1. CLI Flags (`--token`, `--org`, `--base-url`, `--api-version`)
|
|
13
|
+
2. Environment Variables (`DEVIN_API_TOKEN`, `DEVIN_ORG_ID`, `DEVIN_BASE_URL`, `DEVIN_API_VERSION`)
|
|
14
|
+
3. Config File Profile (`DEVIN_CONFIG_FILE` or `~/.config/devin/config.json`)
|
|
15
|
+
4. Defaults (`base_url: https://api.devin.ai/v3`, `api_version: v3`)
|
|
16
|
+
- **Zero Disk Writes / Deferred File Creation**: Initializing `Config` or invoking commands with flags/environment variables never touches the filesystem. Config file/directory creation is deferred strictly to explicit save actions (such as `devin configure`), fully unblocking read-only and ephemeral CI runners.
|
|
17
|
+
- **Non-Interactive `configure` Command**: Added `--yes` / `-y` flag and non-TTY stdin autodetection to `devin configure`. Omitting options in non-interactive mode falls back to default values and credentials instead of aborting.
|
|
18
|
+
- **Dynamic Config File Override**: Full support for `DEVIN_CONFIG_FILE` environment variable and `--config-file` flag to direct the CLI to custom config files.
|
|
19
|
+
- **Thread/Context-Safe Runtime Overrides**: Runtime overrides are stored via `contextvars.ContextVar` and cleanly scoped per invocation with `config.reset_runtime()`.
|
|
20
|
+
- **Pre-flight Validation & Scoping Hints**: `validate_for_api()` validates credentials before network requests and outputs helpful diagnostic hints (`Org ID: ...`) when endpoints require organization scoping.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- **Sanitized Token and Org Inputs**: Automatic trimming of leading/trailing whitespace and newlines from tokens and organization IDs.
|
|
24
|
+
- **Configure Prompt Bugs**: Fixed an issue where `devin configure` always prompted for API version even when non-interactive, and improved option handling so explicit flags bypass interactive prompts.
|
|
25
|
+
|
|
8
26
|
## [1.4.0] - 2026-08-01
|
|
9
27
|
### Added
|
|
10
28
|
- **Model Selection & Agent Modes**: Full support for `--devin-mode` / `--mode` / `--model` (`normal`, `fast`, `lite`, `ultra`, `fusion`), `--platform`, `--resumable / --no-resumable`, and `--structured-output-required` on `devin sessions create` and `devin create-session`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devin-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Unofficial CLI for Devin AI - The first AI Software Engineer
|
|
5
5
|
Project-URL: Homepage, https://github.com/revanthpobala/devin-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/revanthpobala/devin-cli.git
|
|
@@ -75,9 +75,13 @@ pip install devin-cli
|
|
|
75
75
|
|
|
76
76
|
### 2. Configuration
|
|
77
77
|
```bash
|
|
78
|
+
# Interactive setup:
|
|
78
79
|
devin configure
|
|
79
80
|
# Paste your API token (apk_... or cog_...) from https://preview.devin.ai/settings
|
|
80
81
|
# Select API version: v3 (default) or v1 (legacy)
|
|
82
|
+
|
|
83
|
+
# Or non-interactive / CI setup:
|
|
84
|
+
devin configure --token "$DEVIN_API_TOKEN" --org "$DEVIN_ORG_ID" --yes
|
|
81
85
|
```
|
|
82
86
|
|
|
83
87
|
### 3. Your First Session
|
|
@@ -108,6 +112,53 @@ devin --profile personal sessions create "Fix the failing tests"
|
|
|
108
112
|
|
|
109
113
|
Profiles are stored in `~/.config/devin/config.json` — fully isolated including session caches and active session IDs.
|
|
110
114
|
|
|
115
|
+
### Automation & CI / CD
|
|
116
|
+
|
|
117
|
+
`devin-cli` is fully optimized for headless CI/CD runners (GitHub Actions, GitLab CI, Jenkins) and containerized workflows.
|
|
118
|
+
|
|
119
|
+
#### Precedence Resolution
|
|
120
|
+
Credentials and configuration resolve in a strict, predictable order:
|
|
121
|
+
1. **Global CLI Flags** (`--token`, `--org`, `--base-url`, `--api-version`)
|
|
122
|
+
2. **Environment Variables** (`DEVIN_API_TOKEN`, `DEVIN_ORG_ID`, `DEVIN_BASE_URL`, `DEVIN_API_VERSION`)
|
|
123
|
+
3. **Config File** (`DEVIN_CONFIG_FILE` or `~/.config/devin/config.json`)
|
|
124
|
+
4. **Built-in Defaults** (`base_url: https://api.devin.ai/v3`, `api_version: v3`)
|
|
125
|
+
|
|
126
|
+
#### Ephemeral Runs (Zero Disk Writes)
|
|
127
|
+
In CI pipelines, you do not need to create or write configuration files. Pass credentials via environment variables or root CLI flags:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Option A: Environment variables
|
|
131
|
+
export DEVIN_API_TOKEN="cog_..."
|
|
132
|
+
export DEVIN_ORG_ID="org-..."
|
|
133
|
+
devin repos list --json
|
|
134
|
+
|
|
135
|
+
# Option B: Global CLI flags on-the-fly
|
|
136
|
+
devin --token "$DEVIN_API_TOKEN" --org "$DEVIN_ORG_ID" repos status my-org/my-repo --json
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Non-Interactive Configuration
|
|
140
|
+
If your pipeline writes a config file, use `--yes` (or run in non-TTY environments) to disable interactive prompts:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
devin configure \
|
|
144
|
+
--token "$DEVIN_API_TOKEN" \
|
|
145
|
+
--org "$DEVIN_ORG_ID" \
|
|
146
|
+
--base-url https://api.devin.ai/v3 \
|
|
147
|
+
--api-version v3 \
|
|
148
|
+
--yes
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Custom Config File Path
|
|
152
|
+
Override the default `~/.config/devin/config.json` location:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Via flag
|
|
156
|
+
devin --config-file /tmp/devin-ci.json repos list
|
|
157
|
+
|
|
158
|
+
# Via environment variable
|
|
159
|
+
export DEVIN_CONFIG_FILE=/tmp/devin-ci.json
|
|
160
|
+
```
|
|
161
|
+
|
|
111
162
|
---
|
|
112
163
|
|
|
113
164
|
## 🤖 All Commands
|
|
@@ -39,9 +39,13 @@ pip install devin-cli
|
|
|
39
39
|
|
|
40
40
|
### 2. Configuration
|
|
41
41
|
```bash
|
|
42
|
+
# Interactive setup:
|
|
42
43
|
devin configure
|
|
43
44
|
# Paste your API token (apk_... or cog_...) from https://preview.devin.ai/settings
|
|
44
45
|
# Select API version: v3 (default) or v1 (legacy)
|
|
46
|
+
|
|
47
|
+
# Or non-interactive / CI setup:
|
|
48
|
+
devin configure --token "$DEVIN_API_TOKEN" --org "$DEVIN_ORG_ID" --yes
|
|
45
49
|
```
|
|
46
50
|
|
|
47
51
|
### 3. Your First Session
|
|
@@ -72,6 +76,53 @@ devin --profile personal sessions create "Fix the failing tests"
|
|
|
72
76
|
|
|
73
77
|
Profiles are stored in `~/.config/devin/config.json` — fully isolated including session caches and active session IDs.
|
|
74
78
|
|
|
79
|
+
### Automation & CI / CD
|
|
80
|
+
|
|
81
|
+
`devin-cli` is fully optimized for headless CI/CD runners (GitHub Actions, GitLab CI, Jenkins) and containerized workflows.
|
|
82
|
+
|
|
83
|
+
#### Precedence Resolution
|
|
84
|
+
Credentials and configuration resolve in a strict, predictable order:
|
|
85
|
+
1. **Global CLI Flags** (`--token`, `--org`, `--base-url`, `--api-version`)
|
|
86
|
+
2. **Environment Variables** (`DEVIN_API_TOKEN`, `DEVIN_ORG_ID`, `DEVIN_BASE_URL`, `DEVIN_API_VERSION`)
|
|
87
|
+
3. **Config File** (`DEVIN_CONFIG_FILE` or `~/.config/devin/config.json`)
|
|
88
|
+
4. **Built-in Defaults** (`base_url: https://api.devin.ai/v3`, `api_version: v3`)
|
|
89
|
+
|
|
90
|
+
#### Ephemeral Runs (Zero Disk Writes)
|
|
91
|
+
In CI pipelines, you do not need to create or write configuration files. Pass credentials via environment variables or root CLI flags:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Option A: Environment variables
|
|
95
|
+
export DEVIN_API_TOKEN="cog_..."
|
|
96
|
+
export DEVIN_ORG_ID="org-..."
|
|
97
|
+
devin repos list --json
|
|
98
|
+
|
|
99
|
+
# Option B: Global CLI flags on-the-fly
|
|
100
|
+
devin --token "$DEVIN_API_TOKEN" --org "$DEVIN_ORG_ID" repos status my-org/my-repo --json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Non-Interactive Configuration
|
|
104
|
+
If your pipeline writes a config file, use `--yes` (or run in non-TTY environments) to disable interactive prompts:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
devin configure \
|
|
108
|
+
--token "$DEVIN_API_TOKEN" \
|
|
109
|
+
--org "$DEVIN_ORG_ID" \
|
|
110
|
+
--base-url https://api.devin.ai/v3 \
|
|
111
|
+
--api-version v3 \
|
|
112
|
+
--yes
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### Custom Config File Path
|
|
116
|
+
Override the default `~/.config/devin/config.json` location:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Via flag
|
|
120
|
+
devin --config-file /tmp/devin-ci.json repos list
|
|
121
|
+
|
|
122
|
+
# Via environment variable
|
|
123
|
+
export DEVIN_CONFIG_FILE=/tmp/devin-ci.json
|
|
124
|
+
```
|
|
125
|
+
|
|
75
126
|
---
|
|
76
127
|
|
|
77
128
|
## 🤖 All Commands
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.5.0"
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import httpx
|
|
2
|
-
from devin_cli.config import config
|
|
2
|
+
from devin_cli.config import config, APIError
|
|
3
3
|
from rich.console import Console
|
|
4
4
|
import sys
|
|
5
5
|
from typing import Optional, Any, Dict
|
|
6
6
|
|
|
7
7
|
console = Console()
|
|
8
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
9
|
class BaseClient:
|
|
15
10
|
def __init__(self):
|
|
16
11
|
pass
|
|
@@ -34,9 +29,7 @@ class BaseClient:
|
|
|
34
29
|
return config.base_url.rstrip("/")
|
|
35
30
|
|
|
36
31
|
def _ensure_token(self):
|
|
37
|
-
|
|
38
|
-
# Only raise error if meaningful operation is attempted
|
|
39
|
-
raise APIError("API token not found. Run 'devin configure' to set your API token.")
|
|
32
|
+
config.validate_for_api()
|
|
40
33
|
|
|
41
34
|
def _handle_response(self, response: httpx.Response) -> Any:
|
|
42
35
|
try:
|
|
@@ -53,6 +46,10 @@ class BaseClient:
|
|
|
53
46
|
error_detail = e.response.text.strip()
|
|
54
47
|
|
|
55
48
|
msg_suffix = f"\n Path: {path}"
|
|
49
|
+
if config.org_id:
|
|
50
|
+
msg_suffix += f"\n Org ID: {config.org_id}"
|
|
51
|
+
elif "/organizations/" not in path and not path.startswith("/enterprise"):
|
|
52
|
+
msg_suffix += f"\n Org ID: None (Hint: this endpoint may require --org or DEVIN_ORG_ID)"
|
|
56
53
|
if error_detail:
|
|
57
54
|
msg_suffix += f"\n Detail: {error_detail}"
|
|
58
55
|
|
|
@@ -36,19 +36,50 @@ import importlib.metadata
|
|
|
36
36
|
from rich.prompt import Prompt
|
|
37
37
|
|
|
38
38
|
try:
|
|
39
|
-
|
|
40
|
-
except
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
from devin_cli import __version__
|
|
40
|
+
except Exception:
|
|
41
|
+
try:
|
|
42
|
+
__version__ = importlib.metadata.version("devin-cli")
|
|
43
|
+
except importlib.metadata.PackageNotFoundError:
|
|
44
|
+
__version__ = "1.5.0"
|
|
45
|
+
|
|
46
|
+
# Early CLI flag detection for accurate menus, config loading, and mode setup
|
|
47
|
+
def _extract_cli_arg(flags: list) -> Optional[str]:
|
|
48
|
+
for flag in flags:
|
|
49
|
+
if flag in sys.argv:
|
|
50
|
+
try:
|
|
51
|
+
idx = sys.argv.index(flag)
|
|
52
|
+
if idx + 1 < len(sys.argv) and not sys.argv[idx + 1].startswith("-"):
|
|
53
|
+
return sys.argv[idx + 1]
|
|
54
|
+
except ValueError:
|
|
55
|
+
pass
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
early_config_file = _extract_cli_arg(["--config-file"])
|
|
59
|
+
if early_config_file:
|
|
60
|
+
config.set_config_file(early_config_file)
|
|
61
|
+
elif os.environ.get("DEVIN_CONFIG_FILE"):
|
|
62
|
+
config.set_config_file(os.environ["DEVIN_CONFIG_FILE"])
|
|
63
|
+
|
|
64
|
+
early_profile = _extract_cli_arg(["--profile", "-p"])
|
|
65
|
+
if early_profile:
|
|
66
|
+
config.active_profile = early_profile
|
|
67
|
+
|
|
68
|
+
early_token = _extract_cli_arg(["--token"])
|
|
69
|
+
if early_token:
|
|
70
|
+
config.runtime.api_token = early_token
|
|
71
|
+
|
|
72
|
+
early_org = _extract_cli_arg(["--org"])
|
|
73
|
+
if early_org:
|
|
74
|
+
config.runtime.org_id = early_org
|
|
75
|
+
|
|
76
|
+
early_base_url = _extract_cli_arg(["--base-url"])
|
|
77
|
+
if early_base_url:
|
|
78
|
+
config.runtime.base_url = early_base_url
|
|
79
|
+
|
|
80
|
+
early_api_version = _extract_cli_arg(["--api-version"])
|
|
81
|
+
if early_api_version:
|
|
82
|
+
config.runtime.api_version = early_api_version
|
|
52
83
|
|
|
53
84
|
IS_V1 = config.api_version == "v1"
|
|
54
85
|
v1_tag = " [bold yellow](Legacy v1 API)[/bold yellow]" if IS_V1 else ""
|
|
@@ -101,12 +132,19 @@ ASCII_LOGO = r"""
|
|
|
101
132
|
def main(
|
|
102
133
|
ctx: typer.Context,
|
|
103
134
|
profile: Optional[str] = typer.Option(None, "--profile", "-p", help="Configuration profile to use (e.g., service, personal)"),
|
|
135
|
+
token: Optional[str] = typer.Option(None, "--token", help="Devin API token (starts with apk_ or cog_). Fallback: DEVIN_API_TOKEN"),
|
|
136
|
+
org: Optional[str] = typer.Option(None, "--org", help="Organization ID (org-...). Fallback: DEVIN_ORG_ID"),
|
|
137
|
+
base_url: Optional[str] = typer.Option(None, "--base-url", help="Devin API base URL (default: https://api.devin.ai/v3). Fallback: DEVIN_BASE_URL"),
|
|
138
|
+
api_version: Optional[str] = typer.Option(None, "--api-version", help="Devin API version: v3 or v1 (default: v3). Fallback: DEVIN_API_VERSION"),
|
|
139
|
+
config_file: Optional[Path] = typer.Option(None, "--config-file", help="Custom path to config file. Fallback: DEVIN_CONFIG_FILE"),
|
|
104
140
|
version: Optional[bool] = typer.Option(None, "--version", "-v", help="Show the application's version and exit."),
|
|
105
141
|
json_format: bool = typer.Option(False, "--json", help="Output raw JSON (for AI agents & scripting)"),
|
|
106
142
|
):
|
|
107
143
|
"""
|
|
108
144
|
Unofficial CLI for Devin AI v3.
|
|
109
145
|
"""
|
|
146
|
+
config.reset_runtime()
|
|
147
|
+
|
|
110
148
|
if json_format:
|
|
111
149
|
os.environ["DEVIN_OUTPUT_FORMAT"] = "json"
|
|
112
150
|
|
|
@@ -114,8 +152,19 @@ def main(
|
|
|
114
152
|
console.print(f"devin CLI version: {__version__}")
|
|
115
153
|
raise typer.Exit()
|
|
116
154
|
|
|
155
|
+
if config_file:
|
|
156
|
+
config.set_config_file(config_file)
|
|
117
157
|
if profile:
|
|
118
158
|
config.active_profile = profile
|
|
159
|
+
if token:
|
|
160
|
+
config.runtime.api_token = token
|
|
161
|
+
if org:
|
|
162
|
+
config.runtime.org_id = org
|
|
163
|
+
if base_url:
|
|
164
|
+
config.runtime.base_url = base_url
|
|
165
|
+
if api_version:
|
|
166
|
+
config.runtime.api_version = api_version
|
|
167
|
+
|
|
119
168
|
if ctx.invoked_subcommand is None:
|
|
120
169
|
console.print(ASCII_LOGO)
|
|
121
170
|
|
|
@@ -150,10 +199,10 @@ def handle_api_error(func):
|
|
|
150
199
|
console.print(f"[bold red]Connection Error:[/bold red] Failed to connect to {config.base_url}")
|
|
151
200
|
console.print(f"[dim]{str(e)}[/dim]")
|
|
152
201
|
raise typer.Exit(1)
|
|
153
|
-
elif hasattr(e, "response"): # Standard APIError handling
|
|
202
|
+
elif isinstance(e, APIError) or hasattr(e, "response"): # Standard APIError handling
|
|
154
203
|
if is_json:
|
|
155
204
|
try:
|
|
156
|
-
err_json = e.response.json()
|
|
205
|
+
err_json = e.response.json() if hasattr(e, "response") else {"error": str(e)}
|
|
157
206
|
except Exception:
|
|
158
207
|
err_json = {"error": str(e)}
|
|
159
208
|
print(json.dumps(err_json, indent=2))
|
|
@@ -178,10 +227,12 @@ def get_current_session_id():
|
|
|
178
227
|
|
|
179
228
|
@app.command()
|
|
180
229
|
def configure(
|
|
181
|
-
token: str = typer.Option(
|
|
182
|
-
base_url: str = typer.Option("
|
|
183
|
-
org_id: Optional[str] = typer.Option(None, "--org",
|
|
230
|
+
token: Optional[str] = typer.Option(None, "--token", "-t", help="Your Devin API Token (starts with apk_ or cog_)"),
|
|
231
|
+
base_url: Optional[str] = typer.Option(None, "--base-url", "-b", help="Devin API Base URL (default: https://api.devin.ai/v3)"),
|
|
232
|
+
org_id: Optional[str] = typer.Option(None, "--org", help="Default Organization ID"),
|
|
233
|
+
api_version: Optional[str] = typer.Option(None, "--api-version", help="API Version (v3 or v1)"),
|
|
184
234
|
profile: Optional[str] = typer.Option(None, "--profile", help="Profile to configure (overrides global --profile)"),
|
|
235
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Non-interactive mode (never prompt for input)"),
|
|
185
236
|
):
|
|
186
237
|
"""
|
|
187
238
|
Configure the CLI with your Devin API token and organization.
|
|
@@ -189,21 +240,50 @@ def configure(
|
|
|
189
240
|
if profile:
|
|
190
241
|
config.active_profile = profile
|
|
191
242
|
|
|
192
|
-
|
|
243
|
+
is_interactive = not yes and sys.stdin.isatty()
|
|
244
|
+
|
|
245
|
+
if not is_interactive:
|
|
246
|
+
final_token = token or config.api_token
|
|
247
|
+
if not final_token:
|
|
248
|
+
console.print("[bold red]Error:[/bold red] API token is required. Pass --token or set DEVIN_API_TOKEN.")
|
|
249
|
+
raise typer.Exit(1)
|
|
250
|
+
final_base_url = base_url or config.base_url or "https://api.devin.ai/v3"
|
|
251
|
+
final_api_version = (api_version or config.api_version or "v3").lower()
|
|
252
|
+
final_org = org_id if org_id is not None else config.org_id
|
|
253
|
+
else:
|
|
254
|
+
final_token = token
|
|
255
|
+
if not final_token:
|
|
256
|
+
final_token = Prompt.ask("Devin API Token (starts with apk_ or cog_)")
|
|
257
|
+
if not final_token:
|
|
258
|
+
console.print("[bold red]Error:[/bold red] Token cannot be empty.")
|
|
259
|
+
raise typer.Exit(1)
|
|
260
|
+
|
|
261
|
+
final_base_url = base_url or Prompt.ask("Devin API Base URL", default=config.base_url or "https://api.devin.ai/v3")
|
|
262
|
+
|
|
263
|
+
if org_id is not None:
|
|
264
|
+
final_org = org_id
|
|
265
|
+
else:
|
|
266
|
+
org_prompt = Prompt.ask("Organization ID (optional)", default=config.org_id or "")
|
|
267
|
+
final_org = org_prompt if org_prompt else None
|
|
268
|
+
|
|
269
|
+
if api_version is not None:
|
|
270
|
+
final_api_version = api_version.lower()
|
|
271
|
+
else:
|
|
272
|
+
final_api_version = Prompt.ask(f"API Version for profile '{config.active_profile}' (v3/v1)", default=config.api_version or "v3").lower()
|
|
273
|
+
|
|
274
|
+
if not (final_token.startswith("apk_") or final_token.startswith("cog_")):
|
|
193
275
|
console.print("[bold yellow]Warning:[/bold yellow] Token format might be outdated. v3 tokens usually start with 'apk_' or 'cog_'.")
|
|
194
276
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
config.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if org_id:
|
|
206
|
-
config.org_id = org_id
|
|
277
|
+
if "api.devin.ai" in final_base_url and "v3" in final_base_url and final_api_version == "v1":
|
|
278
|
+
final_base_url = final_base_url.replace("/v3", "/v1")
|
|
279
|
+
elif "api.devin.ai" in final_base_url and "v1" in final_base_url and final_api_version == "v3":
|
|
280
|
+
final_base_url = final_base_url.replace("/v1", "/v3")
|
|
281
|
+
|
|
282
|
+
config.api_token = final_token
|
|
283
|
+
config.base_url = final_base_url
|
|
284
|
+
config.api_version = final_api_version
|
|
285
|
+
if final_org:
|
|
286
|
+
config.org_id = final_org
|
|
207
287
|
console.print(f"[green]Configuration saved to {config.config_file}[/green]")
|
|
208
288
|
return None
|
|
209
289
|
|