contree-cli 0.7.1__tar.gz → 0.9.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.
- {contree_cli-0.7.1 → contree_cli-0.9.0}/PKG-INFO +19 -19
- {contree_cli-0.7.1 → contree_cli-0.9.0}/README.md +17 -18
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/__init__.py +5 -4
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/__main__.py +47 -21
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/agent.md +27 -25
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/arguments.py +2 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/auth.py +31 -30
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/build.py +58 -44
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/cat.py +3 -5
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/cd.py +5 -9
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/cp.py +33 -36
- contree_cli-0.9.0/contree_cli/cli/export.py +172 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/file.py +41 -73
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/images.py +49 -59
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/ls.py +18 -11
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/operation.py +65 -72
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/run.py +234 -58
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/session.py +16 -9
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/show.py +14 -13
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/skill.py +10 -8
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/tag.py +7 -4
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/use.py +11 -14
- contree_cli-0.9.0/contree_cli/client.py +110 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/config.py +69 -94
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/context.py +29 -3
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/kw_add.py +3 -2
- contree_cli-0.9.0/contree_cli/docker/kw_copy.py +350 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/kw_from.py +53 -26
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/kw_run.py +50 -39
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/url_fetch.py +13 -54
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/log.py +5 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/manual.md +9 -9
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/session.py +8 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/argmap.py +2 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/completer.py +8 -6
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/repl.py +8 -6
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/sources.py +45 -29
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/skill.py +174 -79
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/skill_body.md +15 -30
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/types.py +4 -6
- {contree_cli-0.7.1 → contree_cli-0.9.0}/pyproject.toml +5 -2
- contree_cli-0.7.1/contree_cli/client.py +0 -668
- contree_cli-0.7.1/contree_cli/docker/kw_copy.py +0 -178
- {contree_cli-0.7.1 → contree_cli-0.9.0}/LICENSE +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/__init__.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/agent.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/cli/env.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/__init__.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/dockerignore.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/keyword.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/kw_arg.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/kw_env.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/kw_skipped.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/kw_user.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/kw_workdir.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/local_context.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/docker/parser.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/man.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/mapped_file.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/migrations.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/output.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/py.typed +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/refs.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/__init__.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/argspec.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/cache.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/history.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/parser.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/shell/trie.py +0 -0
- {contree_cli-0.7.1 → contree_cli-0.9.0}/contree_cli/update_check.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: contree-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: CLI client for the ConTree sandbox platform
|
|
5
5
|
Keywords: sandbox,cli,vm,ai-agent,contree
|
|
6
6
|
Author: Dmitry Orlov
|
|
@@ -197,6 +197,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
197
197
|
Classifier: Topic :: Software Development :: Build Tools
|
|
198
198
|
Classifier: Topic :: System :: Shells
|
|
199
199
|
Classifier: Typing :: Typed
|
|
200
|
+
Requires-Dist: contree-client>=0.1.3
|
|
200
201
|
Requires-Python: >=3.10
|
|
201
202
|
Project-URL: Homepage, https://contree.dev
|
|
202
203
|
Project-URL: Documentation, https://docs.contree.dev/cli/
|
|
@@ -208,7 +209,6 @@ Description-Content-Type: text/markdown
|
|
|
208
209
|
# contree-cli
|
|
209
210
|
|
|
210
211
|
[](https://www.python.org/downloads/)
|
|
211
|
-
[](#zero-dependencies)
|
|
212
212
|
[](https://pypi.org/project/contree-cli/)
|
|
213
213
|
|
|
214
214
|
Command-line client for the [ConTree](https://contree.dev) sandboxing platform — secure, VM-isolated sandboxes with git-like branching for AI agents and developers.
|
|
@@ -218,8 +218,9 @@ eval $(contree use tag:ubuntu:latest) # pick a base image for current session
|
|
|
218
218
|
contree run apt-get update -qq # each run snapshots the result
|
|
219
219
|
contree run apt-get install -y curl # builds on the previous snapshot
|
|
220
220
|
contree session branch experiment # branch the sandbox state
|
|
221
|
+
contree session checkout experiment # switch to the branch
|
|
221
222
|
contree run -- make test # experiment freely
|
|
222
|
-
contree session checkout main # switch back
|
|
223
|
+
contree session checkout main # switch back
|
|
223
224
|
contree session rollback -- -2 # or rewind two steps
|
|
224
225
|
```
|
|
225
226
|
|
|
@@ -234,8 +235,6 @@ contree session rollback -- -2 # or rewind two steps
|
|
|
234
235
|
- **Safe code execution** — run untrusted or LLM-generated code inside VM-level isolation; crashes and side effects stay in the sandbox
|
|
235
236
|
- **Session continuity** — rewind and resume long-running agent workflows with full filesystem context preserved
|
|
236
237
|
|
|
237
|
-
`contree-cli` talks to the ConTree API. Install it, authenticate with your project token, and create sandboxes, run commands, inspect filesystems, and manage sessions — all from your terminal, shell scripts, or agent toolchains.
|
|
238
|
-
|
|
239
238
|
## Install
|
|
240
239
|
|
|
241
240
|
```bash
|
|
@@ -269,7 +268,7 @@ Verify:
|
|
|
269
268
|
contree --help
|
|
270
269
|
```
|
|
271
270
|
|
|
272
|
-
**Requirements:** Python 3.10
|
|
271
|
+
**Requirements:** Python 3.10+.
|
|
273
272
|
|
|
274
273
|
## Quick Start
|
|
275
274
|
|
|
@@ -289,7 +288,7 @@ If `--token`/`--url`/`--project` flags are omitted, `contree auth` reads `CONTRE
|
|
|
289
288
|
contree skill install
|
|
290
289
|
```
|
|
291
290
|
|
|
292
|
-
Autodetects installed agents (Claude Code, Codex, OpenCode, Cline, Amp) and installs ConTree skill files into their skill directories. Use `contree skill install -
|
|
291
|
+
Autodetects installed agents (Claude Code, Codex, OpenCode, Cline, Amp) and installs ConTree skill files into their skill directories. Use `contree skill install -f` to force-overwrite.
|
|
293
292
|
|
|
294
293
|
### 3. Start a session
|
|
295
294
|
|
|
@@ -321,7 +320,8 @@ contree cp /app/output.log . # download to local machine
|
|
|
321
320
|
|
|
322
321
|
```bash
|
|
323
322
|
contree session branch experiment # create a branch
|
|
324
|
-
contree
|
|
323
|
+
contree session checkout experiment # switch to it
|
|
324
|
+
contree run -- make test # experiment on the branch
|
|
325
325
|
contree session checkout main # switch back
|
|
326
326
|
contree session rollback # undo last run (default: back 1 entry)
|
|
327
327
|
```
|
|
@@ -441,7 +441,7 @@ main: A ── B ── C ── D
|
|
|
441
441
|
experiment: E ── F
|
|
442
442
|
```
|
|
443
443
|
|
|
444
|
-
Every `run` creates a checkpoint
|
|
444
|
+
Every non-disposable `run` creates a checkpoint in the session history.
|
|
445
445
|
|
|
446
446
|
```bash
|
|
447
447
|
contree session # show current state
|
|
@@ -454,14 +454,14 @@ contree session use other-session # import image from another session
|
|
|
454
454
|
|
|
455
455
|
## Output Formats
|
|
456
456
|
|
|
457
|
-
All commands support structured output via `-
|
|
457
|
+
All commands support structured output via `-o`/`--format`/`--output`:
|
|
458
458
|
|
|
459
459
|
```bash
|
|
460
|
-
contree
|
|
461
|
-
contree
|
|
462
|
-
contree
|
|
463
|
-
contree
|
|
464
|
-
contree
|
|
460
|
+
contree -o json images # JSON (one object per line)
|
|
461
|
+
contree -o json-pretty images # pretty-printed JSON array
|
|
462
|
+
contree -o csv ps # RFC 4180 CSV
|
|
463
|
+
contree -o tsv ps # tab-separated values
|
|
464
|
+
contree -o table ls # ASCII table
|
|
465
465
|
```
|
|
466
466
|
|
|
467
467
|
Pipe JSON output into `jq`, feed CSV into spreadsheets, or parse programmatically in your agent toolchain.
|
|
@@ -490,7 +490,7 @@ contree auth --profile=staging # save staging token
|
|
|
490
490
|
contree auth --profile=prod # save production token
|
|
491
491
|
contree auth profiles # list all profiles + status probe
|
|
492
492
|
contree auth profiles --offline # list profiles without network checks
|
|
493
|
-
contree -
|
|
493
|
+
contree -o json auth profiles # structured profile health output
|
|
494
494
|
contree auth switch staging # switch active profile
|
|
495
495
|
```
|
|
496
496
|
|
|
@@ -515,9 +515,9 @@ Read only by `contree auth` (registration-time fallbacks for omitted flags):
|
|
|
515
515
|
|
|
516
516
|
Credentials come strictly from the saved profile at runtime. `--token`, `--url`, `--project` CLI flags override profile fields for a single invocation.
|
|
517
517
|
|
|
518
|
-
##
|
|
518
|
+
## Dependencies
|
|
519
519
|
|
|
520
|
-
`contree-cli`
|
|
520
|
+
`contree-cli` has a single runtime dependency: the `contree-client` library, which provides the HTTP transport and typed API bindings.
|
|
521
521
|
|
|
522
522
|
## Development
|
|
523
523
|
|
|
@@ -534,7 +534,7 @@ make check # lint + types
|
|
|
534
534
|
make tests # lint + types + pytest
|
|
535
535
|
```
|
|
536
536
|
|
|
537
|
-
The project enforces strict mypy
|
|
537
|
+
The project enforces strict mypy and ruff linting (E/F/W/I/UP/B/SIM/RUF rules).
|
|
538
538
|
|
|
539
539
|
## Documentation
|
|
540
540
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# contree-cli
|
|
2
2
|
|
|
3
3
|
[](https://www.python.org/downloads/)
|
|
4
|
-
[](#zero-dependencies)
|
|
5
4
|
[](https://pypi.org/project/contree-cli/)
|
|
6
5
|
|
|
7
6
|
Command-line client for the [ConTree](https://contree.dev) sandboxing platform — secure, VM-isolated sandboxes with git-like branching for AI agents and developers.
|
|
@@ -11,8 +10,9 @@ eval $(contree use tag:ubuntu:latest) # pick a base image for current session
|
|
|
11
10
|
contree run apt-get update -qq # each run snapshots the result
|
|
12
11
|
contree run apt-get install -y curl # builds on the previous snapshot
|
|
13
12
|
contree session branch experiment # branch the sandbox state
|
|
13
|
+
contree session checkout experiment # switch to the branch
|
|
14
14
|
contree run -- make test # experiment freely
|
|
15
|
-
contree session checkout main # switch back
|
|
15
|
+
contree session checkout main # switch back
|
|
16
16
|
contree session rollback -- -2 # or rewind two steps
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -27,8 +27,6 @@ contree session rollback -- -2 # or rewind two steps
|
|
|
27
27
|
- **Safe code execution** — run untrusted or LLM-generated code inside VM-level isolation; crashes and side effects stay in the sandbox
|
|
28
28
|
- **Session continuity** — rewind and resume long-running agent workflows with full filesystem context preserved
|
|
29
29
|
|
|
30
|
-
`contree-cli` talks to the ConTree API. Install it, authenticate with your project token, and create sandboxes, run commands, inspect filesystems, and manage sessions — all from your terminal, shell scripts, or agent toolchains.
|
|
31
|
-
|
|
32
30
|
## Install
|
|
33
31
|
|
|
34
32
|
```bash
|
|
@@ -62,7 +60,7 @@ Verify:
|
|
|
62
60
|
contree --help
|
|
63
61
|
```
|
|
64
62
|
|
|
65
|
-
**Requirements:** Python 3.10
|
|
63
|
+
**Requirements:** Python 3.10+.
|
|
66
64
|
|
|
67
65
|
## Quick Start
|
|
68
66
|
|
|
@@ -82,7 +80,7 @@ If `--token`/`--url`/`--project` flags are omitted, `contree auth` reads `CONTRE
|
|
|
82
80
|
contree skill install
|
|
83
81
|
```
|
|
84
82
|
|
|
85
|
-
Autodetects installed agents (Claude Code, Codex, OpenCode, Cline, Amp) and installs ConTree skill files into their skill directories. Use `contree skill install -
|
|
83
|
+
Autodetects installed agents (Claude Code, Codex, OpenCode, Cline, Amp) and installs ConTree skill files into their skill directories. Use `contree skill install -f` to force-overwrite.
|
|
86
84
|
|
|
87
85
|
### 3. Start a session
|
|
88
86
|
|
|
@@ -114,7 +112,8 @@ contree cp /app/output.log . # download to local machine
|
|
|
114
112
|
|
|
115
113
|
```bash
|
|
116
114
|
contree session branch experiment # create a branch
|
|
117
|
-
contree
|
|
115
|
+
contree session checkout experiment # switch to it
|
|
116
|
+
contree run -- make test # experiment on the branch
|
|
118
117
|
contree session checkout main # switch back
|
|
119
118
|
contree session rollback # undo last run (default: back 1 entry)
|
|
120
119
|
```
|
|
@@ -234,7 +233,7 @@ main: A ── B ── C ── D
|
|
|
234
233
|
experiment: E ── F
|
|
235
234
|
```
|
|
236
235
|
|
|
237
|
-
Every `run` creates a checkpoint
|
|
236
|
+
Every non-disposable `run` creates a checkpoint in the session history.
|
|
238
237
|
|
|
239
238
|
```bash
|
|
240
239
|
contree session # show current state
|
|
@@ -247,14 +246,14 @@ contree session use other-session # import image from another session
|
|
|
247
246
|
|
|
248
247
|
## Output Formats
|
|
249
248
|
|
|
250
|
-
All commands support structured output via `-
|
|
249
|
+
All commands support structured output via `-o`/`--format`/`--output`:
|
|
251
250
|
|
|
252
251
|
```bash
|
|
253
|
-
contree
|
|
254
|
-
contree
|
|
255
|
-
contree
|
|
256
|
-
contree
|
|
257
|
-
contree
|
|
252
|
+
contree -o json images # JSON (one object per line)
|
|
253
|
+
contree -o json-pretty images # pretty-printed JSON array
|
|
254
|
+
contree -o csv ps # RFC 4180 CSV
|
|
255
|
+
contree -o tsv ps # tab-separated values
|
|
256
|
+
contree -o table ls # ASCII table
|
|
258
257
|
```
|
|
259
258
|
|
|
260
259
|
Pipe JSON output into `jq`, feed CSV into spreadsheets, or parse programmatically in your agent toolchain.
|
|
@@ -283,7 +282,7 @@ contree auth --profile=staging # save staging token
|
|
|
283
282
|
contree auth --profile=prod # save production token
|
|
284
283
|
contree auth profiles # list all profiles + status probe
|
|
285
284
|
contree auth profiles --offline # list profiles without network checks
|
|
286
|
-
contree -
|
|
285
|
+
contree -o json auth profiles # structured profile health output
|
|
287
286
|
contree auth switch staging # switch active profile
|
|
288
287
|
```
|
|
289
288
|
|
|
@@ -308,9 +307,9 @@ Read only by `contree auth` (registration-time fallbacks for omitted flags):
|
|
|
308
307
|
|
|
309
308
|
Credentials come strictly from the saved profile at runtime. `--token`, `--url`, `--project` CLI flags override profile fields for a single invocation.
|
|
310
309
|
|
|
311
|
-
##
|
|
310
|
+
## Dependencies
|
|
312
311
|
|
|
313
|
-
`contree-cli`
|
|
312
|
+
`contree-cli` has a single runtime dependency: the `contree-client` library, which provides the HTTP transport and typed API bindings.
|
|
314
313
|
|
|
315
314
|
## Development
|
|
316
315
|
|
|
@@ -327,7 +326,7 @@ make check # lint + types
|
|
|
327
326
|
make tests # lint + types + pytest
|
|
328
327
|
```
|
|
329
328
|
|
|
330
|
-
The project enforces strict mypy
|
|
329
|
+
The project enforces strict mypy and ruff linting (E/F/W/I/UP/B/SIM/RUF rules).
|
|
331
330
|
|
|
332
331
|
## Documentation
|
|
333
332
|
|
|
@@ -6,13 +6,14 @@ from contextvars import ContextVar
|
|
|
6
6
|
from typing import TYPE_CHECKING, Protocol
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
|
-
from
|
|
10
|
-
|
|
9
|
+
from contree_client.profiles import Profile
|
|
10
|
+
|
|
11
|
+
from contree_cli.client import CliClient
|
|
11
12
|
from contree_cli.output import OutputFormatter
|
|
12
13
|
from contree_cli.session import SessionStore
|
|
13
14
|
|
|
14
|
-
PROFILE: ContextVar[
|
|
15
|
-
CLIENT: ContextVar[
|
|
15
|
+
PROFILE: ContextVar[Profile] = ContextVar("PROFILE")
|
|
16
|
+
CLIENT: ContextVar[CliClient] = ContextVar("CLIENT")
|
|
16
17
|
FORMATTER: ContextVar[OutputFormatter] = ContextVar("FORMATTER")
|
|
17
18
|
SESSION_STORE: ContextVar[SessionStore] = ContextVar("SESSION_STORE")
|
|
18
19
|
IN_SHELL: ContextVar[bool] = ContextVar("IN_SHELL", default=False)
|
|
@@ -3,13 +3,15 @@ import http.client
|
|
|
3
3
|
import logging
|
|
4
4
|
import sys
|
|
5
5
|
from collections.abc import Callable
|
|
6
|
-
from contextlib import suppress
|
|
6
|
+
from contextlib import ExitStack, suppress
|
|
7
7
|
from dataclasses import replace
|
|
8
8
|
|
|
9
|
+
from contree_client.exceptions import ContreeError
|
|
10
|
+
|
|
9
11
|
import contree_cli.config as config_mod
|
|
10
12
|
from contree_cli import CLIENT, FORMATTER, PROFILE, SESSION_STORE, ArgumentsProtocol
|
|
11
13
|
from contree_cli.arguments import parser
|
|
12
|
-
from contree_cli.client import
|
|
14
|
+
from contree_cli.client import client_from_profile
|
|
13
15
|
from contree_cli.config import SETTINGS, Config
|
|
14
16
|
from contree_cli.log import setup_logging
|
|
15
17
|
from contree_cli.output import FORMATTERS
|
|
@@ -18,6 +20,26 @@ from contree_cli.update_check import UpdateChecker
|
|
|
18
20
|
|
|
19
21
|
log = logging.getLogger(__name__)
|
|
20
22
|
|
|
23
|
+
# Network errors raised by the auto-detected transport backend. The
|
|
24
|
+
# stdlib ones are always available; urllib3/httpx raise their own
|
|
25
|
+
# hierarchies (neither inherits from OSError) when auto-detected as
|
|
26
|
+
# the backend instead of `requests` (whose exceptions do subclass
|
|
27
|
+
# OSError). Both imports are optional, mirroring how
|
|
28
|
+
# contree_client.sync.detect_backend() itself probes for them.
|
|
29
|
+
_NETWORK_ERRORS: tuple[type[BaseException], ...] = (OSError, http.client.HTTPException)
|
|
30
|
+
try:
|
|
31
|
+
import urllib3.exceptions # type: ignore[import-not-found]
|
|
32
|
+
|
|
33
|
+
_NETWORK_ERRORS = (*_NETWORK_ERRORS, urllib3.exceptions.HTTPError)
|
|
34
|
+
except ModuleNotFoundError:
|
|
35
|
+
pass
|
|
36
|
+
try:
|
|
37
|
+
import httpx # type: ignore[import-not-found]
|
|
38
|
+
|
|
39
|
+
_NETWORK_ERRORS = (*_NETWORK_ERRORS, httpx.HTTPError)
|
|
40
|
+
except ModuleNotFoundError:
|
|
41
|
+
pass
|
|
42
|
+
|
|
21
43
|
|
|
22
44
|
def main() -> None:
|
|
23
45
|
if len(sys.argv) == 1:
|
|
@@ -71,24 +93,30 @@ def main() -> None:
|
|
|
71
93
|
)
|
|
72
94
|
exit(1)
|
|
73
95
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
96
|
+
# One stack owns every resource the command needs; entries are
|
|
97
|
+
# added as they come to life and unwound together on the way out.
|
|
98
|
+
with ExitStack() as stack:
|
|
99
|
+
if needs_client:
|
|
100
|
+
# Session-based transports (requests/httpx/urllib3) hold
|
|
101
|
+
# pooled connections; enter the client so open()/close()
|
|
102
|
+
# run around the whole command.
|
|
103
|
+
try:
|
|
104
|
+
client = client_from_profile(profile)
|
|
105
|
+
except ValueError as exc:
|
|
106
|
+
log.error("%s", exc)
|
|
107
|
+
exit(1)
|
|
108
|
+
CLIENT.set(stack.enter_context(client))
|
|
109
|
+
|
|
110
|
+
formatter = FORMATTERS[args.output_format]()
|
|
111
|
+
stack.callback(formatter.close)
|
|
112
|
+
|
|
113
|
+
session_key = get_session_key(profile.name, override=args.session_key)
|
|
114
|
+
db_path = config_mod.session_db_path(profile.name)
|
|
115
|
+
log.debug("Running in session: %s", session_key)
|
|
87
116
|
|
|
88
|
-
with SessionStore(db_path, session_key) as store:
|
|
89
117
|
PROFILE.set(profile)
|
|
90
118
|
FORMATTER.set(formatter)
|
|
91
|
-
SESSION_STORE.set(
|
|
119
|
+
SESSION_STORE.set(stack.enter_context(SessionStore(db_path, session_key)))
|
|
92
120
|
ctx = contextvars.copy_context()
|
|
93
121
|
|
|
94
122
|
loader: type[ArgumentsProtocol] = args.load_args
|
|
@@ -96,7 +124,7 @@ def main() -> None:
|
|
|
96
124
|
|
|
97
125
|
try:
|
|
98
126
|
exit_code = ctx.run(handler, loader.from_args(args))
|
|
99
|
-
except
|
|
127
|
+
except ContreeError as exc:
|
|
100
128
|
log.error("%s", exc)
|
|
101
129
|
exit(1)
|
|
102
130
|
except ValueError as exc:
|
|
@@ -104,14 +132,12 @@ def main() -> None:
|
|
|
104
132
|
# (invalid UUIDs, etc.); the message is already user-facing.
|
|
105
133
|
log.error("%s", exc)
|
|
106
134
|
exit(1)
|
|
107
|
-
except
|
|
135
|
+
except _NETWORK_ERRORS as exc:
|
|
108
136
|
log.error("Network error: %s", exc)
|
|
109
137
|
exit(1)
|
|
110
138
|
except KeyboardInterrupt:
|
|
111
139
|
log.error("User interrupted")
|
|
112
140
|
exit(1)
|
|
113
|
-
finally:
|
|
114
|
-
formatter.close()
|
|
115
141
|
|
|
116
142
|
exit(exit_code or 0)
|
|
117
143
|
|
|
@@ -18,6 +18,7 @@ Agent protocol — follow this sequence for every task:
|
|
|
18
18
|
4. Inspect first (read-only):
|
|
19
19
|
contree ls /path
|
|
20
20
|
contree cat /path/file
|
|
21
|
+
contree export /path -F subtree.tar.gz
|
|
21
22
|
contree images --prefix=...
|
|
22
23
|
contree session show
|
|
23
24
|
|
|
@@ -104,7 +105,7 @@ Listing:
|
|
|
104
105
|
contree images --prefix=python filter by tag prefix
|
|
105
106
|
contree images -a include untagged
|
|
106
107
|
contree images --since 1d last 24 hours
|
|
107
|
-
contree -
|
|
108
|
+
contree -o json images JSON output for scripting
|
|
108
109
|
|
|
109
110
|
Tagging:
|
|
110
111
|
contree tag my-app:v1.0 tag current session image
|
|
@@ -145,7 +146,7 @@ Building from a Dockerfile:
|
|
|
145
146
|
|
|
146
147
|
.dockerignore is applied to every COPY/ADD walk on top of the default exclude list (.git, __pycache__, node_modules, etc.).
|
|
147
148
|
|
|
148
|
-
build runs in its own session keyed by abspath(CONTEXT) (visible as "session": "build:<hash>" in -
|
|
149
|
+
build runs in its own session keyed by abspath(CONTEXT) (visible as "session": "build:<hash>" in -o json output). `-S <agent_key>` on `build` is harmless but does not bind the build to your agent session. Verify the resulting image from a normal session:
|
|
149
150
|
contree build . --tag myapp:dev
|
|
150
151
|
contree -S agent_verify use tag:myapp:dev
|
|
151
152
|
contree -S agent_verify run -D -- myapp --version
|
|
@@ -188,7 +189,7 @@ Listing uploaded files:
|
|
|
188
189
|
contree file ls list all uploaded files in the project
|
|
189
190
|
contree file ls --since 1d narrow by upload time
|
|
190
191
|
contree file ls -q uuid + sha256 + source only (quiet)
|
|
191
|
-
contree -
|
|
192
|
+
contree -o json file ls JSON output for jq
|
|
192
193
|
|
|
193
194
|
Output joins remote files (uuid, sha256, size, created_at) with the local upload cache. The SOURCE column shows whatever this machine used to produce the file:
|
|
194
195
|
- absolute host path for files uploaded via `run --file` / `COPY`;
|
|
@@ -243,7 +244,7 @@ Detached mode (-d):
|
|
|
243
244
|
contree session wait block until done + advance branch
|
|
244
245
|
contree session wait UUID1 UUID2 poll only (NO branch advance)
|
|
245
246
|
|
|
246
|
-
NOTE: status filtering uses --status, NOT -S. `-S` is the global session flag and only works BEFORE the subcommand. Also, the default `run -d` output is plain/table -- use `contree -
|
|
247
|
+
NOTE: status filtering uses --status, NOT -S. `-S` is the global session flag and only works BEFORE the subcommand. Also, the default `run -d` output is plain/table -- use `contree -o json run -d ...` to capture the UUID via `jq -r .uuid` reliably.
|
|
247
248
|
|
|
248
249
|
Operation references (UUID_OR_REF):
|
|
249
250
|
Every positional that `--help` labels `UUID_OR_REF` -- `op show`, `op cancel`, `op wait`, top-level `show`/`kill`, and `session wait` -- accepts either a real operation UUID OR a session-history reference. References are resolved against the active session (the one selected by `-S <key>`) before the API is called, so the same notation works everywhere.
|
|
@@ -281,24 +282,24 @@ Monitoring background operations:
|
|
|
281
282
|
|
|
282
283
|
Default `op ls`/`ps` lists only `EXECUTING`; `PENDING` and `ASSIGNED` are hidden until `-a` or an explicit `--status`. For a full active snapshot, fetch with `-a` and filter client-side.
|
|
283
284
|
|
|
284
|
-
`op wait` is a pure observer: polls and prints one operation record per completion. Default formatter pins uuid, status, exit_code, timed_out, duration first and error last; every other scalar API field appears between them, so column count is not fixed. For scripts use `-
|
|
285
|
+
`op wait` is a pure observer: polls and prints one operation record per completion. Default formatter pins uuid, status, exit_code, timed_out, duration first and error last; every other scalar API field appears between them, so column count is not fixed. For scripts use `-o json` (one object per line) or `-o tsv` and select fields explicitly. `status` is the server's word verbatim (orchestration outcome — did the API run the job?); the sandbox process's exit code lives in the separate `exit_code` column. The CLI exit code is 1 when any op finishes non-SUCCESS, or the actual `exit_code` when a SUCCESS op had a non-zero sandbox exit (so `op wait && next-step` still composes naturally with `run -- false`). --timeout (default 60s) caps the wait. Use --all to wait for every currently active op in the project.
|
|
285
286
|
|
|
286
287
|
Rule of thumb -- use `op wait` ONLY outside session context: `op wait` is the right tool when the UUIDs came from somewhere else (different session, different agent, `images import`, raw API call) and you only need "is it done yet?". For ops you spawned in *this* session, use `session wait` (no-arg form) instead -- it polls AND advances the active branch to each result image, which `op wait` will not do.
|
|
287
288
|
|
|
288
289
|
Caveat 1 -- `op wait` does NOT advance session state: each `run -d` (non-disposable) creates a `detached-<op-uuid>` branch pointing at the START image. `op wait` does not move those branches to the result image; the result lives only on the server. After fan-out + wait the session looks the same as before the wait, just with `detached-*` branches accumulated.
|
|
289
290
|
|
|
290
291
|
PREFERRED fan-out (--disposable, no image-tracking concerns):
|
|
291
|
-
A=$(contree -S <key> -
|
|
292
|
-
B=$(contree -S <key> -
|
|
293
|
-
C=$(contree -S <key> -
|
|
292
|
+
A=$(contree -S <key> -o json run -d --disposable -- pytest tests/a | jq -r .uuid)
|
|
293
|
+
B=$(contree -S <key> -o json run -d --disposable -- pytest tests/b | jq -r .uuid)
|
|
294
|
+
C=$(contree -S <key> -o json run -d --disposable -- pytest tests/c | jq -r .uuid)
|
|
294
295
|
contree -S <key> op wait "$A" "$B" "$C" block until all complete
|
|
295
296
|
contree -S <key> op show "$A" "$B" "$C" stdout/stderr per op
|
|
296
297
|
|
|
297
298
|
Non-disposable fan-out (must recover images manually):
|
|
298
|
-
A=$(contree -S <key> -
|
|
299
|
-
B=$(contree -S <key> -
|
|
299
|
+
A=$(contree -S <key> -o json run -d -- apt-get install -y curl | jq -r .uuid)
|
|
300
|
+
B=$(contree -S <key> -o json run -d -- apt-get install -y wget | jq -r .uuid)
|
|
300
301
|
contree -S <key> op wait "$A" "$B"
|
|
301
|
-
IMG_A=$(contree -
|
|
302
|
+
IMG_A=$(contree -o json op show "$A" | jq -r .image)
|
|
302
303
|
contree use "$IMG_A" bind chosen result back
|
|
303
304
|
|
|
304
305
|
Caveat 2 -- `op wait --all` is project-wide: if another agent (or another shell of yours) is running concurrently in the same project, your --all will block on its ops too. The result is still a valid wait, just possibly not over the set you expected. For session-spawned fan-out the correct alternative is `contree -S <key> session wait` (no args): it drains only this session's pending detached ops and advances the active branch with each result image. Reach for `op wait --all` only when you really want a project-wide observer (admin/cleanup tooling).
|
|
@@ -362,10 +363,10 @@ Rules for reliable agent workflows:
|
|
|
362
363
|
3. Why split? Chained runs collapse into one checkpoint. If `make test` fails, you can't rollback to just after `apt install`. Split runs give you granular rollback.
|
|
363
364
|
|
|
364
365
|
4. Global flags (-f, -S, -p) MUST come before the subcommand:
|
|
365
|
-
Right: contree -S key -
|
|
366
|
-
Wrong: contree images -S key -
|
|
366
|
+
Right: contree -S key -o json images
|
|
367
|
+
Wrong: contree images -S key -o json
|
|
367
368
|
|
|
368
|
-
5. Use -
|
|
369
|
+
5. Use -o json for structured output in automation: `contree -o json images | jq '.uuid'`.
|
|
369
370
|
|
|
370
371
|
6. Agents must never run `contree auth`. Only users manage auth.
|
|
371
372
|
|
|
@@ -378,23 +379,23 @@ Rules for reliable agent workflows:
|
|
|
378
379
|
Output formats
|
|
379
380
|
==============
|
|
380
381
|
|
|
381
|
-
Global -
|
|
382
|
+
Global -o flag goes before the subcommand. Always available formats:
|
|
382
383
|
|
|
383
|
-
contree -
|
|
384
|
-
contree -
|
|
385
|
-
contree -
|
|
386
|
-
contree -
|
|
387
|
-
contree -
|
|
384
|
+
contree -o json images one JSON object per line (JSONL)
|
|
385
|
+
contree -o json-pretty ps pretty-printed JSON array
|
|
386
|
+
contree -o csv images CSV with header row
|
|
387
|
+
contree -o tsv ps tab-separated values
|
|
388
|
+
contree -o plain images key: value blocks
|
|
388
389
|
|
|
389
|
-
`-
|
|
390
|
+
`-o toml` is available only on Python 3.11+ (it relies on stdlib `tomllib`). On Python 3.10 it is silently absent from --help.
|
|
390
391
|
|
|
391
392
|
Scripting examples:
|
|
392
|
-
contree -
|
|
393
|
-
contree -
|
|
394
|
-
contree -
|
|
393
|
+
contree -o json images --prefix=python | jq -r '.uuid'
|
|
394
|
+
contree -o json ps -a | jq 'select(.status=="SUCCESS")'
|
|
395
|
+
contree -o csv images > images.csv
|
|
395
396
|
contree ps -q | xargs contree show
|
|
396
397
|
|
|
397
|
-
Note: `run` with default formatter prints raw stdout/stderr. Use -
|
|
398
|
+
Note: `run` with default formatter prints raw stdout/stderr. Use -o json to get structured operation metadata instead.
|
|
398
399
|
|
|
399
400
|
Profiles
|
|
400
401
|
========
|
|
@@ -450,6 +451,7 @@ All commands
|
|
|
450
451
|
ls [PATH] List files in image (no VM)
|
|
451
452
|
cat PATH Show file content (no VM)
|
|
452
453
|
cp PATH DEST Download file from image
|
|
454
|
+
export [PATH] Export rootfs or a subtree as tar.gz (-F FILE or pipe; --decompress for plain tar)
|
|
453
455
|
cd [PATH] Change session working directory
|
|
454
456
|
env [KEY=VALUE ...] Session env vars (-U to unset)
|
|
455
457
|
file edit PATH Edit remote file via $EDITOR
|
|
@@ -11,6 +11,7 @@ from contree_cli.cli import (
|
|
|
11
11
|
cd,
|
|
12
12
|
cp,
|
|
13
13
|
env,
|
|
14
|
+
export,
|
|
14
15
|
file,
|
|
15
16
|
images,
|
|
16
17
|
ls,
|
|
@@ -235,6 +236,7 @@ register(
|
|
|
235
236
|
register("ls", "List files in image", ls.setup_parser)
|
|
236
237
|
register("cat", "Show file content from image", cat.setup_parser)
|
|
237
238
|
register("cp", "Copy file from image to local path", cp.setup_parser)
|
|
239
|
+
register("export", "Export image rootfs or a subtree as tar.gz", export.setup_parser)
|
|
238
240
|
register("file", "Manage files in session image", file.setup_parser, aliases=["f"])
|
|
239
241
|
register(
|
|
240
242
|
"session",
|