diff --git a/pmoves/configs/pinokio-network-inventory.yaml b/pmoves/configs/pinokio-network-inventory.yaml new file mode 100644 index 0000000000..530bfcfc54 --- /dev/null +++ b/pmoves/configs/pinokio-network-inventory.yaml @@ -0,0 +1,247 @@ +# Pinokio Network Auto-Discovery Inventory +# Classifies all services exposed via Pinokio LWW (LAN-Wide-Web) on the PMOVES fleet. +# +# Pinokio 7 LWW automatically discovers ALL localhost TCP listeners and +# exposes them in the network view. This is always-on — no toggle exists. +# Non-PMOVES services (Steam, Chrome DevTools, Razer, etc.) also appear. +# +# Classification: +# intentional — PMOVES service, expected in network view +# noise — Non-PMOVES listener, harmless but clutters UI +# system — OS/platform service, always running +# +# Risk assessment: LAN is trusted home network, Tailscale encrypts cross-machine. +# Non-PMOVES services don't respond to API calls — noise, not security risk. +# Filtering would require forking Pinokio's core Caddy logic (high maintenance). +# +# Cross-refs: +# pmoves/configs/tac_trees/networking-defense-in-depth.tac.yaml +# pmoves/configs/tac_trees/node-5090-powerfulmoves.tac.yaml + +version: "1.0.0" +last_audit: "2026-03-22" + +# ───────────────────────────────────────────────────────────────────────────── +# Node: 5090 (POWERFULMOVES) — Primary GPU / TTS Host +# ───────────────────────────────────────────────────────────────────────────── +nodes: + - hostname: POWERFULMOVES + tailscale_host: powerfulmoves-1 + role: primary-gpu-tts + pinokio_lww: true + services: + + # === PMOVES Intentional Services === + - name: Ultimate-TTS-Studio + port: 7860 + class: intentional + owner: pinokio + description: "14-engine TTS hub (Gradio)" + health_endpoint: "/gradio_api/info" + + - name: Qwen3-TTS + port: dynamic + class: intentional + owner: pinokio + description: "Qwen3 standalone TTS with VoiceDesign mode" + health_endpoint: "/gradio_api/info" + + - name: VibeVoice-Realtime + port: dynamic + class: intentional + owner: pinokio + description: "WebSocket streaming TTS (uvicorn)" + health_endpoint: null + + - name: VoxForge-Pro + port: dynamic + class: intentional + owner: pinokio + description: "PDF-to-audiobook pipeline" + health_endpoint: "/gradio_api/info" + + - name: Ollama + port: 11434 + class: intentional + owner: docker + description: "Local LLM inference" + health_endpoint: "/api/tags" + + - name: N8N + port: dynamic + class: intentional + owner: pinokio + description: "Workflow automation" + health_endpoint: "/healthz" + + - name: SillyTavern + port: dynamic + class: intentional + owner: pinokio + description: "Chat interface for LLMs" + health_endpoint: null + + - name: ACE-Step + port: dynamic + class: intentional + owner: pinokio + description: "Music generation" + health_endpoint: null + + - name: WAN + port: dynamic + class: intentional + owner: pinokio + description: "Video generation" + health_endpoint: null + + # === Noise Services (auto-discovered, non-PMOVES) === + - name: Steam + port: various + class: noise + owner: valve + description: "Steam client — multiple TCP listeners for game downloads/chat" + + - name: Chrome-DevTools + port: 9222 + class: noise + owner: google + description: "Chrome remote debugging protocol" + + - name: Razer-Synapse + port: various + class: noise + owner: razer + description: "Razer peripheral management" + + - name: Logitech-GHub + port: various + class: noise + owner: logitech + description: "Logitech peripheral management" + + - name: Docker-Desktop + port: various + class: system + owner: docker + description: "Docker Desktop internal listeners" + + - name: Surfshark-VPN + port: various + class: noise + owner: surfshark + description: "VPN client internal listeners" + + - name: NVIDIA-Container + port: various + class: system + owner: nvidia + description: "NVIDIA container runtime/toolkit" + + # ───────────────────────────────────────────────────────────────────────────── + # Node: Z890 (pmoves-z890) — Infrastructure Coordinator + # ───────────────────────────────────────────────────────────────────────────── + - hostname: pmoves-z890 + tailscale_host: pmoves-z890 + role: infra-coordinator + pinokio_lww: true + services: + + # === PMOVES Intentional Services === + - name: Pinokio-UI + port: 42000 + class: intentional + owner: pinokio + description: "Pinokio Electron app" + health_endpoint: null + + # === TTS Apps — REMOVED (delegated to 5090) === + # OpenAudio (Fish Speech) — REMOVED 2026-03-22 + # Qwen3-TTS — REMOVED 2026-03-22 + # Ultimate-TTS-Studio SUP3R Edition — REMOVED 2026-03-22 + + # === Docker Compose Services (exposed via container port mappings) === + - name: NATS + port: 4222 + class: intentional + owner: docker-compose + description: "JetStream message bus (hub)" + health_endpoint: "http://localhost:8222/varz" + + - name: Agent-Zero + port: 8080 + class: intentional + owner: docker-compose + description: "Control-plane orchestrator" + health_endpoint: "/healthz" + + - name: TensorZero + port: 3030 + class: intentional + owner: docker-compose + description: "Centralized LLM gateway" + health_endpoint: null + + - name: Grafana + port: 3000 + class: intentional + owner: docker-compose + description: "Monitoring dashboards" + health_endpoint: "/api/health" + + - name: Prometheus + port: 9090 + class: intentional + owner: docker-compose + description: "Metrics scraping" + health_endpoint: "/-/healthy" + + - name: Supabase-Kong + port: 8000 + class: intentional + owner: docker-compose + description: "Supabase API gateway" + health_endpoint: null + + - name: Flute-Gateway + port: 8055 + class: intentional + owner: docker-compose + description: "Voice communication layer" + health_endpoint: "/healthz" + + # === Noise Services === + - name: Docker-Desktop + port: various + class: system + owner: docker + description: "Docker Desktop internal listeners" + + # ───────────────────────────────────────────────────────────────────────────── + # Node: 4090 (pmoves-laptop) — Mobile Relay + # ───────────────────────────────────────────────────────────────────────────── + - hostname: pmoves-laptop + tailscale_host: pmoves-laptop + role: mobile-relay + pinokio_lww: false + note: "Consumer node — accesses 5090/Z890 via Pinokio LWW network view" + services: [] + +# ───────────────────────────────────────────────────────────────────────────── +# Security Posture +# ───────────────────────────────────────────────────────────────────────────── +security: + risk_level: low + rationale: > + LAN is trusted home network. Tailscale provides WireGuard encryption for + cross-machine traffic. Non-PMOVES noise services (Steam, Chrome, Razer, etc.) + don't respond to meaningful API calls — they're TCP listeners that appear in + auto-discovery but have no exploitable surface via Pinokio's Caddy proxy. + mitigation: + - "LWW auto-assigns Caddy share proxy ports 42003+ (one per localhost listener)" + - "This inventory file classifies intentional vs noise for audit" + - "No PII or secrets exposed through auto-discovered noise services" + recommendations: + - "Allow TCP 42000-42100 on Tailscale/LAN adapter for cross-machine Pinokio LWW access" + - "Periodically re-audit this inventory when new apps are installed" + - "If Pinokio adds per-service opt-in, migrate from blanket LWW discovery" diff --git a/pmoves/configs/tac_trees/networking-defense-in-depth.tac.yaml b/pmoves/configs/tac_trees/networking-defense-in-depth.tac.yaml new file mode 100644 index 0000000000..aaea315a45 --- /dev/null +++ b/pmoves/configs/tac_trees/networking-defense-in-depth.tac.yaml @@ -0,0 +1,479 @@ +# TAC Tree: Defense-in-Depth Networking +# Validates the full 7-layer networking stack from app binding through production edge. +# Every layer must be correct — no workarounds (especially not tailscale serve). + +name: "Defense-in-Depth Networking" +version: "1.0.0" +description: > + 7-layer networking audit: app binding, Docker network isolation, service health chain, + NATS leaf nodes (cross-machine), Tailscale mesh, Headscale coordination, and production + VPS edge. Ensures cross-machine access works through correct configuration at every layer, + not through workarounds like tailscale serve. + +root: + id: net + task: "Defense-in-depth networking stack review" + context: "pmoves/docker-compose.yml + configs/ + Pinokio launcher" + agent_hint: codex + children: + + # ========================================================================= + # Phase 1: App Binding Correctness (Pinokio Native) + # ========================================================================= + - id: net.binding + task: "App binding correctness" + context: "Pinokio Caddy proxy + Gradio binding. Caddy on 0.0.0.0, Gradio on 127.0.0.1" + agent_hint: codex + children: + - id: net.binding.start-js-capture + task: "start.js uses correct regex capture group" + action: + type: grep + target: "D:/pinokio/api/Ultimate-TTS-Studio.git/start.js" + pattern: "input\\.event\\[1\\]" + expect: "URL extracted from capture group [1], not [0]" + context: > + Pinokio event regex: /(http:\\/\\/\\S+)/ — parentheses create capture group. + input.event[0] = full match, input.event[1] = captured URL. + Using [0] works accidentally but breaks URL for local.set. + agent_hint: codex + + - id: net.binding.gradio-server-name + task: "GRADIO_SERVER_NAME not hardcoded to 127.0.0.1" + action: + type: grep + target: "D:/pinokio/api/Ultimate-TTS-Studio.git/start.js" + pattern: "GRADIO_SERVER_NAME" + expect: "Not hardcoded to 127.0.0.1 — let Pinokio LWW control binding" + context: > + Pinokio LWW sets GRADIO_SERVER_NAME=0.0.0.0 automatically. + Hardcoding 127.0.0.1 in start.js overrides this and blocks + cross-machine access. The correct approach: let Pinokio manage it. + agent_hint: codex + + - id: net.binding.lww-active + task: "Pinokio LWW (LAN-Wide-Web) active for mesh access" + action: + type: manual + instruction: "Verify Pinokio Network tab shows discovered services from other LAN nodes" + expect: "LWW is always-on in Pinokio 7 — no toggle needed" + context: > + LWW (PINOKIO.md Section 8) automatically discovers all localhost TCP + listeners and exposes them to other Pinokio instances on the LAN. + There is no SHARE_LOCAL toggle — the feature is always active in P7. + Cross-machine access goes through Pinokio UI on port 42000. + agent_hint: codex + + - id: net.binding.port-assignment + task: "Port 7860 (native) vs 7861 (Docker) documented" + action: + type: manual + instruction: "Verify port 7860 is Pinokio native, 7861 is Docker (if used)" + expect: "No port conflict between native and containerized TTS" + context: "Pinokio native uses Caddy on 7860, Docker compose may use 7861" + agent_hint: codex + + - id: net.binding.caddy-https + task: "Caddy HTTPS proxy responds" + action: + type: http + url: "https://7860.localhost" + expect: "200 OK or Gradio redirect" + context: "Pinokio auto-provisions HTTPS via Caddy for all HTTP servers" + agent_hint: codex + + - id: net.binding.no-tailscale-serve + task: "No tailscale serve workaround active" + action: + type: shell + command: "tailscale serve status" + expect: "No active serve proxies (empty output or error)" + context: > + tailscale serve masks broken networking by proxying through localhost. + Direct access must work: curl http://:7860/gradio_api/info + agent_hint: codex + + # ========================================================================= + # Phase 1b: Pinokio Auto-Discovery Audit + # ========================================================================= + - id: net.pinokio-autodiscovery + task: "Pinokio LWW auto-discovery audit" + context: "LWW (LAN-Wide-Web) exposes ALL localhost TCP listeners via Caddy. Classify intentional vs noise." + agent_hint: codex + children: + - id: net.pinokio-autodiscovery.inventory + task: "All auto-discovered services classified" + action: + type: file_exists + target: "pmoves/configs/pinokio-network-inventory.yaml" + expect: "Inventory file exists with per-node service classification" + context: > + LWW auto-discovers Steam, Chrome DevTools, Razer, + Logitech, Docker, Surfshark, etc. These are noise, not risk. + The inventory file classifies each as intentional/noise/system. + agent_hint: codex + + - id: net.pinokio-autodiscovery.lww-port-range + task: "LWW share proxy port range documented (42003+)" + action: + type: manual + instruction: > + Query Caddy admin API: curl -s http://127.0.0.1:2019/config/apps/http/servers + Count share proxy servers, document port range. + expect: "42003-420xx ports auto-assigned by Pinokio LWW, one per discovered localhost listener" + context: > + Pinokio 7 LWW (LAN-Wide-Web) has no user-configurable port setting. + Caddy auto-assigns share proxy ports starting at 42003, one per + discovered localhost TCP listener. Port 42000 is Pinokio's own UI. + Firewall rules should allow TCP 42000-42100 range for cross-machine access. + Reference: PINOKIO.md Section 8 (LWW) and Section 9 (HTTPS domains). + agent_hint: codex + + - id: net.pinokio-autodiscovery.noise-count + task: "Noise service count documented per node" + action: + type: manual + instruction: > + Count non-PMOVES auto-discovered services on each node. + Cross-reference pmoves/configs/pinokio-network-inventory.yaml. + expect: > + 5090: ~7 noise services (Steam, Chrome, Razer, Logitech, Docker, Surfshark, NVIDIA). + Z890: ~1 noise service (Docker Desktop). + 4090: consumer node, accesses 5090/Z890 via Pinokio LWW network view. + context: > + Noise count helps track drift. If it grows significantly, + investigate whether new intentional services were installed + without being classified. + agent_hint: codex + + - id: net.pinokio-autodiscovery.no-filtering-fork + task: "Accepted: no Pinokio Caddy filtering (too costly)" + action: + type: manual + instruction: "Document decision: filtering requires forking Pinokio core Caddy logic" + expect: "Decision documented — accept noise, classify and monitor" + context: > + Filtering auto-discovered services would require modifying + Pinokio's core reverse proxy logic (Caddy). This is high + maintenance and fragile across Pinokio upgrades. The + accept-and-document approach is the correct trade-off. + agent_hint: codex + status: accepted_risk + + # ========================================================================= + # Phase 2: Docker Network Isolation + # ========================================================================= + - id: net.docker + task: "Docker network isolation" + context: "5-tier network segmentation in docker-compose.yml" + agent_hint: codex + children: + - id: net.docker.networks-exist + task: "All 5 internal networks defined with correct CIDRs" + action: + type: grep + target: "pmoves/docker-compose.yml" + pattern: "pmoves_(data|api|app|bus|monitoring)" + expect: "5 networks: pmoves_data, pmoves_api, pmoves_app, pmoves_bus, pmoves_monitoring" + context: "Each network uses internal: true with CIDR blocks 172.30.x.0/24" + agent_hint: codex + + - id: net.docker.data-tier-binding + task: "Data-tier services bind 127.0.0.1 not 0.0.0.0" + action: + type: grep + target: "pmoves/docker-compose.yml" + pattern: "127\\.0\\.0\\.1:.*(6333|7700|7474|9000)" + expect: "Qdrant(6333), Meilisearch(7700), Neo4j(7474), MinIO(9000) bind localhost" + context: "Data services should never be directly accessible from the network" + agent_hint: codex + + - id: net.docker.api-binding-documented + task: "API gateways intentionally bind 0.0.0.0 — documented exception" + action: + type: manual + instruction: "Verify Agent Zero (8080), TensorZero (3030) bind 0.0.0.0 intentionally" + expect: "Documented in CLAUDE.md or compose comments as intentional" + context: "These need external access; all other 0.0.0.0 bindings are suspect" + agent_hint: codex + + - id: net.docker.worker-audit + task: "Worker services audit — which need 0.0.0.0" + action: + type: manual + instruction: > + Audit these 9 services for 0.0.0.0 binding: + media workers, DeepResearch, SupaSerch, TensorZero, + ffmpeg-whisper, media-video, media-audio, extract-worker, langextract + expect: "Each either bound to 127.0.0.1 or documented exception" + context: "Workers that only communicate via NATS should not bind 0.0.0.0" + agent_hint: codex + + - id: net.docker.host-internal + task: "host.docker.internal resolves correctly" + action: + type: grep + target: "pmoves/docker-compose.yml" + pattern: "host.docker.internal" + expect: "extra_hosts entries consistent across all services needing host access" + context: "11+ services use host.docker.internal for Windows host access" + agent_hint: codex + + # ========================================================================= + # Phase 3: Service Health Chain + # ========================================================================= + - id: net.health + task: "Service health chain" + context: "NATS healthcheck → dependent services → application readiness" + agent_hint: codex + children: + - id: net.health.nats-healthcheck + task: "NATS healthcheck at :8222/varz returns 200" + action: + type: http + url: "http://localhost:8222/varz" + expect: "200 OK with JSON server info" + context: "10+ services depend on NATS being healthy" + agent_hint: codex + + - id: net.health.service-healthy-deps + task: "All service_healthy dependencies resolve" + action: + type: grep + target: "pmoves/docker-compose.yml" + pattern: "service_healthy" + expect: "No circular dependency chains" + context: "Dependencies must form a DAG — circular deps cause deadlock" + agent_hint: codex + + - id: net.health.start-period-gpu + task: "start_period adequate for GPU-heavy services" + action: + type: grep + target: "pmoves/docker-compose.yml" + pattern: "start_period" + expect: "GPU services have 30s+ start_period" + context: "GPU model loading can take 30-60s — short start_period causes false failures" + agent_hint: codex + + # ========================================================================= + # Phase 4: NATS Leaf Node (Cross-Machine) + # ========================================================================= + - id: net.nats-leaf + task: "NATS leaf node cross-machine connectivity" + context: "Z890 leaf → 5090 hub via leafnodes protocol" + agent_hint: codex + children: + - id: net.nats-leaf.config-exists + task: "Leaf node config exists" + action: + type: file_exists + target: "pmoves/configs/nats-leaf-z890.conf" + expect: "Z890 leaf node configuration present" + context: "Connects to hub via host.docker.internal:7422" + agent_hint: codex + + - id: net.nats-leaf.auth-not-hardcoded + task: "Leaf node credentials use env vars, not hardcoded" + action: + type: grep + target: "pmoves/configs/nats-leaf-z890.conf" + pattern: "nats:pmoves@" + expect: "WARN: credentials hardcoded — should use include or env var" + context: > + Current: nats://nats:pmoves@host.docker.internal:7422 + Ideal: use NATS include directive or env var substitution. + Acceptable for now (internal Tailscale network) but noted for hardening. + agent_hint: codex + status: known_issue + + - id: net.nats-leaf.portproxy + task: "Windows netsh portproxy forwards port 7422" + action: + type: shell + command: "netsh interface portproxy show v4tov4" + expect: "listenport=7422 → connectaddress=<5090-ip>" + context: "Routes Docker container traffic through Windows host to 5090 hub" + agent_hint: codex + + - id: net.nats-leaf.jetstream-replication + task: "JetStream replication across leaf" + action: + type: manual + instruction: "Publish on Z890, verify receipt on 5090" + expect: "Messages replicate bidirectionally" + context: "Leaf has local JetStream store (256MB mem, 1GB disk) for disconnect survival" + agent_hint: codex + + - id: net.nats-leaf.tls-status + task: "TLS status documented" + action: + type: grep + target: "pmoves/configs/nats-leaf-z890.conf" + pattern: "tls" + expect: "No TLS configured — acceptable on Tailscale (encrypted tunnel)" + context: > + Plaintext NATS over Tailscale is acceptable since Tailscale provides + WireGuard encryption. TLS would be defense-in-depth but not critical. + agent_hint: codex + status: accepted_risk + + # ========================================================================= + # Phase 5: Tailscale Mesh Connectivity + # ========================================================================= + - id: net.tailscale + task: "Tailscale mesh connectivity" + context: "3-node mesh: 5090, 4090 laptop, Z890" + agent_hint: codex + children: + - id: net.tailscale.nodes-online + task: "All nodes online with correct hostnames" + action: + type: shell + command: "tailscale status" + expect: > + powerfulmoves-1 (powerfulmoves-1.ts), + pmoves-laptop (pmoves-laptop.ts), + pmoves-z890 (pmoves-z890.ts) + context: "All three nodes should show as online" + agent_hint: codex + + - id: net.tailscale.acl-tags + task: "ACL policy permits tag-based access" + action: + type: grep + target: "pmoves/configs/tailscale-acl-policy.json" + pattern: "tag:pmoves" + expect: "Full mesh: tag:pmoves → tag:pmoves:* on all ports" + context: "All PMOVES nodes can reach each other on all ports" + agent_hint: codex + + - id: net.tailscale.subnet-routes + task: "Subnet routes advertised for Docker networks" + action: + type: shell + command: "tailscale status --json | jq '.Self.AllowedIPs'" + expect: "172.30.x.0/24 routes advertised (Docker internal networks)" + context: > + Without subnet routes, cross-machine containers can't reach each other. + Route advertisement must cover all Docker CIDR blocks. + agent_hint: codex + status: not_yet_configured + + - id: net.tailscale.lww-reachable + task: "Cross-machine Pinokio LWW reachable (port 42000)" + action: + type: http + url: "http://powerfulmoves-1.ts:42000/" + expect: "200 OK — Pinokio UI accessible, Network tab shows remote apps" + context: > + Cross-machine app access goes through Pinokio LWW on port 42000, + NOT direct HTTP to app ports. Caddy HTTPS proxy on 0.0.0.0:PORT + requires TLS SNI (hostname), so raw IP access to app ports fails. + Validation: open http://:42000 → Network tab → app. + Reference: PINOKIO.md Section 8 (LWW) and Section 9 (HTTPS domains). + agent_hint: codex + + - id: net.tailscale.magicdns + task: "MagicDNS resolution works" + action: + type: shell + command: "nslookup pmoves-z890.tailcad9b4.ts.net" + expect: "Resolves to pmoves-z890.ts" + context: "MagicDNS provides human-readable names for Tailscale IPs" + agent_hint: codex + + # ========================================================================= + # Phase 6: Headscale Coordination + # ========================================================================= + - id: net.headscale + task: "Headscale self-hosted coordination" + context: "PMOVES-Headscale submodule — replaces Tailscale cloud control plane" + agent_hint: codex + children: + - id: net.headscale.service-healthy + task: "Headscale service healthy on designated node" + action: + type: http + url: "https://headscale.pmoves.ai/health" + expect: "200 OK" + context: "Self-hosted control plane for the mesh" + agent_hint: codex + status: pending_deployment + + - id: net.headscale.tls-cert + task: "TLS certificate valid for headscale.pmoves.ai" + action: + type: manual + instruction: "Check TLS cert expiry and issuer for headscale.pmoves.ai" + expect: "Valid cert, auto-renewing (Let's Encrypt or Cloudflare Origin)" + context: "Control plane must be HTTPS — handles auth tokens and node registration" + agent_hint: codex + status: pending_deployment + + - id: net.headscale.nodes-registered + task: "All nodes registered and synced" + action: + type: manual + instruction: "headscale nodes list — verify 5090, Z890, 4090 registered" + expect: "All 3 nodes online with matching IPs" + context: "Headscale maintains the node registry and distributes WireGuard keys" + agent_hint: codex + status: pending_deployment + + - id: net.headscale.auth-tokens + task: "Auth tokens not expired" + action: + type: manual + instruction: "headscale apikeys list — check expiry dates" + expect: "No expired tokens; rotation schedule documented" + context: "Expired tokens cause silent node disconnection" + agent_hint: codex + status: pending_deployment + + # ========================================================================= + # Phase 7: Production Edge (VPS + Cloudflare) + # ========================================================================= + - id: net.edge + task: "Production VPS and Cloudflare edge" + context: "Cloudflare Worker → KVM nodes → services" + agent_hint: codex + children: + - id: net.edge.worker-routes + task: "Cloudflare Worker routing rules match TOPOLOGY.md" + action: + type: manual + instruction: "Compare deploy/cloudflare/worker.js routes with pmoves/docs/operations/TOPOLOGY.md" + expect: "All documented routes present in Worker config" + context: "Worker routes requests to correct KVM backend based on subdomain" + agent_hint: codex + + - id: net.edge.kvm-reachable + task: "KVM node services reachable via Cloudflare proxy" + action: + type: manual + instruction: "curl https://api.pmoves.ai/healthz via Cloudflare" + expect: "200 OK through full proxy chain" + context: "Traffic path: client → Cloudflare → Worker → KVM → service" + agent_hint: codex + + - id: net.edge.dns-subdomains + task: "DNS subdomains resolve to correct backends" + action: + type: manual + instruction: > + Verify DNS for: tts.pmoves.ai, api.pmoves.ai, agent.pmoves.ai, + rag.pmoves.ai, grafana.pmoves.ai, search.pmoves.ai + expect: "All resolve through Cloudflare proxy (orange cloud)" + context: "pmoves.ai zone pending Cloudflare migration per TOPOLOGY.md" + agent_hint: codex + status: pending_dns_migration + + - id: net.edge.no-direct-exposure + task: "No direct public IP exposure for internal services" + action: + type: manual + instruction: "Verify KVM public IPs not directly accessible on service ports" + expect: "All traffic flows through Cloudflare — no IP:port direct access" + context: "Direct exposure bypasses WAF, DDoS protection, and rate limiting" + agent_hint: codex diff --git a/pmoves/configs/tac_trees/node-4090-laptop.tac.yaml b/pmoves/configs/tac_trees/node-4090-laptop.tac.yaml new file mode 100644 index 0000000000..fe409e3e8e --- /dev/null +++ b/pmoves/configs/tac_trees/node-4090-laptop.tac.yaml @@ -0,0 +1,215 @@ +# TAC Tree: Node 4090 (Laptop) +# Per-node capability tree for the mobile relay / PR triage machine. +# 4090 laptop — consumer node, not a producer. Reaches 5090/Z890 via mesh. +# +# Cross-refs: +# pmoves/configs/tac_trees/networking-defense-in-depth.tac.yaml +# pmoves/configs/tac_trees/node-5090-powerfulmoves.tac.yaml +# pmoves/configs/tac_trees/node-z890-coordinator.tac.yaml +# pmoves/configs/tac_trees/pinokio-p7.tac.yaml + +name: "Node 4090 Laptop" +version: "1.0.0" +description: > + Per-node TAC tree for the 4090 laptop (pmoves-laptop). + Mobile relay node — consumer of 5090 TTS and Z890 infrastructure. + PR triage, code review, and lightweight agent work. + Hostname: pmoves-laptop | Tailscale: pmoves-laptop.ts | Role: Mobile Relay + +root: + id: n4090 + task: "4090 laptop node health and connectivity review" + context: "Consumer node — depends on 5090 (GPU) and Z890 (infra)" + agent_hint: 4090-claude + children: + + # ========================================================================= + # Phase 1: Pinokio P7 Status + # ========================================================================= + - id: n4090.pinokio + task: "Pinokio P7 status on 4090 laptop" + context: "Upgraded to P7, consumer not producer" + agent_hint: 4090-claude + children: + - id: n4090.pinokio.version + task: "Pinokio 7.x installed and functional" + action: + type: manual + instruction: "Verify Pinokio UI shows Agents tab, version >= 7.0.0" + expect: "P7 running, Agents tab visible" + context: > + Confirmed upgraded 2026-03-21. + Cross-ref: pinokio-p7.tac.yaml → p7.upgrade.4090 + agent_hint: 4090-claude + status: done + notes: "Upgraded 2026-03-21 — user confirmed manual Pinokio desktop upgrade" + + - id: n4090.pinokio.network-view + task: "Pinokio network view shows 5090 apps" + action: + type: manual + instruction: "Open Pinokio > Network tab, verify 5090 apps visible" + expect: > + Ultimate-TTS-Studio, Qwen3-TTS, and other 5090 apps + discoverable via Pinokio LWW + context: > + 4090 should discover 5090's Pinokio apps via the network. + Requires both machines on same LAN or Tailscale mesh with + Pinokio LWW on 5090. + agent_hint: 4090-claude + + # ========================================================================= + # Phase 2: Remote TTS Access + # ========================================================================= + - id: n4090.tts + task: "Remote TTS access to 5090" + context: "4090 consumes TTS from 5090 — no local TTS engines" + agent_hint: 4090-claude + children: + - id: n4090.tts.lww-access + task: "TTS reachable via Pinokio LWW network view" + action: + type: manual + instruction: > + Verify cross-machine TTS access via Pinokio LWW: + 1. Open http://powerfulmoves-1.ts:42000 from 4090 browser + 2. Navigate to Network tab, verify Ultimate-TTS-Studio appears + 3. Click to open — Gradio UI should load with 14 engine tabs + expect: "TTS accessible via Pinokio LWW network view on port 42000" + context: > + Direct HTTP to Tailscale IP on app ports does NOT work — Pinokio's + Caddy HTTPS proxy on 0.0.0.0:PORT requires TLS SNI (hostname, not IP). + Cross-machine access works through Pinokio LWW network view (port 42000). + Reference: PINOKIO.md Section 8 (LWW) and Section 9 (HTTPS domains). + agent_hint: 4090-claude + + - id: n4090.tts.pinokio-network + task: "TTS accessible via Pinokio network proxy" + action: + type: manual + instruction: > + Open Ultimate-TTS-Studio from Pinokio network view on 4090. + Verify Gradio UI loads and synthesis works. + expect: "Full TTS functionality via Pinokio network proxy from 4090" + context: > + Pinokio network view proxies remote apps via Caddy. + The URL will be something like + Ultimate-TTS-Studio.git.pmoves-net.localhost + agent_hint: 4090-claude + + - id: n4090.tts.no-tailscale-serve + task: "No tailscale serve workaround active" + action: + type: shell + command: "tailscale serve status" + expect: "No active serve proxies — direct access only" + context: > + tailscale serve is a workaround that masks broken networking. + Cross-ref: networking-defense-in-depth.tac.yaml → net.binding.no-tailscale-serve + agent_hint: 4090-claude + + # ========================================================================= + # Phase 3: PR Triage Role + # ========================================================================= + - id: n4090.pr-triage + task: "PR triage and code review capability" + context: "4090 laptop is the primary PR triage machine" + agent_hint: 4090-claude + children: + - id: n4090.pr-triage.gh-cli + task: "GitHub CLI (gh) authenticated and functional" + action: + type: shell + command: "gh auth status" + expect: "Logged in to github.com as POWERFULMOVES" + agent_hint: 4090-claude + + - id: n4090.pr-triage.skills + task: "/pr-trim and /pr-monitor skills available" + action: + type: manual + instruction: "Run /pr-monitor in Claude Code to verify PR state collection" + expect: "PR state report generated with actionable counts" + context: > + PR review workflow: /pr-monitor → /pr-trim → /chit:review-sweep. + See CLAUDE.md PR Review & Merge Workflow section. + agent_hint: 4090-claude + + - id: n4090.pr-triage.worktrees + task: "Git worktrees functional for parallel PR review" + action: + type: shell + command: "git worktree list" + expect: "Main worktree listed, ability to create feature worktrees" + context: "Worktrees enable parallel branch work without full clone" + agent_hint: 4090-claude + + # ========================================================================= + # Phase 4: Tailscale Mesh Connectivity + # ========================================================================= + - id: n4090.mesh + task: "Tailscale mesh connectivity to 5090 and Z890" + context: "All 3 nodes must be reachable for full fleet operation" + agent_hint: 4090-claude + children: + - id: n4090.mesh.status + task: "pmoves-laptop online in Tailscale" + action: + type: shell + command: "tailscale status" + expect: "pmoves-laptop online at pmoves-laptop.ts" + agent_hint: 4090-claude + + - id: n4090.mesh.reach-5090 + task: "Can reach 5090 (POWERFULMOVES)" + action: + type: shell + command: "tailscale ping powerfulmoves-1.ts --timeout 5s --c 1" + expect: "Pong from powerfulmoves-1.ts via direct connection" + context: "Direct connection preferred over DERP relay" + agent_hint: 4090-claude + + - id: n4090.mesh.reach-z890 + task: "Can reach Z890 (pmoves-z890)" + action: + type: shell + command: "tailscale ping pmoves-z890.ts --timeout 5s --c 1" + expect: "Pong from pmoves-z890.ts via direct connection" + agent_hint: 4090-claude + + - id: n4090.mesh.z890-services + task: "Z890 key services reachable" + action: + type: manual + instruction: > + Verify these Z890 services reachable from 4090: + - NATS: pmoves-z890.ts:4222 + - Grafana: pmoves-z890.ts:3000 + - Agent Zero: pmoves-z890.ts:8080 + expect: "All Z890 services accessible via Tailscale IP" + agent_hint: 4090-claude + + # ========================================================================= + # Phase 5: NATS Announcement + # ========================================================================= + - id: n4090.announce + task: "4090 NATS capability announcement" + context: "Lightweight node — PR triage, code review, mobile access" + agent_hint: 4090-claude + children: + - id: n4090.announce.subject + task: "Publishes mesh.agent.4090.capabilities.v1" + action: + type: manual + instruction: > + Verify mesh agent publishes capability announcement. + Payload should include: hostname (pmoves-laptop), + role (mobile-relay), capabilities (pr-triage, code-review), + no GPU, no TTS, consumer-only. + expect: "NATS message received with 4090 relay capabilities" + context: > + 4090 is a noise-reducer and PR triage node. Its announcement + should NOT claim GPU or TTS capabilities. It consumes from + 5090 (TTS/GPU) and Z890 (infra/NATS/monitoring). + agent_hint: 4090-claude + status: future diff --git a/pmoves/configs/tac_trees/node-5090-powerfulmoves.tac.yaml b/pmoves/configs/tac_trees/node-5090-powerfulmoves.tac.yaml new file mode 100644 index 0000000000..033d1382a3 --- /dev/null +++ b/pmoves/configs/tac_trees/node-5090-powerfulmoves.tac.yaml @@ -0,0 +1,342 @@ +# TAC Tree: Node 5090 (POWERFULMOVES) +# Per-node capability tree for the primary GPU workstation. +# RTX 5090 32GB — canonical TTS host, Pinokio native, 18 apps installed. +# +# Key paths: +# D:\pinokio\api\ (Pinokio app root) +# D:\pinokio\api\Ultimate-TTS-Studio.git\ (14-engine TTS hub) +# D:\pinokio\api\Qwen3-TTS-Pinokio.git\ (Qwen3 standalone) +# D:\pinokio\api\vibevoice-realtime.git\ (WebSocket streaming TTS) +# D:\pinokio\api\VoxForge-Pro.git\ (PDF-to-audiobook pipeline) +# Cross-refs: +# pmoves/configs/tac_trees/networking-defense-in-depth.tac.yaml +# pmoves/configs/tac_trees/pinokio-p7.tac.yaml +# pmoves/configs/tts-engine-capabilities.yaml + +name: "Node 5090 POWERFULMOVES" +version: "1.0.0" +description: > + Per-node TAC tree for the 5090 (POWERFULMOVES) GPU workstation. + RTX 5090 32GB VRAM. Primary TTS host, Pinokio native runtime. + 18 Pinokio apps installed (5 TTS-related, 13 non-TTS). + Hostname: POWERFULMOVES | Tailscale: powerfulmoves-1.ts | Role: Primary GPU / TTS + +root: + id: n5090 + task: "5090 POWERFULMOVES node health and capability review" + context: "D:\\pinokio\\api\\ — 18 apps, RTX 5090 32GB" + agent_hint: 5090-claude + children: + + # ========================================================================= + # Phase 1: Pinokio App Inventory + # ========================================================================= + - id: n5090.inventory + task: "Pinokio app inventory — 18 apps enumerated" + context: "D:\\pinokio\\api\\ — all installed Pinokio apps" + agent_hint: 5090-claude + children: + - id: n5090.inventory.tts-apps + task: "TTS apps classified (5 total, 4 kept, 1 removed)" + action: + type: manual + instruction: "Verify 5 TTS-related apps in D:\\pinokio\\api\\" + expect: > + KEEP: Ultimate-TTS-Studio.git (14 engines, primary hub), + Qwen3-TTS-Pinokio.git (VoiceDesign mode, 9 presets), + vibevoice-realtime.git (WebSocket streaming), + VoxForge-Pro.git (PDF-to-audiobook pipeline). + REMOVED: vibevoice-realtime2.git (exact duplicate of v1) + context: > + Overlap audit completed 2026-03-22. Each kept app has unique value: + Ultimate-TTS = hub, Qwen3 = VoiceDesign, VibeVoice-RT = streaming, + VoxForge = audiobook pipeline. vibevoice-realtime2 is byte-for-byte + duplicate of v1 with only a pinokio.json version difference. + agent_hint: 5090-claude + + - id: n5090.inventory.non-tts-apps + task: "Non-TTS apps enumerated (13 apps)" + action: + type: manual + instruction: "Verify non-TTS apps are functional" + expect: > + ace-step.pinokio.git, ace-step-ui.pinokio.git (music gen), + clawdbot.git (Claude agent), cropper.git (image crop), + discord-id-bypass-tool.pinokio.git, heartmula-studio.git, + hermes-agent.pinokio.git, LightOnOCR-2-1B-Pinokio.git, + N8N-Pinokio.git, sillytavern.git, SongGeneration-Studio.git, + unsloth.git (fine-tuning), wan.git (video gen) + agent_hint: 5090-claude + + - id: n5090.inventory.duplicate-removed + task: "vibevoice-realtime2.git removed (duplicate)" + action: + type: file_exists + target: "D:/pinokio/api/vibevoice-realtime2.git" + expect: "Directory removed — saves ~2-3 GB disk and eliminates confusion" + context: > + vibevoice-realtime2 is an exact duplicate of vibevoice-realtime with + only a pinokio.json version difference. No unique features. + agent_hint: 5090-claude + status: pending + + # ========================================================================= + # Phase 2: TTS Engine Health + # ========================================================================= + - id: n5090.tts-health + task: "TTS engine health verification" + context: "All TTS apps on 5090 — verify startup, port, and API response" + agent_hint: 5090-claude + children: + - id: n5090.tts-health.ultimate + task: "Ultimate-TTS-Studio healthy at :7860" + action: + type: http + url: "http://localhost:7860/gradio_api/info" + expect: "200 OK with JSON describing 14 engine tabs" + context: > + Primary TTS hub. 14 engines: Kokoro, F5-TTS, KittenTTS, VoxCPM, + Higgs Audio, Fish Speech S2 Pro, Chatterbox Turbo, Chatterbox + Multilingual, Qwen3 TTS, IndexTTS, IndexTTS2, VibeVoice, + Fish Speech, ChatterboxTTS. Port 7860 (native Pinokio). + agent_hint: 5090-claude + + - id: n5090.tts-health.qwen3 + task: "Qwen3-TTS-Pinokio healthy at dynamic port" + action: + type: manual + instruction: "Start Qwen3-TTS via Pinokio, verify Gradio UI loads" + expect: "Gradio UI at dynamic port, 3 modes: text-to-speech, VoiceDesign, voice-to-voice" + context: > + Uses Gradio's auto-port assignment. start.json capture group + extracts URL. Unique value: VoiceDesign mode (text→voice persona) + + 9 speaker presets + fine-grained Qwen3 model management. + agent_hint: 5090-claude + + - id: n5090.tts-health.vibevoice-rt + task: "VibeVoice Realtime healthy at dynamic port" + action: + type: manual + instruction: "Start vibevoice-realtime via Pinokio, verify WebSocket endpoint" + expect: "Uvicorn WebSocket server running (NOT Gradio)" + context: > + Different architecture from Ultimate-TTS VibeVoice tab. + Streams audio via WebSocket, uses uvicorn not Gradio. + Unique value: real-time streaming synthesis. + agent_hint: 5090-claude + + - id: n5090.tts-health.voxforge + task: "VoxForge Pro healthy at dynamic port" + action: + type: manual + instruction: "Start VoxForge via Pinokio, verify audiobook pipeline UI" + expect: "Gradio UI with PDF upload, chapter detection, voice selection" + context: > + PDF-to-audiobook pipeline. Uses Kokoro + Chatterbox as backend. + Engines overlap with Ultimate-TTS but use case is distinct: + long-form audiobook generation with chapter detection and OCR. + agent_hint: 5090-claude + + # ========================================================================= + # Phase 3: Network Security + # ========================================================================= + - id: n5090.network + task: "Pinokio network security on 5090" + context: "LWW (LAN-Wide-Web) exposes all localhost TCP listeners via Caddy" + agent_hint: 5090-claude + children: + - id: n5090.network.autodiscovery + task: "Auto-discovery audit — intentional vs noise services" + action: + type: manual + instruction: > + Check Pinokio network view. Cross-reference with + pmoves/configs/pinokio-network-inventory.yaml. + Classify each discovered service. + expect: "All services classified as intentional or noise" + context: > + LWW auto-discovers ALL localhost TCP listeners. + Steam, Chrome DevTools, Razer, Docker, etc. get exposed. + These are noise (no API response) not risk. Cross-ref: + networking-defense-in-depth.tac.yaml Phase 1b. + agent_hint: 5090-claude + + - id: n5090.network.gradio-binding + task: "GRADIO_SERVER_NAME not hardcoded to 127.0.0.1" + action: + type: grep + target: "D:/pinokio/api/Ultimate-TTS-Studio.git/start.js" + pattern: "GRADIO_SERVER_NAME" + expect: "Not present OR set dynamically — Pinokio manages via LWW" + context: > + Cross-ref: networking-defense-in-depth.tac.yaml → net.binding.gradio-server-name. + Pinokio LWW sets GRADIO_SERVER_NAME=0.0.0.0 automatically. + agent_hint: 5090-claude + + - id: n5090.network.start-js-pattern + task: "All TTS start scripts use correct capture group pattern" + action: + type: shell + command: "grep -r 'input.event\\[0\\]' D:/pinokio/api/*/start.* 2>/dev/null" + expect: "No matches — all apps use input.event[1] with capture group" + context: > + Critical Pattern Lock: regex must have capture parentheses, + local.set must use input.event[1] not [0]. + Fixed in this session: Qwen3-TTS-Pinokio, VoxForge-Pro. + Already correct: Ultimate-TTS-Studio. + agent_hint: 5090-claude + + # ========================================================================= + # Phase 4: Docker Services on 5090 + # ========================================================================= + - id: n5090.docker + task: "Docker services running on 5090" + context: "Ollama, ffmpeg-whisper, and other GPU-dependent containers" + agent_hint: 5090-claude + children: + - id: n5090.docker.ollama + task: "Ollama container healthy at :11434" + action: + type: http + url: "http://localhost:11434/api/tags" + expect: "200 OK with model list" + context: > + GOTCHA: TLS x509 errors from Windows SSL env leak. GPU needs + deploy block in compose. Host port 11434 to avoid collision. + See memory: feedback_ollama_container_gotchas.md + agent_hint: 5090-claude + + - id: n5090.docker.whisper + task: "ffmpeg-whisper transcription service" + action: + type: http + url: "http://localhost:8078/healthz" + expect: "200 OK — Faster-Whisper with GPU acceleration" + context: "Media transcription service, Whisper small model" + agent_hint: 5090-claude + + - id: n5090.docker.yt + task: "PMOVES.YT ingestion at :8077" + action: + type: http + url: "http://localhost:8077/healthz" + expect: "200 OK" + context: "YouTube ingestion, downloads to MinIO, publishes NATS events" + agent_hint: 5090-claude + + # ========================================================================= + # Phase 5: GPU VRAM Budget + # ========================================================================= + - id: n5090.vram + task: "RTX 5090 32GB VRAM allocation" + context: "Multiple TTS engines and Docker GPU services compete for VRAM" + agent_hint: 5090-claude + children: + - id: n5090.vram.baseline + task: "Baseline VRAM usage with no TTS running" + action: + type: shell + command: "nvidia-smi --query-gpu=memory.used,memory.total --format=csv,noheader" + expect: "Baseline < 4GB (system + Ollama idle)" + context: "RTX 5090 = 32768 MB total. Baseline includes driver overhead." + agent_hint: 5090-claude + + - id: n5090.vram.concurrent-limit + task: "Max concurrent TTS engines documented" + action: + type: manual + instruction: > + Cross-reference pmoves/configs/tts-engine-capabilities.yaml + VRAM requirements per engine. Document which combos fit in 32GB. + expect: > + Kokoro (2GB) + Fish S2 (4GB) + IndexTTS (6GB) = 12GB → fits. + All 14 engines simultaneously: does NOT fit. + Document safe concurrent loading combos. + context: > + VRAM is the primary constraint for multi-engine TTS. + The GPU orchestrator should enforce limits based on + tts-engine-capabilities.yaml data. + agent_hint: 5090-claude + + # ========================================================================= + # Phase 6: Cross-Machine Connectivity + # ========================================================================= + - id: n5090.cross-machine + task: "5090 cross-machine connectivity" + context: "Consumes NATS from Z890, serves TTS to mesh, Tailscale link" + agent_hint: 5090-claude + children: + - id: n5090.cross-machine.nats + task: "NATS connectivity to Z890 hub" + action: + type: manual + instruction: "Verify NATS messages from Z890 are received on 5090" + expect: "Bidirectional NATS messaging across Tailscale mesh" + context: > + Z890 runs the NATS hub. 5090 connects as leaf node or direct client. + Cross-ref: networking-defense-in-depth.tac.yaml → net.nats-leaf + agent_hint: 5090-claude + + - id: n5090.cross-machine.tensorzero + task: "TensorZero on Z890 reachable from 5090" + action: + type: http + url: "http://pmoves-z890.ts:3030/v1/models" + expect: "200 OK — TensorZero model list from Z890" + context: "5090 uses Z890's TensorZero for LLM routing. Port 3030 = host port." + agent_hint: 5090-claude + + - id: n5090.cross-machine.tts-served + task: "5090 TTS reachable from Z890 and 4090 via Pinokio LWW" + action: + type: manual + instruction: > + Verify cross-machine TTS access via Pinokio LWW: + 1. HTTPS proxy (local): curl -sk https://7860.localhost/gradio_api/info + 2. Pinokio network view: open http://powerfulmoves-1.ts:42000 from remote node, + navigate to Network tab, click Ultimate-TTS-Studio + 3. pterm (local): pterm status Ultimate-TTS-Studio.git — check ready_url + expect: "All paths confirm TTS accessible with 71 named endpoints" + context: > + Direct HTTP to Tailscale IP on app ports does NOT work — Pinokio's + Caddy HTTPS proxy on 0.0.0.0:PORT requires TLS SNI (hostname, not IP). + Cross-machine access works through Pinokio LWW network view (port 42000) + or HTTPS proxy with hostname (https://7860.localhost). + Reference: PINOKIO.md Section 8 (LWW) and Section 9 (HTTPS domains). + agent_hint: 5090-claude + + # ========================================================================= + # Phase 7: NATS Announcement + # ========================================================================= + - id: n5090.announce + task: "5090 NATS capability announcement" + context: "Mesh agent announces node presence and capabilities" + agent_hint: 5090-claude + children: + - id: n5090.announce.subject + task: "Publishes mesh.agent.5090.capabilities.v1" + action: + type: manual + instruction: > + Verify mesh agent publishes capability announcement to NATS. + Payload should include: hostname, GPU (RTX 5090), VRAM (32GB), + TTS engines available, Pinokio app count. + expect: "NATS message received on Z890 with 5090 capabilities" + context: > + Mesh Agent announces every 15s. The announcement should include + node-specific capabilities so the orchestrator can route + TTS requests to the correct machine. + agent_hint: 5090-claude + status: future + + - id: n5090.announce.tts-registration + task: "TTS endpoint registered in service mesh" + action: + type: manual + instruction: "Verify TTS endpoint URL appears in mesh registry" + expect: "powerfulmoves-1.ts:7860 registered as canonical TTS endpoint" + context: > + Flute Gateway and Agent Zero should discover 5090's TTS + endpoint via NATS announcements, not hardcoded URLs. + agent_hint: 5090-claude + status: future diff --git a/pmoves/configs/tac_trees/node-z890-coordinator.tac.yaml b/pmoves/configs/tac_trees/node-z890-coordinator.tac.yaml new file mode 100644 index 0000000000..7334007750 --- /dev/null +++ b/pmoves/configs/tac_trees/node-z890-coordinator.tac.yaml @@ -0,0 +1,303 @@ +# TAC Tree: Node Z890 (Coordinator) +# Per-node capability tree for the infrastructure coordinator workstation. +# Z890 runs Docker Compose (66+ services), NATS hub, and delegates TTS to 5090. +# +# Key paths: +# pmoves/docker-compose.yml (main compose stack) +# pmoves/configs/ (all config files) +# Cross-refs: +# pmoves/configs/tac_trees/networking-defense-in-depth.tac.yaml +# pmoves/configs/tac_trees/node-5090-powerfulmoves.tac.yaml +# pmoves/configs/tac_trees/pinokio-p7.tac.yaml + +name: "Node Z890 Coordinator" +version: "1.0.0" +description: > + Per-node TAC tree for Z890 (pmoves-z890) infrastructure coordinator. + Runs Docker Compose (~66 services), NATS hub, Supabase, monitoring stack. + Does NOT run TTS — delegates to 5090 via Tailscale mesh. + Hostname: pmoves-z890 | Tailscale: pmoves-z890.ts | Role: Infra Coordinator + +root: + id: nz890 + task: "Z890 coordinator node health and capability review" + context: "pmoves/docker-compose.yml — ~66 services, infra coordinator" + agent_hint: z890-claude + children: + + # ========================================================================= + # Phase 1: Docker Compose Health + # ========================================================================= + - id: nz890.compose + task: "Docker Compose stack health (~66 services)" + context: "pmoves/docker-compose.yml with tier+hardening anchors" + agent_hint: z890-claude + children: + - id: nz890.compose.core-infra + task: "Core infrastructure services healthy" + action: + type: shell + command: "docker compose -f pmoves/docker-compose.yml ps --format json | jq -r 'select(.Health==\"healthy\") | .Name'" + expect: > + nats, supabase-db, supabase-kong, qdrant, meilisearch, + neo4j, minio — all showing healthy + context: > + Core infra must be healthy before any dependent services. + NATS is the most critical — 10+ services depend on service_healthy. + agent_hint: z890-claude + + - id: nz890.compose.agent-profile + task: "Agent profile services healthy" + action: + type: shell + command: "docker compose --profile agents ps" + expect: "agent-zero, archon, mesh-agent all running" + context: "Agent Zero (8080), Archon (8091), Mesh Agent (no HTTP)" + agent_hint: z890-claude + + - id: nz890.compose.worker-profile + task: "Worker profile services healthy" + action: + type: shell + command: "docker compose --profile workers ps" + expect: "extract-worker, langextract, media analyzers running" + context: "Extract Worker (8083), LangExtract (8084), media workers" + agent_hint: z890-claude + + - id: nz890.compose.monitoring + task: "Monitoring stack healthy" + action: + type: shell + command: "docker compose --profile monitoring ps" + expect: "prometheus, grafana, loki, promtail, cadvisor running" + context: "Prometheus (9090), Grafana (3000), Loki (3100)" + agent_hint: z890-claude + + - id: nz890.compose.container-count + task: "Total running containers matches expected count" + action: + type: shell + command: "docker ps --format '{{.Names}}' | wc -l" + expect: ">= 60 containers running (of ~66 defined)" + context: > + As of 2026-03-16: 92 containers, 63 healthy. Some services + are profile-gated and may not always be running. + agent_hint: z890-claude + + # ========================================================================= + # Phase 2: NATS Hub Status + # ========================================================================= + - id: nz890.nats + task: "NATS hub status and JetStream" + context: "Z890 runs the primary NATS hub for the mesh" + agent_hint: z890-claude + children: + - id: nz890.nats.healthy + task: "NATS healthcheck at :8222/varz" + action: + type: http + url: "http://localhost:8222/varz" + expect: "200 OK with server info, JetStream enabled" + context: "Primary message bus. Auth: nats://nats:pmoves@nats:4222" + agent_hint: z890-claude + + - id: nz890.nats.jetstream + task: "JetStream enabled with adequate storage" + action: + type: http + url: "http://localhost:8222/jsz" + expect: "JetStream active, streams and consumers listed" + context: "JetStream provides persistent messaging for reliable delivery" + agent_hint: z890-claude + + - id: nz890.nats.leaf-config + task: "Leaf node configuration for 5090 connectivity" + action: + type: file_exists + target: "pmoves/configs/nats-leaf-z890.conf" + expect: "Leaf node config exists, points to 5090 hub" + context: > + Cross-ref: networking-defense-in-depth.tac.yaml → net.nats-leaf. + Z890 connects to 5090 NATS via leaf node protocol. + agent_hint: z890-claude + + - id: nz890.nats.websocket + task: "WebSocket ports correctly mapped" + action: + type: manual + instruction: "Verify WS ports: 9222 (standalone DoX), 9223 (docked via compose)" + expect: "Both WebSocket ports accessible for browser-based NATS clients" + context: "DoX and A2UI use WebSocket NATS connections" + agent_hint: z890-claude + + # ========================================================================= + # Phase 3: Pinokio TTS Delegation + # ========================================================================= + - id: nz890.tts-delegation + task: "Z890 delegates TTS to 5090 — no local TTS apps" + context: > + Z890 had 3 redundant TTS apps (OpenAudio, Qwen3-TTS, Ultimate-TTS-Studio). + These should be removed — 5090 is the canonical TTS host. + agent_hint: z890-claude + children: + - id: nz890.tts-delegation.apps-removed + task: "All 3 TTS apps removed from Z890 Pinokio" + action: + type: manual + instruction: > + Verify no TTS apps in Z890's Pinokio network view: + - OpenAudio (Fish Speech) — REMOVED + - Qwen3-TTS — REMOVED + - Ultimate-TTS-Studio SUP3R Edition — REMOVED + expect: "Zero TTS apps installed on Z890. TTS comes from 5090 via mesh." + context: > + Cross-machine duplication audit 2026-03-22. 5090 (RTX 5090 GPU) + is the canonical TTS host. Z890 has no GPU suitable for TTS. + Removing these saves disk and eliminates confusion. + agent_hint: z890-claude + status: pending + + - id: nz890.tts-delegation.mesh-reachable + task: "5090 TTS reachable from Z890 via Pinokio LWW" + action: + type: manual + instruction: > + Verify cross-machine TTS access via Pinokio LWW: + 1. Open http://powerfulmoves-1.ts:42000 from Z890 browser + 2. Navigate to Network tab, verify Ultimate-TTS-Studio appears + 3. Click to open — Gradio UI should load with 14 engine tabs + expect: "TTS accessible via Pinokio LWW network view on port 42000" + context: > + Direct HTTP to Tailscale IP on app ports does NOT work — Pinokio's + Caddy HTTPS proxy on 0.0.0.0:PORT requires TLS SNI (hostname, not IP). + Cross-machine access works through Pinokio LWW network view (port 42000). + Reference: PINOKIO.md Section 8 (LWW) and Section 9 (HTTPS domains). + agent_hint: z890-claude + + - id: nz890.tts-delegation.flute-bridge + task: "Flute Gateway routes to 5090 TTS" + action: + type: http + url: "http://localhost:8055/healthz" + expect: "200 OK, ultimate_tts endpoint configured to powerfulmoves-1.ts:7860" + context: > + Flute Gateway (port 8055) on Z890 should route TTS synthesis + requests to 5090's Ultimate-TTS-Studio via Tailscale mesh. + agent_hint: z890-claude + + # ========================================================================= + # Phase 4: CI Runner + # ========================================================================= + - id: nz890.ci + task: "CI runner container healthy" + context: "myoung34/github-runner with self-hosted, ai-lab labels" + agent_hint: z890-claude + children: + - id: nz890.ci.runner-up + task: "GitHub runner container running" + action: + type: shell + command: "docker ps --filter name=runner --format '{{.Names}} {{.Status}}'" + expect: "Runner container Up, healthy" + context: > + Started via make ci-runners-local-cert-up. Uses unless-stopped + restart policy. Needs restart after machine reboot. + agent_hint: z890-claude + + - id: nz890.ci.labels + task: "Runner registered with correct labels" + action: + type: manual + instruction: "Check GitHub Settings > Actions > Runners for ai-lab labels" + expect: "Labels: self-hosted, ai-lab (for GPU workflows)" + agent_hint: z890-claude + + - id: nz890.ci.volume-mount + task: "Secrets volume mounted for secrets-sync" + action: + type: manual + instruction: "Verify $APPDATA/pmoves mounted in runner container" + expect: "Volume mount present for secrets-sync-trigger workflow" + context: > + Containerized runners need host volume mount for secrets persistence. + See memory: feedback_secrets_sync_runner_gap.md + agent_hint: z890-claude + + # ========================================================================= + # Phase 5: Cross-Machine Connectivity + # ========================================================================= + - id: nz890.cross-machine + task: "Z890 cross-machine connectivity to 5090 and 4090" + context: "Tailscale mesh, port forwarding, service discovery" + agent_hint: z890-claude + children: + - id: nz890.cross-machine.tts-5090 + task: "TTS on 5090 reachable via Pinokio LWW" + action: + type: manual + instruction: > + Open http://powerfulmoves-1.ts:42000 from Z890 browser. + Navigate to Network tab, verify Ultimate-TTS-Studio is listed. + expect: "5090 TTS visible in Pinokio LWW network view" + context: > + Cross-machine access uses Pinokio LWW on port 42000, not direct + app port access. Caddy HTTPS proxy on app ports requires TLS SNI. + agent_hint: z890-claude + + - id: nz890.cross-machine.ollama-5090 + task: "Ollama on 5090 reachable at powerfulmoves-1.ts:11434" + action: + type: http + url: "http://powerfulmoves-1.ts:11434/api/tags" + expect: "200 OK — model list from 5090's Ollama" + context: "GPU inference delegation to 5090" + agent_hint: z890-claude + + - id: nz890.cross-machine.portproxy + task: "Windows netsh portproxy rules active" + action: + type: shell + command: "netsh interface portproxy show v4tov4" + expect: "Port 7422 → 5090 NATS leaf, other forwarding rules" + context: > + Routes Docker container traffic through Windows host to 5090. + Cross-ref: networking-defense-in-depth.tac.yaml → net.nats-leaf.portproxy + agent_hint: z890-claude + + # ========================================================================= + # Phase 6: NATS Announcement + # ========================================================================= + - id: nz890.announce + task: "Z890 NATS capability announcement" + context: "Mesh agent announces coordinator role and capabilities" + agent_hint: z890-claude + children: + - id: nz890.announce.subject + task: "Publishes mesh.agent.z890.capabilities.v1" + action: + type: manual + instruction: > + Verify mesh agent publishes capability announcement. + Payload should include: hostname (pmoves-z890), role (coordinator), + services running (~66), NATS hub, Docker Compose, no GPU. + expect: "NATS message received with Z890 coordinator capabilities" + context: > + Z890 is the infrastructure coordinator. Its announcement + should reflect: NATS hub, Docker Compose orchestrator, + CI runner, monitoring stack — NOT GPU/TTS capabilities. + agent_hint: z890-claude + status: future + + - id: nz890.announce.coordinator-role + task: "Coordinator role includes TTS delegation metadata" + action: + type: manual + instruction: > + Announcement payload should include: + tts_delegate: { node: "5090", endpoint: "powerfulmoves-1.ts:7860" } + expect: "Other agents know Z890 delegates TTS to 5090" + context: > + When agents on Z890 need TTS, the coordinator announcement + tells them to route to 5090 instead of looking locally. + agent_hint: z890-claude + status: future diff --git a/pmoves/docs/PINOKIO_EXAMPLE_MANIFESTS.md b/pmoves/docs/PINOKIO_EXAMPLE_MANIFESTS.md new file mode 100644 index 0000000000..31f82fe5bb --- /dev/null +++ b/pmoves/docs/PINOKIO_EXAMPLE_MANIFESTS.md @@ -0,0 +1,1241 @@ +# PMOVES Pinokio Example Manifests + +> **Last Updated:** 2026-03-22 +> **Related:** [PINOKIO_PACKAGING_GUIDE.md](./PINOKIO_PACKAGING_GUIDE.md) + +This document provides complete, copy-paste ready Pinokio manifest examples for various PMOVES agent types. + +--- + +## Table of Contents + +1. [Agent Zero - Full Agent Runtime](#example-1-agent-zero---full-agent-runtime) +2. [Hi-RAG v2 - API/Worker Service](#example-2-hi-rag-v2---apiworker-service) +3. [Ultimate-TTS - GPU Media Service](#example-3-ultimate-tts---gpu-media-service) +4. [PMOVES Services - Multi-Profile Stack](#example-4-pmoves-services---multi-profile-stack) +5. [Crush - UI-Only App](#example-5-crush---ui-only-app) +6. [Cipher Beats Analyst - CLI Tool](#example-6-cipher-beats-analyst---cli-tool) + +--- + +## Example 1: Agent Zero - Full Agent Runtime + +**Agent Type:** Tier 6+2 (Agent+API) +**GPU:** Optional +**Port:** 8080 + +### Folder Structure + +``` +pmoves-agent-zero/ +├── pinokio/ +│ ├── install.js +│ ├── start.js +│ ├── reset.js +│ ├── update.js +│ └── pinokio.js +├── pinokio.json +├── README.md +└── icon.png +``` + +### pinokio.json + +```json +{ + "version": "1", + "title": "PMOVES Agent Zero", + "description": "Primary L1 orchestrator with embedded agent runtime and MCP API. Autonomous task execution with memory persistence, multi-LLM support, and 100+ tools via MCP discovery.", + "icon": "icon.png", + "platform": ["win32", "darwin", "linux"], + "arch": ["x64", "arm64"], + "gpu": "optional", + "author": "POWERFULMOVES", + "repo": "https://github.com/POWERFULMOVES/PMOVES-Agent-Zero", + "homepage": "https://pmoves.ai", + "pmoves_version": "1.4.0", + "keywords": ["agent", "orchestrator", "mcp", "llm", "pmoves", "autonomous"] +} +``` + +### pinokio/pinokio.js + +```javascript +// pinokio/pinokio.js +// Dynamic menu for PMOVES Agent Zero + +const installed = info.exists("app/.installed") +const running = info.running("start.js") +const url = info.local("start.js", "url") + +module.exports = { + run: !installed ? [ + { method: "script", params: { uri: "install.js" } } + ] : [], + menu: [ + { + id: "install", + title: "Install", + description: "Clone and install Agent Zero", + script: "install.js", + default: !installed + }, + { + id: "start", + title: "Start Agent", + description: "Launch Agent Zero runtime", + script: "start.js", + running: running, + default: installed && !running + }, + { + id: "api", + title: "Open API (Swagger)", + href: url ? `${url}/docs` : "http://localhost:8080/docs", + default: running + }, + { + id: "mcp", + title: "MCP Endpoint", + href: url ? `${url}/mcp` : "http://localhost:8080/mcp", + disabled: !running + }, + { + id: "health", + title: "Health Check", + href: url ? `${url}/healthz` : "http://localhost:8080/healthz", + disabled: !running + }, + // Divider + { type: "divider", title: "Maintenance" }, + { + id: "update", + title: "Update", + description: "Pull latest changes", + script: "update.js", + disabled: !installed + }, + { + id: "reset", + title: "Reset", + description: "Clean installation", + script: "reset.js", + disabled: running + } + ] +} +``` + +### pinokio/install.js + +```javascript +// pinokio/install.js +// Installation script for PMOVES Agent Zero + +module.exports = { + run: [ + // Clean previous installation + { + method: "fs.rm", + params: { path: "app" } + }, + + // Clone repository + { + method: "git.clone", + params: { + url: "https://github.com/POWERFULMOVES/PMOVES-Agent-Zero", + path: "app", + branch: "main" + } + }, + + // Install Python dependencies + { + method: "shell.run", + params: { + path: "app", + message: platform === "win32" + ? "pip install -r requirements.txt" + : "pip3 install -r requirements.txt" + } + }, + + // Create example environment file + { + method: "fs.write", + params: { + path: "app/.env.example", + text: ` +# ============================================================================= +# PMOVES Agent Zero Configuration +# ============================================================================= +# Copy this file to .env and fill in your values + +# ----------------------------------------------------------------------------- +# LLM Providers (at least one required) +# ----------------------------------------------------------------------------- +OPENAI_API_KEY=sk-xxx +ANTHROPIC_API_KEY=sk-ant-xxx +GOOGLE_API_KEY=xxx + +# ----------------------------------------------------------------------------- +# Supabase (required for persistence) +# ----------------------------------------------------------------------------- +SUPABASE_URL=https://xxx.supabase.co +SUPABASE_SERVICE_ROLE_KEY=eyJxxx + +# ----------------------------------------------------------------------------- +# NATS Event Bus (optional) +# ----------------------------------------------------------------------------- +NATS_URL=nats://localhost:4222 + +# ----------------------------------------------------------------------------- +# MCP Servers (optional - for tool discovery) +# ----------------------------------------------------------------------------- +# Format: name: "mcp://transport?params" +A0_MCP_SERVERS= + archon: "mcp://http?endpoint=http://localhost:8091"; + neo4j: "mcp://neo4j?url=bolt://localhost:7687&user=neo4j&password=xxx"; + filesystem: "mcp://filesystem?roots=/data"; + +# ----------------------------------------------------------------------------- +# Server Configuration +# ----------------------------------------------------------------------------- +HOST=0.0.0.0 +PORT=8080 + `.trim() + } + }, + + // Create installation marker + { + method: "fs.write", + params: { + path: "app/.installed", + text: new Date().toISOString() + } + }, + + // Notify user + { + method: "notify", + params: { + title: "Agent Zero Installed", + message: "Copy app/.env.example to app/.env and configure before starting." + } + } + ] +} +``` + +### pinokio/start.js + +```javascript +// pinokio/start.js +// Launch script for PMOVES Agent Zero + +module.exports = { + run: [ + // Check if .env exists + { + method: "fs.exists", + params: { path: "app/.env" }, + returns: "env_exists" + }, + + // Warn if no .env + { + method: "notify", + params: { + title: "Configuration Required", + message: "Please copy app/.env.example to app/.env and configure before starting.", + type: "warning" + }, + when: !input.env_exists + }, + + // Start the agent + { + method: "shell.run", + params: { + path: "app", + message: platform === "win32" + ? "python main.py" + : "python3 main.py", + env: { + HOST: "0.0.0.0", + PORT: "8080", + PYTHONUNBUFFERED: "1" + }, + on: [ + { + // Capture the URL from startup output + event: "/Uvicorn running on (http:\\/\\/[0-9.:]+)/", + done: true + } + ] + } + }, + + // Store the URL for menu access + { + method: "local.set", + params: { + url: input.event[1], + started: new Date().toISOString() + } + }, + + // Notify user + { + method: "notify", + params: { + title: "Agent Zero Started", + message: `Running at ${input.event[1]}\nAPI Docs: ${input.event[1]}/docs` + } + } + ] +} +``` + +### pinokio/reset.js + +```javascript +// pinokio/reset.js +// Reset script for PMOVES Agent Zero + +module.exports = { + run: [ + // Remove Python cache and dependencies + { + method: "shell.run", + params: { + path: "app", + message: platform === "win32" + ? "rmdir /s /q __pycache__ 2>nul & del /f .installed 2>nul" + : "rm -rf __pycache__ .installed", + throws: false + } + }, + + // Remove environment file + { + method: "fs.rm", + params: { path: "app/.env" } + }, + + // Remove installation marker + { + method: "fs.rm", + params: { path: "app/.installed" } + }, + + // Notify user + { + method: "notify", + params: { + title: "Agent Zero Reset", + message: "Installation cleaned. Run Install to set up again." + } + } + ] +} +``` + +### pinokio/update.js + +```javascript +// pinokio/update.js +// Update script for PMOVES Agent Zero + +module.exports = { + run: [ + // Pull latest changes + { + method: "shell.run", + params: { + path: "app", + message: "git pull origin main" + } + }, + + // Update dependencies + { + method: "shell.run", + params: { + path: "app", + message: platform === "win32" + ? "pip install -r requirements.txt --upgrade" + : "pip3 install -r requirements.txt --upgrade" + } + }, + + // Update installation marker + { + method: "fs.write", + params: { + path: "app/.installed", + text: new Date().toISOString() + } + }, + + // Notify user + { + method: "notify", + params: { + title: "Agent Zero Updated", + message: "Latest changes pulled and dependencies updated." + } + } + ] +} +``` + +--- + +## Example 2: Hi-RAG v2 - API/Worker Service + +**Agent Type:** Tier 4+1 (Worker+Data) +**GPU:** Optional (CPU on 8086, GPU on 8087) +**Ports:** 8086 (CPU), 8087 (GPU) + +### Folder Structure + +``` +pmoves-hirag/ +├── pinokio/ +│ ├── install.js +│ ├── start-cpu.js +│ ├── start-gpu.js +│ ├── reset.js +│ └── pinokio.js +├── pinokio.json +├── README.md +└── icon.png +``` + +### pinokio.json + +```json +{ + "version": "1", + "title": "PMOVES Hi-RAG v2", + "description": "Hybrid RAG gateway combining Qdrant vector search + Neo4j knowledge graphs + Meilisearch full-text search with optional GPU cross-encoder reranking.", + "icon": "icon.png", + "platform": ["win32", "darwin", "linux"], + "arch": ["x64", "arm64"], + "gpu": "optional", + "author": "POWERFULMOVES", + "repo": "https://github.com/POWERFULMOVES/PMOVES-HiRAG", + "homepage": "https://pmoves.ai", + "keywords": ["rag", "search", "vector", "neo4j", "qdrant", "gpu", "reranker"] +} +``` + +### pinokio/pinokio.js + +```javascript +// pinokio/pinokio.js +// Dynamic menu for Hi-RAG v2 + +const installed = info.exists("app/.installed") +const cpuRunning = info.running("start-cpu.js") +const gpuRunning = info.running("start-gpu.js") +const cpuUrl = info.local("start-cpu.js", "url") +const gpuUrl = info.local("start-gpu.js", "url") +const hasGpu = gpu === "nvidia" + +module.exports = { + menu: [ + { + id: "install", + title: "Install", + description: "Clone and install Hi-RAG v2", + script: "install.js", + default: !installed + }, + // Divider + { type: "divider", title: "Start Service" }, + { + id: "start-cpu", + title: "Start CPU Instance", + description: "Launch CPU-only instance on port 8086", + script: "start-cpu.js", + running: cpuRunning, + default: installed && !cpuRunning && !gpuRunning + }, + { + id: "start-gpu", + title: "Start GPU Instance", + description: "Launch GPU-accelerated instance on port 8087 (requires NVIDIA)", + script: "start-gpu.js", + running: gpuRunning, + disabled: !hasGpu, + default: installed && !cpuRunning && !gpuRunning && hasGpu + }, + // Divider + { type: "divider", title: "Endpoints" }, + { + id: "cpu-api", + title: "CPU API", + description: "Hi-RAG CPU instance stats", + href: cpuUrl || "http://localhost:8086/hirag/admin/stats", + disabled: !cpuRunning + }, + { + id: "gpu-api", + title: "GPU API", + description: "Hi-RAG GPU instance stats with reranking", + href: gpuUrl || "http://localhost:8087/hirag/admin/stats", + disabled: !gpuRunning + }, + { + id: "cpu-docs", + title: "CPU Docs", + href: cpuUrl ? `${cpuUrl}/docs` : "http://localhost:8086/docs", + disabled: !cpuRunning + }, + { + id: "gpu-docs", + title: "GPU Docs", + href: gpuUrl ? `${gpuUrl}/docs` : "http://localhost:8087/docs", + disabled: !gpuRunning + }, + // Divider + { type: "divider", title: "Maintenance" }, + { + id: "reset", + title: "Reset", + script: "reset.js" + } + ] +} +``` + +### pinokio/start-cpu.js + +```javascript +// pinokio/start-cpu.js +// Launch CPU instance + +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: platform === "win32" + ? "python -m hirag.gateway --host 0.0.0.0 --port 8086" + : "python3 -m hirag.gateway --host 0.0.0.0 --port 8086", + env: { + EMBEDDING_MODEL: "BAAI/bge-m3", + PYTHONUNBUFFERED: "1" + }, + on: [ + { + event: "/Uvicorn running on (http:\\/\\/[0-9.:]+)/", + done: true + } + ] + } + }, + { + method: "local.set", + params: { + url: input.event[1], + mode: "cpu", + started: new Date().toISOString() + } + }, + { + method: "notify", + params: { + title: "Hi-RAG CPU Started", + message: `CPU instance running at ${input.event[1]}\nEmbedding model: BAAI/bge-m3` + } + } + ] +} +``` + +### pinokio/start-gpu.js + +```javascript +// pinokio/start-gpu.js +// Launch GPU instance with cross-encoder reranking + +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: platform === "win32" + ? "python -m hirag.gateway --host 0.0.0.0 --port 8087 --gpu" + : "python3 -m hirag.gateway --host 0.0.0.0 --port 8087 --gpu", + env: { + CUDA_VISIBLE_DEVICES: "0", + RERANKER_MODEL: "BAAI/bge-reranker-v2-m3", + EMBEDDING_MODEL: "BAAI/bge-m3", + PYTHONUNBUFFERED: "1" + }, + on: [ + { + event: "/Uvicorn running on (http:\\/\\/[0-9.:]+)/", + done: true + } + ] + } + }, + { + method: "local.set", + params: { + url: input.event[1], + mode: "gpu", + reranker: "BAAI/bge-reranker-v2-m3", + started: new Date().toISOString() + } + }, + { + method: "notify", + params: { + title: "Hi-RAG GPU Started", + message: `GPU instance running at ${input.event[1]}\nReranking enabled with BGE-v2-m3` + } + } + ] +} +``` + +--- + +## Example 3: Ultimate-TTS - GPU Media Service + +**Agent Type:** Tier 5+3 (Media+LLM) +**GPU:** Recommended +**Port:** 7861 + +### pinokio.json + +```json +{ + "version": "1", + "title": "PMOVES Ultimate TTS Studio", + "description": "Multi-engine TTS studio with 14 voice engines including Coqui XTTS, StyleTTS2, VITS, RVC, and more. GPU recommended for real-time synthesis.", + "icon": "icon.png", + "platform": ["win32", "darwin", "linux"], + "arch": ["x64", "arm64"], + "gpu": "recommended", + "author": "POWERFULMOVES", + "repo": "https://github.com/POWERFULMOVES/PMOVES-Ultimate-TTS-Studio", + "homepage": "https://pmoves.ai", + "keywords": ["tts", "voice", "synthesis", "audio", "gpu", "gradio", "coqui", "styletts"] +} +``` + +### pinokio/pinokio.js + +```javascript +// pinokio/pinokio.js +// Dynamic menu for Ultimate TTS Studio + +const installed = info.exists("app/.installed") +const running = info.running("start.js") +const url = info.local("start.js", "url") +const mode = info.local("start.js", "mode") + +module.exports = { + menu: [ + { + id: "install", + title: "Install", + description: "Clone and install TTS Studio (may take several minutes)", + script: "install.js", + default: !installed + }, + { + id: "start", + title: running ? "Stop TTS Studio" : "Start TTS Studio", + description: mode + ? `Running in ${mode.toUpperCase()} mode` + : "Launch TTS Studio (auto-detects GPU)", + script: "start.js", + running: running, + default: installed && !running + }, + { + id: "open", + title: "Open Gradio UI", + href: url || "http://localhost:7861", + default: running + }, + { + id: "api", + title: "API Docs", + href: url ? `${url}/gradio_api` : "http://localhost:7861/gradio_api", + disabled: !running + }, + // Divider + { type: "divider", title: "Maintenance" }, + { + id: "clear-cache", + title: "Clear Cache", + description: "Remove Gradio temp files", + script: "clear-cache.js", + disabled: !installed + }, + { + id: "reset", + title: "Reset", + script: "reset.js" + } + ] +} +``` + +### pinokio/start.js + +```javascript +// pinokio/start.js +// Launch Ultimate TTS Studio with Gradio UI + +const hasGpu = gpu === "nvidia" + +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: hasGpu + ? "python launch.py --gpu --port 7861" + : "python launch.py --cpu --port 7861", + env: { + GRADIO_SERVER_NAME: "0.0.0.0", + GRADIO_SERVER_PORT: "7861", + PYTHONUNBUFFERED: "1" + }, + on: [ + { + event: "/Running on (http:\\/\\/[0-9.:]+)/", + done: true + } + ] + } + }, + { + method: "local.set", + params: { + url: input.event[1], + mode: hasGpu ? "gpu" : "cpu", + engines: 14, + started: new Date().toISOString() + } + }, + { + method: "notify", + params: { + title: "Ultimate TTS Studio Started", + message: `Running at ${input.event[1]}\n14 TTS engines available (${hasGpu ? "GPU" : "CPU"} mode)` + } + } + ] +} +``` + +--- + +## Example 4: PMOVES Services - Multi-Profile Stack + +**Type:** Multi-service Docker Compose orchestration +**Profiles:** data, workers, agents, gpu + +### pinokio/pinokio.js + +```javascript +// pinokio/pinokio.js +// Multi-profile launcher for PMOVES core services + +const installed = info.exists(".installed") +const dataRunning = info.running("start-data.js") +const workersRunning = info.running("start-workers.js") +const agentsRunning = info.running("start-agents.js") +const gpuRunning = info.running("start-gpu.js") + +const anyRunning = dataRunning || workersRunning || agentsRunning || gpuRunning +const allRunning = dataRunning && workersRunning && agentsRunning && gpuRunning +const hasGpu = gpu === "nvidia" + +module.exports = { + menu: [ + { + id: "install", + title: "Install Dependencies", + description: "Clone submodules and install all dependencies", + script: "install.js", + default: !installed + }, + // Divider + { type: "divider", title: "Service Profiles" }, + { + id: "start-data", + title: dataRunning ? "✓ Data Layer Running" : "Start Data Layer", + description: "NATS :4222, Qdrant :6333, Neo4j :7474, Meilisearch :7700", + script: "start-data.js", + running: dataRunning + }, + { + id: "start-workers", + title: workersRunning ? "✓ Workers Running" : "Start Workers", + description: "Hi-RAG :8086, Extract :8083, PDF Ingest :8092, Channel Monitor :8097", + script: "start-workers.js", + running: workersRunning + }, + { + id: "start-agents", + title: agentsRunning ? "✓ Agents Running" : "Start Agents", + description: "Agent Zero :8080, Archon :8091, SupaSerch :8099", + script: "start-agents.js", + running: agentsRunning + }, + { + id: "start-gpu", + title: gpuRunning ? "✓ GPU Services Running" : "Start GPU Services", + description: "Ultimate-TTS :7861, Media-Video :8079, FFmpeg-Whisper :8078", + script: "start-gpu.js", + running: gpuRunning, + disabled: !hasGpu + }, + // Divider + { type: "divider", title: "Quick Actions" }, + { + id: "start-all", + title: "Start All Services", + description: "Launch all profiles at once", + script: "start-all.js", + disabled: anyRunning + }, + { + id: "stop-all", + title: "Stop All Services", + description: "Stop all running profiles", + script: "stop-all.js", + disabled: !anyRunning + }, + // Divider + { type: "divider", title: "Status & Logs" }, + { + id: "status", + title: "Service Status", + description: "Check health of all services", + script: "status.js" + }, + { + id: "logs", + title: "View Logs", + href: "logs/" + } + ] +} +``` + +### pinokio/start-data.js + +```javascript +// pinokio/start-data.js +// Start data layer services via Docker Compose + +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "../..", // PMOVES.AI root with docker-compose.yml + message: "docker compose --profile data up -d", + on: [ + { + event: "/Container (.*) Started/", + done: false + }, + { + event: "/Network pmoves-net (created|already exists)/", + done: true + } + ] + } + }, + { + method: "local.set", + params: { + profile: "data", + services: ["nats", "qdrant", "neo4j", "meilisearch"], + started: new Date().toISOString() + } + }, + { + method: "notify", + params: { + title: "Data Layer Started", + message: "NATS :4222\nQdrant :6333\nNeo4j :7474\nMeilisearch :7700" + } + } + ] +} +``` + +### pinokio/stop-all.js + +```javascript +// pinokio/stop-all.js +// Stop all PMOVES services + +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "../..", + message: "docker compose --profile data --profile workers --profile agents --profile gpu down", + on: [ + { + event: "/Container (.*) Stopped/", + done: false + }, + { + event: "/Network pmoves-net removed/", + done: true + } + ] + } + }, + { + method: "notify", + params: { + title: "All Services Stopped", + message: "All PMOVES service profiles have been stopped." + } + } + ] +} +``` + +### pinokio/status.js + +```javascript +// pinokio/status.js +// Check health of all PMOVES services + +const services = [ + { name: "NATS", url: "http://localhost:8222/varz", port: 4222 }, + { name: "Qdrant", url: "http://localhost:6333/healthz", port: 6333 }, + { name: "Neo4j", url: "http://localhost:7474", port: 7474 }, + { name: "Meilisearch", url: "http://localhost:7700/health", port: 7700 }, + { name: "Agent Zero", url: "http://localhost:8080/healthz", port: 8080 }, + { name: "Archon", url: "http://localhost:8091/healthz", port: 8091 }, + { name: "Hi-RAG CPU", url: "http://localhost:8086/healthz", port: 8086 }, + { name: "Hi-RAG GPU", url: "http://localhost:8087/healthz", port: 8087 }, + { name: "Ultimate TTS", url: "http://localhost:7861/gradio_api/info", port: 7861 } +] + +const results = [] + +module.exports = { + run: [ + // Check each service + ...services.map(service => ({ + method: "request", + params: { + uri: service.url, + method: "GET", + timeout: 3000 + }, + returns: `${service.name}_status`, + error: `${service.name}_error` + })), + + // Build status report + { + method: "notify", + params: { + title: "PMOVES Service Status", + message: services.map(service => { + const status = input[`${service.name}_status`] + const error = input[`${service.name}_error`] + const icon = status?.status === 200 || status ? "✅" : "❌" + return `${icon} ${service.name} (:${service.port})` + }).join("\n") + } + } + ] +} +``` + +--- + +## Example 5: Crush - UI-Only App + +**Agent Type:** Tier 7+6 (UI+Agent) +**GPU:** Not required +**Port:** 3000 + +### pinokio.json + +```json +{ + "version": "1", + "title": "PMOVES Crush", + "description": "Terminal AI coding assistant — the gateway where model and user begin their journey. Web-based terminal interface with multi-model support.", + "icon": "icon.png", + "platform": ["win32", "darwin", "linux"], + "arch": ["x64", "arm64"], + "gpu": false, + "author": "POWERFULMOVES", + "repo": "https://github.com/POWERFULMOVES/PMOVES-crush", + "homepage": "https://pmoves.ai", + "keywords": ["terminal", "coding", "assistant", "ui", "web"] +} +``` + +### pinokio/pinokio.js + +```javascript +// pinokio/pinokio.js +// Dynamic menu for Crush terminal UI + +const installed = info.exists("app/node_modules") +const running = info.running("start.js") +const url = info.local("start.js", "url") + +module.exports = { + menu: [ + { + id: "install", + title: "Install", + description: "Clone and install Crush dependencies", + script: "install.js", + default: !installed + }, + { + id: "start", + title: running ? "Stop Crush" : "Start Crush", + description: "Launch the terminal UI", + script: "start.js", + running: running, + default: installed && !running + }, + { + id: "open", + title: "Open Terminal", + href: url || "http://localhost:3000", + default: running + }, + // Divider + { type: "divider", title: "Configuration" }, + { + id: "config", + title: "Edit Configuration", + href: "app/.env" + }, + // Divider + { type: "divider", title: "Maintenance" }, + { + id: "reset", + title: "Reset", + script: "reset.js" + } + ] +} +``` + +### pinokio/start.js + +```javascript +// pinokio/start.js +// Launch Crush web terminal + +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: "npm run dev", + env: { + PORT: "3000", + NEXT_PUBLIC_AGENT_ZERO_URL: "http://localhost:8080", + NEXT_PUBLIC_ARCHON_URL: "http://localhost:8091" + }, + on: [ + { + event: "/Local:\\s+(http:\\/\\/[0-9.:]+)/", + done: true + } + ] + } + }, + { + method: "local.set", + params: { + url: input.event[1], + started: new Date().toISOString() + } + }, + { + method: "notify", + params: { + title: "Crush Started", + message: `Terminal ready at ${input.event[1]}` + } + } + ] +} +``` + +--- + +## Example 6: Cipher Beats Analyst - CLI Tool + +**Agent Type:** Tier 5+6 (Media+Agent) +**Runtime:** CLI (no HTTP server) +**Signature:** Gemini CLI driven + +### pinokio.json + +```json +{ + "version": "1", + "title": "PMOVES Cipher Beats Analyst", + "description": "Level 11 Cipher Gateway Specialist. Extracts sonic fingerprints via ffprobe/ffmpeg, clusters DARKXSIDE beats into named constellations, and generates M3U8 playlists.", + "icon": "icon.png", + "platform": ["win32", "darwin", "linux"], + "arch": ["x64", "arm64"], + "gpu": false, + "author": "POWERFULMOVES", + "repo": "https://github.com/POWERFULMOVES/PMOVES-cipher-beats", + "homepage": "https://pmoves.ai", + "keywords": ["audio", "beats", "analysis", "ffmpeg", "cli", "playlist"] +} +``` + +### pinokio/pinokio.js + +```javascript +// pinokio/pinokio.js +// Menu for Cipher Beats Analyst CLI tool + +const installed = info.exists("app/.installed") + +module.exports = { + menu: [ + { + id: "install", + title: "Install", + description: "Clone and install beats analyzer", + script: "install.js", + default: !installed + }, + // Divider + { type: "divider", title: "Analysis Tools" }, + { + id: "analyze-folder", + title: "Analyze Folder", + description: "Select a folder and analyze all audio files", + script: "analyze-folder.js" + }, + { + id: "analyze-file", + title: "Analyze Single File", + description: "Select and analyze a single audio file", + script: "analyze-file.js" + }, + { + id: "cluster", + title: "Cluster Beats", + description: "Cluster analyzed beats into constellations", + script: "cluster.js" + }, + { + id: "generate-playlist", + title: "Generate Playlist", + description: "Create M3U8 playlist from clusters", + script: "generate-playlist.js" + }, + // Divider + { type: "divider", title: "Results" }, + { + id: "view-results", + title: "View Results", + href: "app/output/" + }, + // Divider + { type: "divider", title: "Maintenance" }, + { + id: "reset", + title: "Reset", + script: "reset.js" + } + ] +} +``` + +### pinokio/analyze-folder.js + +```javascript +// pinokio/analyze-folder.js +// Analyze all audio files in a folder + +module.exports = { + run: [ + // Open file picker for folder selection + { + method: "shell.run", + params: { + message: "pterm filepicker --directory", + returns: "folder_path" + } + }, + + // Run analysis + { + method: "shell.run", + params: { + path: "app", + message: `uv run python pmoves/tools/analyze_beats.py "${input.folder_path}" --output output/`, + env: { + PYTHONUNBUFFERED: "1" + } + } + }, + + // Notify user + { + method: "notify", + params: { + title: "Analysis Complete", + message: `Analyzed audio files from ${input.folder_path}\nResults saved to app/output/` + } + } + ] +} +``` + +--- + +## Summary + +| Example | Agent Type | GPU | Key Features | +|---------|-----------|-----|--------------| +| Agent Zero | Agent+API | Optional | Full runtime, MCP, environment config | +| Hi-RAG v2 | Worker+Data | Optional | Dual-mode (CPU/GPU), port selection | +| Ultimate-TTS | Media+LLM | Recommended | Gradio UI, auto GPU detection | +| PMOVES Services | Multi-profile | Mixed | Docker Compose, health checks | +| Crush | UI+Agent | No | Next.js frontend, terminal UI | +| Cipher Beats | Media+Agent | No | CLI tool, file picker integration | + +--- + +**Next Steps:** +- See [PINOKIO_PACKAGING_GUIDE.md](./PINOKIO_PACKAGING_GUIDE.md) for detailed API reference +- See [pbnj/pinokio/api/](../../../pbnj/pinokio/api/) for existing implementations +- See [CLAUDE.md](../../../CLAUDE.md) for Pinokio scripting rules diff --git a/pmoves/docs/PINOKIO_PACKAGING_GUIDE.md b/pmoves/docs/PINOKIO_PACKAGING_GUIDE.md new file mode 100644 index 0000000000..20658574be --- /dev/null +++ b/pmoves/docs/PINOKIO_PACKAGING_GUIDE.md @@ -0,0 +1,1286 @@ +# PMOVES Agent Packaging Guide for Pinokio + +> **Last Updated:** 2026-03-22 +> **Pinokio Version:** 7.x+ +> **PMOVES Taxonomy Version:** 1.4.0 + +This guide explains how to package PMOVES agents and services for deployment through [Pinokio](https://pinokio.computer), the AI app browser that enables one-click installation and execution of complex AI applications. + +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Pinokio Architecture](#pinokio-architecture) +3. [PMOVES Agent Types & Pinokio Mapping](#pmoves-agent-types--pinokio-mapping) +4. [Folder Structure](#folder-structure) +5. [Required Files](#required-files) +6. [Script APIs](#script-apis) +7. [Packaging Patterns by Agent Type](#packaging-patterns-by-agent-type) +8. [Examples](#examples) +9. [Testing & Validation](#testing--validation) +10. [Distribution](#distribution) +11. [Troubleshooting](#troubleshooting) + +--- + +## Overview + +### What is Pinokio? + +Pinokio is a browser-based AI engine that provides: +- **One-click installation** of complex AI applications +- **Cross-platform scripting** (Windows, macOS, Linux) +- **Dynamic UI generation** through JavaScript manifests +- **GPU-aware deployment** with automatic hardware detection +- **Network view** for discovering and accessing apps across nodes + +### Why Package PMOVES for Pinokio? + +| Benefit | Description | +|---------|-------------| +| **Simplified Deployment** | Users can install PMOVES agents with a single click | +| **Cross-Platform** | Scripts work on Windows, macOS, and Linux automatically | +| **GPU Orchestration** | Automatic detection and utilization of NVIDIA GPUs | +| **Network Discovery** | Apps automatically available across Pinokio network | +| **Agent Interpreter (P7)** | Pinokio 7+ can discover and interact with installed agents | + +--- + +## Pinokio Architecture + +### Core Components + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Pinokio Application │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Electron │ │ Caddy │ │ Pterm │ │ +│ │ UI │ │ Proxy │ │ CLI │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ API Layer │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ ~/pinokio/api/ │ │ +│ │ ├── app1.git/ (installed app) │ │ +│ │ ├── app2.git/ (installed app) │ │ +│ │ └── pmoves-agent/ (PMOVES agent package) │ │ +│ └──────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ Script Runtime │ +│ • shell.run • fs.write • request • local.set │ +│ • git.clone • fs.read • dependencies • info.* │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Script Execution Flow + +``` +pinokio.js ──► Dynamic Menu Generation + │ + ├── install.js ──► Clone repos, install dependencies + ├── start.js ──► Launch the application + ├── reset.js ──► Clean/reset state + └── update.js ──► Pull latest changes +``` + +--- + +## PMOVES Agent Types & Pinokio Mapping + +PMOVES uses a 7-tier agent taxonomy. Here's how each maps to Pinokio packaging patterns: + +### Agent Class Prefixes + +| Class | Prefix | Example | Pinokio Packaging | +|-------|--------|---------|-------------------| +| **Legendary** | `POWERFULMOVES` | Organization brand | N/A (doctrine level) | +| **Standard** | `PMOVES-` | Agent Zero, Archon | Full launcher with all scripts | +| **Specialized** | `Pmoves-` | Cipher Memory, Hyperdimensions | Full or simplified launcher | +| **Utility** | `pmoves-` | NATS, Qdrant, Neo4j | Often infrastructure-only | + +### Service Tiers & Packaging + +| Tier | Type | Element | Pinokio Pattern | +|------|------|---------|-----------------| +| 1 | Data | Earth | Database/infrastructure launcher | +| 2 | API | Water | API server launcher | +| 3 | LLM | Fire | Model download + inference server | +| 4 | Worker | Electric | Background service launcher | +| 5 | Media | Wind | Media processing pipeline | +| 6 | Agent | Psychic | Full agent runtime + UI | +| 7 | UI | Light | Frontend-only or static app | + +### PMOVES Agents Ready for Pinokio Packaging + +Based on [`pmoves/config/agent_registry.yaml`](../../config/agent_registry.yaml): + +| Agent | Type | Port | Pinokio Package Name | +|-------|------|------|---------------------| +| Agent Zero | Agent+API | 8080 | `pmoves-agent-zero` | +| Archon | Agent+LLM | 8091 | `pmoves-archon` | +| Hi-RAG v2 | Worker+Data | 8086/8087 | `pmoves-hirag` | +| SupaSerch | Agent+LLM | 8099 | `pmoves-supaserch` | +| PMOVES.YT | Media+Worker | 8077 | `pmoves-yt` | +| Ultimate-TTS | Media+LLM | 7861 | `pmoves-ultimate-tts` | +| Jellyfin Bridge | Media+Data | 8093 | `pmoves-jellyfin-bridge` | +| Crush | UI+Agent | - | `pmoves-crush` | + +--- + +## Folder Structure + +### Standard Launcher Structure + +For agents with backend services: + +``` +pmoves-agent-name/ +├── pinokio/ +│ ├── install.js # Installation script +│ ├── start.js # Launch script +│ ├── reset.js # Reset/cleanup script +│ ├── update.js # Update script (optional) +│ ├── pinokio.js # Dynamic menu generator +│ └── logs/ # Runtime logs (auto-created) +├── pinokio.json # Metadata (title, description, icon) +├── README.md # Documentation +└── app/ # Application code (or submodule) +``` + +### Serverless Web App Structure + +For frontend-only applications: + +``` +pmoves-web-ui/ +├── index.html # Entry point (auto-launched) +├── pinokio.json # Metadata only +└── README.md # Documentation +``` + +### Script-Only Structure + +For CLI tools and utilities: + +``` +pmoves-cli-tool/ +├── pinokio/ +│ ├── tool.js # Main script +│ └── pinokio.js # Menu linking to tool.js +├── pinokio.json # Metadata +└── README.md # Documentation +``` + +### PMOVES Multi-Service Structure + +For multi-container deployments (like PMOVES core stack): + +``` +pmoves-services/ +├── pinokio/ +│ ├── install.js # Clone submodules, setup env +│ ├── start-core.js # Launch data + worker profiles +│ ├── start-agents.js # Launch agent profile +│ ├── start-voice.js # Launch voice stack +│ ├── start-gpu.js # Launch GPU services +│ ├── stop.js # Stop all profiles +│ ├── pinokio.js # Dynamic menu with all options +│ └── logs/ +├── pinokio.json +├── README.md +└── docker-compose.yml # Reference to pmoves/docker-compose.yml +``` + +--- + +## Required Files + +### 1. `pinokio.json` (Metadata) + +```json +{ + "version": "1", + "title": "PMOVES Agent Name", + "description": "Brief description of what this agent does", + "icon": "icon.png", + "platform": ["win32", "darwin", "linux"], + "arch": ["x64", "arm64"], + "gpu": "optional", + "author": "POWERFULMOVES", + "repo": "https://github.com/POWERFULMOVES/PMOVES-AgentName", + "homepage": "https://pmoves.ai" +} +``` + +#### Field Reference + +| Field | Required | Description | +|-------|----------|-------------| +| `version` | ✅ | Schema version (always `"1"`) | +| `title` | ✅ | Display name in Pinokio UI | +| `description` | ✅ | Short description for app listing | +| `icon` | ✅ | Path to icon file (512x512 PNG recommended) | +| `platform` | ⚪ | Supported OS: `win32`, `darwin`, `linux` | +| `arch` | ⚪ | Architecture: `x64`, `arm64` | +| `gpu` | ⚪ | GPU requirement: `required`, `optional`, or omit | +| `author` | ⚪ | Author name | +| `repo` | ⚪ | Git repository URL | +| `homepage` | ⚪ | Project homepage URL | + +### 2. `pinokio.js` (Dynamic Menu) + +```javascript +// pinokio/pinokio.js +module.exports = { + run: [ + { + method: "script", + params: { + uri: "install.js", + params: {} + } + }, + { + method: "script.start", + params: { + uri: "start.js", + params: { env: "production" } + } + } + ], + menu: [ + { + id: "install", + title: "Install", + script: "install.js", + default: !info.exists("app/node_modules") + }, + { + id: "start", + title: "Start", + script: "start.js", + running: info.running("start.js"), + default: info.exists("app/node_modules") && !info.running("start.js") + }, + { + id: "open", + title: "Open WebUI", + href: info.local("start.js", "url") || "http://localhost:8080", + default: info.running("start.js") + }, + { + id: "reset", + title: "Reset", + script: "reset.js" + } + ] +} +``` + +### 3. `install.js` (Installation Script) + +```javascript +// pinokio/install.js +module.exports = { + run: [ + { + method: "fs.rm", + params: { path: "app" } + }, + { + method: "git.clone", + params: { + url: "https://github.com/POWERFULMOVES/PMOVES-AgentName", + path: "app" + } + }, + { + method: "shell.run", + params: { + path: "app", + message: "npm install" + } + }, + { + method: "notify", + params: { + message: "Installation complete!" + } + } + ] +} +``` + +### 4. `start.js` (Launch Script) + +```javascript +// pinokio/start.js +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: "npm start", + on: [{ + event: "/http://localhost:[0-9]+/", + done: true + }] + } + }, + { + method: "local.set", + params: { + url: input.event[1] // Captured URL from regex + } + } + ] +} +``` + +### 5. `reset.js` (Cleanup Script) + +```javascript +// pinokio/reset.js +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: "rm -rf node_modules" + } + }, + { + method: "fs.rm", + params: { path: "app/.env" } + } + ] +} +``` + +--- + +## Script APIs + +### Core APIs + +#### `shell.run` - Execute Shell Commands + +```javascript +{ + method: "shell.run", + params: { + path: "app", // Working directory + message: "npm start", // Command to run + env: { // Environment variables (optional) + NODE_ENV: "production", + PORT: "8080" + }, + on: [ // Completion conditions + { + event: "/Server listening/", + done: true + } + ] + } +} +``` + +#### `git.clone` - Clone Repository + +```javascript +{ + method: "git.clone", + params: { + url: "https://github.com/user/repo", + path: "app", + branch: "main" // Optional + } +} +``` + +#### `fs.write` / `fs.read` - File Operations + +```javascript +// Write file +{ + method: "fs.write", + params: { + path: "app/.env", + text: "API_KEY=xxx\nPORT=8080" + } +} + +// Read file +{ + method: "fs.read", + params: { + path: "app/.env" + }, + returns: "env_content" // Stored in input.env_content +} +``` + +#### `request` - HTTP Requests + +```javascript +{ + method: "request", + params: { + uri: "https://api.example.com/data", + method: "GET", + headers: { + "Authorization": "Bearer xxx" + } + } +} +``` + +#### `local.set` / `local.get` - Local Variables + +```javascript +// Set local variable (persists during script run) +{ + method: "local.set", + params: { + url: "http://localhost:8080", + status: "running" + } +} + +// Access in pinokio.js: info.local("start.js", "url") +``` + +### Conditional Execution + +```javascript +{ + method: "shell.run", + params: { + path: "app", + // Platform-specific commands + message: platform === "win32" + ? "npm run start:windows" + : platform === "darwin" + ? "npm run start:macos" + : "npm run start:linux" + } +} +``` + +### GPU Detection + +```javascript +{ + method: "shell.run", + params: { + path: "app", + message: gpu === "nvidia" + ? "python start.py --gpu" + : "python start.py --cpu" + } +} +``` + +--- + +## Packaging Patterns by Agent Type + +### Pattern 1: API/Worker Services (Tier 2-4) + +**Examples:** Hi-RAG v2, Extract Worker, Channel Monitor + +```javascript +// pinokio/start.js for API/Worker +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: "uvicorn main:app --host 0.0.0.0 --port 8086", + on: [{ + event: "/Uvicorn running on/", + done: true + }] + } + }, + { + method: "local.set", + params: { + url: input.event[1] + } + } + ] +} +``` + +### Pattern 2: LLM Services (Tier 3) + +**Examples:** TensorZero, DeepResearch, Llama Lab + +```javascript +// pinokio/install.js for LLM with model download +module.exports = { + run: [ + { + method: "git.clone", + params: { + url: "https://github.com/POWERFULMOVES/PMOVES-LLM-Service", + path: "app" + } + }, + { + method: "shell.run", + params: { + path: "app", + message: "pip install -r requirements.txt" + } + }, + { + method: "notify", + params: { + message: "Downloading model weights (this may take a while)..." + } + }, + { + method: "shell.run", + params: { + path: "app", + message: "python download_model.py" + } + } + ] +} +``` + +### Pattern 3: Media Services (Tier 5) + +**Examples:** PMOVES.YT, Ultimate-TTS, FFmpeg-Whisper + +```javascript +// pinokio/start.js for GPU-accelerated media +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: gpu === "nvidia" + ? "python launch.py --gpu --port 7861" + : "python launch.py --cpu --port 7861", + env: { + GRADIO_SERVER_NAME: "0.0.0.0", + CUDA_VISIBLE_DEVICES: "0" + }, + on: [{ + event: "/Running on (http:\\/\\/[0-9.:]+)/", + done: true + }] + } + }, + { + method: "local.set", + params: { + url: input.event[1] + } + } + ] +} +``` + +### Pattern 4: Full Agent Runtime (Tier 6) + +**Examples:** Agent Zero, Archon, SupaSerch + +```javascript +// pinokio/install.js for full agent with MCP servers +module.exports = { + run: [ + { + method: "git.clone", + params: { + url: "https://github.com/POWERFULMOVES/PMOVES-Agent-Zero", + path: "app" + } + }, + { + method: "shell.run", + params: { + path: "app", + message: "pip install -r requirements.txt" + } + }, + { + method: "fs.write", + params: { + path: "app/.env", + text: ` +SUPABASE_URL=${args.supabase_url} +SUPABASE_KEY=${args.supabase_key} +NATS_URL=nats://localhost:4222 + `.trim() + } + }, + { + method: "notify", + params: { + message: "Agent Zero installed. Configure MCP servers in .env" + } + } + ] +} +``` + +### Pattern 5: UI-Only Apps (Tier 7) + +**Examples:** Crush, MAI-UI, Hyperdimensions + +```javascript +// pinokio/start.js for Next.js/React UI +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: "npm run dev", + env: { + NEXT_PUBLIC_API_URL: args.api_url || "http://localhost:8080" + }, + on: [{ + event: "/Local:\\s+(http:\\/\\/[0-9.:]+)/", + done: true + }] + } + }, + { + method: "local.set", + params: { + url: input.event[1] + } + } + ] +} +``` + +### Pattern 6: Docker Compose Stack + +**Examples:** PMOVES Core Services, Monitoring Stack + +```javascript +// pinokio/start-core.js for Docker Compose +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "..", // Parent directory with docker-compose.yml + message: "docker compose --profile data --profile workers up -d", + on: [{ + event: "/Container .* Started/", + done: true + }] + } + }, + { + method: "local.set", + params: { + status: "running", + services: ["nats", "qdrant", "neo4j", "meilisearch"] + } + } + ] +} +``` + +--- + +## Examples + +### Complete Example: PMOVES Agent Zero + +``` +pmoves-agent-zero/ +├── pinokio/ +│ ├── install.js +│ ├── start.js +│ ├── reset.js +│ ├── update.js +│ └── pinokio.js +├── pinokio.json +├── README.md +└── icon.png +``` + +**pinokio.json:** +```json +{ + "version": "1", + "title": "PMOVES Agent Zero", + "description": "Primary L1 orchestrator with embedded agent runtime and MCP API", + "icon": "icon.png", + "platform": ["win32", "darwin", "linux"], + "arch": ["x64", "arm64"], + "gpu": "optional", + "author": "POWERFULMOVES", + "repo": "https://github.com/POWERFULMOVES/PMOVES-Agent-Zero" +} +``` + +**pinokio/pinokio.js:** +```javascript +module.exports = { + run: [ + { + method: "script", + params: { uri: "install.js" } + } + ], + menu: [ + { + id: "install", + title: "Install", + script: "install.js", + default: !info.exists("app/.installed") + }, + { + id: "start", + title: "Start Agent", + script: "start.js", + running: info.running("start.js"), + default: info.exists("app/.installed") && !info.running("start.js") + }, + { + id: "api", + title: "Open API (Swagger)", + href: info.local("start.js", "url") + ? `${info.local("start.js", "url")}/docs` + : "http://localhost:8080/docs", + default: info.running("start.js") + }, + { + id: "mcp", + title: "MCP Endpoint", + href: info.local("start.js", "url") + ? `${info.local("start.js", "url")}/mcp` + : "http://localhost:8080/mcp" + }, + { + id: "update", + title: "Update", + script: "update.js" + }, + { + id: "reset", + title: "Reset", + script: "reset.js" + } + ] +} +``` + +**pinokio/install.js:** +```javascript +module.exports = { + run: [ + { + method: "fs.rm", + params: { path: "app" } + }, + { + method: "git.clone", + params: { + url: "https://github.com/POWERFULMOVES/PMOVES-Agent-Zero", + path: "app", + branch: "main" + } + }, + { + method: "shell.run", + params: { + path: "app", + message: "pip install -r requirements.txt" + } + }, + { + method: "fs.write", + params: { + path: "app/.env.example", + text: ` +# PMOVES Agent Zero Configuration +SUPABASE_URL=your_supabase_url +SUPABASE_SERVICE_ROLE_KEY=your_key +NATS_URL=nats://localhost:4222 +OPENAI_API_KEY=your_key +ANTHROPIC_API_KEY=your_key + +# MCP Servers (optional) +A0_MCP_SERVERS= + archon: "mcp://http?endpoint=http://localhost:8091"; + neo4j: "mcp://neo4j?url=bolt://localhost:7687"; + `.trim() + } + }, + { + method: "fs.write", + params: { + path: "app/.installed", + text: new Date().toISOString() + } + }, + { + method: "notify", + params: { + message: "Agent Zero installed! Copy .env.example to .env and configure." + } + } + ] +} +``` + +**pinokio/start.js:** +```javascript +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: "python main.py", + env: { + HOST: "0.0.0.0", + PORT: "8080" + }, + on: [ + { + event: "/Uvicorn running on (http:\\/\\/[0-9.:]+)/", + done: true + } + ] + } + }, + { + method: "local.set", + params: { + url: input.event[1] + } + } + ] +} +``` + +### Complete Example: PMOVES Services (Multi-Profile) + +**pinokio/pinokio.js:** +```javascript +module.exports = { + menu: [ + { + id: "install", + title: "Install Dependencies", + script: "install.js", + default: !info.exists(".installed") + }, + // Divider + { type: "divider", title: "Service Profiles" }, + // Core data layer + { + id: "start-data", + title: "Start Data Layer", + description: "NATS, Qdrant, Neo4j, Meilisearch", + script: "start-data.js", + running: info.running("start-data.js") + }, + // Workers + { + id: "start-workers", + title: "Start Workers", + description: "Hi-RAG, Extract, PDF Ingest", + script: "start-workers.js", + running: info.running("start-workers.js") + }, + // Agents + { + id: "start-agents", + title: "Start Agents", + description: "Agent Zero, Archon", + script: "start-agents.js", + running: info.running("start-agents.js") + }, + // GPU services + { + id: "start-gpu", + title: "Start GPU Services", + description: "TTS, Media Analysis", + script: "start-gpu.js", + running: info.running("start-gpu.js"), + disabled: gpu !== "nvidia" + }, + // Divider + { type: "divider", title: "Utilities" }, + // Stop all + { + id: "stop-all", + title: "Stop All Services", + script: "stop-all.js" + }, + // Status + { + id: "status", + title: "Service Status", + script: "status.js" + } + ] +} +``` + +--- + +## Testing & Validation + +### Local Testing with Pterm + +```bash +# Test install script +pterm start /path/to/pmoves-agent/install.js + +# Test start script +pterm start /path/to/pmoves-agent/start.js + +# View logs +pterm logs /path/to/pmoves-agent +``` + +### Validation Checklist + +Before publishing a PMOVES Pinokio package: + +- [ ] **Metadata Complete** + - [ ] `pinokio.json` has all required fields + - [ ] Icon is 512x512 PNG + - [ ] Description is clear and concise + +- [ ] **Scripts Work** + - [ ] `install.js` completes without errors + - [ ] `start.js` launches the service + - [ ] URL capture works with `on` event + - [ ] `reset.js` cleans up properly + +- [ ] **Cross-Platform** + - [ ] Tested on Windows, macOS, Linux (or documented limitations) + - [ ] Platform-specific paths handled correctly + - [ ] Environment variables work across platforms + +- [ ] **GPU Handling** + - [ ] GPU detection works + - [ ] Fallback to CPU when no GPU + - [ ] `gpu` field set correctly in `pinokio.json` + +- [ ] **Documentation** + - [ ] README.md explains what the agent does + - [ ] Configuration options documented + - [ ] Environment variables listed + - [ ] Ports and endpoints documented + +- [ ] **Dynamic Menu** + - [ ] Default states change based on installation status + - [ ] Running state reflected in menu + - [ ] URLs appear when service is running + +### Automated Testing + +Create a test script: + +```javascript +// pinokio/test.js +module.exports = { + run: [ + { + method: "shell.run", + params: { + path: "app", + message: "npm test" + } + }, + { + method: "request", + params: { + uri: "http://localhost:8080/healthz", + method: "GET" + } + }, + { + method: "notify", + params: { + message: input.status === 200 + ? "✅ All tests passed" + : "❌ Health check failed" + } + } + ] +} +``` + +--- + +## Distribution + +### Publishing to Pinokio Registry + +1. **Host on GitHub** + ```bash + git push origin main + ``` + +2. **Create Release Tag** + ```bash + git tag v1.0.0 + git push origin v1.0.0 + ``` + +3. **Submit to Pinokio** + - Visit https://pinokio.computer + - Submit app URL: `https://github.com/POWERFULMOVES/PMOVES-AgentName` + +### PMOVES Internal Distribution + +For PMOVES team members, packages are available at: + +``` +pbnj/pinokio/api/ +├── pmoves-agent-zero/ # Agent Zero launcher +├── pmoves-archon/ # Archon launcher +├── pmoves-services/ # Multi-service launcher +├── pmoves-ultimate-tts/ # TTS Studio launcher +└── pmoves-pbnj/ # PBnJ control panel +``` + +**Installation via Symlink:** + +```bash +# macOS/Linux +ln -s /path/to/PMOVES.AI/pbnj/pinokio/api/pmoves-agent-zero \ + ~/pinokio/api/pmoves-agent-zero + +# Windows (PowerShell) +New-Item -ItemType SymbolicLink ` + -Path "$env:USERPROFILE\pinokio\api\pmoves-agent-zero" ` + -Target "C:\path\to\PMOVES.AI\pbnj\pinokio\api\pmoves-agent-zero" +``` + +### Version Management + +```json +// pinokio.json with versioning +{ + "version": "1", + "title": "PMOVES Agent Zero", + "engine": "0.1.0", // Minimum Pinokio version + "pmoves_version": "1.4.0", // PMOVES taxonomy version + ... +} +``` + +--- + +## Troubleshooting + +### Common Issues + +#### 1. Script Fails Silently + +**Check logs:** +```bash +# Pinokio logs location +~/pinokio/api/pmoves-agent/logs/ + +# View with Pterm +pterm logs pmoves-agent +``` + +#### 2. Port Already in Use + +**Add port detection:** +```javascript +{ + method: "shell.run", + params: { + message: platform === "win32" + ? "netstat -ano | findstr :8080" + : "lsof -i :8080", + throws: false // Don't fail if port is free + } +} +``` + +#### 3. Environment Variables Not Loading + +**Debug env:** +```javascript +{ + method: "shell.run", + params: { + message: platform === "win32" + ? "set" // Windows + : "env", // Unix + path: "app" + } +} +``` + +#### 4. GPU Not Detected + +**Manual GPU check:** +```javascript +{ + method: "shell.run", + params: { + message: "nvidia-smi", + throws: false + } +} +``` + +#### 5. Regex Pattern Not Matching + +**Debug event capture:** +```javascript +{ + method: "shell.run", + params: { + message: "npm start", + on: [ + { + event: "/.*/", // Match everything + done: false + }, + { + event: "/http:\\/\\/[0-9.:]+/", + done: true + } + ] + } +} +``` + +### Debug Mode + +Enable verbose logging: + +```javascript +// pinokio/start.js with debug +module.exports = { + debug: true, // Enable debug mode + run: [ + // ... scripts + ] +} +``` + +### Getting Help + +1. **Pinokio Docs:** https://pinokio.co/docs +2. **PMOVES Discord:** [Join PMOVES Community](https://discord.gg/pmoves) +3. **GitHub Issues:** https://github.com/POWERFULMOVES/PMOVES.AI/issues + +--- + +## Appendix A: PMOVES Agent Registry Quick Reference + +| Agent | Class | Tier | Port | GPU | Pinokio Package | +|-------|-------|------|------|-----|-----------------| +| Agent Zero | Standard | 6+2 | 8080 | Optional | `pmoves-agent-zero` | +| Archon | Standard | 6+3 | 8091 | Optional | `pmoves-archon` | +| Hi-RAG v2 | Standard | 4+1 | 8086/8087 | Optional | `pmoves-hirag` | +| SupaSerch | Standard | 6+3 | 8099 | Optional | `pmoves-supaserch` | +| DeepResearch | Standard | 3+4 | 8098 | Optional | `pmoves-deepresearch` | +| TensorZero | Standard | 2+3 | 3030 | No | `pmoves-tensorzero` | +| Flute Gateway | Standard | 2+5 | 8055 | Optional | `pmoves-flute` | +| PMOVES.YT | Standard | 5+4 | 8077 | Optional | `pmoves-yt` | +| Ultimate-TTS | Standard | 5+3 | 7861 | Recommended | `pmoves-ultimate-tts` | +| FFmpeg-Whisper | Standard | 5+4 | 8078 | Recommended | `pmoves-whisper` | +| Media-Video | Standard | 5+4 | 8079 | Required | `pmoves-media-video` | +| Jellyfin Bridge | Specialized | 5+1 | 8093 | No | `pmoves-jellyfin-bridge` | +| Cipher Memory | Specialized | 1+6 | 8096 | No | `pmoves-cipher` | +| Crush | Standard | 7+6 | - | No | `pmoves-crush` | +| EvoSwarm | Standard | 4+6 | 8113 | Required | `pmoves-evoswarm` | + +--- + +## Appendix B: Environment Variables Reference + +### Common PMOVES Environment Variables + +```bash +# Supabase (required for most agents) +SUPABASE_URL=https://xxx.supabase.co +SUPABASE_SERVICE_ROLE_KEY=xxx + +# NATS (event bus) +NATS_URL=nats://localhost:4222 + +# LLM Providers (at least one required) +OPENAI_API_KEY=xxx +ANTHROPIC_API_KEY=xxx +GOOGLE_API_KEY=xxx + +# Vector DBs +QDRANT_URL=http://localhost:6333 +NEO4J_URI=bolt://localhost:7687 +NEO4J_USER=neo4j +NEO4J_PASSWORD=xxx + +# Search +MEILISEARCH_URL=http://localhost:7700 +MEILISEARCH_KEY=xxx + +# Storage (Supabase Storage S3) +MINIO_ENDPOINT=http://localhost:65421/storage/v1/s3 +MINIO_ACCESS_KEY=xxx +MINIO_SECRET_KEY=xxx + +# GPU +CUDA_VISIBLE_DEVICES=0 +``` + +--- + +## Appendix C: Pinokio Pterm CLI Reference + +```bash +# Start an app +pterm start /path/to/app + +# Stop an app +pterm stop /path/to/app + +# View logs +pterm logs /path/to/app + +# Send notification +pterm push "Message" + +# Clipboard access +pterm clipboard read +pterm clipboard write "text" + +# File picker +pterm filepicker +pterm filepicker --directory + +# Version +pterm --version +``` + +--- + +**Document maintained by:** PMOVES Documentation Team +**Related Documentation:** +- [PMOVES Agent Registry](../../config/agent_registry.yaml) +- [PBnJ README](../../../pbnj/README.md) +- [Pinokio Documentation](https://pinokio.co/docs) +- [CLAUDE.md (Pinokio Rules)](../../../CLAUDE.md) diff --git a/pmoves/docs/PINOKIO_TESTING_DEPLOYMENT.md b/pmoves/docs/PINOKIO_TESTING_DEPLOYMENT.md new file mode 100644 index 0000000000..a65517712e --- /dev/null +++ b/pmoves/docs/PINOKIO_TESTING_DEPLOYMENT.md @@ -0,0 +1,782 @@ +# PMOVES Pinokio Testing & Deployment Workflow + +> **Last Updated:** 2026-03-22 +> **Related:** [PINOKIO_PACKAGING_GUIDE.md](./PINOKIO_PACKAGING_GUIDE.md) | [PINOKIO_EXAMPLE_MANIFESTS.md](./PINOKIO_EXAMPLE_MANIFESTS.md) + +This document covers the complete workflow for testing, validating, and deploying PMOVES agents as Pinokio packages. + +--- + +## Table of Contents + +1. [Development Workflow](#development-workflow) +2. [Local Testing](#local-testing) +3. [Validation Checklist](#validation-checklist) +4. [CI/CD Integration](#cicd-integration) +5. [Distribution & Publishing](#distribution--publishing) +6. [Maintenance & Updates](#maintenance--updates) + +--- + +## Development Workflow + +### Phase 1: Package Creation + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Package Creation Flow │ +├─────────────────────────────────────────────────────────────────┤ +│ 1. Create folder structure │ +│ └── pbnj/pinokio/api/pmoves-agent-name/ │ +│ │ +│ 2. Create pinokio.json (metadata) │ +│ └── Define title, description, requirements │ +│ │ +│ 3. Create pinokio/pinokio.js (menu) │ +│ └── Define menu items, default states │ +│ │ +│ 4. Create pinokio/install.js │ +│ └── Clone repo, install dependencies │ +│ │ +│ 5. Create pinokio/start.js │ +│ └── Launch command, URL capture │ +│ │ +│ 6. Create pinokio/reset.js (optional) │ +│ └── Cleanup script │ +│ │ +│ 7. Create README.md │ +│ └── User-facing documentation │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Phase 2: Local Testing + +```bash +# 1. Create symlink to Pinokio apps directory +ln -s $(pwd)/pbnj/pinokio/api/pmoves-agent-name ~/pinokio/api/pmoves-agent-name + +# 2. Restart Pinokio or refresh apps +# Pinokio will auto-detect the new app + +# 3. Test via Pinokio UI +# Open Pinokio → Find app → Click Install → Click Start +``` + +### Phase 3: Validation + +Run through the complete validation checklist (see below) before publishing. + +### Phase 4: Publishing + +```bash +# 1. Commit to repository +git add pbnj/pinokio/api/pmoves-agent-name/ +git commit -m "feat(pinokio): add PMOVES Agent Name launcher" + +# 2. Tag release +git tag pinokio-agent-name-v1.0.0 + +# 3. Push to GitHub +git push origin main --tags + +# 4. Submit to Pinokio registry (optional) +# Visit https://pinokio.computer/submit +``` + +--- + +## Local Testing + +### Method 1: Symlink Development + +Best for active development with hot-reload: + +```bash +# macOS/Linux +ln -s /path/to/PMOVES.AI/pbnj/pinokio/api/pmoves-agent-name \ + ~/pinokio/api/pmoves-agent-name + +# Windows (PowerShell - Admin) +New-Item -ItemType SymbolicLink ` + -Path "$env:USERPROFILE\pinokio\api\pmoves-agent-name" ` + -Target "C:\path\to\PMOVES.AI\pbnj\pinokio\api\pmoves-agent-name" +``` + +**Benefits:** +- Changes reflect immediately in Pinokio +- No need to copy files after each edit +- Full Pinokio environment testing + +### Method 2: Pterm CLI Testing + +Test individual scripts without opening Pinokio UI: + +```bash +# Test install script +pterm start /path/to/pmoves-agent-name/install.js + +# Test start script +pterm start /path/to/pmoves-agent-name/start.js + +# View logs +pterm logs /path/to/pmoves-agent-name + +# Stop running script +pterm stop /path/to/pmoves-agent-name/start.js +``` + +### Method 3: Direct Script Testing + +For debugging script logic, test JavaScript directly: + +```bash +# Navigate to pinokio folder +cd pbnj/pinokio/api/pmoves-agent-name/pinokio + +# Test script with Node.js (limited - no Pinokio APIs) +node -e "const script = require('./install.js'); console.log(JSON.stringify(script, null, 2))" +``` + +### Testing Matrix + +Test your package across all supported configurations: + +| Platform | Architecture | GPU | Status | +|----------|--------------|-----|--------| +| Windows | x64 | NVIDIA | ☐ | +| Windows | x64 | None | ☐ | +| macOS | arm64 (M1/M2) | Metal | ☐ | +| macOS | x64 (Intel) | None | ☐ | +| Linux | x64 | NVIDIA | ☐ | +| Linux | x64 | AMD | ☐ | +| Linux | arm64 | None | ☐ | + +--- + +## Validation Checklist + +### Pre-Flight Checks + +#### Metadata Validation + +- [ ] `pinokio.json` exists and is valid JSON +- [ ] `version` field is `"1"` +- [ ] `title` is concise and descriptive +- [ ] `description` explains what the agent does +- [ ] `icon` path points to existing file (512x512 PNG recommended) +- [ ] `platform` array includes all supported OS +- [ ] `arch` array includes all supported architectures +- [ ] `gpu` field set appropriately (`required`, `optional`, or omitted) + +#### Script Validation + +- [ ] `pinokio/pinokio.js` exists and exports valid menu structure +- [ ] `pinokio/install.js` exists and completes without errors +- [ ] `pinokio/start.js` exists and captures URL correctly +- [ ] `pinokio/reset.js` exists (optional but recommended) +- [ ] All scripts use correct API methods (see API Reference) + +#### Menu Validation + +- [ ] Default states change based on installation status +- [ ] Running state reflected in menu items +- [ ] URLs appear when service is running +- [ ] Disabled states work correctly (GPU, platform restrictions) +- [ ] Menu items have clear titles and descriptions + +### Functional Testing + +#### Installation Test + +```bash +# Clean slate test +rm -rf ~/pinokio/api/pmoves-agent-name/app + +# Run install +pterm start /path/to/pmoves-agent-name/install.js + +# Verify +ls ~/pinokio/api/pmoves-agent-name/app/ +# Should show: cloned repo, .installed marker +``` + +**Checklist:** +- [ ] Repository clones successfully +- [ ] Dependencies install without errors +- [ ] `.installed` marker created +- [ ] Example config files created (if applicable) +- [ ] User notified of completion + +#### Start Test + +```bash +# Start the service +pterm start /path/to/pmoves-agent-name/start.js + +# Check if running +curl http://localhost:PORT/healthz + +# Check logs +pterm logs /path/to/pmoves-agent-name +``` + +**Checklist:** +- [ ] Service starts without errors +- [ ] URL captured correctly via regex +- [ ] URL stored in local variables +- [ ] Service accessible at captured URL +- [ ] Health endpoint returns 200 + +#### Menu State Test + +```bash +# Before install +# - "Install" should be default +# - "Start" should be disabled/hidden + +# After install +# - "Start" should be default +# - "Install" should show "Installed" state + +# After start +# - "Open WebUI" should be default +# - URL should be accessible +``` + +#### Reset Test + +```bash +# Run reset +pterm start /path/to/pmoves-agent-name/reset.js + +# Verify cleanup +ls ~/pinokio/api/pmoves-agent-name/app/ +# Should NOT show: .installed, node_modules, .env +``` + +**Checklist:** +- [ ] Dependencies removed +- [ ] Config files cleaned (optional) +- [ ] Installation marker removed +- [ ] User notified of reset + +### Cross-Platform Testing + +#### Windows-Specific Checks + +- [ ] Shell commands use Windows-compatible syntax +- [ ] Path separators handled correctly +- [ ] Environment variables set properly +- [ ] PowerShell vs CMD compatibility + +```javascript +// Example: Windows-compatible command +{ + method: "shell.run", + params: { + message: platform === "win32" + ? "pip install -r requirements.txt" + : "pip3 install -r requirements.txt" + } +} +``` + +#### macOS-Specific Checks + +- [ ] Homebrew dependencies documented +- [ ] M1/M2 (arm64) compatibility +- [ ] Metal GPU acceleration (if applicable) + +#### Linux-Specific Checks + +- [ ] System dependencies documented (ffmpeg, etc.) +- [ ] NVIDIA CUDA setup for GPU services +- [ ] Systemd service compatibility (optional) + +### GPU Testing + +#### GPU Detection Test + +```javascript +// Verify GPU detection works +{ + method: "notify", + params: { + message: `GPU detected: ${gpu || "None"}\nArchitecture: ${arch}` + } +} +``` + +#### GPU Service Test + +- [ ] Service starts with GPU if available +- [ ] Falls back to CPU if no GPU +- [ ] GPU memory usage is reasonable +- [ ] CUDA errors handled gracefully + +--- + +## CI/CD Integration + +### GitHub Actions Workflow + +Create `.github/workflows/pinokio-validate.yml`: + +```yaml +name: Validate Pinokio Package + +on: + push: + paths: + - 'pbnj/pinokio/api/**' + pull_request: + paths: + - 'pbnj/pinokio/api/**' + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Validate JSON files + run: | + for file in pbnj/pinokio/api/*/pinokio.json; do + echo "Validating $file" + python -m json.tool "$file" > /dev/null || exit 1 + done + + - name: Validate JavaScript files + run: | + for file in pbnj/pinokio/api/*/pinokio/*.js; do + echo "Checking syntax: $file" + node --check "$file" || exit 1 + done + + - name: Check required files + run: | + for dir in pbnj/pinokio/api/*/; do + name=$(basename "$dir") + echo "Checking $name..." + + # Required files + test -f "$dir/pinokio.json" || { echo "Missing pinokio.json in $name"; exit 1; } + test -f "$dir/README.md" || { echo "Missing README.md in $name"; exit 1; } + test -f "$dir/pinokio/pinokio.js" || { echo "Missing pinokio.js in $name"; exit 1; } + test -f "$dir/pinokio/install.js" || { echo "Missing install.js in $name"; exit 1; } + test -f "$dir/pinokio/start.js" || { echo "Missing start.js in $name"; exit 1; } + done + + - name: Validate metadata + run: | + python3 << 'EOF' + import json + import os + import sys + + required_fields = ['version', 'title', 'description', 'icon'] + valid_platforms = ['win32', 'darwin', 'linux'] + valid_archs = ['x64', 'arm64'] + + errors = [] + + for root, dirs, files in os.walk('pbnj/pinokio/api'): + if 'pinokio.json' in files: + path = os.path.join(root, 'pinokio.json') + with open(path) as f: + try: + data = json.load(f) + except json.JSONError as e: + errors.append(f"{path}: Invalid JSON - {e}") + continue + + # Check required fields + for field in required_fields: + if field not in data: + errors.append(f"{path}: Missing required field '{field}'") + + # Validate version + if data.get('version') != '1': + errors.append(f"{path}: version must be '1'") + + # Validate platforms + if 'platform' in data: + for p in data['platform']: + if p not in valid_platforms: + errors.append(f"{path}: Invalid platform '{p}'") + + # Validate architectures + if 'arch' in data: + for a in data['arch']: + if a not in valid_archs: + errors.append(f"{path}: Invalid arch '{a}'") + + if errors: + for e in errors: + print(f"ERROR: {e}") + sys.exit(1) + + print("All metadata valid!") + EOF +``` + +### Pre-commit Hook + +Create `.git/hooks/pre-commit`: + +```bash +#!/bin/bash + +# Validate Pinokio packages before commit +changed=$(git diff --cached --name-only | grep 'pbnj/pinokio/api') + +if [ -n "$changed" ]; then + echo "Validating Pinokio packages..." + + # Check JSON syntax + for file in $(echo "$changed" | grep 'pinokio.json'); do + if [ -f "$file" ]; then + python3 -m json.tool "$file" > /dev/null || { + echo "ERROR: Invalid JSON in $file" + exit 1 + } + fi + done + + # Check JS syntax + for file in $(echo "$changed" | grep '\.js$'); do + if [ -f "$file" ]; then + node --check "$file" || { + echo "ERROR: Invalid JavaScript in $file" + exit 1 + } + fi + done + + echo "✓ All Pinokio files valid" +fi +``` + +--- + +## Distribution & Publishing + +### Internal Distribution (PMOVES Team) + +PMOVES packages are distributed internally via the monorepo: + +``` +PMOVES.AI/ +└── pbnj/ + └── pinokio/ + └── api/ + ├── pmoves-agent-zero/ + ├── pmoves-archon/ + ├── pmoves-hirag/ + ├── pmoves-services/ + └── pmoves-pbnj/ +``` + +**Installation Methods:** + +1. **Symlink (Recommended for Development)** + ```bash + # One-time setup + ln -s /path/to/PMOVES.AI/pbnj/pinokio/api/* ~/pinokio/api/ + ``` + +2. **Copy (For Production)** + ```bash + # Copy specific package + cp -r pbnj/pinokio/api/pmoves-agent-zero ~/pinokio/api/ + ``` + +3. **Install Script** + ```bash + # Run PMOVES installer + ./pbnj/scripts/install-pinokio-apps.sh + ``` + +### Public Distribution (Pinokio Registry) + +To publish to the public Pinokio registry: + +1. **Prepare Repository** + ```bash + # Create standalone repo for the launcher + mkdir pmoves-agent-zero-pinokio + cd pmoves-agent-zero-pinokio + git init + + # Copy launcher files + cp -r /path/to/PMOVES.AI/pbnj/pinokio/api/pmoves-agent-zero/* . + + # Create README + echo "# PMOVES Agent Zero - Pinokio Launcher" > README.md + + # Commit + git add . + git commit -m "Initial release" + git tag v1.0.0 + ``` + +2. **Push to GitHub** + ```bash + git remote add origin https://github.com/POWERFULMOVES/pmoves-agent-zero-pinokio + git push -u origin main --tags + ``` + +3. **Submit to Pinokio** + - Visit https://pinokio.computer + - Click "Submit App" + - Enter repository URL + - Wait for review + +### Version Management + +Follow semantic versioning for launcher updates: + +| Version Change | Meaning | +|----------------|---------| +| `v1.0.0` → `v1.0.1` | Bug fixes, script improvements | +| `v1.0.0` → `v1.1.0` | New features, menu additions | +| `v1.0.0` → `v2.0.0` | Breaking changes, restructure | + +--- + +## Maintenance & Updates + +### Update Workflow + +When updating a PMOVES Pinokio package: + +```bash +# 1. Make changes to launcher files +vim pbnj/pinokio/api/pmoves-agent-name/pinokio/start.js + +# 2. Test locally +pterm start pbnj/pinokio/api/pmoves-agent-name/start.js + +# 3. Update version in pinokio.json (if needed) +# Note: "version" field is schema version, NOT app version + +# 4. Commit changes +git add pbnj/pinokio/api/pmoves-agent-name/ +git commit -m "fix(pinokio): update start script for Agent Name" + +# 5. Update changelog +echo "- Fixed start script timeout issue" >> pbnj/pinokio/api/pmoves-agent-name/CHANGELOG.md +``` + +### Monitoring Deployed Packages + +#### Health Check Script + +Create `pbnj/pinokio/api/pmoves-agent-name/pinokio/health.js`: + +```javascript +// pinokio/health.js +// Health check for deployed Agent Zero + +module.exports = { + run: [ + { + method: "request", + params: { + uri: "http://localhost:8080/healthz", + method: "GET", + timeout: 5000 + } + }, + { + method: "notify", + params: { + title: input.status === 200 ? "✅ Agent Zero Healthy" : "❌ Agent Zero Unhealthy", + message: input.status === 200 + ? "All systems operational" + : `Health check failed: ${input.error || "Unknown error"}` + } + } + ] +} +``` + +#### Log Rotation + +Pinokio automatically handles log rotation, but you can add custom cleanup: + +```javascript +// pinokio/cleanup-logs.js +// Clean old logs + +module.exports = { + run: [ + { + method: "shell.run", + params: { + message: platform === "win32" + ? "forfiles /p logs /m *.log /d -7 /c \"cmd /c del @path\"" + : "find logs -name '*.log' -mtime +7 -delete", + throws: false + } + } + ] +} +``` + +### Deprecation Process + +When deprecating a package: + +1. **Add deprecation notice to README.md:** + ```markdown + > ⚠️ **DEPRECATED**: This package is no longer maintained. + > Please use [pmoves-new-agent](link) instead. + ``` + +2. **Update pinokio.json:** + ```json + { + "deprecated": true, + "deprecation_message": "Use pmoves-new-agent instead", + "replacement": "https://github.com/POWERFULMOVES/pmoves-new-agent-pinokio" + } + ``` + +3. **Create final release:** + ```bash + git tag v1.0.0-deprecated + git push origin v1.0.0-deprecated + ``` + +--- + +## Troubleshooting Guide + +### Common Issues + +#### Script Not Found + +``` +Error: Script not found: install.js +``` + +**Solution:** Check file path and ensure you're in the correct directory: +```bash +ls -la pbnj/pinokio/api/pmoves-agent-name/pinokio/ +``` + +#### Regex Not Matching + +``` +Error: Timeout waiting for event +``` + +**Solution:** Debug the regex pattern: +```javascript +{ + method: "shell.run", + params: { + message: "npm start", + on: [ + { + event: "/.*/", // Match everything to see output + done: false + }, + { + event: "/http:\\/\\/[0-9.:]+/", // Your actual pattern + done: true + } + ] + } +} +``` + +#### Environment Variables Not Loading + +**Solution:** Check `.env` file encoding (must be UTF-8) and format: +```bash +# Verify .env format +cat app/.env +# Should be: KEY=value (no quotes for simple values) +``` + +#### GPU Not Detected + +**Solution:** Verify GPU detection: +```javascript +{ + method: "notify", + params: { + message: `GPU: ${gpu}\nArch: ${arch}\nPlatform: ${platform}` + } +} +``` + +### Debug Mode + +Enable verbose logging in scripts: + +```javascript +module.exports = { + debug: true, // Enable debug output + run: [ + // ... scripts + ] +} +``` + +### Getting Help + +1. **Pinokio Documentation:** https://pinokio.co/docs +2. **PMOVES Discord:** https://discord.gg/pmoves +3. **GitHub Issues:** https://github.com/POWERFULMOVES/PMOVES.AI/issues +4. **Pinokio Community:** https://github.com/pinokio/community + +--- + +## Quick Reference + +### File Checklist + +``` +pmoves-agent-name/ +├── pinokio.json ☐ Required - Metadata +├── README.md ☐ Required - Documentation +├── icon.png ☐ Required - 512x512 icon +├── CHANGELOG.md ☐ Optional - Version history +└── pinokio/ + ├── pinokio.js ☐ Required - Menu definition + ├── install.js ☐ Required - Installation + ├── start.js ☐ Required - Launch script + ├── reset.js ☐ Recommended - Cleanup + ├── update.js ☐ Optional - Update script + ├── health.js ☐ Optional - Health check + └── logs/ ☐ Auto-created - Logs +``` + +### Command Reference + +```bash +# Install app +pterm start /path/to/app/install.js + +# Start app +pterm start /path/to/app/start.js + +# Stop app +pterm stop /path/to/app/start.js + +# View logs +pterm logs /path/to/app + +# Check status +pterm status /path/to/app +``` + +--- + +**Related Documentation:** +- [PINOKIO_PACKAGING_GUIDE.md](./PINOKIO_PACKAGING_GUIDE.md) - Complete API reference +- [PINOKIO_EXAMPLE_MANIFESTS.md](./PINOKIO_EXAMPLE_MANIFESTS.md) - Copy-paste examples +- [pbnj/pinokio/api/](../../../pbnj/pinokio/api/) - Existing implementations