Add lab-agent hot-dev mode

This commit is contained in:
rcourtman
2026-06-14 09:55:56 +01:00
parent 6a9aefc94e
commit 566c5b9da4
13 changed files with 202 additions and 25 deletions
+16 -1
View File
@@ -1,6 +1,6 @@
# Pulse Makefile for development
.PHONY: build run dev dev-status dev-logs dev-stop dev-restart dev-backend-restart dev-verify dev-foreground frontend backend all clean distclean dev-hot lint lint-backend lint-frontend format format-backend format-frontend build-agents control-plane handoff
.PHONY: build run dev dev-lab dev-status dev-status-lab dev-logs dev-stop dev-restart dev-restart-lab dev-backend-restart dev-verify dev-verify-lab dev-foreground dev-foreground-lab frontend backend all clean distclean dev-hot lint lint-backend lint-frontend format format-backend format-frontend build-agents control-plane handoff
FRONTEND_DIR := frontend-modern
FRONTEND_DIST := $(FRONTEND_DIR)/dist
@@ -30,9 +30,15 @@ run: build
dev:
npm run dev
dev-lab:
npm run dev:lab
dev-status:
npm run dev:status
dev-status-lab:
npm run dev:status:lab
dev-logs:
npm run dev:logs
@@ -42,15 +48,24 @@ dev-stop:
dev-restart:
npm run dev:restart
dev-restart-lab:
npm run dev:restart:lab
dev-backend-restart:
npm run dev:backend-restart
dev-verify:
npm run dev:verify
dev-verify-lab:
npm run dev:verify:lab
dev-foreground:
npm run dev:foreground
dev-foreground-lab:
npm run dev:foreground:lab
dev-hot:
npm run dev:foreground
+4
View File
@@ -132,12 +132,16 @@ on startup unless you override it with `HOT_DEV_AUTH_USER` and
Canonical local dev commands:
- `npm run dev` — start the managed runtime and reclaim the canonical dev ports if an older unmanaged session is still using them
- `npm run dev:lab` — start the managed runtime in lab-agent mode, with the frontend/backend exposed on the LAN and Proxmox LXC Docker inventory enabled for installed lab agents
- `npm run dev:status` — show frontend shell health, proxied API health, direct backend health, and listener ownership
- `npm run dev:status:lab` — show status using the same LAN-bound lab-agent defaults used by `dev:lab`
- `npm run dev:verify` — run the managed browser proof pack against the live dev runtime, including runtime recovery, the Patrol blocked-runtime page contract, and the desktop Recovery layout guard while the launcher suppresses unrelated backend rebuild churn for the duration of the proof pack
- `npm run dev:verify:lab` — run the managed proof pack after applying lab-agent runtime defaults
- `npm run dev:logs` — tail the managed runtime log
- `npm run dev:backend-restart` — bounce only the managed backend through the launcher contract
- `npm run dev:stop` — stop the managed runtime
- `npm run dev:foreground` — run the foreground hot-reload launcher intentionally if you need an attached shell
- `npm run dev:foreground:lab` — run the foreground hot-reload launcher with lab-agent defaults for troubleshooting
If `npm run dev:verify` passes, the managed dev shell, proxy path, backend
health endpoint, browser recovery path, Patrol blocked-runtime page behavior,
@@ -388,6 +388,15 @@ TLS floor in the dynamic config.
explicit `PULSE_DEV_LAN=true` opt-in; only that mode may bind Vite/backend
listeners to `0.0.0.0`, include LAN origins, or advertise the detected LAN
browser entrypoint.
The canonical shortcut for the installed homelab-agent case is lab-agent
mode: `PULSE_DEV_LAB_AGENTS=true` must enable LAN exposure plus the
Proxmox guest Docker detection/inventory opt-ins together, and the
repo-root wrapper surface must expose that mode through `npm run dev:lab`,
`npm run dev:restart:lab`, `npm run dev:status:lab`,
`npm run dev:verify:lab`, and `npm run dev:foreground:lab`. The frontend
workspace package and Makefile must delegate their lab-agent targets through
those same repo-root npm wrappers rather than duplicating raw launcher
commands or teaching developers to paste one-off environment strings.
The hot-dev supervisor must also recover its managed PID file from a live
`hot-dev-bg.sh supervise` process before treating the runtime as unmanaged.
Backend health monitoring must distinguish HTTP startup grace from a missing
@@ -1509,8 +1518,11 @@ route through the managed runtime control plane when they are operating on the
local dev stack, instead of resurrecting lane-local `hot-dev.sh` or raw Vite
process management through separate shell folklore. For Makefile targets, that
means dispatching through the canonical repo-root npm wrappers (`npm run dev`,
`npm run dev:status`, `npm run dev:restart`, `npm run dev:backend-restart`,
`npm run dev:verify`, `npm run dev:stop`, and `npm run dev:foreground`) rather
`npm run dev:lab`, `npm run dev:status`, `npm run dev:status:lab`,
`npm run dev:restart`, `npm run dev:restart:lab`,
`npm run dev:backend-restart`, `npm run dev:verify`,
`npm run dev:verify:lab`, `npm run dev:stop`, `npm run dev:foreground`, and
`npm run dev:foreground:lab`) rather
than shelling directly into `scripts/hot-dev-bg.sh`.
When `scripts/clean-mock-alerts.sh` needs to quiesce a local dev runtime, it
must stop the managed session through `hot-dev-bg` before touching legacy
@@ -55,25 +55,26 @@ truth for live infrastructure data.
31. `internal/monitoring/availability_poller.go`
32. `internal/monitoring/scheduler.go`
33. `internal/monitoring/docker_detection.go`
34. `internal/mock/fixture_graph.go`
35. `internal/dockeragent/docker_client.go`
36. `pkg/agents/docker/report.go`
37. `internal/models/models.go`
38. `internal/models/models_frontend.go`
39. `internal/models/converters.go`
40. `internal/models/deepcopy.go`
41. `internal/mock/generator.go`
42. `internal/mock/demo_scenarios.go`
43. `internal/kubernetesagent/agent.go`
44. `pkg/agents/kubernetes/report.go`
45. `internal/monitoring/temperature.go`
46. `internal/truenas/client.go`
47. `internal/truenas/disk_health.go`
48. `internal/truenas/provider.go`
49. `internal/models/ceph_cluster_identity.go`
50. `internal/truenas/types.go`
51. `internal/monitoring/monitor_alert_sync.go`
52. `internal/monitoring/platform_poller_shared.go`
34. `internal/monitoring/monitor_polling_containers.go`
35. `internal/mock/fixture_graph.go`
36. `internal/dockeragent/docker_client.go`
37. `pkg/agents/docker/report.go`
38. `internal/models/models.go`
39. `internal/models/models_frontend.go`
40. `internal/models/converters.go`
41. `internal/models/deepcopy.go`
42. `internal/mock/generator.go`
43. `internal/mock/demo_scenarios.go`
44. `internal/kubernetesagent/agent.go`
45. `pkg/agents/kubernetes/report.go`
46. `internal/monitoring/temperature.go`
47. `internal/truenas/client.go`
48. `internal/truenas/disk_health.go`
49. `internal/truenas/provider.go`
50. `internal/models/ceph_cluster_identity.go`
51. `internal/truenas/types.go`
52. `internal/monitoring/monitor_alert_sync.go`
53. `internal/monitoring/platform_poller_shared.go`
## Shared Boundaries
@@ -162,9 +163,15 @@ truth for live infrastructure data.
host agent remains one hybrid top-level system across rebuild ticks.
14. Add or change Proxmox-side LXC Docker detection or inventory through
`internal/monitoring/docker_detection.go`,
`internal/monitoring/monitor_pve_guest_poll.go`, and monitoring guardrails
together. Socket detection may only annotate LXC guests after explicit
server opt-in. LXC Docker inventory may only emit Docker / Podman
`internal/monitoring/monitor_pve_guest_poll.go`,
`internal/monitoring/monitor_polling_containers.go`, and monitoring
guardrails together. Socket detection may only annotate LXC guests after
explicit server opt-in. Both the efficient cluster/resources guest poll and
the traditional per-node container polling path must run
`CollectProxmoxGuestDockerInventory` after Docker presence detection and
before updating container state, so the Docker runtime lens does not depend
on which Proxmox polling path is active. LXC Docker inventory may only emit
Docker / Podman
module-compatible reports into `ApplyDockerReport`, must skip guests with a
linked online guest-local host agent, and must keep the command set to
minimal read-only Docker summary and aggregate stats collection. The socket
+5
View File
@@ -15,13 +15,18 @@
"homepage": "https://github.com/rcourtman/Pulse",
"scripts": {
"dev": "npm --prefix .. run dev",
"dev:lab": "npm --prefix .. run dev:lab",
"dev:logs": "npm --prefix .. run dev:logs",
"dev:restart": "npm --prefix .. run dev:restart",
"dev:restart:lab": "npm --prefix .. run dev:restart:lab",
"dev:backend-restart": "npm --prefix .. run dev:backend-restart",
"dev:status": "npm --prefix .. run dev:status",
"dev:status:lab": "npm --prefix .. run dev:status:lab",
"dev:stop": "npm --prefix .. run dev:stop",
"dev:verify": "npm --prefix .. run dev:verify",
"dev:verify:lab": "npm --prefix .. run dev:verify:lab",
"dev:foreground": "npm --prefix .. run dev:foreground",
"dev:foreground:lab": "npm --prefix .. run dev:foreground:lab",
"dev:frontend-only": "vite",
"build": "node scripts/build-embed-assets.mjs",
"preview": "vite preview",
@@ -899,6 +899,10 @@ func TestProxmoxGuestDockerInventoryUsesCanonicalReportIngestPath(t *testing.T)
"m.CollectProxmoxGuestDockerInventory(ctx, allContainers)",
"m.state.UpdateContainersForInstance(instanceName, allContainers)",
},
"monitor_polling_containers.go": {
"m.CollectProxmoxGuestDockerInventory(ctx, allContainers)",
"m.state.UpdateContainersForInstance(instanceName, allContainers)",
},
}
for file, snippets := range requiredSnippets {
@@ -322,6 +322,7 @@ func (m *Monitor) pollContainersWithNodes(ctx context.Context, instanceName stri
// Check Docker presence for containers that need it (new, restarted, started)
allContainers = m.CheckContainersForDocker(ctx, allContainers)
m.CollectProxmoxGuestDockerInventory(ctx, allContainers)
// Update state with all containers
m.state.UpdateContainersForInstance(instanceName, allContainers)
+5
View File
@@ -9,13 +9,18 @@
},
"scripts": {
"dev": "./scripts/hot-dev-bg.sh start --takeover",
"dev:lab": "PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev-bg.sh start --takeover",
"dev:status": "./scripts/hot-dev-bg.sh status",
"dev:status:lab": "PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev-bg.sh status",
"dev:logs": "./scripts/hot-dev-bg.sh logs",
"dev:stop": "./scripts/hot-dev-bg.sh stop",
"dev:restart": "./scripts/hot-dev-bg.sh restart --takeover",
"dev:restart:lab": "PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev-bg.sh restart --takeover",
"dev:backend-restart": "./scripts/hot-dev-bg.sh backend-restart",
"dev:verify": "./scripts/hot-dev-bg.sh verify --takeover",
"dev:verify:lab": "PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev-bg.sh verify --takeover",
"dev:foreground": "./scripts/hot-dev.sh",
"dev:foreground:lab": "PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev.sh",
"test": "echo \"Error: no test specified\" && exit 1",
"prepare": "husky",
"mock:on": "sed -i 's/PULSE_MOCK_MODE=.*/PULSE_MOCK_MODE=true/' .env || echo 'PULSE_MOCK_MODE=true' >> .env",
+16
View File
@@ -4,11 +4,14 @@
#
# Usage:
# npm run dev # Canonical managed dev runtime
# npm run dev:lab # LAN-bound lab-agent runtime
# npm run dev:status
# npm run dev:logs
# npm run dev:restart
# npm run dev:restart:lab
# npm run dev:backend-restart
# npm run dev:verify
# npm run dev:verify:lab
# ./scripts/hot-dev-bg.sh <command> # Direct troubleshooting only
set -euo pipefail
@@ -568,6 +571,10 @@ start_hot_dev_child() {
PULSE_DEV_API_PORT="${PULSE_DEV_API_PORT}" \
PULSE_DEV_API_URL="${PULSE_DEV_API_URL}" \
PULSE_DEV_WS_URL="${PULSE_DEV_WS_URL}" \
PULSE_DEV_LAN="${PULSE_DEV_LAN:-}" \
PULSE_DEV_LAB_AGENTS="${PULSE_DEV_LAB_AGENTS:-}" \
BIND_ADDRESS="${BIND_ADDRESS:-}" \
ALLOWED_ORIGINS="${ALLOWED_ORIGINS:-}" \
LOG_LEVEL="${LOG_LEVEL:-}" \
PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION="${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION:-}" \
PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY="${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY:-}" \
@@ -652,6 +659,10 @@ spawn_detached_supervisor() {
PULSE_DEV_API_PORT="${PULSE_DEV_API_PORT}" \
PULSE_DEV_API_URL="${PULSE_DEV_API_URL}" \
PULSE_DEV_WS_URL="${PULSE_DEV_WS_URL}" \
PULSE_DEV_LAN="${PULSE_DEV_LAN:-}" \
PULSE_DEV_LAB_AGENTS="${PULSE_DEV_LAB_AGENTS:-}" \
BIND_ADDRESS="${BIND_ADDRESS:-}" \
ALLOWED_ORIGINS="${ALLOWED_ORIGINS:-}" \
LOG_LEVEL="${LOG_LEVEL:-}" \
PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION="${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION:-}" \
PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY="${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY:-}" \
@@ -1087,13 +1098,18 @@ Usage: $(basename "$0") <command>
Managed entrypoints:
npm run dev
npm run dev:lab
npm run dev:status
npm run dev:status:lab
npm run dev:logs
npm run dev:stop
npm run dev:restart
npm run dev:restart:lab
npm run dev:backend-restart
npm run dev:verify
npm run dev:verify:lab
npm run dev:foreground
npm run dev:foreground:lab
Direct troubleshooting subcommands:
start [--takeover]
+8
View File
@@ -18,6 +18,7 @@
# PULSE_DEV_API_PORT=7655 Backend API port (default: 7655)
# FRONTEND_DEV_PORT=5173 Frontend dev server port (default: 5173)
# PULSE_DEV_LAN=true Expose frontend/backend on the LAN for agent/mobile testing (default: false)
# PULSE_DEV_LAB_AGENTS=true Enable LAN binding and Proxmox LXC Docker inventory for installed lab agents
# LOG_LEVEL=debug Opt into verbose backend logs (default: info)
# PULSE_DEV_DISABLE_BACKGROUND_AI=false
# Allow automatic Patrol/discovery/alert AI in dev
@@ -38,7 +39,9 @@
#
# Usage:
# npm run dev # Canonical managed dev runtime
# npm run dev:lab # LAN-bound lab-agent runtime
# ./scripts/hot-dev.sh # Foreground/manual runtime troubleshooting
# npm run dev:foreground:lab # Foreground lab-agent troubleshooting
# HOT_DEV_USE_PROD_DATA=true ./scripts/hot-dev.sh # Foreground/manual runtime with production data
#
set -euo pipefail
@@ -357,6 +360,7 @@ PULSE_DEV_DISABLE_BACKGROUND_AI="${PULSE_DEV_DISABLE_BACKGROUND_AI:-true}"
PULSE_AUTH_USER="$(hot_dev_resolve_auth_user)"
PULSE_AUTH_PASS="$(hot_dev_resolve_auth_pass)"
export FRONTEND_PORT PULSE_DEV_API_PORT PORT PULSE_DEV ALLOW_ADMIN_BYPASS PULSE_AUTH_USER PULSE_AUTH_PASS LOG_LEVEL PULSE_DEV_DISABLE_BACKGROUND_AI
export PULSE_DEV_LAN PULSE_DEV_LAB_AGENTS BIND_ADDRESS ALLOWED_ORIGINS
export PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS
# Data Directory Setup
@@ -495,6 +499,10 @@ start_backend_process() {
PULSE_ENCRYPTION_KEY="${PULSE_ENCRYPTION_KEY:-}" \
ALLOW_ADMIN_BYPASS="${ALLOW_ADMIN_BYPASS:-1}" \
PULSE_DEV="${PULSE_DEV:-true}" \
PULSE_DEV_LAN="${PULSE_DEV_LAN:-}" \
PULSE_DEV_LAB_AGENTS="${PULSE_DEV_LAB_AGENTS:-}" \
BIND_ADDRESS="${BIND_ADDRESS:-}" \
ALLOWED_ORIGINS="${ALLOWED_ORIGINS:-}" \
PULSE_AUTH_USER="${PULSE_AUTH_USER:-}" \
PULSE_AUTH_PASS="${PULSE_AUTH_PASS:-}" \
PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION="${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION:-}" \
+20
View File
@@ -60,10 +60,29 @@ hot_dev_lan_enabled() {
hot_dev_truthy "${PULSE_DEV_LAN:-false}"
}
hot_dev_lab_agents_enabled() {
hot_dev_truthy "${PULSE_DEV_LAB_AGENTS:-false}"
}
hot_dev_apply_lab_agent_defaults() {
if ! hot_dev_lab_agents_enabled; then
return 0
fi
PULSE_DEV_LAN=true
PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION=true
PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY=true
export PULSE_DEV_LAB_AGENTS PULSE_DEV_LAN
export PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY
}
hot_dev_configure_network_defaults() {
FRONTEND_PORT="${FRONTEND_PORT:-${PORT:-5173}}"
PORT="${PORT:-${FRONTEND_PORT}}"
hot_dev_apply_lab_agent_defaults
if [[ -z "${LAN_IP:-}" ]]; then
LAN_IP="$(hot_dev_detect_lan_ip)"
fi
@@ -118,6 +137,7 @@ hot_dev_configure_network_defaults() {
export FRONTEND_PORT PORT
export LAN_IP ALL_IPS
export PULSE_DEV_LAN PULSE_DEV_LAB_AGENTS
export FRONTEND_DEV_HOST FRONTEND_DEV_PORT
export PULSE_DEV_API_HOST PULSE_DEV_API_PORT PULSE_DEV_API_URL PULSE_DEV_WS_URL
export BIND_ADDRESS ALLOWED_ORIGINS
+36
View File
@@ -551,26 +551,36 @@ with open(os.environ["PACKAGE_JSON_PATH"], "r", encoding="utf-8") as fh:
for key in [
"dev",
"dev:lab",
"dev:status",
"dev:status:lab",
"dev:logs",
"dev:stop",
"dev:restart",
"dev:restart:lab",
"dev:backend-restart",
"dev:verify",
"dev:verify:lab",
"dev:foreground",
"dev:foreground:lab",
]:
print(f"{key}={scripts.get(key, '')}")
PY
)"
assert_contains "root package exposes managed dev start" "${output}" "dev=./scripts/hot-dev-bg.sh start --takeover"
assert_contains "root package exposes lab-agent dev start" "${output}" "dev:lab=PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev-bg.sh start --takeover"
assert_contains "root package exposes managed dev status" "${output}" "dev:status=./scripts/hot-dev-bg.sh status"
assert_contains "root package exposes lab-agent dev status" "${output}" "dev:status:lab=PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev-bg.sh status"
assert_contains "root package exposes managed dev logs" "${output}" "dev:logs=./scripts/hot-dev-bg.sh logs"
assert_contains "root package exposes managed dev stop" "${output}" "dev:stop=./scripts/hot-dev-bg.sh stop"
assert_contains "root package exposes managed dev restart" "${output}" "dev:restart=./scripts/hot-dev-bg.sh restart --takeover"
assert_contains "root package exposes lab-agent dev restart" "${output}" "dev:restart:lab=PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev-bg.sh restart --takeover"
assert_contains "root package exposes managed backend restart" "${output}" "dev:backend-restart=./scripts/hot-dev-bg.sh backend-restart"
assert_contains "root package exposes managed dev verify" "${output}" "dev:verify=./scripts/hot-dev-bg.sh verify --takeover"
assert_contains "root package exposes lab-agent dev verify" "${output}" "dev:verify:lab=PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev-bg.sh verify --takeover"
assert_contains "root package keeps foreground escape hatch" "${output}" "dev:foreground=./scripts/hot-dev.sh"
assert_contains "root package exposes lab-agent foreground escape hatch" "${output}" "dev:foreground:lab=PULSE_DEV_LAB_AGENTS=true ./scripts/hot-dev.sh"
}
test_frontend_package_exposes_managed_runtime_entrypoints() {
@@ -586,13 +596,18 @@ with open(os.environ["PACKAGE_JSON_PATH"], "r", encoding="utf-8") as fh:
for key in [
"dev",
"dev:lab",
"dev:logs",
"dev:restart",
"dev:restart:lab",
"dev:backend-restart",
"dev:status",
"dev:status:lab",
"dev:stop",
"dev:verify",
"dev:verify:lab",
"dev:foreground",
"dev:foreground:lab",
"dev:frontend-only",
]:
print(f"{key}={scripts.get(key, '')}")
@@ -600,13 +615,18 @@ PY
)"
assert_contains "frontend package delegates managed dev start to repo-root wrapper" "${output}" "dev=npm --prefix .. run dev"
assert_contains "frontend package delegates lab-agent dev start to repo-root wrapper" "${output}" "dev:lab=npm --prefix .. run dev:lab"
assert_contains "frontend package delegates managed dev logs to repo-root wrapper" "${output}" "dev:logs=npm --prefix .. run dev:logs"
assert_contains "frontend package delegates managed dev restart to repo-root wrapper" "${output}" "dev:restart=npm --prefix .. run dev:restart"
assert_contains "frontend package delegates lab-agent dev restart to repo-root wrapper" "${output}" "dev:restart:lab=npm --prefix .. run dev:restart:lab"
assert_contains "frontend package delegates managed backend restart to repo-root wrapper" "${output}" "dev:backend-restart=npm --prefix .. run dev:backend-restart"
assert_contains "frontend package delegates managed dev status to repo-root wrapper" "${output}" "dev:status=npm --prefix .. run dev:status"
assert_contains "frontend package delegates lab-agent dev status to repo-root wrapper" "${output}" "dev:status:lab=npm --prefix .. run dev:status:lab"
assert_contains "frontend package delegates managed dev stop to repo-root wrapper" "${output}" "dev:stop=npm --prefix .. run dev:stop"
assert_contains "frontend package delegates managed dev verify to repo-root wrapper" "${output}" "dev:verify=npm --prefix .. run dev:verify"
assert_contains "frontend package delegates lab-agent dev verify to repo-root wrapper" "${output}" "dev:verify:lab=npm --prefix .. run dev:verify:lab"
assert_contains "frontend package delegates foreground escape hatch to repo-root wrapper" "${output}" "dev:foreground=npm --prefix .. run dev:foreground"
assert_contains "frontend package delegates lab-agent foreground escape hatch to repo-root wrapper" "${output}" "dev:foreground:lab=npm --prefix .. run dev:foreground:lab"
assert_contains "frontend package keeps explicit frontend-only escape hatch" "${output}" "dev:frontend-only=vite"
}
@@ -695,13 +715,18 @@ test_makefile_routes_managed_runtime_through_npm() {
output="$(cat "${ROOT_DIR}/Makefile")"
assert_contains "make dev routes through npm wrapper" "${output}" $'dev:\n\tnpm run dev'
assert_contains "make dev-lab routes through npm wrapper" "${output}" $'dev-lab:\n\tnpm run dev:lab'
assert_contains "make dev-status routes through npm wrapper" "${output}" $'dev-status:\n\tnpm run dev:status'
assert_contains "make dev-status-lab routes through npm wrapper" "${output}" $'dev-status-lab:\n\tnpm run dev:status:lab'
assert_contains "make dev-logs routes through npm wrapper" "${output}" $'dev-logs:\n\tnpm run dev:logs'
assert_contains "make dev-stop routes through npm wrapper" "${output}" $'dev-stop:\n\tnpm run dev:stop'
assert_contains "make dev-restart routes through npm wrapper" "${output}" $'dev-restart:\n\tnpm run dev:restart'
assert_contains "make dev-restart-lab routes through npm wrapper" "${output}" $'dev-restart-lab:\n\tnpm run dev:restart:lab'
assert_contains "make dev-backend-restart routes through npm wrapper" "${output}" $'dev-backend-restart:\n\tnpm run dev:backend-restart'
assert_contains "make dev-verify routes through npm wrapper" "${output}" $'dev-verify:\n\tnpm run dev:verify'
assert_contains "make dev-verify-lab routes through npm wrapper" "${output}" $'dev-verify-lab:\n\tnpm run dev:verify:lab'
assert_contains "make dev-foreground routes through npm wrapper" "${output}" $'dev-foreground:\n\tnpm run dev:foreground'
assert_contains "make dev-foreground-lab routes through npm wrapper" "${output}" $'dev-foreground-lab:\n\tnpm run dev:foreground:lab'
assert_contains "make dev-hot routes through foreground wrapper" "${output}" $'dev-hot:\n\tnpm run dev:foreground'
}
@@ -717,7 +742,9 @@ test_hot_dev_script_advertises_foreground_escape_hatch() {
assert_contains "hot-dev header identifies foreground escape hatch" "${output}" "hot-dev.sh - Foreground Pulse dev runtime escape hatch"
assert_contains "hot-dev usage points to managed runtime first" "${output}" "npm run dev # Canonical managed dev runtime"
assert_contains "hot-dev usage advertises lab-agent managed runtime" "${output}" "npm run dev:lab # LAN-bound lab-agent runtime"
assert_contains "hot-dev usage reserves direct script for manual troubleshooting" "${output}" "./scripts/hot-dev.sh # Foreground/manual runtime troubleshooting"
assert_contains "hot-dev usage advertises lab-agent foreground troubleshooting" "${output}" "npm run dev:foreground:lab # Foreground lab-agent troubleshooting"
assert_not_contains "hot-dev usage no longer claims standard dev mode" "${output}" "Standard dev mode"
}
@@ -871,6 +898,9 @@ test_hot_dev_bg_script_advertises_managed_entrypoint() {
output="$(cat "${ROOT_DIR}/scripts/hot-dev-bg.sh")"
assert_contains "hot-dev-bg usage points to managed runtime first" "${output}" "npm run dev # Canonical managed dev runtime"
assert_contains "hot-dev-bg usage advertises lab-agent managed runtime" "${output}" "npm run dev:lab # LAN-bound lab-agent runtime"
assert_contains "hot-dev-bg usage advertises lab-agent restart" "${output}" "npm run dev:restart:lab"
assert_contains "hot-dev-bg usage advertises lab-agent verify" "${output}" "npm run dev:verify:lab"
assert_contains "hot-dev-bg documents direct launcher as troubleshooting only" "${output}" "./scripts/hot-dev-bg.sh <command> # Direct troubleshooting only"
assert_contains "hot-dev-bg sources shared runtime helpers" "${output}" 'source "${SCRIPT_DIR}/lib/hot-dev-runtime.sh"'
assert_contains "hot-dev-bg uses shared network defaults" "${output}" "hot_dev_configure_network_defaults"
@@ -889,6 +919,9 @@ test_hot_dev_bg_preserves_proxmox_guest_docker_env() {
assert_contains "hot-dev-bg child preserves Proxmox guest Docker detection opt-in" "${output}" 'PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION="${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION:-}"'
assert_contains "hot-dev-bg child preserves Proxmox guest Docker inventory opt-in" "${output}" 'PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY="${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY:-}"'
assert_contains "hot-dev-bg child preserves scoped Proxmox guest Docker VMIDs" "${output}" 'PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS="${PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS:-}"'
assert_contains "hot-dev-bg child preserves lab-agent mode" "${output}" 'PULSE_DEV_LAB_AGENTS="${PULSE_DEV_LAB_AGENTS:-}"'
assert_contains "hot-dev-bg child preserves LAN opt-in" "${output}" 'PULSE_DEV_LAN="${PULSE_DEV_LAN:-}"'
assert_contains "hot-dev-bg child preserves backend bind address" "${output}" 'BIND_ADDRESS="${BIND_ADDRESS:-}"'
assert_contains "hot-dev-bg child preserves debug log level" "${output}" 'LOG_LEVEL="${LOG_LEVEL:-}"'
assert_contains "hot-dev-bg restart takeover clears launchd before env-sensitive relaunch" "${output}" 'stop_launchd_hot_dev_job'
}
@@ -1032,7 +1065,10 @@ test_hot_dev_bg_usage_prefers_managed_wrappers() {
assert_contains "hot-dev-bg usage shows managed entrypoints heading" "${output}" "Managed entrypoints:"
assert_contains "hot-dev-bg usage advertises npm dev wrapper" "${output}" "npm run dev"
assert_contains "hot-dev-bg usage advertises lab-agent dev wrapper" "${output}" "npm run dev:lab"
assert_contains "hot-dev-bg usage advertises lab-agent status wrapper" "${output}" "npm run dev:status:lab"
assert_contains "hot-dev-bg usage advertises npm verify wrapper" "${output}" "npm run dev:verify"
assert_contains "hot-dev-bg usage advertises lab-agent verify wrapper" "${output}" "npm run dev:verify:lab"
assert_contains "hot-dev-bg usage labels raw commands as troubleshooting-only" "${output}" "Direct troubleshooting subcommands:"
assert_contains "hot-dev-bg usage retains direct start subcommand" "${output}" "start [--takeover]"
}
+44
View File
@@ -133,7 +133,11 @@ test_hot_dev_preserves_proxmox_guest_docker_env() {
local output
output="$(sed -n '1,760p' "${HOT_DEV}")"
assert_contains "hot-dev exports lab-agent mode opt-in" "${output}" "export PULSE_DEV_LAN PULSE_DEV_LAB_AGENTS BIND_ADDRESS ALLOWED_ORIGINS"
assert_contains "hot-dev exports Proxmox guest Docker detection opt-in" "${output}" "export PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS"
assert_contains "backend launch preserves LAN opt-in" "${output}" 'PULSE_DEV_LAN="${PULSE_DEV_LAN:-}"'
assert_contains "backend launch preserves lab-agent mode opt-in" "${output}" 'PULSE_DEV_LAB_AGENTS="${PULSE_DEV_LAB_AGENTS:-}"'
assert_contains "backend launch preserves backend bind address" "${output}" 'BIND_ADDRESS="${BIND_ADDRESS:-}"'
assert_contains "backend launch preserves Proxmox guest Docker detection opt-in" "${output}" 'PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION="${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION:-}"'
assert_contains "backend launch preserves Proxmox guest Docker inventory opt-in" "${output}" 'PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY="${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY:-}"'
assert_contains "backend launch preserves scoped Proxmox guest Docker VMIDs" "${output}" 'PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS="${PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS:-}"'
@@ -201,6 +205,45 @@ test_hot_dev_network_defaults_are_local_first_with_explicit_lan_opt_in() {
assert_contains "LAN opt-in allows the wildcard dev origin Electron may report" "${output}" "http://0.0.0.0:5173"
}
test_hot_dev_lab_agent_mode_enables_lan_and_guest_docker_inventory_defaults() {
local output
output="$(
HOT_DEV_RUNTIME_LIB="${HOT_DEV_RUNTIME_LIB}" \
bash -c '
set -euo pipefail
source "${HOT_DEV_RUNTIME_LIB}"
unset FRONTEND_PORT PORT FRONTEND_DEV_HOST FRONTEND_DEV_PORT BIND_ADDRESS
unset PULSE_DEV_LAN PULSE_DEV_API_HOST PULSE_DEV_API_PORT PULSE_DEV_API_URL PULSE_DEV_WS_URL
unset PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY
unset ALLOWED_ORIGINS
LAN_IP=192.168.50.10
ALL_IPS="192.168.50.10 10.10.10.5"
PULSE_DEV_LAB_AGENTS=true
hot_dev_configure_network_defaults
printf "lab_mode=%s\n" "${PULSE_DEV_LAB_AGENTS}"
printf "lab_lan=%s\n" "${PULSE_DEV_LAN}"
printf "lab_detection=%s\n" "${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION}"
printf "lab_inventory=%s\n" "${PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY}"
printf "lab_frontend_host=%s\n" "${FRONTEND_DEV_HOST}"
printf "lab_api_host=%s\n" "${PULSE_DEV_API_HOST}"
printf "lab_api_url=%s\n" "${PULSE_DEV_API_URL}"
printf "lab_bind=%s\n" "${BIND_ADDRESS}"
printf "lab_origins=%s\n" "${ALLOWED_ORIGINS}"
'
)"
assert_contains "lab-agent mode remains explicitly marked" "${output}" "lab_mode=true"
assert_contains "lab-agent mode enables LAN runtime exposure" "${output}" "lab_lan=true"
assert_contains "lab-agent mode enables Proxmox guest Docker detection" "${output}" "lab_detection=true"
assert_contains "lab-agent mode enables Proxmox guest Docker inventory" "${output}" "lab_inventory=true"
assert_contains "lab-agent mode exposes the frontend on all interfaces" "${output}" "lab_frontend_host=0.0.0.0"
assert_contains "lab-agent mode targets the detected LAN API host" "${output}" "lab_api_host=192.168.50.10"
assert_contains "lab-agent mode derives the LAN API URL" "${output}" "lab_api_url=http://192.168.50.10:7655"
assert_contains "lab-agent mode binds the backend on all interfaces" "${output}" "lab_bind=0.0.0.0"
assert_contains "lab-agent mode allows detected LAN frontend origins" "${output}" "lab_origins=http://192.168.50.10"
assert_contains "lab-agent mode allows the wildcard dev origin Electron may report" "${output}" "http://0.0.0.0:5173"
}
test_hot_dev_browser_urls_distinguish_bind_and_browser_hosts() {
local output
output="$(
@@ -228,6 +271,7 @@ test_hot_dev_keeps_backend_launch_errors_in_debug_log
test_hot_dev_preserves_proxmox_guest_docker_env
test_hot_dev_avoids_self_killing_npm_wrapper
test_hot_dev_network_defaults_are_local_first_with_explicit_lan_opt_in
test_hot_dev_lab_agent_mode_enables_lan_and_guest_docker_inventory_defaults
test_hot_dev_browser_urls_distinguish_bind_and_browser_hosts
if (( failures > 0 )); then