add: track shared modules, vendor scripts, and update gitignore

- lib/common.py: shared run/load_json/save_json utilities
- webui/api/common.py: _ok/_error response helpers
- webui/static/htmx.min.js, json-enc.js: vendored frontend deps
- scripts/update-vendor.sh: frontend vendor updater
- vendor/acme.sh: bundled ACME client
- .gitignore: add .playwright-mcp/ and opencode.json.pwenv
This commit is contained in:
2026-05-25 01:13:34 +00:00
parent ecb5a9a44d
commit f81be96e59
7 changed files with 8536 additions and 0 deletions
+4
View File
@@ -14,6 +14,10 @@ __pycache__/
# Local AI tool config (contains internal hostnames)
opencode.json
opencode.json.pwenv
# Playwright MCP artifacts
.playwright-mcp/
# Runtime artifacts
build/
+137
View File
@@ -0,0 +1,137 @@
"""Shared utilities for Vacuum Wall lib/ modules.
Provides common helpers for JSON persistence, subprocess execution,
deep merging, and directory creation used across all subsystem modules.
"""
import json
import os
import subprocess
from copy import deepcopy
from pathlib import Path
from typing import Any
def run(
cmd: list[str],
check: bool = True,
sudo: bool = False,
timeout: int | None = None,
) -> str:
"""Run a command and return stripped stdout.
Args:
cmd: Command arguments.
check: Raise RuntimeError on non-zero exit.
sudo: Prefix command with ``sudo``.
timeout: Timeout in seconds (``None`` → no timeout).
Returns:
``stdout`` with trailing whitespace removed.
Raises:
RuntimeError: When ``check=True`` and the process exits non-zero.
"""
full_cmd = ["sudo", *cmd] if sudo else list(cmd)
try:
result = subprocess.run(
full_cmd,
capture_output=True,
text=True,
check=check,
timeout=timeout,
)
return result.stdout.strip()
except subprocess.CalledProcessError as exc:
raise RuntimeError(
f"Command failed: {' '.join(full_cmd)} (rc={exc.returncode}): "
f"{exc.stderr.strip()}"
) from exc
def run_proc(
cmd: list[str],
check: bool = True,
sudo: bool = False,
timeout: int | None = None,
input: str | None = None,
) -> subprocess.CompletedProcess[str]:
"""Run a command and return the full ``CompletedProcess``.
Args:
cmd: Command arguments.
check: Raise ``subprocess.CalledProcessError`` on non-zero exit.
sudo: Prefix command with ``sudo``.
timeout: Timeout in seconds.
input: String to pass as stdin to the subprocess.
Returns:
The completed process object.
"""
full_cmd = ["sudo", *cmd] if sudo else list(cmd)
return subprocess.run(
full_cmd,
capture_output=True,
text=True,
check=check,
timeout=timeout,
input=input,
)
def load_json(path: Path, default: dict[str, Any] | None = None) -> dict[str, Any]:
"""Load JSON from *path*.
Returns *default* (default ``{}``) if the file does not exist.
"""
if default is None:
default = {}
if not path.exists():
return deepcopy(default)
with open(path) as f:
return json.load(f)
def save_json(path: Path, data: dict[str, Any], indent: int = 4) -> None:
"""Atomically write *data* as JSON to *path*.
Writes to ``path.tmp`` first, then replaces *path* via ``os.replace()``
to avoid partial writes.
"""
path.parent.mkdir(parents=True, exist_ok=True)
tmp = path.with_suffix(path.suffix + ".tmp")
with open(tmp, "w") as f:
json.dump(data, f, indent=indent)
f.write("\n")
os.replace(tmp, path)
def deep_merge(base: dict[str, Any], overrides: dict[str, Any]) -> dict[str, Any]:
"""Recursively merge *overrides* into a deep copy of *base*.
For nested dicts the merge recurses; for all other values
*overrides* wins.
"""
result = deepcopy(base)
for k, v in overrides.items():
if k in result and isinstance(result[k], dict) and isinstance(v, dict):
result[k] = deep_merge(result[k], v)
else:
result[k] = deepcopy(v)
return result
def ensure_dirs(*dirs: Path) -> None:
"""Create each directory (and parents) if it does not exist."""
for d in dirs:
d.mkdir(parents=True, exist_ok=True)
__all__ = [
"deep_merge",
"ensure_dirs",
"load_json",
"run",
"run_proc",
"save_json",
]
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Download and vendor frontend JS libraries into webui/static/
# and backend binaries into vendor/.
# Run from the project root after updating the VERSION variables below.
set -euo pipefail
# ---- Library versions ----
HTMX_VERSION="2.0.4"
HTMX_JSON_ENC_VERSION="2.0.0"
ACME_VERSION="3.1.3"
STATIC_DIR="webui/static"
download() {
local name="$1" url="$2" dest="$3"
if [[ -n "${SKIP_DOWNLOAD:-}" ]]; then
echo "[skip] $name (SKIP_DOWNLOAD is set)"
return
fi
echo "[download] $name$dest"
curl -sfL -o "$dest" "$url"
}
download "htmx@${HTMX_VERSION}" \
"https://unpkg.com/htmx.org@${HTMX_VERSION}/dist/htmx.min.js" \
"${STATIC_DIR}/htmx.min.js"
download "htmx-ext-json-enc@${HTMX_JSON_ENC_VERSION}" \
"https://unpkg.com/htmx-ext-json-enc@${HTMX_JSON_ENC_VERSION}/json-enc.js" \
"${STATIC_DIR}/json-enc.js"
download "acme.sh@${ACME_VERSION}" \
"https://raw.githubusercontent.com/acmesh-official/acme.sh/${ACME_VERSION}/acme.sh" \
"vendor/acme.sh"
chmod +x "vendor/acme.sh"
echo "[done] All libraries vendored."
Vendored Executable
+8326
View File
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
"""Shared API response helpers.
Used by all API blueprints to produce consistent JSON responses
per the API response contract: ``{"ok": true, "data": <value>}`` /
``{"ok": false, "error": "msg"}``.
"""
from flask import jsonify
def _ok(data=None):
"""Return a success JSON response."""
return jsonify({"ok": True, "data": data})
def _error(msg: str, code: int = 400):
"""Return an error JSON response with the given HTTP status code."""
return jsonify({"ok": False, "error": msg}), code
+1
View File
File diff suppressed because one or more lines are too long
+12
View File
@@ -0,0 +1,12 @@
htmx.defineExtension('json-enc', {
onEvent: function(name, evt) {
if (name === 'htmx:configRequest') {
evt.detail.headers['Content-Type'] = 'application/json'
}
},
encodeParameters: function(xhr, parameters, elt) {
xhr.overrideMimeType('text/json')
return (JSON.stringify(parameters))
}
})