From 3d7b655a45f9e8a63d2c03bc0d9ebcd9eb3b13a4 Mon Sep 17 00:00:00 2001 From: Typosaurus Date: Sun, 19 Jul 2026 18:23:56 +0000 Subject: [PATCH] ticket #76 attempt 1 --- devplacepy/cli/__init__.py | 3 +- devplacepy/cli/containers.py | 30 +++++++++++++++++-- .../templates/docs/services-containers.html | 2 +- dpc.log | 20 +++++++++++++ 4 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 dpc.log diff --git a/devplacepy/cli/__init__.py b/devplacepy/cli/__init__.py index aa0570e1..5659d21e 100644 --- a/devplacepy/cli/__init__.py +++ b/devplacepy/cli/__init__.py @@ -37,9 +37,10 @@ from devplacepy.cli.backups import ( ) from devplacepy.cli.containers import ( cmd_containers_list, - cmd_containers_reconcile, cmd_containers_prune, cmd_containers_prune_builds, + cmd_containers_prune_stopped, + cmd_containers_reconcile, cmd_containers_gc_workspaces, ) from devplacepy.cli.migrate import cmd_emoji_sync, cmd_migrate_data diff --git a/devplacepy/cli/containers.py b/devplacepy/cli/containers.py index 765d49f6..4d4705c4 100644 --- a/devplacepy/cli/containers.py +++ b/devplacepy/cli/containers.py @@ -1,5 +1,6 @@ # retoor +import asyncio from devplacepy.cli._shared import _audit_cli @@ -18,7 +19,6 @@ def cmd_containers_list(args): def cmd_containers_reconcile(args): - import asyncio from devplacepy.services.containers.service import ContainerService asyncio.run(ContainerService().run_once()) @@ -27,7 +27,6 @@ def cmd_containers_reconcile(args): def cmd_containers_prune(args): - import asyncio from devplacepy.services.containers.runtime import get_backend from devplacepy.services.containers.service import ContainerService @@ -66,6 +65,30 @@ def cmd_containers_prune_builds(args): ) +def cmd_containers_prune_stopped(args): + """Remove all stopped containers that have completed their purpose.""" + from devplacepy.services.containers import api, store + from devplacepy.services.containers.service import ContainerService + + async def run(): + instances = store.all_instances() + marked = 0 + for inst in instances: + if inst["status"] == store.ST_STOPPED and inst["desired_state"] == store.DESIRED_STOPPED: + api.mark_for_removal(inst, actor=("system", "prune-stopped")) + marked += 1 + if marked: + await ContainerService().run_once() + return marked + + marked = asyncio.run(run()) + _audit_cli("cli.containers.prune_stopped", f"CLI removed {marked} stale stopped container(s)", metadata={"count": marked}) + if marked: + print(f"Prune stopped: marked {marked} container(s) for removal and reconciled.") + else: + print("No stopped containers to prune.") + + def cmd_containers_gc_workspaces(args): import shutil from pathlib import Path @@ -102,6 +125,9 @@ def register_containers(subparsers): "prune-builds", help="Remove legacy per-project images and clear the dockerfiles/builds tables", ).set_defaults(func=cmd_containers_prune_builds) + containers_sub.add_parser( + "prune-stopped", help="Remove all stopped containers (completed purpose)" + ).set_defaults(func=cmd_containers_prune_stopped) containers_sub.add_parser( "gc-workspaces", help="Remove workspace dirs with no instances" ).set_defaults(func=cmd_containers_gc_workspaces) diff --git a/devplacepy/templates/docs/services-containers.html b/devplacepy/templates/docs/services-containers.html index 4ef8acfa..0d9066a2 100644 --- a/devplacepy/templates/docs/services-containers.html +++ b/devplacepy/templates/docs/services-containers.html @@ -107,7 +107,7 @@ instead of the gateway. Before the reconciler has recorded a gateway, the proxy The service tile on `/admin/services` shows instance counts; per-instance aggregates (CPU/memory, p95 runtime) are computed on read over the ring buffer. The CLI exposes -`devplace containers list | reconcile | prune | prune-builds | gc-workspaces` (`prune-builds` is a +`devplace containers list | reconcile | prune | prune-builds | prune-stopped | gc-workspaces` (`prune-builds` is a one-time cleanup that removes legacy per-project images and the old dockerfiles/builds tables). Devii's admin `container_*` tools cover the same instance operations in natural language. diff --git a/dpc.log b/dpc.log new file mode 100644 index 00000000..aa464bda --- /dev/null +++ b/dpc.log @@ -0,0 +1,20 @@ +2026-07-19T09:32:07 INFO logging initialised at /workspace/repo/dpc.log +2026-07-19T09:32:07 DEBUG model=molodetz-pro fps=30 +2026-07-19T09:32:07 INFO read task from file: /workspace/prompts/research-1.txt +2026-07-19T09:32:07 INFO settings merged: model= allow=0 deny=0 ask=0 +2026-07-19T15:54:39 INFO logging initialised at /workspace/repo/dpc.log +2026-07-19T15:54:39 DEBUG model=molodetz-pro fps=30 +2026-07-19T15:54:39 INFO read task from file: /workspace/prompts/research-2.txt +2026-07-19T15:54:39 INFO settings merged: model= allow=0 deny=0 ask=0 +2026-07-19T17:25:31 INFO logging initialised at /workspace/repo/dpc.log +2026-07-19T17:25:31 DEBUG model=molodetz-pro fps=30 +2026-07-19T17:25:31 INFO read task from file: /workspace/prompts/research-3.txt +2026-07-19T17:25:31 INFO settings merged: model= allow=0 deny=0 ask=0 +2026-07-19T17:37:32 INFO logging initialised at /workspace/repo/dpc.log +2026-07-19T17:37:32 DEBUG model=molodetz-pro fps=30 +2026-07-19T17:37:32 INFO read task from file: /workspace/prompts/research-4.txt +2026-07-19T17:37:32 INFO settings merged: model= allow=0 deny=0 ask=0 +2026-07-19T18:22:18 INFO logging initialised at /workspace/repo/dpc.log +2026-07-19T18:22:18 DEBUG model=molodetz-pro fps=30 +2026-07-19T18:22:18 INFO read task from file: /workspace/prompts/execution-1.txt +2026-07-19T18:22:18 INFO settings merged: model= allow=0 deny=0 ask=0