# retoor <retoor@molodetz.nl>
from devplacepy.database import get_table
from devplacepy.cli._shared import _audit_cli
def _remove_zip_artifacts(job):
import shutil
from pathlib import Path
from devplacepy.config import ZIP_STAGING_DIR
local_path = (job.get("result") or {}).get("local_path")
if local_path:
Path(local_path).unlink(missing_ok=True)
shutil.rmtree(ZIP_STAGING_DIR / job["uid"], ignore_errors=True)
def cmd_zips_prune(args):
from datetime import datetime, timezone
from devplacepy.services.jobs import queue
now = datetime.now(timezone.utc)
removed = 0
for job in queue.list_jobs(kind="zip", status=queue.DONE):
expires_at = job.get("expires_at")
if not expires_at:
continue
try:
expiry = datetime.fromisoformat(expires_at)
except (ValueError, TypeError):
continue
if expiry < now:
_remove_zip_artifacts(job)
get_table("jobs").delete(uid=job["uid"])
removed += 1
_audit_cli("cli.zips.prune", f"CLI pruned {removed} expired zip jobs", metadata={"count": removed})
print(f"Pruned {removed} expired zip job(s)")
def cmd_zips_clear(args):
from devplacepy.services.jobs import queue
jobs = queue.list_jobs(kind="zip")
for job in jobs:
_remove_zip_artifacts(job)
get_table("jobs").delete(uid=job["uid"])
_audit_cli("cli.zips.clear", f"CLI cleared all zip jobs ({len(jobs)})", metadata={"count": len(jobs)})
print(f"Cleared {len(jobs)} zip job(s) and their archives")
def cmd_forks_prune(args):
from datetime import datetime, timezone
from devplacepy.services.jobs import queue
now = datetime.now(timezone.utc)
removed = 0
for job in queue.list_jobs(kind="fork", status=queue.DONE):
expires_at = job.get("expires_at")
if not expires_at:
continue
try:
expiry = datetime.fromisoformat(expires_at)
except (ValueError, TypeError):
continue
if expiry < now:
get_table("jobs").delete(uid=job["uid"])
removed += 1
_audit_cli("cli.forks.prune", f"CLI pruned {removed} expired fork jobs", metadata={"count": removed})
print(f"Pruned {removed} expired fork job(s)")
def cmd_forks_clear(args):
from devplacepy.services.jobs import queue
jobs = queue.list_jobs(kind="fork")
for job in jobs:
get_table("jobs").delete(uid=job["uid"])
_audit_cli("cli.forks.clear", f"CLI cleared all fork jobs ({len(jobs)})", metadata={"count": len(jobs)})
print(f"Cleared {len(jobs)} fork job(s)")
def _remove_seo_artifacts(job):
import shutil
from devplacepy.config import SEO_REPORTS_DIR
shutil.rmtree(SEO_REPORTS_DIR / job["uid"], ignore_errors=True)
def cmd_seo_prune(args):
from datetime import datetime, timezone
from devplacepy.services.jobs import queue
now = datetime.now(timezone.utc)
removed = 0
for job in queue.list_jobs(kind="seo", status=queue.DONE):
expires_at = job.get("expires_at")
if not expires_at:
continue
try:
expiry = datetime.fromisoformat(expires_at)
except (ValueError, TypeError):
continue
if expiry < now:
_remove_seo_artifacts(job)
get_table("jobs").delete(uid=job["uid"])
removed += 1
_audit_cli("cli.seo.prune", f"CLI pruned {removed} expired SEO jobs", metadata={"count": removed})
print(f"Pruned {removed} expired SEO audit(s)")
def cmd_seo_clear(args):
from devplacepy.services.jobs import queue
jobs = queue.list_jobs(kind="seo")
for job in jobs:
_remove_seo_artifacts(job)
get_table("jobs").delete(uid=job["uid"])
_audit_cli("cli.seo.clear", f"CLI cleared all SEO jobs ({len(jobs)})", metadata={"count": len(jobs)})
print(f"Cleared {len(jobs)} SEO audit(s) and their reports")
def cmd_seo_meta_prune(args):
from datetime import datetime, timezone
from devplacepy.services.jobs import queue
now = datetime.now(timezone.utc)
removed = 0
for job in queue.list_jobs(kind="seo_meta", status=queue.DONE):
expires_at = job.get("expires_at")
if not expires_at:
continue
try:
expiry = datetime.fromisoformat(expires_at)
except (ValueError, TypeError):
continue
if expiry < now:
get_table("jobs").delete(uid=job["uid"])
removed += 1
_audit_cli(
"cli.seo_meta.prune",
f"CLI pruned {removed} expired SEO metadata jobs",
metadata={"count": removed},
)
print(f"Pruned {removed} expired SEO metadata job(s)")
def cmd_seo_meta_clear(args):
from devplacepy.services.jobs import queue
jobs = queue.list_jobs(kind="seo_meta")
for job in jobs:
get_table("jobs").delete(uid=job["uid"])
_audit_cli(
"cli.seo_meta.clear",
f"CLI cleared all SEO metadata jobs ({len(jobs)})",
metadata={"count": len(jobs)},
)
print(f"Cleared {len(jobs)} SEO metadata job(s); generated metadata persists")
def _remove_deepsearch_artifacts(job):
import shutil
from devplacepy.config import DEEPSEARCH_DIR
from devplacepy.services.deepsearch.store import VectorStore
uid = job["uid"]
collection = f"ds_{uid.replace('-', '')}"
VectorStore(collection).drop()
shutil.rmtree(DEEPSEARCH_DIR / uid, ignore_errors=True)
def cmd_deepsearch_prune(args):
from datetime import datetime, timezone
from devplacepy.services.jobs import queue
now = datetime.now(timezone.utc)
removed = 0
for job in queue.list_jobs(kind="deepsearch", status=queue.DONE):
expires_at = job.get("expires_at")
if not expires_at:
continue
try:
expiry = datetime.fromisoformat(expires_at)
except (ValueError, TypeError):
continue
if expiry < now:
_remove_deepsearch_artifacts(job)
get_table("jobs").delete(uid=job["uid"])
removed += 1
_audit_cli(
"cli.deepsearch.prune",
f"CLI pruned {removed} expired DeepSearch jobs",
metadata={"count": removed},
)
print(f"Pruned {removed} expired DeepSearch job(s)")
def cmd_deepsearch_clear(args):
from devplacepy.services.jobs import queue
jobs = queue.list_jobs(kind="deepsearch")
for job in jobs:
_remove_deepsearch_artifacts(job)
get_table("jobs").delete(uid=job["uid"])
_audit_cli(
"cli.deepsearch.clear",
f"CLI cleared all DeepSearch jobs ({len(jobs)})",
metadata={"count": len(jobs)},
)
print(f"Cleared {len(jobs)} DeepSearch job(s) and their collections")
def cmd_isslop_prune(args):
from datetime import datetime, timezone
from devplacepy.services.jobs import queue
now = datetime.now(timezone.utc)
removed = 0
for job in queue.list_jobs(kind="isslop", status=queue.DONE):
expires_at = job.get("expires_at")
if not expires_at:
continue
try:
expiry = datetime.fromisoformat(expires_at)
except (ValueError, TypeError):
continue
if expiry < now:
get_table("jobs").delete(uid=job["uid"])
removed += 1
_audit_cli("cli.isslop.prune", f"CLI pruned {removed} expired AI usage analysis jobs", metadata={"count": removed})
print(f"Pruned {removed} expired AI usage analysis job(s) (reports persist)")
def cmd_isslop_clear(args):
from devplacepy.services.jobs import queue
from devplacepy.services.jobs.isslop import store
jobs = queue.list_jobs(kind="isslop")
for job in jobs:
get_table("jobs").delete(uid=job["uid"])
analyses = list(get_table(store.TABLE_ANALYSES).find())
for analysis in analyses:
store.purge_analysis(analysis["uid"])
_audit_cli(
"cli.isslop.clear",
f"CLI cleared {len(analyses)} AI usage analyses and {len(jobs)} job rows",
metadata={"analyses": len(analyses), "jobs": len(jobs)},
)
print(f"Cleared {len(analyses)} AI usage analysis(es), their reports and {len(jobs)} job row(s)")
def cmd_isslop_analyze(args):
import asyncio
from devplacepy.config import ISSLOP_WORKSPACES_DIR, ensure_data_dirs
from devplacepy.database import INTERNAL_GATEWAY_URL, internal_gateway_key
from devplacepy.models import IsslopRunForm
from devplacepy.services.jobs.isslop import store
from devplacepy.services.jobs.isslop.acquisition.workspace import remove_workspace, workspace_for
from devplacepy.services.jobs.isslop.config import settings_from_payload
from devplacepy.services.jobs.isslop.events import KIND_DONE, KIND_ERROR
from devplacepy.services.jobs.isslop.persistence import EventPersister
from devplacepy.services.jobs.isslop.pipeline import run_pipeline
from devplacepy.utils import generate_uid
url = IsslopRunForm(url=args.url).url
ensure_data_dirs()
uid = generate_uid()
settings = settings_from_payload(
{
"url": url,
"llm_endpoint": INTERNAL_GATEWAY_URL,
"api_key": internal_gateway_key(),
"allow_private": bool(args.allow_private),
"media_dir": str(store.media_dir_for(uid)),
}
)
store.create_analysis(uid, url, "system", "cli")
persister = EventPersister(uid)
store.update_analysis(uid, status="running")
workspace = workspace_for(ISSLOP_WORKSPACES_DIR, url, uid)
async def run() -> int:
failed = False
try:
async for event in run_pipeline(url, workspace, settings):
persister.apply(event)
if args.json:
print(event.to_json(), flush=True)
else:
print(f"[{event.kind}] {event.message}", flush=True)
if event.kind == KIND_ERROR:
failed = True
if event.kind == KIND_DONE and not args.json:
print(f"Report: /tools/isslop/{uid}/report")
print(f"Badge: /tools/isslop/{uid}/badge.svg")
finally:
remove_workspace(workspace)
return 1 if failed else 0
exit_code = asyncio.run(run())
_audit_cli(
"cli.isslop.analyze",
f"CLI AI usage analysis of {url}",
metadata={"uid": uid, "failed": bool(exit_code)},
)
raise SystemExit(exit_code)
def register_jobs(subparsers):
zips = subparsers.add_parser("zips", help="Zip archive job management")
zips_sub = zips.add_subparsers(title="action", dest="action")
zips_prune = zips_sub.add_parser(
"prune", help="Delete expired zip archives and their job rows"
)
zips_prune.set_defaults(func=cmd_zips_prune)
zips_clear = zips_sub.add_parser(
"clear", help="Delete every zip archive and job row"
)
zips_clear.set_defaults(func=cmd_zips_clear)
forks = subparsers.add_parser("forks", help="Fork job management")
forks_sub = forks.add_subparsers(title="action", dest="action")
forks_prune = forks_sub.add_parser(
"prune", help="Delete expired completed fork job rows (forked projects persist)"
)
forks_prune.set_defaults(func=cmd_forks_prune)
forks_clear = forks_sub.add_parser(
"clear", help="Delete every fork job row (forked projects persist)"
)
forks_clear.set_defaults(func=cmd_forks_clear)
seo = subparsers.add_parser("seo", help="SEO Diagnostics job management")
seo_sub = seo.add_subparsers(title="action", dest="action")
seo_prune = seo_sub.add_parser(
"prune", help="Delete expired SEO audit reports and their job rows"
)
seo_prune.set_defaults(func=cmd_seo_prune)
seo_clear = seo_sub.add_parser(
"clear", help="Delete every SEO audit report and job row"
)
seo_clear.set_defaults(func=cmd_seo_clear)
seo_meta = subparsers.add_parser("seo-meta", help="SEO metadata job management")
seo_meta_sub = seo_meta.add_subparsers(title="action", dest="action")
seo_meta_prune = seo_meta_sub.add_parser(
"prune", help="Delete expired SEO metadata job rows (generated metadata persists)"
)
seo_meta_prune.set_defaults(func=cmd_seo_meta_prune)
seo_meta_clear = seo_meta_sub.add_parser(
"clear", help="Delete every SEO metadata job row (generated metadata persists)"
)
seo_meta_clear.set_defaults(func=cmd_seo_meta_clear)
deepsearch = subparsers.add_parser("deepsearch", help="DeepSearch job management")
deepsearch_sub = deepsearch.add_subparsers(title="action", dest="action")
deepsearch_prune = deepsearch_sub.add_parser(
"prune", help="Delete expired DeepSearch sessions and their job rows"
)
deepsearch_prune.set_defaults(func=cmd_deepsearch_prune)
deepsearch_clear = deepsearch_sub.add_parser(
"clear", help="Delete every DeepSearch session and job row"
)
deepsearch_clear.set_defaults(func=cmd_deepsearch_clear)
isslop = subparsers.add_parser("isslop", help="AI Usage Analyzer job management")
isslop_sub = isslop.add_subparsers(title="action", dest="action")
isslop_prune = isslop_sub.add_parser(
"prune", help="Delete expired AI usage analysis job rows (analyses and reports persist)"
)
isslop_prune.set_defaults(func=cmd_isslop_prune)
isslop_clear = isslop_sub.add_parser(
"clear", help="Delete every AI usage analysis, its report and job rows"
)
isslop_clear.set_defaults(func=cmd_isslop_clear)
isslop_analyze = isslop_sub.add_parser(
"analyze", help="Run a AI usage analysis from the terminal and persist its report"
)
isslop_analyze.add_argument("url", help="Repository or website URL to classify")
isslop_analyze.add_argument("--json", action="store_true", help="Emit raw JSON events")
isslop_analyze.add_argument("--allow-private", action="store_true", dest="allow_private", help="Permit private and loopback hosts")
isslop_analyze.set_defaults(func=cmd_isslop_analyze)