Scenarios - memo-inbox: mirrored by copying; the live directory was not moved or modified and the service was not restarted. All four tracked files match byte for byte (pi-diff.sh reports SAME). Marked deploy = "mirror" so deploy-scenario.sh refuses --apply: applying a mirror would invert the direction of truth and could change a service in daily use. - curator: target configuration, not yet deployed. .pi/SYSTEM.md replaces pi's coding-assistant prompt; durable role text is in .pi/APPEND_SYSTEM.md; profile.toml is the single source of truth for the launch contract. - pi-grok: registered only. It is genuinely a coding agent, so the isolation baseline does not apply in full. Corrections to the documentation, found by testing rather than by reading - AGENTS.override.md does NOT block parent-directory context files; it only shadows its own directory. Verified: with an override file in the workspace, a marker in /tmp/AGENTS.md still reached the system prompt. The only effective switch is --no-context-files, so durable role text must live in .pi/APPEND_SYSTEM.md, which is a system-prompt file and unaffected by -nc. Verified end state: no coding-assistant framing, no pi-docs block, own identity and role text present, no parent pollution, only own skills/tools. - PI_CODING_AGENT_DIR isolates settings/models/auth/trust/extensions/skills/ prompts/themes under the agent directory -- stronger than the --no-* flags because it also repoints credentials -- but does NOT cover ~/.agents/skills. Measured: find-skills, modsearch and summarize still leak. So it complements --no-skills rather than replacing it. - --append-system-prompt accepts a file path, which pi-grok relies on. - cwd is what anchors .pi discovery: a probe that forgot cwd silently lost .pi/SYSTEM.md and kept the coding-assistant persona. Tooling (all dry-run by default; none of them restarts a service) - pi-diff.sh: compares tracked config against the live install in both directions, with a key-redacted comparison for models.json - deploy-scenario.sh: installs a workspace and renders profile.toml into .pi/launch.json, then checks that every referenced path exists - deploy-runtime.sh: renders models.json from its template, refusing placeholder or missing keys. Verified byte-identical to the live file - pi-backup.sh / pi-restore.sh: archives outside the repo, sha256 manifest verified before any restore, live paths preserved rather than overwritten Fixed while testing: pi-backup.sh compared the destination against the repo root literally, so a relative --dest ./backups wrote credential archives into the work tree. Now canonicalised with realpath; ./backups, an absolute in-repo path and ./docs/../backups are all refused.
111 lines
3.8 KiB
Bash
Executable File
111 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ---------------------------------------------------------------------------
|
|
# Install the user-level Pi configuration from runtime/ into ~/.pi/agent,
|
|
# rendering models.json from its template.
|
|
#
|
|
# Dry run by default.
|
|
#
|
|
# Usage:
|
|
# scripts/deploy-runtime.sh
|
|
# scripts/deploy-runtime.sh --apply
|
|
# ---------------------------------------------------------------------------
|
|
# shellcheck source=lib/common.sh
|
|
. "$(dirname "${BASH_SOURCE[0]}")/lib/common.sh"
|
|
|
|
APPLY=0
|
|
[ "${1:-}" = "--apply" ] && APPLY=1
|
|
|
|
SRC="$REPO_ROOT/runtime/agent"
|
|
DST="$HOME/.pi/agent"
|
|
SECRETS="$REPO_ROOT/secrets/zenmux.env"
|
|
|
|
head1 "deploy runtime -> $DST"
|
|
[ "$APPLY" -eq 1 ] || info "${C_DIM}(dry run; pass --apply to write)${C_OFF}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# models.json: render the template
|
|
# ---------------------------------------------------------------------------
|
|
head1 "models.json"
|
|
if [ ! -f "$SECRETS" ]; then
|
|
die "missing $SECRETS
|
|
cp secrets/zenmux.env.example secrets/zenmux.env && chmod 600 secrets/zenmux.env
|
|
then fill in ZENMUX_API_KEY"
|
|
fi
|
|
PERM="$(stat -c '%a' "$SECRETS")"
|
|
[ "$PERM" = "600" ] || warn "$SECRETS has mode $PERM; expected 600"
|
|
|
|
# shellcheck disable=SC1090
|
|
set -a; . "$SECRETS"; set +a
|
|
[ -n "${ZENMUX_API_KEY:-}" ] || die "ZENMUX_API_KEY is empty in $SECRETS"
|
|
case "$ZENMUX_API_KEY" in
|
|
'<REDACTED>'|'<'*'>'|'${'*) die "ZENMUX_API_KEY in $SECRETS is still a placeholder" ;;
|
|
esac
|
|
|
|
RENDERED="$(python3 - "$SRC/models.json.template" <<'PY'
|
|
import json, os, pathlib, re, sys
|
|
text = pathlib.Path(sys.argv[1]).read_text()
|
|
|
|
def sub(match):
|
|
name = match.group(1)
|
|
value = os.environ.get(name)
|
|
if not value:
|
|
sys.exit(f"unresolved placeholder ${{{name}}}")
|
|
return json.dumps(value)[1:-1] # escape for a JSON string context
|
|
|
|
text = re.sub(r'\$\{([A-Z_][A-Z0-9_]*)\}', sub, text)
|
|
json.loads(text) # fail early on malformed output
|
|
sys.stdout.write(text)
|
|
PY
|
|
)" || die "cannot render models.json.template"
|
|
|
|
if [ -f "$DST/models.json" ] && [ "$RENDERED" = "$(cat "$DST/models.json")" ]; then
|
|
ok "models.json already current"
|
|
else
|
|
info " write models.json ${C_DIM}(rendered, mode 600)${C_OFF}"
|
|
if [ "$APPLY" -eq 1 ]; then
|
|
install -d -m 700 "$DST"
|
|
printf '%s' "$RENDERED" > "$DST/models.json"
|
|
chmod 600 "$DST/models.json"
|
|
fi
|
|
fi
|
|
unset ZENMUX_API_KEY
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Everything else is copied verbatim
|
|
# ---------------------------------------------------------------------------
|
|
head1 "settings, extensions, prompts"
|
|
CHANGES=0
|
|
while IFS= read -r rel; do
|
|
src="$SRC/$rel"
|
|
dst="$DST/$rel"
|
|
if [ -f "$dst" ] && cmp -s "$src" "$dst"; then continue; fi
|
|
CHANGES=$((CHANGES + 1))
|
|
[ -f "$dst" ] && info " update $rel" || info " create $rel"
|
|
if [ "$APPLY" -eq 1 ]; then
|
|
install -d -m 700 "$(dirname "$dst")"
|
|
install -m 600 "$src" "$dst"
|
|
fi
|
|
done < <(cd "$SRC" && find settings.json extensions prompts -type f 2>/dev/null | sort)
|
|
[ "$CHANGES" -eq 0 ] && ok "already current"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Report host-side files this repository deliberately does not own
|
|
# ---------------------------------------------------------------------------
|
|
head1 "not managed here"
|
|
for f in auth.json trust.json models-store.json; do
|
|
[ -e "$DST/$f" ] && info " ${C_DIM}$f (host state)${C_OFF}"
|
|
done
|
|
if [ -e "$DST/extensions/herdr-agent-state.ts" ]; then
|
|
info " ${C_DIM}extensions/herdr-agent-state.ts (installed by herdr)${C_OFF}"
|
|
fi
|
|
|
|
printf '\n'
|
|
if [ "$APPLY" -eq 1 ]; then
|
|
ok "applied"
|
|
info ""
|
|
info "Restart any Pi gateway that should pick up new provider settings:"
|
|
info " systemctl --user restart curator.service pi-memo-telegram.service"
|
|
else
|
|
info "${C_DIM}dry run complete; re-run with --apply${C_OFF}"
|
|
fi
|