feat(smolmachines): smolvm subprocess wrapper (PRD 0023 chunk 2b) #65

Merged
didericis merged 1 commits from prd-0023-chunk-2b-smolvm-wrapper into main 2026-05-27 04:16:09 -04:00
Owner

Summary

Chunk 2b: one thin Python function per smolvm CLI subcommand the launch flow needs. Mocked unit tests cover argv construction; a small integration smoke probes the real binary so we catch CLI drift across smolvm releases.

API

pack_create(image, output)                       # → smolvm pack create --image ... -o ...
machine_create(name, from_path=, smolfile=)      # → smolvm machine create [--from] [--smolfile] NAME
machine_start(name)                              # → smolvm machine start --name NAME
machine_stop(name)                               # → smolvm machine stop --name NAME
machine_delete(name)                             # → smolvm machine delete -f NAME
machine_exec(name, argv, env=, workdir=, ...)    # → smolvm machine exec --name NAME -- ARGV
machine_cp(src, dst)                             # → smolvm machine cp SRC DST
is_available()                                   # → bool

The wrapper hides the CLI's inconsistent name-flag style behind a uniform name= kwarg (positional NAME on create / delete, --name NAME on start / stop / exec / status).

Two return shapes

  • machine_exec returns SmolvmRunResult(returncode, stdout, stderr) — callers (most importantly Bottle.exec) need to see the in-VM command's exit status, not just whether smolvm itself ran.
  • Every other call raises SmolvmError on non-zero. Failing to start a VM is fatal to the launch flow, not something callers branch on.

Tests

  • 15 unit cases (tests/unit/test_smolmachines_smolvm.py) mocking subprocess.run: argv shape per subcommand (with the positional-vs-flag inconsistency locked down), SmolvmError raising, SmolvmRunResult passthrough on exec, empty-path cp no-op.
  • 2 integration cases (tests/integration/test_smolmachines_smolvm_smoke.py) against the real smolvm binary (gated on Darwin + smolvm on PATH + not GITEA_ACTIONS): smolvm --help responds, machine ls --json parses as a list (the contract chunk 4's list_active will consume).

531 unit tests passing. Real-smolvm smoke green locally.

Chunk 2 remaining

  • 2c: sidecar_bundle.py — per-bottle docker bridge create + bundle container with pinned IP.
  • 2d: launch.py end-to-end (bundle + VM bringup + Bottle.exec via machine_exec) + integration smoke + the localhost-reach + egress-port-bypass probes.
## Summary Chunk 2b: one thin Python function per smolvm CLI subcommand the launch flow needs. Mocked unit tests cover argv construction; a small integration smoke probes the real binary so we catch CLI drift across smolvm releases. ## API ```python pack_create(image, output) # → smolvm pack create --image ... -o ... machine_create(name, from_path=, smolfile=) # → smolvm machine create [--from] [--smolfile] NAME machine_start(name) # → smolvm machine start --name NAME machine_stop(name) # → smolvm machine stop --name NAME machine_delete(name) # → smolvm machine delete -f NAME machine_exec(name, argv, env=, workdir=, ...) # → smolvm machine exec --name NAME -- ARGV machine_cp(src, dst) # → smolvm machine cp SRC DST is_available() # → bool ``` The wrapper hides the CLI's inconsistent name-flag style behind a uniform `name=` kwarg (positional `NAME` on `create` / `delete`, `--name NAME` on `start` / `stop` / `exec` / `status`). ## Two return shapes - `machine_exec` returns `SmolvmRunResult(returncode, stdout, stderr)` — callers (most importantly `Bottle.exec`) need to see the in-VM command's exit status, not just whether smolvm itself ran. - Every other call raises `SmolvmError` on non-zero. Failing to start a VM is fatal to the launch flow, not something callers branch on. ## Tests - **15 unit cases** (`tests/unit/test_smolmachines_smolvm.py`) mocking `subprocess.run`: argv shape per subcommand (with the positional-vs-flag inconsistency locked down), `SmolvmError` raising, `SmolvmRunResult` passthrough on exec, empty-path `cp` no-op. - **2 integration cases** (`tests/integration/test_smolmachines_smolvm_smoke.py`) against the real smolvm binary (gated on Darwin + smolvm on PATH + not `GITEA_ACTIONS`): `smolvm --help` responds, `machine ls --json` parses as a list (the contract chunk 4's `list_active` will consume). **531 unit tests passing.** Real-smolvm smoke green locally. ## Chunk 2 remaining - **2c:** `sidecar_bundle.py` — per-bottle docker bridge create + bundle container with pinned IP. - **2d:** `launch.py` end-to-end (bundle + VM bringup + `Bottle.exec` via `machine_exec`) + integration smoke + the localhost-reach + egress-port-bypass probes.
didericis added 1 commit 2026-05-27 04:11:56 -04:00
feat(smolmachines): smolvm subprocess wrapper (PRD 0023 chunk 2b)
test / unit (pull_request) Successful in 21s
test / integration (pull_request) Successful in 41s
9c333bc130
claude_bottle/backend/smolmachines/smolvm.py — one thin Python
function per smolvm CLI subcommand the launch flow needs:

  - pack_create(image, output)            → smolvm pack create
  - machine_create(name, from_path,
                   smolfile)               → smolvm machine create
  - machine_start(name)                   → smolvm machine start
  - machine_stop(name)                    → smolvm machine stop
  - machine_delete(name)                  → smolvm machine delete -f
  - machine_exec(name, argv, env,
                 workdir, timeout)         → smolvm machine exec
  - machine_cp(src, dst)                  → smolvm machine cp
  - is_available()                        → shutil.which check

The wrapper hides the CLI's inconsistent name-flag style
(positional NAME on create/delete, --name on start/stop/exec/
status) behind a uniform `name=` kwarg.

Two return shapes:
  - SmolvmRunResult (returncode + stdout + stderr) from
    machine_exec, because callers care about the in-VM
    command's exit code.
  - Raises SmolvmError on non-zero for all other commands;
    failure to create/start/stop a VM is fatal to the launch
    flow, not branched on.

Tests:
  - 15 unit cases mocking subprocess.run, covering argv shape
    per subcommand (the --name vs positional inconsistency
    locked down), SmolvmError on non-zero for non-exec paths,
    SmolvmRunResult passthrough on exec, empty-path cp no-op.
  - 2 integration cases against the real smolvm binary
    (gated on Darwin + smolvm on PATH + not GITEA_ACTIONS):
    smolvm --help responds, machine ls --json parses as a
    list (the contract chunk 4's list_active will consume).

531 unit tests passing. Real-smolvm smoke green locally.

Bundle bringup + launch wiring + the localhost-reach /
egress-port-bypass probes land in chunks 2c + 2d.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis merged commit 09eb25904f into main 2026-05-27 04:16:09 -04:00
didericis deleted branch prd-0023-chunk-2b-smolvm-wrapper 2026-05-27 04:17:42 -04:00
Sign in to join this conversation.