Implement install stages S3–S5: package, policy, and skills sync.

Attach/onboard sandbox from agents/hermes, additive OpenShell policy overlays, and nemohermes skill install for scaffold skills. No doctor/connect and no push.
This commit is contained in:
Ty
2026-07-27 12:13:25 -07:00
parent e5e179e541
commit 0198ab6881
38 changed files with 1422 additions and 51 deletions
+169
View File
@@ -0,0 +1,169 @@
#!/usr/bin/env bash
# scripts/install/s4-sandbox.sh — S4: sandbox verification / onboard
#
# Two modes:
# attach (default) — verify existing sandbox is healthy; no destructive ops
# onboard — create new sandbox from agent package (clean host only)
#
# Platform-first: all mutations via nemohermes. Never hand-edit in-sandbox config.
#
# Usage:
# ./scripts/install/s4-sandbox.sh # attach mode (default)
# ./scripts/install/s4-sandbox.sh --mode attach
# ./scripts/install/s4-sandbox.sh --mode onboard
# ./scripts/install/s4-sandbox.sh --mode onboard --dry-run
# ./scripts/install/s4-sandbox.sh --help
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Source shared helpers
# shellcheck source=../lib/common.sh
source "$SCRIPT_DIR/../lib/common.sh"
# shellcheck source=../lib/env.sh
source "$SCRIPT_DIR/../lib/env.sh"
# ── Defaults ───────────────────────────────────────────────────────────────
MODE="${LUMINA_INSTALL_MODE:-attach}"
DRY_RUN=0
# ── Parse args ─────────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h)
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
S4: Sandbox verification or onboard.
Options:
--mode <attach|onboard> Install mode (default: attach)
--dry-run Preview onboard without executing
--help Show this help
Modes:
attach Verify existing sandbox is healthy (UAT default)
onboard Create new sandbox from agent package (clean host)
Examples:
$(basename "$0") # attach mode
$(basename "$0") --mode onboard # onboard mode
$(basename "$0") --mode onboard --dry-run # onboard dry-run
EOF
exit 0
;;
--mode)
shift
MODE="${1:-}"
if [[ -z "$MODE" ]]; then
log_error "--mode requires a value (attach or onboard)"
exit 1
fi
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
*)
log_error "Unknown argument: $1"
exit 1
;;
esac
done
# ── Validate mode ──────────────────────────────────────────────────────────
if [[ "$MODE" != "attach" && "$MODE" != "onboard" ]]; then
log_error "Invalid mode: $MODE (must be 'attach' or 'onboard')"
exit 1
fi
log_section "S4: Sandbox ($MODE mode)"
# ── Load .env ──────────────────────────────────────────────────────────────
load_env
# ── Validate required keys ─────────────────────────────────────────────────
validate_env || exit 1
# ── Check CLI prerequisites ────────────────────────────────────────────────
require_cmd nemohermes "Install nemohermes CLI (part of NemoClaw platform)"
SANDBOX_NAME="$(get_sandbox_name)"
AGENT_PKG_DIR="$REPO_ROOT/agents/hermes"
# ── Attach mode ────────────────────────────────────────────────────────────
do_attach() {
log_info "Attach mode: verifying sandbox '$SANDBOX_NAME'"
# Check sandbox exists and report status
if ! nemohermes "$SANDBOX_NAME" status &>/dev/null; then
log_error "Sandbox '$SANDBOX_NAME' not found or not reachable."
log_error "If this is a clean host, re-run with --mode onboard"
log_error "Or create the sandbox manually: nemohermes onboard"
return 1
fi
log_info "Sandbox '$SANDBOX_NAME' is healthy."
# Verify agent package exists (reference only in attach mode)
if [[ -d "$AGENT_PKG_DIR" ]]; then
log_info "Agent package found at $AGENT_PKG_DIR"
if [[ -f "$AGENT_PKG_DIR/skills-manifest/manifest.yaml" ]]; then
log_info "Skills manifest present — S5 will sync skills"
else
log_warn "Skills manifest not found — skills sync (S5) may be incomplete"
fi
else
log_warn "Agent package directory not found at $AGENT_PKG_DIR"
fi
log_info "S4 attach complete."
}
# ── Onboard mode ───────────────────────────────────────────────────────────
do_onboard() {
log_info "Onboard mode: preparing sandbox '$SANDBOX_NAME' from agent package"
# Verify agent package exists
if [[ ! -d "$AGENT_PKG_DIR" ]]; then
log_error "Agent package not found at $AGENT_PKG_DIR"
return 1
fi
# Check if sandbox already exists — do not destroy it
if nemohermes "$SANDBOX_NAME" status &>/dev/null; then
log_warn "Sandbox '$SANDBOX_NAME' already exists."
log_warn "Onboard mode does not destroy existing sandboxes."
log_warn "Switching to attach behavior for safety."
do_attach
return 0
fi
if [[ $DRY_RUN -eq 1 ]]; then
log_info "DRY-RUN: Would execute:"
log_info " nemohermes onboard --from-dir $AGENT_PKG_DIR"
log_info " (with inference from .env: $LUMINA_INFERENCE_BASE_URL)"
log_info "S4 onboard dry-run complete."
return 0
fi
# Onboard with agent package
log_info "Running nemohermes onboard with agent package…"
if nemohermes onboard --from-dir "$AGENT_PKG_DIR"; then
log_info "Sandbox '$SANDBOX_NAME' onboarded successfully."
else
log_error "Onboard failed. Check nemohermes logs for details."
log_error "You may need to run nemohermes onboard manually first."
return 1
fi
log_info "S4 onboard complete."
}
# ── Execute ────────────────────────────────────────────────────────────────
case "$MODE" in
attach) do_attach ;;
onboard) do_onboard ;;
esac
+197
View File
@@ -0,0 +1,197 @@
#!/usr/bin/env bash
# scripts/install/s5-policy-skills.sh — S5: policy apply + skills sync
#
# Applies Lumina policy overlays and syncs skills into the sandbox.
# Additive only: never removes existing presets.
#
# Platform-first: all mutations via nemohermes.
#
# Usage:
# ./scripts/install/s5-policy-skills.sh
# ./scripts/install/s5-policy-skills.sh --policy-only
# ./scripts/install/s5-policy-skills.sh --skills-only
# ./scripts/install/s5-policy-skills.sh --help
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Source shared helpers
# shellcheck source=../lib/common.sh
source "$SCRIPT_DIR/../lib/common.sh"
# shellcheck source=../lib/env.sh
source "$SCRIPT_DIR/../lib/env.sh"
# ── Defaults ───────────────────────────────────────────────────────────────
DO_POLICY=1
DO_SKILLS=1
POLICY_ONLY_SET=0
SKILLS_ONLY_SET=0
# ── Parse args ─────────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h)
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
S5: Apply policy overlays and sync skills into the sandbox.
Options:
--policy-only Apply policy overlays only (skip skills sync)
--skills-only Sync skills only (skip policy apply)
--help Show this help
Examples:
$(basename "$0") # policy + skills
$(basename "$0") --policy-only # policy only
$(basename "$0") --skills-only # skills only
EOF
exit 0
;;
--policy-only)
DO_POLICY=1
DO_SKILLS=0
POLICY_ONLY_SET=1
shift
;;
--skills-only)
DO_POLICY=0
DO_SKILLS=1
SKILLS_ONLY_SET=1
shift
;;
*)
log_error "Unknown argument: $1"
exit 1
;;
esac
done
# ── Validate mutually exclusive flags ──────────────────────────────────────
if [[ $POLICY_ONLY_SET -eq 1 && $SKILLS_ONLY_SET -eq 1 ]]; then
log_error "--policy-only and --skills-only are mutually exclusive"
exit 1
fi
if [[ $DO_POLICY -eq 0 && $DO_SKILLS -eq 0 ]]; then
log_error "Internal error: both DO_POLICY and DO_SKILLS are disabled"
exit 1
fi
log_section "S5: Policy + Skills sync"
# ── Load .env ──────────────────────────────────────────────────────────────
load_env
# ── Validate required keys ─────────────────────────────────────────────────
validate_env || exit 1
# ── Check CLI prerequisites ────────────────────────────────────────────────
require_cmd nemohermes "Install nemohermes CLI (part of NemoClaw platform)"
SANDBOX_NAME="$(get_sandbox_name)"
POLICY_DIR="$REPO_ROOT/policy/openshell/overlays"
SKILLS_DIR="$REPO_ROOT/skills"
# ── Verify sandbox exists ──────────────────────────────────────────────────
if ! nemohermes "$SANDBOX_NAME" status &>/dev/null 2>&1; then
log_error "Sandbox '$SANDBOX_NAME' not found. Run S4 first."
exit 1
fi
# ── Policy apply ───────────────────────────────────────────────────────────
apply_policy() {
log_section "S5a: Apply policy overlays"
if [[ ! -d "$POLICY_DIR" ]]; then
log_warn "Policy overlays directory not found: $POLICY_DIR"
return 0
fi
# Apply inference overlay (always apply at S5)
local inference_policy="$POLICY_DIR/inference.yaml"
if [[ -f "$inference_policy" ]]; then
log_info "Applying inference policy overlay…"
if nemohermes "$SANDBOX_NAME" policy-add --from-file "$inference_policy" --yes 2>&1; then
log_info "Inference policy overlay applied."
else
log_warn "Inference policy overlay may already be applied (idempotent)."
fi
else
log_warn "Inference policy overlay not found: $inference_policy"
fi
# List current policy for verification
log_info "Current policy presets:"
nemohermes "$SANDBOX_NAME" policy-list 2>&1 || log_warn "Could not list policy presets"
log_info "S5a policy apply complete."
}
# ── Skills sync ────────────────────────────────────────────────────────────
sync_skills() {
log_section "S5b: Sync skills"
if [[ ! -d "$SKILLS_DIR" ]]; then
log_warn "Skills directory not found: $SKILLS_DIR"
return 0
fi
local skill_count=0
local skill_ok=0
local skill_skip=0
local skill_fail=0
# Iterate skill directories (skip _lib and hidden dirs)
for skill_dir in "$SKILLS_DIR"/*/; do
# Skip if not a directory
[[ -d "$skill_dir" ]] || continue
local skill_name
skill_name="$(basename "$skill_dir")"
# Skip _lib (shared library, not a skill)
if [[ "$skill_name" == "_lib" ]]; then
log_info "Skipping _lib (shared library)"
continue
fi
skill_count=$((skill_count + 1))
# Check for SKILL.md (required by nemohermes skill install)
local skill_md="$skill_dir/SKILL.md"
if [[ ! -f "$skill_md" ]]; then
log_warn "Skipping '$skill_name': no SKILL.md found"
skill_skip=$((skill_skip + 1))
continue
fi
log_info "Installing skill: $skill_name"
if nemohermes "$SANDBOX_NAME" skill install "$skill_dir" 2>&1; then
log_info "$skill_name installed"
skill_ok=$((skill_ok + 1))
else
log_warn "$skill_name failed (may already be installed)"
skill_fail=$((skill_fail + 1))
fi
done
log_info "Skills sync summary: $skill_count found, $skill_ok installed, $skill_skip skipped, $skill_fail failed"
if [[ $skill_count -eq 0 ]]; then
log_warn "No skill directories found in $SKILLS_DIR"
fi
log_info "S5b skills sync complete."
}
# ── Execute ────────────────────────────────────────────────────────────────
if [[ $DO_POLICY -eq 1 ]]; then
apply_policy
fi
if [[ $DO_SKILLS -eq 1 ]]; then
sync_skills
fi
log_info "S5 complete."