orca-cli-sdk 0.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.
- orca_cli_sdk-0.1.0/.gitignore +10 -0
- orca_cli_sdk-0.1.0/LICENSE +21 -0
- orca_cli_sdk-0.1.0/PKG-INFO +226 -0
- orca_cli_sdk-0.1.0/README.md +202 -0
- orca_cli_sdk-0.1.0/pyproject.toml +52 -0
- orca_cli_sdk-0.1.0/scripts/generate.py +489 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/__init__.py +41 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/_async_client.py +171 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/_client.py +195 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/_core.py +314 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/_exceptions.py +105 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/_generated.py +11094 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/_resource.py +85 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/_version.py +4 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/data/agent-context.json +7811 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/data/flag-types.json +2146 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/py.typed +0 -0
- orca_cli_sdk-0.1.0/src/orca_sdk/schema.py +33 -0
- orca_cli_sdk-0.1.0/tests/conftest.py +77 -0
- orca_cli_sdk-0.1.0/tests/test_async.py +102 -0
- orca_cli_sdk-0.1.0/tests/test_binary.py +78 -0
- orca_cli_sdk-0.1.0/tests/test_client.py +269 -0
- orca_cli_sdk-0.1.0/tests/test_generated.py +123 -0
- orca_cli_sdk-0.1.0/tests/test_integration.py +68 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Prem
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: orca-cli-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Typed Python SDK (sync and asyncio) for the Orca CLI
|
|
5
|
+
Project-URL: Homepage, https://github.com/batprem/orca-sdk-python
|
|
6
|
+
Project-URL: Repository, https://github.com/batprem/orca-sdk-python
|
|
7
|
+
Project-URL: Issues, https://github.com/batprem/orca-sdk-python/issues
|
|
8
|
+
Author: Prem Chotepanit
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,cli,orca,sdk,worktrees
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: POSIX
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# orca-cli-sdk
|
|
26
|
+
|
|
27
|
+
A typed Python SDK for the Orca CLI, with both sync and `asyncio` clients.
|
|
28
|
+
|
|
29
|
+
- **Covers every command.** All 287 commands in Orca CLI 1.4.237 are wrapped, generated from the CLI's own schema (`orca agent-context --json`).
|
|
30
|
+
- **Mirrors the CLI.** `orca worktree list --limit 5` becomes `client.worktree.list(limit=5)`.
|
|
31
|
+
- **Uses the JSON envelope.** Commands run with `--json`, the SDK parses the `{ok, result, error}` envelope, and you get `result` back.
|
|
32
|
+
- **Typed exceptions.** Covers a missing binary, CLI errors (with `code`/`message`/`data`), timeouts and bad JSON.
|
|
33
|
+
- **No runtime dependencies.** Needs Python 3.10+.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv add orca-cli-sdk # or: pip install orca-cli-sdk
|
|
37
|
+
# import name: orca_sdk
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from orca_sdk import Orca
|
|
44
|
+
|
|
45
|
+
orca = Orca()
|
|
46
|
+
|
|
47
|
+
status = orca.status()
|
|
48
|
+
print(status["runtime"]["state"]) # "ready"
|
|
49
|
+
|
|
50
|
+
for project in orca.project.list()["projects"]:
|
|
51
|
+
print(project["id"], project["displayName"])
|
|
52
|
+
|
|
53
|
+
repos = orca.repo.list()
|
|
54
|
+
worktrees = orca.worktree.list(repo="name:orca", limit=10)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Async
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import asyncio
|
|
61
|
+
from orca_sdk import AsyncOrca
|
|
62
|
+
|
|
63
|
+
async def main() -> None:
|
|
64
|
+
async with AsyncOrca() as orca:
|
|
65
|
+
status, projects = await asyncio.gather(orca.status(), orca.project.list())
|
|
66
|
+
print(status["graph"]["state"], len(projects["projects"]))
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`AsyncOrca` has the same methods and signatures as `Orca`, but each command must be awaited. It runs the CLI with `asyncio.create_subprocess_exec`. If a command times out or is cancelled, the SDK kills the CLI process.
|
|
72
|
+
|
|
73
|
+
## Finding the `orca` binary
|
|
74
|
+
|
|
75
|
+
The binary is located the first time it is needed, checking in this order:
|
|
76
|
+
|
|
77
|
+
1. `Orca(binary="/path/to/orca")`
|
|
78
|
+
2. the `ORCA_BIN` environment variable
|
|
79
|
+
3. `orca` on `PATH`
|
|
80
|
+
4. `/home/box/Applications/bin/orca`
|
|
81
|
+
|
|
82
|
+
If none of these is an executable file, the SDK raises `OrcaBinaryNotFoundError`. `orca_sdk.find_binary()` runs the same lookup.
|
|
83
|
+
|
|
84
|
+
## Mapping CLI commands to methods
|
|
85
|
+
|
|
86
|
+
| CLI | Python |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `orca status` | `orca.status()` |
|
|
89
|
+
| `orca worktree create --name t --agent codex --prompt "hi"` | `orca.worktree.create(name="t", agent="codex", prompt="hi")` |
|
|
90
|
+
| `orca worktree rm --worktree active --force` | `orca.worktree.rm(worktree="active", force=True)` |
|
|
91
|
+
| `orca terminal wait --terminal t1 --for exit --timeout-ms 60000` | `orca.terminal.wait(terminal="t1", for_="exit", timeout_ms=60000)` |
|
|
92
|
+
| `orca reference add <url> <url> --worktree active` | `orca.reference.add(["https://…", "https://…"], worktree="active")` |
|
|
93
|
+
| `orca agent hooks status` | `orca.agent.hooks.status()` |
|
|
94
|
+
| `orca linear team list` | `orca.linear.team.list()` |
|
|
95
|
+
| `orca terminal-daemon status` | `orca.terminal_daemon.status()` |
|
|
96
|
+
| `orca serve service status` | `orca.serve.service.status()` (`orca.serve(...)` runs `serve` itself) |
|
|
97
|
+
|
|
98
|
+
Naming rules:
|
|
99
|
+
|
|
100
|
+
- Hyphens become underscores (`setup-clone` → `setup_clone`).
|
|
101
|
+
- Python keywords get a trailing `_` (`--for` → `for_`, `orca is` → `orca.is_`).
|
|
102
|
+
- Positional arguments are positional parameters.
|
|
103
|
+
- Flags are keyword-only parameters.
|
|
104
|
+
- Flags the usage string marks as required are required keywords.
|
|
105
|
+
|
|
106
|
+
How values become arguments:
|
|
107
|
+
|
|
108
|
+
- Boolean flags take `True`.
|
|
109
|
+
- Repeatable flags (`--reference`, `--label`, …) take a list.
|
|
110
|
+
- `None`/`False` leaves the flag out.
|
|
111
|
+
|
|
112
|
+
Every method's docstring holds the CLI usage, notes and examples, so `help(orca.worktree.create)` shows what `orca worktree create --help` would.
|
|
113
|
+
|
|
114
|
+
The CLI's groups are all available, including:
|
|
115
|
+
|
|
116
|
+
- `project`, `repo`, `worktree`, `agent`, `terminal`
|
|
117
|
+
- `automations`, `host`, `environment`, `skills`, `reference`, `account`
|
|
118
|
+
- `orchestration`, `artifacts`, `file`, `tab`, `linear`, `jira`, `computer`, `emulator`, `storage`, `serve`
|
|
119
|
+
|
|
120
|
+
Top-level commands are methods on the client: `status`, `search`, `open`, `agent_context`, `snapshot`, `goto`, `click`, and the rest.
|
|
121
|
+
|
|
122
|
+
## Options: timeouts, env vars, remote runtimes
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
orca = Orca(
|
|
126
|
+
timeout=120, # seconds per command (default 60, None = no limit)
|
|
127
|
+
env={"NO_COLOR": "1"}, # extra env vars for the subprocess
|
|
128
|
+
inherit_env=True, # start from os.environ (default)
|
|
129
|
+
cwd="/path/to/worktree", # Orca infers the current worktree from cwd
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Route every command to a saved remote Orca server (adds --environment <name>)
|
|
133
|
+
remote = Orca(environment="build-box")
|
|
134
|
+
|
|
135
|
+
# ...or to one reached by a pairing code (passed as $ORCA_PAIRING_CODE so it
|
|
136
|
+
# does not appear in the process list)
|
|
137
|
+
paired = Orca(pairing_code="orca://pair?...")
|
|
138
|
+
|
|
139
|
+
# Derive a client with different options; the original is unchanged
|
|
140
|
+
slow = orca.with_options(timeout=600)
|
|
141
|
+
slow.terminal.wait(terminal="term_123", for_="exit", timeout_ms=600_000)
|
|
142
|
+
local = remote.with_options(environment=None)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Some commands only run on the local host and reject routing: `account list`, `host list` and `environment list`. The `environment …` management commands also take `--environment` as a *selector* (which server to act on), not as routing. Calling any of these on a client that has `environment` or `pairing_code` set raises `OrcaUsageError` before running anything. Use `with_options(environment=None, pairing_code=None)` to call them.
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
orca.environment.show(environment="build-box") # selector, not routing
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Errors
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from orca_sdk import (
|
|
155
|
+
OrcaError, # base class
|
|
156
|
+
OrcaBinaryNotFoundError, # no orca executable
|
|
157
|
+
OrcaCommandError, # the CLI reported ok=false or exited non-zero
|
|
158
|
+
OrcaTimeoutError, # killed after the timeout
|
|
159
|
+
OrcaJSONDecodeError, # --json output was not valid JSON
|
|
160
|
+
OrcaUsageError, # e.g. routing a host-local command
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
orca.repo.show(repo="name:missing")
|
|
165
|
+
except OrcaCommandError as err:
|
|
166
|
+
print(err.code) # "repo_not_found"
|
|
167
|
+
print(err.message)
|
|
168
|
+
print(err.data) # error.data from the envelope, if any
|
|
169
|
+
print(err.next_steps) # the CLI's suggested next steps
|
|
170
|
+
print(err.returncode, err.argv, err.stderr)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
If the CLI fails without printing a JSON envelope, you still get an `OrcaCommandError`, with `code == "process_error"` and the message taken from stderr.
|
|
174
|
+
|
|
175
|
+
## Raw access
|
|
176
|
+
|
|
177
|
+
`run_command` runs any argument list. It returns the full `OrcaResponse`: `ok`, `result`, `error`, `id`, `meta`, `stdout`, `stderr`, `returncode` and `argv`.
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
resp = orca.run_command(["worktree", "ps", "--limit", "3"])
|
|
181
|
+
print(resp.meta["runtimeId"], resp.result)
|
|
182
|
+
|
|
183
|
+
resp = orca.run_command(["repo", "show", "--repo", "x"], check=False) # don't raise
|
|
184
|
+
if not resp.ok:
|
|
185
|
+
print(resp.error["code"])
|
|
186
|
+
|
|
187
|
+
orca.run_command(["claude-teams", "--help"], json=False) # plain-text commands
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
To feed stdin, pass `stdin=` to `run_command` or to the generated methods that read stdin (for example `orca.speech.openai_key.set(stdin=key)` or `--text-stdin` commands).
|
|
191
|
+
|
|
192
|
+
The bundled schema is available at runtime:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from orca_sdk.schema import load_schema, get_command, command_names
|
|
196
|
+
get_command("worktree create")["notes"]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
uv sync
|
|
203
|
+
uv run pytest # unit tests (subprocess is mocked)
|
|
204
|
+
ORCA_INTEGRATION=1 uv run pytest # also run read-only tests against the real orca
|
|
205
|
+
uv build
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The integration tests only run read-only commands: `status`, `project list`, `repo list`, `worktree list`, `host list`, `environment list`, `skills list`, `agent-context` and a `repo show` that is expected to fail.
|
|
209
|
+
|
|
210
|
+
### Regenerating the wrappers
|
|
211
|
+
|
|
212
|
+
`src/orca_sdk/_generated.py` is generated, so don't edit it by hand. To regenerate it after an Orca upgrade:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
orca agent-context --json > src/orca_sdk/data/agent-context.json
|
|
216
|
+
# optional, improves flag typing: a dump of `orca <cmd> --help` for every command,
|
|
217
|
+
# each section headed by a line `== orca <cmd> --help`
|
|
218
|
+
python scripts/generate.py --help-dump /path/to/orca-cli-help.txt -v
|
|
219
|
+
uv run pytest
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The schema lists each command's flag names but not whether a flag takes a value. The generator works that out from the usage string, then from the `--help` text, which it caches in `data/flag-types.json`. If neither settles it, the generator uses how the same flag is written in other commands. `-v` lists the flags typed that way. `tests/test_generated.py` checks that the generated file is up to date and that every command builds its expected argv.
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# orca-cli-sdk
|
|
2
|
+
|
|
3
|
+
A typed Python SDK for the Orca CLI, with both sync and `asyncio` clients.
|
|
4
|
+
|
|
5
|
+
- **Covers every command.** All 287 commands in Orca CLI 1.4.237 are wrapped, generated from the CLI's own schema (`orca agent-context --json`).
|
|
6
|
+
- **Mirrors the CLI.** `orca worktree list --limit 5` becomes `client.worktree.list(limit=5)`.
|
|
7
|
+
- **Uses the JSON envelope.** Commands run with `--json`, the SDK parses the `{ok, result, error}` envelope, and you get `result` back.
|
|
8
|
+
- **Typed exceptions.** Covers a missing binary, CLI errors (with `code`/`message`/`data`), timeouts and bad JSON.
|
|
9
|
+
- **No runtime dependencies.** Needs Python 3.10+.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv add orca-cli-sdk # or: pip install orca-cli-sdk
|
|
13
|
+
# import name: orca_sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from orca_sdk import Orca
|
|
20
|
+
|
|
21
|
+
orca = Orca()
|
|
22
|
+
|
|
23
|
+
status = orca.status()
|
|
24
|
+
print(status["runtime"]["state"]) # "ready"
|
|
25
|
+
|
|
26
|
+
for project in orca.project.list()["projects"]:
|
|
27
|
+
print(project["id"], project["displayName"])
|
|
28
|
+
|
|
29
|
+
repos = orca.repo.list()
|
|
30
|
+
worktrees = orca.worktree.list(repo="name:orca", limit=10)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Async
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import asyncio
|
|
37
|
+
from orca_sdk import AsyncOrca
|
|
38
|
+
|
|
39
|
+
async def main() -> None:
|
|
40
|
+
async with AsyncOrca() as orca:
|
|
41
|
+
status, projects = await asyncio.gather(orca.status(), orca.project.list())
|
|
42
|
+
print(status["graph"]["state"], len(projects["projects"]))
|
|
43
|
+
|
|
44
|
+
asyncio.run(main())
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`AsyncOrca` has the same methods and signatures as `Orca`, but each command must be awaited. It runs the CLI with `asyncio.create_subprocess_exec`. If a command times out or is cancelled, the SDK kills the CLI process.
|
|
48
|
+
|
|
49
|
+
## Finding the `orca` binary
|
|
50
|
+
|
|
51
|
+
The binary is located the first time it is needed, checking in this order:
|
|
52
|
+
|
|
53
|
+
1. `Orca(binary="/path/to/orca")`
|
|
54
|
+
2. the `ORCA_BIN` environment variable
|
|
55
|
+
3. `orca` on `PATH`
|
|
56
|
+
4. `/home/box/Applications/bin/orca`
|
|
57
|
+
|
|
58
|
+
If none of these is an executable file, the SDK raises `OrcaBinaryNotFoundError`. `orca_sdk.find_binary()` runs the same lookup.
|
|
59
|
+
|
|
60
|
+
## Mapping CLI commands to methods
|
|
61
|
+
|
|
62
|
+
| CLI | Python |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `orca status` | `orca.status()` |
|
|
65
|
+
| `orca worktree create --name t --agent codex --prompt "hi"` | `orca.worktree.create(name="t", agent="codex", prompt="hi")` |
|
|
66
|
+
| `orca worktree rm --worktree active --force` | `orca.worktree.rm(worktree="active", force=True)` |
|
|
67
|
+
| `orca terminal wait --terminal t1 --for exit --timeout-ms 60000` | `orca.terminal.wait(terminal="t1", for_="exit", timeout_ms=60000)` |
|
|
68
|
+
| `orca reference add <url> <url> --worktree active` | `orca.reference.add(["https://…", "https://…"], worktree="active")` |
|
|
69
|
+
| `orca agent hooks status` | `orca.agent.hooks.status()` |
|
|
70
|
+
| `orca linear team list` | `orca.linear.team.list()` |
|
|
71
|
+
| `orca terminal-daemon status` | `orca.terminal_daemon.status()` |
|
|
72
|
+
| `orca serve service status` | `orca.serve.service.status()` (`orca.serve(...)` runs `serve` itself) |
|
|
73
|
+
|
|
74
|
+
Naming rules:
|
|
75
|
+
|
|
76
|
+
- Hyphens become underscores (`setup-clone` → `setup_clone`).
|
|
77
|
+
- Python keywords get a trailing `_` (`--for` → `for_`, `orca is` → `orca.is_`).
|
|
78
|
+
- Positional arguments are positional parameters.
|
|
79
|
+
- Flags are keyword-only parameters.
|
|
80
|
+
- Flags the usage string marks as required are required keywords.
|
|
81
|
+
|
|
82
|
+
How values become arguments:
|
|
83
|
+
|
|
84
|
+
- Boolean flags take `True`.
|
|
85
|
+
- Repeatable flags (`--reference`, `--label`, …) take a list.
|
|
86
|
+
- `None`/`False` leaves the flag out.
|
|
87
|
+
|
|
88
|
+
Every method's docstring holds the CLI usage, notes and examples, so `help(orca.worktree.create)` shows what `orca worktree create --help` would.
|
|
89
|
+
|
|
90
|
+
The CLI's groups are all available, including:
|
|
91
|
+
|
|
92
|
+
- `project`, `repo`, `worktree`, `agent`, `terminal`
|
|
93
|
+
- `automations`, `host`, `environment`, `skills`, `reference`, `account`
|
|
94
|
+
- `orchestration`, `artifacts`, `file`, `tab`, `linear`, `jira`, `computer`, `emulator`, `storage`, `serve`
|
|
95
|
+
|
|
96
|
+
Top-level commands are methods on the client: `status`, `search`, `open`, `agent_context`, `snapshot`, `goto`, `click`, and the rest.
|
|
97
|
+
|
|
98
|
+
## Options: timeouts, env vars, remote runtimes
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
orca = Orca(
|
|
102
|
+
timeout=120, # seconds per command (default 60, None = no limit)
|
|
103
|
+
env={"NO_COLOR": "1"}, # extra env vars for the subprocess
|
|
104
|
+
inherit_env=True, # start from os.environ (default)
|
|
105
|
+
cwd="/path/to/worktree", # Orca infers the current worktree from cwd
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Route every command to a saved remote Orca server (adds --environment <name>)
|
|
109
|
+
remote = Orca(environment="build-box")
|
|
110
|
+
|
|
111
|
+
# ...or to one reached by a pairing code (passed as $ORCA_PAIRING_CODE so it
|
|
112
|
+
# does not appear in the process list)
|
|
113
|
+
paired = Orca(pairing_code="orca://pair?...")
|
|
114
|
+
|
|
115
|
+
# Derive a client with different options; the original is unchanged
|
|
116
|
+
slow = orca.with_options(timeout=600)
|
|
117
|
+
slow.terminal.wait(terminal="term_123", for_="exit", timeout_ms=600_000)
|
|
118
|
+
local = remote.with_options(environment=None)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Some commands only run on the local host and reject routing: `account list`, `host list` and `environment list`. The `environment …` management commands also take `--environment` as a *selector* (which server to act on), not as routing. Calling any of these on a client that has `environment` or `pairing_code` set raises `OrcaUsageError` before running anything. Use `with_options(environment=None, pairing_code=None)` to call them.
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
orca.environment.show(environment="build-box") # selector, not routing
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Errors
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from orca_sdk import (
|
|
131
|
+
OrcaError, # base class
|
|
132
|
+
OrcaBinaryNotFoundError, # no orca executable
|
|
133
|
+
OrcaCommandError, # the CLI reported ok=false or exited non-zero
|
|
134
|
+
OrcaTimeoutError, # killed after the timeout
|
|
135
|
+
OrcaJSONDecodeError, # --json output was not valid JSON
|
|
136
|
+
OrcaUsageError, # e.g. routing a host-local command
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
orca.repo.show(repo="name:missing")
|
|
141
|
+
except OrcaCommandError as err:
|
|
142
|
+
print(err.code) # "repo_not_found"
|
|
143
|
+
print(err.message)
|
|
144
|
+
print(err.data) # error.data from the envelope, if any
|
|
145
|
+
print(err.next_steps) # the CLI's suggested next steps
|
|
146
|
+
print(err.returncode, err.argv, err.stderr)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
If the CLI fails without printing a JSON envelope, you still get an `OrcaCommandError`, with `code == "process_error"` and the message taken from stderr.
|
|
150
|
+
|
|
151
|
+
## Raw access
|
|
152
|
+
|
|
153
|
+
`run_command` runs any argument list. It returns the full `OrcaResponse`: `ok`, `result`, `error`, `id`, `meta`, `stdout`, `stderr`, `returncode` and `argv`.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
resp = orca.run_command(["worktree", "ps", "--limit", "3"])
|
|
157
|
+
print(resp.meta["runtimeId"], resp.result)
|
|
158
|
+
|
|
159
|
+
resp = orca.run_command(["repo", "show", "--repo", "x"], check=False) # don't raise
|
|
160
|
+
if not resp.ok:
|
|
161
|
+
print(resp.error["code"])
|
|
162
|
+
|
|
163
|
+
orca.run_command(["claude-teams", "--help"], json=False) # plain-text commands
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
To feed stdin, pass `stdin=` to `run_command` or to the generated methods that read stdin (for example `orca.speech.openai_key.set(stdin=key)` or `--text-stdin` commands).
|
|
167
|
+
|
|
168
|
+
The bundled schema is available at runtime:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from orca_sdk.schema import load_schema, get_command, command_names
|
|
172
|
+
get_command("worktree create")["notes"]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Development
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
uv sync
|
|
179
|
+
uv run pytest # unit tests (subprocess is mocked)
|
|
180
|
+
ORCA_INTEGRATION=1 uv run pytest # also run read-only tests against the real orca
|
|
181
|
+
uv build
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The integration tests only run read-only commands: `status`, `project list`, `repo list`, `worktree list`, `host list`, `environment list`, `skills list`, `agent-context` and a `repo show` that is expected to fail.
|
|
185
|
+
|
|
186
|
+
### Regenerating the wrappers
|
|
187
|
+
|
|
188
|
+
`src/orca_sdk/_generated.py` is generated, so don't edit it by hand. To regenerate it after an Orca upgrade:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
orca agent-context --json > src/orca_sdk/data/agent-context.json
|
|
192
|
+
# optional, improves flag typing: a dump of `orca <cmd> --help` for every command,
|
|
193
|
+
# each section headed by a line `== orca <cmd> --help`
|
|
194
|
+
python scripts/generate.py --help-dump /path/to/orca-cli-help.txt -v
|
|
195
|
+
uv run pytest
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The schema lists each command's flag names but not whether a flag takes a value. The generator works that out from the usage string, then from the `--help` text, which it caches in `data/flag-types.json`. If neither settles it, the generator uses how the same flag is written in other commands. `-v` lists the flags typed that way. `tests/test_generated.py` checks that the generated file is up to date and that every command builds its expected argv.
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "orca-cli-sdk"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Typed Python SDK (sync and asyncio) for the Orca CLI"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
authors = [{ name = "Prem Chotepanit" }]
|
|
10
|
+
keywords = ["orca", "cli", "sdk", "agents", "worktrees"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Operating System :: POSIX",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
dependencies = []
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/batprem/orca-sdk-python"
|
|
28
|
+
Repository = "https://github.com/batprem/orca-sdk-python"
|
|
29
|
+
Issues = "https://github.com/batprem/orca-sdk-python/issues"
|
|
30
|
+
|
|
31
|
+
[dependency-groups]
|
|
32
|
+
dev = ["pytest>=8", "pytest-asyncio>=0.23"]
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["hatchling>=1.26"]
|
|
36
|
+
build-backend = "hatchling.build"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/orca_sdk"]
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.sdist]
|
|
42
|
+
include = ["src/orca_sdk", "scripts", "tests", "README.md", "LICENSE"]
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
testpaths = ["tests"]
|
|
46
|
+
asyncio_mode = "auto"
|
|
47
|
+
markers = ["integration: runs against the real orca binary (set ORCA_INTEGRATION=1)"]
|
|
48
|
+
|
|
49
|
+
[tool.ruff]
|
|
50
|
+
line-length = 110
|
|
51
|
+
target-version = "py310"
|
|
52
|
+
extend-exclude = ["src/orca_sdk/_generated.py"]
|