pocketdock 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.
- pocketdock-1.1.0/.github/workflows/ci.yml +58 -0
- pocketdock-1.1.0/.github/workflows/docs.yml +18 -0
- pocketdock-1.1.0/.github/workflows/publish.yml +18 -0
- pocketdock-1.1.0/.gitignore +51 -0
- pocketdock-1.1.0/.pre-commit-config.yaml +24 -0
- pocketdock-1.1.0/CHANGELOG.md +201 -0
- pocketdock-1.1.0/CONTRIBUTING.md +56 -0
- pocketdock-1.1.0/LICENSE +24 -0
- pocketdock-1.1.0/PKG-INFO +126 -0
- pocketdock-1.1.0/README.md +102 -0
- pocketdock-1.1.0/dev/status.md +76 -0
- pocketdock-1.1.0/docs/changelog.md +3 -0
- pocketdock-1.1.0/docs/cli.md +298 -0
- pocketdock-1.1.0/docs/concepts/architecture.md +104 -0
- pocketdock-1.1.0/docs/concepts/connection-model.md +117 -0
- pocketdock-1.1.0/docs/contributing.md +21 -0
- pocketdock-1.1.0/docs/guide/async.md +135 -0
- pocketdock-1.1.0/docs/guide/commands.md +144 -0
- pocketdock-1.1.0/docs/guide/containers.md +159 -0
- pocketdock-1.1.0/docs/guide/files.md +76 -0
- pocketdock-1.1.0/docs/guide/persistence.md +134 -0
- pocketdock-1.1.0/docs/guide/profiles.md +122 -0
- pocketdock-1.1.0/docs/guide/projects.md +146 -0
- pocketdock-1.1.0/docs/guide/sessions.md +113 -0
- pocketdock-1.1.0/docs/index.md +74 -0
- pocketdock-1.1.0/docs/quickstart.md +105 -0
- pocketdock-1.1.0/docs/reference/api.md +303 -0
- pocketdock-1.1.0/docs/reference/configuration.md +119 -0
- pocketdock-1.1.0/docs/reference/errors.md +188 -0
- pocketdock-1.1.0/docs/reference/types.md +143 -0
- pocketdock-1.1.0/examples/README.md +5 -0
- pocketdock-1.1.0/images/agent/Dockerfile +13 -0
- pocketdock-1.1.0/images/dev/Dockerfile +10 -0
- pocketdock-1.1.0/images/embedded/Dockerfile +30 -0
- pocketdock-1.1.0/images/minimal/Dockerfile +6 -0
- pocketdock-1.1.0/mkdocs.yml +65 -0
- pocketdock-1.1.0/plan/acceptance-gates.md +214 -0
- pocketdock-1.1.0/plan/checklist.md +106 -0
- pocketdock-1.1.0/plan/pocketdock-plan.md +2537 -0
- pocketdock-1.1.0/pyproject.toml +114 -0
- pocketdock-1.1.0/python/pocketdock/__init__.py +183 -0
- pocketdock-1.1.0/python/pocketdock/_async_container.py +678 -0
- pocketdock-1.1.0/python/pocketdock/_buffer.py +76 -0
- pocketdock-1.1.0/python/pocketdock/_callbacks.py +54 -0
- pocketdock-1.1.0/python/pocketdock/_config.py +76 -0
- pocketdock-1.1.0/python/pocketdock/_helpers.py +181 -0
- pocketdock-1.1.0/python/pocketdock/_logger.py +161 -0
- pocketdock-1.1.0/python/pocketdock/_process.py +239 -0
- pocketdock-1.1.0/python/pocketdock/_session.py +237 -0
- pocketdock-1.1.0/python/pocketdock/_socket_client.py +886 -0
- pocketdock-1.1.0/python/pocketdock/_stream.py +147 -0
- pocketdock-1.1.0/python/pocketdock/_sync_container.py +435 -0
- pocketdock-1.1.0/python/pocketdock/async_.py +48 -0
- pocketdock-1.1.0/python/pocketdock/cli/__init__.py +2 -0
- pocketdock-1.1.0/python/pocketdock/cli/_commands.py +883 -0
- pocketdock-1.1.0/python/pocketdock/cli/_output.py +168 -0
- pocketdock-1.1.0/python/pocketdock/cli/main.py +86 -0
- pocketdock-1.1.0/python/pocketdock/errors.py +96 -0
- pocketdock-1.1.0/python/pocketdock/persistence.py +249 -0
- pocketdock-1.1.0/python/pocketdock/profiles.py +111 -0
- pocketdock-1.1.0/python/pocketdock/projects.py +255 -0
- pocketdock-1.1.0/python/pocketdock/py.typed +0 -0
- pocketdock-1.1.0/python/pocketdock/types.py +78 -0
- pocketdock-1.1.0/tests/__init__.py +0 -0
- pocketdock-1.1.0/tests/conftest.py +43 -0
- pocketdock-1.1.0/tests/test_buffer.py +189 -0
- pocketdock-1.1.0/tests/test_callbacks.py +97 -0
- pocketdock-1.1.0/tests/test_cli_integration.py +99 -0
- pocketdock-1.1.0/tests/test_cli_output.py +270 -0
- pocketdock-1.1.0/tests/test_cli_unit.py +1635 -0
- pocketdock-1.1.0/tests/test_concurrent.py +106 -0
- pocketdock-1.1.0/tests/test_config.py +151 -0
- pocketdock-1.1.0/tests/test_container_async.py +149 -0
- pocketdock-1.1.0/tests/test_container_sync.py +100 -0
- pocketdock-1.1.0/tests/test_container_unit.py +2068 -0
- pocketdock-1.1.0/tests/test_doctor_unit.py +228 -0
- pocketdock-1.1.0/tests/test_errors.py +164 -0
- pocketdock-1.1.0/tests/test_file_ops_async.py +168 -0
- pocketdock-1.1.0/tests/test_file_ops_sync.py +72 -0
- pocketdock-1.1.0/tests/test_file_ops_unit.py +289 -0
- pocketdock-1.1.0/tests/test_helpers.py +380 -0
- pocketdock-1.1.0/tests/test_import.py +162 -0
- pocketdock-1.1.0/tests/test_info_integration.py +173 -0
- pocketdock-1.1.0/tests/test_logger.py +237 -0
- pocketdock-1.1.0/tests/test_persistence_integration.py +247 -0
- pocketdock-1.1.0/tests/test_persistence_unit.py +703 -0
- pocketdock-1.1.0/tests/test_process_unit.py +432 -0
- pocketdock-1.1.0/tests/test_profiles.py +124 -0
- pocketdock-1.1.0/tests/test_projects.py +324 -0
- pocketdock-1.1.0/tests/test_session_integration.py +162 -0
- pocketdock-1.1.0/tests/test_session_unit.py +581 -0
- pocketdock-1.1.0/tests/test_socket_client.py +302 -0
- pocketdock-1.1.0/tests/test_socket_client_unit.py +1186 -0
- pocketdock-1.1.0/tests/test_stream.py +268 -0
- pocketdock-1.1.0/tests/test_streaming_integration.py +203 -0
- pocketdock-1.1.0/tests/test_types.py +166 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v4
|
|
15
|
+
- run: uv sync --dev --all-extras
|
|
16
|
+
- run: uv run ruff check .
|
|
17
|
+
- run: uv run ruff format --check .
|
|
18
|
+
- run: uv run mypy --strict python/pocketdock/
|
|
19
|
+
- run: uv run bandit -r python/pocketdock/ -c pyproject.toml
|
|
20
|
+
- name: Check license headers
|
|
21
|
+
run: |
|
|
22
|
+
find python/pocketdock -name '*.py' | while read f; do
|
|
23
|
+
head -2 "$f" | grep -q 'SPDX-License-Identifier: BSD-2-Clause' || \
|
|
24
|
+
(echo "Missing license header: $f" && exit 1)
|
|
25
|
+
done
|
|
26
|
+
|
|
27
|
+
test:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
strategy:
|
|
30
|
+
matrix:
|
|
31
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: astral-sh/setup-uv@v4
|
|
35
|
+
- run: uv python install ${{ matrix.python-version }}
|
|
36
|
+
- run: uv sync --dev --all-extras --python ${{ matrix.python-version }}
|
|
37
|
+
- run: systemctl --user start podman.socket
|
|
38
|
+
- run: podman build -t pocketdock/minimal images/minimal/
|
|
39
|
+
- name: Run parallel-safe tests
|
|
40
|
+
run: uv run pytest -n auto --ignore=tests/test_persistence_integration.py
|
|
41
|
+
- name: Run serial tests (race-prone)
|
|
42
|
+
run: uv run pytest tests/test_persistence_integration.py --cov-append --cov-fail-under=100
|
|
43
|
+
|
|
44
|
+
docs:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
- uses: astral-sh/setup-uv@v4
|
|
49
|
+
- run: uv sync --dev
|
|
50
|
+
- run: uv run mkdocs build --strict
|
|
51
|
+
|
|
52
|
+
audit:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v4
|
|
56
|
+
- uses: astral-sh/setup-uv@v4
|
|
57
|
+
- run: uv sync --dev
|
|
58
|
+
- run: uv run pip-audit
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: Deploy docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
deploy:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v4
|
|
17
|
+
- run: uv sync --dev
|
|
18
|
+
- run: uv run mkdocs gh-deploy --force
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment: pypi
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v4
|
|
17
|
+
- run: uv build
|
|
18
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
*.egg
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.whl
|
|
10
|
+
|
|
11
|
+
# Virtual environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
|
|
15
|
+
# uv
|
|
16
|
+
uv.lock
|
|
17
|
+
|
|
18
|
+
# Testing / coverage
|
|
19
|
+
.coverage
|
|
20
|
+
.coverage.*
|
|
21
|
+
htmlcov/
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
|
|
24
|
+
# mypy
|
|
25
|
+
.mypy_cache/
|
|
26
|
+
|
|
27
|
+
# ruff
|
|
28
|
+
.ruff_cache/
|
|
29
|
+
|
|
30
|
+
# IDE
|
|
31
|
+
.vscode/
|
|
32
|
+
.idea/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
*~
|
|
36
|
+
|
|
37
|
+
# OS
|
|
38
|
+
.DS_Store
|
|
39
|
+
Thumbs.db
|
|
40
|
+
|
|
41
|
+
# mkdocs
|
|
42
|
+
site/
|
|
43
|
+
|
|
44
|
+
# pocketdock runtime data
|
|
45
|
+
.pocketdock/
|
|
46
|
+
|
|
47
|
+
# Environment / secrets
|
|
48
|
+
.env
|
|
49
|
+
.env.*
|
|
50
|
+
|
|
51
|
+
.claude
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.9.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
|
|
9
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
10
|
+
rev: v1.14.0
|
|
11
|
+
hooks:
|
|
12
|
+
- id: mypy
|
|
13
|
+
additional_dependencies: [click, types-click]
|
|
14
|
+
|
|
15
|
+
- repo: https://github.com/PyCQA/bandit
|
|
16
|
+
rev: "1.8.0"
|
|
17
|
+
hooks:
|
|
18
|
+
- id: bandit
|
|
19
|
+
args: [-c, pyproject.toml]
|
|
20
|
+
|
|
21
|
+
- repo: https://github.com/mgedmin/check-manifest
|
|
22
|
+
rev: "0.50"
|
|
23
|
+
hooks:
|
|
24
|
+
- id: check-manifest
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.1.0] - 2026-02-11
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Renamed project from `pocket-dock` to `pocketdock` — package, imports, CLI command, labels, image tags, project directories, and all references
|
|
15
|
+
- PyPI package name: `pocketdock` (was `pocket-dock`)
|
|
16
|
+
- CLI command: `pocketdock` (was `pocket-dock`)
|
|
17
|
+
- Python import: `import pocketdock` (was `import pocket_dock`)
|
|
18
|
+
- Image tags: `pocketdock/minimal` etc. (was `pocket-dock/minimal`)
|
|
19
|
+
- Project directory: `.pocketdock/` (was `.pocket-dock/`)
|
|
20
|
+
- Config file: `pocketdock.yaml` (was `pocket-dock.yaml`)
|
|
21
|
+
- Container labels: `pocketdock.*` (was `pocket-dock.*`)
|
|
22
|
+
|
|
23
|
+
## [1.0.1] - 2026-02-11
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Rewrote README as a focused landing page with features list, quick example, and docs links
|
|
28
|
+
- Comprehensive docs site with 17 pages: user guide, CLI reference, API reference, concepts
|
|
29
|
+
- GitHub Pages deployment via `mkdocs gh-deploy` workflow
|
|
30
|
+
- Added `navigation.tabs`, `navigation.top`, and `content.tabs.link` theme features to mkdocs
|
|
31
|
+
|
|
32
|
+
## [1.0.0] - 2026-02-10
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- Four image profiles: `minimal`, `dev`, `agent`, `embedded` — pre-baked Dockerfiles for common use cases
|
|
37
|
+
- `profiles.py` module — `ProfileInfo` dataclass, `resolve_profile()`, `list_profiles()`, `get_dockerfile_path()`
|
|
38
|
+
- `profile` parameter on `create_new_container()` — resolves profile name to image tag automatically
|
|
39
|
+
- `devices` parameter on `create_new_container()` — USB/device passthrough to containers
|
|
40
|
+
- `build_image()`, `save_image()`, `load_image()` socket client methods for image management via engine API
|
|
41
|
+
- CLI `build` command — build profile images from Dockerfiles via socket API
|
|
42
|
+
- CLI `export` command — save images to tar/tar.gz files for air-gap transfer
|
|
43
|
+
- CLI `import` command — load images from tar/tar.gz files
|
|
44
|
+
- CLI `profiles` command — list available profiles (table or `--json`)
|
|
45
|
+
- `--profile` and `--device` options on CLI `create` command
|
|
46
|
+
- Exported `ProfileInfo`, `resolve_profile`, `list_profiles` from `pocketdock` and `pocketdock.async_`
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- Version bump to 1.0.0 — stable API
|
|
51
|
+
|
|
52
|
+
## [0.9.0] - 2026-02-10
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
- Full CLI with 17 commands: `init`, `list`, `info`, `doctor`, `status`, `logs`, `create`, `run`, `push`, `pull`, `reboot`, `stop`, `resume`, `shutdown`, `snapshot`, `prune`, `shell`
|
|
57
|
+
- `stop_container()` — stop a running container by name without removing it (sync and async)
|
|
58
|
+
- `--json` flag on read commands (`list`, `info`, `doctor`, `status`, `logs`) for machine-readable output
|
|
59
|
+
- `--stream` and `--detach` flags on `run` for streaming and background execution
|
|
60
|
+
- `--yes/-y` flag on destructive commands (`shutdown`, `prune`) to skip confirmation prompts
|
|
61
|
+
- `--socket` global option and `POCKET_DOCK_SOCKET` env var for engine socket override
|
|
62
|
+
- Rich-formatted output: tables for container lists, panels for info/doctor, colored success/error messages
|
|
63
|
+
- `shell` command — interactive shell via engine CLI passthrough (`podman`/`docker exec -it`)
|
|
64
|
+
- Entry point: `pocketdock` (via `pyproject.toml` console script)
|
|
65
|
+
- `click` and `rich` CLI dependencies (optional `[cli]` extra)
|
|
66
|
+
|
|
67
|
+
## [0.8.0] - 2026-02-10
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
|
|
71
|
+
- `.pocketdock/` project management — `init_project()`, `find_project_root()`, `get_project_name()`
|
|
72
|
+
- Instance directory lifecycle — `ensure_instance_dir()`, `write_instance_metadata()`, `read_instance_metadata()`, `remove_instance_dir()`, `list_instance_dirs()`
|
|
73
|
+
- `PocketDockConfig` dataclass and `load_config()` with install-level → project-level precedence
|
|
74
|
+
- `pocketdock.yaml` project configuration file with logging and persistence defaults
|
|
75
|
+
- `instance.toml` metadata files per persistent container (container info, resources, provenance)
|
|
76
|
+
- `project` parameter on `create_new_container()` — associates containers with a project
|
|
77
|
+
- `pocketdock.project` and `pocketdock.data-path` labels on persistent containers
|
|
78
|
+
- `list_containers(project=...)` and `prune(project=...)` — filter by project name
|
|
79
|
+
- `destroy_container()` now cleans up the instance directory when `pocketdock.data-path` label is present
|
|
80
|
+
- `InstanceLogger` — auto-logging of `run()` results, session I/O, and detached process output to disk
|
|
81
|
+
- `history.jsonl` command history per instance
|
|
82
|
+
- `doctor()` — cross-references local instance dirs with engine containers to find orphaned containers and stale dirs
|
|
83
|
+
- `DoctorReport` dataclass with `orphaned_containers`, `stale_instance_dirs`, `healthy`
|
|
84
|
+
- `ProjectNotInitialized` exception
|
|
85
|
+
- `PyYAML` and `tomli` (Python < 3.11) added as core dependencies for config/metadata parsing
|
|
86
|
+
- All new functions available as sync (from `pocketdock`) and async (from `pocketdock.async_`)
|
|
87
|
+
|
|
88
|
+
## [0.7.0] - 2026-02-10
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
|
|
92
|
+
- `persist=True` parameter for `create_new_container()` — container survives `shutdown()` (stop without remove)
|
|
93
|
+
- `volumes` parameter for `create_new_container()` — mount host directories into the container
|
|
94
|
+
- `container.snapshot(image_name)` — commit container filesystem as a new reusable image
|
|
95
|
+
- `container.persist` property — check if container is persistent
|
|
96
|
+
- `resume_container(name)` — resume a stopped persistent container by name
|
|
97
|
+
- `list_containers()` — list all pocketdock managed containers (running and stopped)
|
|
98
|
+
- `destroy_container(name)` — permanently remove a container regardless of persist setting
|
|
99
|
+
- `prune()` — remove all stopped pocketdock managed containers
|
|
100
|
+
- `ContainerListItem` dataclass with `id`, `name`, `status`, `image`, `created_at`, `persist`
|
|
101
|
+
- `pocketdock.persist` and `pocketdock.created-at` labels on all containers
|
|
102
|
+
- `list_containers()` and `commit_container()` socket client methods
|
|
103
|
+
- All new functions available as sync (from `pocketdock`) and async (from `pocketdock.async_`)
|
|
104
|
+
|
|
105
|
+
## [0.6.0] - 2026-02-09
|
|
106
|
+
|
|
107
|
+
### Added
|
|
108
|
+
|
|
109
|
+
- `AsyncSession` / `Session` — persistent shell sessions with state persistence (cwd, env vars, shell history)
|
|
110
|
+
- `session()` method on `AsyncContainer` and `Container` to open a persistent shell
|
|
111
|
+
- `send()` — fire-and-forget command to the shell
|
|
112
|
+
- `send_and_wait()` — send a command and wait for completion with exit code, stdout, stderr, and duration
|
|
113
|
+
- `read()` — drain accumulated output from the session (thread-safe)
|
|
114
|
+
- `on_output()` — register callbacks for session output
|
|
115
|
+
- `close()` — close the session without stopping the container
|
|
116
|
+
- Sentinel protocol (`__PD_{uuid}_${?}__`) for reliable command boundary and exit code detection
|
|
117
|
+
- `SessionClosed` error for operations on closed sessions
|
|
118
|
+
- `attach_stdin` support in socket client `_exec_create()` for bidirectional exec connections
|
|
119
|
+
- Automatic session cleanup during `shutdown()`
|
|
120
|
+
- Exported `AsyncSession` from `pocketdock.async_` and `Session` (alias for `SyncSession`) from `pocketdock`
|
|
121
|
+
|
|
122
|
+
## [0.5.0] - 2026-02-08
|
|
123
|
+
|
|
124
|
+
### Added
|
|
125
|
+
|
|
126
|
+
- `run(stream=True)` — streaming mode returns `AsyncExecStream` / `ExecStream` async iterator yielding `StreamChunk` objects in real-time
|
|
127
|
+
- `run(detach=True)` — detached mode returns `AsyncProcess` / `Process` handle for background execution
|
|
128
|
+
- `StreamChunk` dataclass with `stream` ("stdout"/"stderr") and `data` fields
|
|
129
|
+
- `AsyncExecStream` / `ExecStream` — async iterator with `.result` property after iteration
|
|
130
|
+
- `AsyncProcess` / `Process` — detached process handle with `id`, `is_running()`, `kill()`, `wait()`, `read()`, `peek()`, `buffer_size`, `buffer_overflow`
|
|
131
|
+
- `RingBuffer` — thread-safe bounded ring buffer (1 MB default) for detached process output
|
|
132
|
+
- `BufferSnapshot` dataclass with `stdout` and `stderr` strings
|
|
133
|
+
- `CallbackRegistry` — register callbacks for stdout, stderr, and exit events
|
|
134
|
+
- `on_stdout()`, `on_stderr()`, `on_exit()` callback methods on `AsyncContainer` and `Container`
|
|
135
|
+
- `demux_stream_iter()` — incremental async generator for stream frame parsing
|
|
136
|
+
- `_exec_start_stream()` — streaming exec with Docker chunked TE and Podman raw stream support
|
|
137
|
+
- `_demux_chunked_stream()` — handles misalignment between HTTP chunk and demux frame boundaries
|
|
138
|
+
- Automatic cleanup of active streams and processes in `shutdown()`
|
|
139
|
+
- `@overload` type signatures on `run()` for all three output modes
|
|
140
|
+
- Exported `ExecStream`, `Process`, `BufferSnapshot`, `StreamChunk` from `pocketdock`
|
|
141
|
+
- Exported `AsyncExecStream`, `AsyncProcess` from `pocketdock.async_`
|
|
142
|
+
|
|
143
|
+
## [0.4.0] - 2026-02-08
|
|
144
|
+
|
|
145
|
+
### Added
|
|
146
|
+
|
|
147
|
+
- `info()` — live container snapshot: status, uptime, memory, CPU, PIDs, processes, network
|
|
148
|
+
- `reboot()` — restart in place; `reboot(fresh=True)` recreates with same config
|
|
149
|
+
- `mem_limit` parameter for `create_new_container()` (e.g. `"256m"`, `"1g"`)
|
|
150
|
+
- `cpu_percent` parameter for `create_new_container()` (e.g. `50` for 50% CPU cap)
|
|
151
|
+
- `ContainerInfo` dataclass with 15 fields for container introspection
|
|
152
|
+
- `get_container_stats()`, `get_container_top()`, `restart_container()` socket client endpoints
|
|
153
|
+
- `_helpers.py` module with `format_bytes`, `parse_mem_limit`, `parse_iso_timestamp`, `compute_cpu_percent`
|
|
154
|
+
- Resource limits via `HostConfig` (`Memory`, `NanoCpus`) in container creation
|
|
155
|
+
- All new methods available on both `AsyncContainer` (async) and `Container` (sync)
|
|
156
|
+
|
|
157
|
+
## [0.3.0] - 2026-02-07
|
|
158
|
+
|
|
159
|
+
### Added
|
|
160
|
+
|
|
161
|
+
- `write_file()` — write text or binary content into the container via tar archive API
|
|
162
|
+
- `read_file()` — read file contents from the container via tar archive API
|
|
163
|
+
- `list_files()` — list directory contents inside the container
|
|
164
|
+
- `push()` — copy a file or directory from the host into the container
|
|
165
|
+
- `pull()` — copy a file or directory from the container to the host
|
|
166
|
+
- `push_archive()` and `pull_archive()` in socket client for raw tar transfer
|
|
167
|
+
- All methods available on both `AsyncContainer` (async) and `Container` (sync)
|
|
168
|
+
|
|
169
|
+
## [0.2.0] - 2026-02-07
|
|
170
|
+
|
|
171
|
+
### Added
|
|
172
|
+
|
|
173
|
+
- `AsyncContainer` class with async `run()`, `shutdown()`, and context manager
|
|
174
|
+
- `Container` class (sync facade) with background event loop thread
|
|
175
|
+
- `create_new_container()` factory function (sync in `pocketdock`, async in `pocketdock.async_`)
|
|
176
|
+
- Blocking `run()` with configurable timeout and output capping
|
|
177
|
+
- `lang` parameter for `run()` (e.g. `lang="python"` wraps command with `python3 -c`)
|
|
178
|
+
- Timeout support in `exec_command` via `asyncio.wait_for`
|
|
179
|
+
- Container labels (`pocketdock.managed`, `pocketdock.instance`) for discovery
|
|
180
|
+
- Auto-generated container names (`pd-{8 hex}`)
|
|
181
|
+
- Multiple independent containers from a single process
|
|
182
|
+
- Thread-safe sync facade via `asyncio.run_coroutine_threadsafe`
|
|
183
|
+
|
|
184
|
+
## [0.1.0] - 2026-02-07
|
|
185
|
+
|
|
186
|
+
### Added
|
|
187
|
+
|
|
188
|
+
- Project scaffold: README, CONTRIBUTING, LICENSE (BSD-2-Clause), docs site, CI pipeline
|
|
189
|
+
- Async socket client over Unix socket (`_socket_client.py`)
|
|
190
|
+
- HTTP/1.1 request/response over Unix domain sockets
|
|
191
|
+
- Stream demultiplexing for Docker/Podman exec stream protocol (`_stream.py`)
|
|
192
|
+
- Podman and Docker socket auto-detection
|
|
193
|
+
- Container lifecycle operations: create, start, stop, remove, inspect
|
|
194
|
+
- Exec command with stdout/stderr demux and exit code retrieval
|
|
195
|
+
- Error hierarchy: `PocketDockError`, `SocketError`, `ContainerError`, `ImageNotFound`
|
|
196
|
+
- `ExecResult` dataclass with `ok` property, timing, and truncation tracking
|
|
197
|
+
- Minimal container image Dockerfile (Alpine 3.21 + Python + bash, ~25MB)
|
|
198
|
+
- GitHub Actions CI: lint + test matrix (Python 3.10-3.13) + docs + audit
|
|
199
|
+
- Pre-commit hooks: ruff, mypy, bandit, check-manifest
|
|
200
|
+
- mkdocs-material documentation site
|
|
201
|
+
- PEP 561 `py.typed` marker for type checking support
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Contributing to pocketdock
|
|
2
|
+
|
|
3
|
+
## Development Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Clone the repo
|
|
7
|
+
git clone https://github.com/deftio/pocketdock.git
|
|
8
|
+
cd pocketdock
|
|
9
|
+
|
|
10
|
+
# Install uv (if not already installed)
|
|
11
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
12
|
+
|
|
13
|
+
# Install all dependencies
|
|
14
|
+
uv sync --dev
|
|
15
|
+
|
|
16
|
+
# Install pre-commit hooks
|
|
17
|
+
uv run pre-commit install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quality Bar
|
|
21
|
+
|
|
22
|
+
Every commit must pass:
|
|
23
|
+
|
|
24
|
+
- **100% line coverage** — no `# pragma: no cover`, no exclusions
|
|
25
|
+
- **Zero ruff warnings** — `select = ["ALL"]`, `ignore = ["D1"]`
|
|
26
|
+
- **mypy --strict clean** — every function has type annotations
|
|
27
|
+
- **bandit clean** — security linting for socket/file/tar operations
|
|
28
|
+
- **BSD-2-Clause SPDX header** in every `.py` source file under `python/pocketdock/`
|
|
29
|
+
|
|
30
|
+
## Running Checks
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv run ruff check . # Lint
|
|
34
|
+
uv run ruff format --check . # Format check
|
|
35
|
+
uv run mypy --strict python/pocketdock/ # Type check
|
|
36
|
+
uv run bandit -r python/pocketdock/ -c pyproject.toml # Security lint
|
|
37
|
+
uv run pytest # Tests (100% coverage)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Test Strategy
|
|
41
|
+
|
|
42
|
+
All tests are integration tests against a real Podman socket. No mocks.
|
|
43
|
+
|
|
44
|
+
- Tests skip gracefully if no Podman socket is found locally
|
|
45
|
+
- CI always has Podman available — all tests must pass there
|
|
46
|
+
- Use the `minimal` profile (~25MB, <500ms startup) for tests
|
|
47
|
+
|
|
48
|
+
## Git Workflow
|
|
49
|
+
|
|
50
|
+
- **Branch naming**: `{type}/{description}` — e.g., `feat/m1-blocking-run`, `fix/stream-demux`
|
|
51
|
+
- **Commit messages**: Conventional prefix — `feat:`, `fix:`, `test:`, `docs:`, `ci:`, `refactor:`
|
|
52
|
+
- **Development cycle**: TDD — write tests first, red, implement, green, full lint suite, doc sync, PR
|
|
53
|
+
|
|
54
|
+
## License
|
|
55
|
+
|
|
56
|
+
By contributing, you agree that your contributions will be licensed under the BSD-2-Clause license.
|
pocketdock-1.1.0/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) deftio llc
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
19
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
21
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
22
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
23
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pocketdock
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Portable, offline-first container sandboxes for LLM agents and dev workflows
|
|
5
|
+
Author: deftio llc
|
|
6
|
+
License-Expression: BSD-2-Clause
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: pyyaml>=6.0
|
|
19
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
20
|
+
Provides-Extra: cli
|
|
21
|
+
Requires-Dist: click; extra == 'cli'
|
|
22
|
+
Requires-Dist: rich; extra == 'cli'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# pocketdock
|
|
26
|
+
|
|
27
|
+
[](https://github.com/deftio/pocketdock/actions/workflows/ci.yml)
|
|
28
|
+
[](https://github.com/deftio/pocketdock/actions/workflows/ci.yml)
|
|
29
|
+
[](https://deftio.github.io/pocketdock/)
|
|
30
|
+
[](https://opensource.org/licenses/BSD-2-Clause)
|
|
31
|
+
|
|
32
|
+
**Portable, offline-first container sandboxes for LLM agents and dev workflows.**
|
|
33
|
+
|
|
34
|
+
One Container class. Podman-first, Docker-compatible. Python SDK + CLI. Zero cloud. Zero API keys.
|
|
35
|
+
|
|
36
|
+
## Why pocketdock?
|
|
37
|
+
|
|
38
|
+
Managed sandbox platforms require API keys, cloud accounts, and an internet connection. Rolling your own container glue means rewriting hundreds of lines of boilerplate every time. pocketdock sits in between: a clean Python SDK that talks directly to your container engine over its Unix socket, works entirely offline, and has zero external dependencies for the core SDK.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Three execution modes** — blocking, streaming, and detached (background) with ring buffer
|
|
43
|
+
- **File operations** — read, write, list, push, and pull files between host and container
|
|
44
|
+
- **Persistent sessions** — long-lived shell sessions with state (cwd, env vars, history)
|
|
45
|
+
- **Resource limits** — memory caps, CPU throttling, per-container isolation
|
|
46
|
+
- **Container persistence** — stop/resume, snapshot to image, volume mounts
|
|
47
|
+
- **Project management** — `.pocketdock/` project directories with config, logging, and health checks
|
|
48
|
+
- **Image profiles** — four pre-baked Dockerfiles: minimal, dev, agent, embedded
|
|
49
|
+
- **Full CLI** — 21 commands for container lifecycle, file ops, and project management
|
|
50
|
+
- **Async-first** — sync facade over async core; use either API style
|
|
51
|
+
- **Callbacks** — register handlers for stdout, stderr, and exit events
|
|
52
|
+
|
|
53
|
+
## Quick Example
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from pocketdock import create_new_container
|
|
57
|
+
|
|
58
|
+
with create_new_container() as c:
|
|
59
|
+
result = c.run("echo hello")
|
|
60
|
+
print(result.stdout) # "hello\n"
|
|
61
|
+
print(result.ok) # True
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install pocketdock # SDK only (zero dependencies)
|
|
68
|
+
pip install pocketdock[cli] # SDK + CLI (click, rich)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Requires [Podman](https://podman.io/getting-started/installation) (recommended) or [Docker](https://docs.docker.com/get-docker/).
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Build the minimal image (~25MB, <500ms startup)
|
|
75
|
+
pocketdock build minimal
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Documentation
|
|
79
|
+
|
|
80
|
+
Full documentation is available at **[deftio.github.io/pocketdock](https://deftio.github.io/pocketdock/)**.
|
|
81
|
+
|
|
82
|
+
- [Quickstart](https://deftio.github.io/pocketdock/quickstart/) — install, build, run your first container
|
|
83
|
+
- [User Guide](https://deftio.github.io/pocketdock/guide/containers/) — containers, commands, files, sessions, persistence, profiles
|
|
84
|
+
- [CLI Reference](https://deftio.github.io/pocketdock/cli/) — all 21 commands with examples
|
|
85
|
+
- [API Reference](https://deftio.github.io/pocketdock/reference/api/) — full SDK reference
|
|
86
|
+
|
|
87
|
+
## Architecture
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
User Code / LLM Agent / CLI
|
|
91
|
+
|
|
|
92
|
+
v
|
|
93
|
+
pocketdock SDK
|
|
94
|
+
+--------------------------------------+
|
|
95
|
+
| Container (sync) -> AsyncContainer | facade pattern
|
|
96
|
+
| +- _socket_client (raw HTTP/Unix) |
|
|
97
|
+
+- ProjectManager (.pocketdock/) |
|
|
98
|
+
+- Persistence (resume, snapshot) |
|
|
99
|
+
+- Sessions (persistent shells) |
|
|
100
|
+
+--------------------------------------+
|
|
101
|
+
| raw HTTP over Unix socket
|
|
102
|
+
| (one connection per operation)
|
|
103
|
+
v
|
|
104
|
+
Podman (rootless) / Docker Engine
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Design principles:**
|
|
108
|
+
|
|
109
|
+
- **Connection-per-operation** — each API call opens its own Unix socket. No pooling.
|
|
110
|
+
- **Async core, sync facade** — `AsyncContainer` does all real work. `Container` is a sync wrapper.
|
|
111
|
+
- **No cached state** — always polls live from the engine.
|
|
112
|
+
- **Minimal dependencies** — stdlib-only for the core SDK.
|
|
113
|
+
|
|
114
|
+
## Development
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uv sync --dev # Install dependencies
|
|
118
|
+
uv run pytest # Run tests (100% coverage enforced)
|
|
119
|
+
uv run ruff check . # Lint (zero warnings)
|
|
120
|
+
uv run mypy --strict python/ # Type checking (strict mode)
|
|
121
|
+
uv run mkdocs serve # Local docs site
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
BSD-2-Clause. Copyright (c) deftio llc.
|