Skip to content

Commit 8c2c88b

Browse files
committed
config: git-clone into ~/.pi/ at startup, no more copy step
- Dockerfile: removed config repo bake (image stays lean) - start.sh: clones config repo directly into ~/.pi/ (runtime dir) + fetches on every boot for freshness + sets PI_CODING_AGENT_DIR so Pi reads from config repo root - New lpb-config utility: status/update/reset/merge commands - Removed stale LPB_GITHUB_MCP_SERVER_VERSION from lpb.conf.env - Updated docs to reflect new design (no more ~/.pi/agent/ copy) - Config repo additions: .env.example, .gitignore
1 parent 975a7bc commit 8c2c88b

6 files changed

Lines changed: 227 additions & 81 deletions

File tree

Dockerfile

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -124,42 +124,8 @@ RUN set -eux; \
124124
rm -rf /opt/pi-src/.git /opt/pi-src/src /opt/pi-src/test /opt/pi-src/tests
125125

126126
# ── Extensions + Config ─────────────────────────────────────────────────────
127-
USER root
128-
RUN set -eux; \
129-
export PATH="/home/lpb/.npm-global/bin:${PATH}"; \
130-
export HOME="/home/lpb"; \
131-
mkdir -p /home/lpb/.local/pi-config; \
132-
rm -rf /tmp/pi-config-repo; \
133-
git clone --depth=1 --branch main https://github.com/localpibox/config.git /tmp/pi-config-repo 2>&1 && \
134-
(cd /tmp/pi-config-repo && cp -r . /home/lpb/.local/pi-config/) || echo "WARN: config clone failed"; \
135-
rm -rf /tmp/pi-config-repo; \
136-
# Copy lpb.conf.env into the image for start.sh to load at runtime \
137-
[ -f /opt/devstack/lpb.conf.env ] && cp /opt/devstack/lpb.conf.env /home/lpb/.local/lpb.conf.env || echo "WARN: no lpb.conf.env"; \
138-
mkdir -p /home/lpb/.pi/agent; \
139-
[ -f /home/lpb/.local/pi-config/settings.json ] && cp /home/lpb/.local/pi-config/settings.json /home/lpb/.pi/agent/ || echo "WARN: no settings.json"; \
140-
[ -f /home/lpb/.local/pi-config/mcp.json ] && cp /home/lpb/.local/pi-config/mcp.json /home/lpb/.pi/agent/ || echo "WARN: no mcp.json"; \
141-
[ -f /home/lpb/.local/pi-config/lpb-memory-config.json ] && cp /home/lpb/.local/pi-config/lpb-memory-config.json /home/lpb/.pi/agent/ || echo "WARN: no lpb-memory-config.json"; \
142-
[ -f /home/lpb/.local/pi-config/pi-defaults.json ] && cp /home/lpb/.local/pi-config/pi-defaults.json /home/lpb/.pi/agent/ || echo "WARN: no pi-defaults.json"; \
143-
[ -f /home/lpb/.local/pi-config/subagents.json ] && cp /home/lpb/.local/pi-config/subagents.json /home/lpb/.pi/agent/ || echo "WARN: no subagents.json"; \
144-
[ -f /home/lpb/.local/pi-config/models.json ] && cp /home/lpb/.local/pi-config/models.json /home/lpb/.pi/agent/ || echo "WARN: no models.json"; \
145-
[ -f /home/lpb/.local/pi-config/AGENTS.md ] && cp /home/lpb/.local/pi-config/AGENTS.md /home/lpb/.pi/agent/ || echo "WARN: no AGENTS.md"; \
146-
[ -f /home/lpb/.local/pi-config/SYSTEM.md ] && cp /home/lpb/.local/pi-config/SYSTEM.md /home/lpb/.pi/agent/ || true; \
147-
[ -f /home/lpb/.local/pi-config/APPEND_SYSTEM.md ] && cp /home/lpb/.local/pi-config/APPEND_SYSTEM.md /home/lpb/.pi/agent/ || true; \
148-
mkdir -p /home/lpb/.pi/agent/skills; \
149-
if [ -d /home/lpb/.local/pi-config/skills ]; then \
150-
for d in /home/lpb/.local/pi-config/skills/*/; do \
151-
[ -d "$d" ] || continue; \
152-
name=$(basename "$d"); \
153-
mkdir -p "/home/lpb/.pi/agent/skills/$name"; \
154-
cp "$d"* "/home/lpb/.pi/agent/skills/$name/" 2>/dev/null || true; \
155-
done; \
156-
fi; \
157-
mkdir -p /home/lpb/.pi/agent/agents; \
158-
if [ -d /home/lpb/.local/pi-config/agents ]; then \
159-
rsync -a --delete /home/lpb/.local/pi-config/agents/ /home/lpb/.pi/agent/agents/ 2>/dev/null || \
160-
cp -r /home/lpb/.local/pi-config/agents/* /home/lpb/.pi/agent/agents/ 2>/dev/null || true; \
161-
fi; \
162-
chown -R 1000:1000 /home/lpb/.pi /home/lpb/.local
127+
# Config repo is cloned at container start by start.sh — the image stays lean.
128+
USER lpb
163129

164130
# ═══════════════════════════════════════════════════════════════════════════
165131
# CLI IMAGE
@@ -175,7 +141,9 @@ COPY support/validate-subagent-output.ts /opt/pi-support/validate-subagent-outpu
175141
COPY support/config/ /opt/pi-support/config/
176142
COPY support/docs/ /opt/pi-support/docs/
177143
COPY support/schemas/ /opt/pi-support/schemas/
178-
RUN chmod +x /opt/pi-support/browser-state-cleanup.sh
144+
COPY support/lpb-config /opt/pi-support/lpb-config
145+
RUN chmod +x /opt/pi-support/browser-state-cleanup.sh \
146+
&& chmod +x /opt/pi-support/lpb-config
179147

180148
# ── Devstack deployment scripts ──
181149
COPY support/install-browser.sh /opt/devstack/install-browser.sh
@@ -187,11 +155,13 @@ COPY support/entrypoint-cli.sh /opt/devstack/entrypoint-cli.sh
187155
RUN ln -sf /opt/devstack/install-browser.sh /usr/local/bin/install-browser \
188156
&& ln -sf /opt/devstack/validate.sh /usr/local/bin/validate-devstack \
189157
&& ln -sf /opt/pi-support/install-openspec.sh /usr/local/bin/install-openspec \
158+
&& ln -sf /opt/pi-support/lpb-config /usr/local/bin/lpb-config \
190159
&& chmod +x /opt/devstack/install-browser.sh \
191160
/opt/devstack/validate.sh \
192161
/opt/devstack/start.sh \
193162
/opt/devstack/entrypoint-cli.sh \
194-
/opt/pi-support/install-openspec.sh
163+
/opt/pi-support/install-openspec.sh \
164+
/opt/pi-support/lpb-config
195165

196166
RUN mkdir -p /home/lpb/.agent-browser/sessions && chown -R 1000:1000 /home/lpb/.agent-browser
197167

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ cd ~/.local/pi-config
290290
git remote set-url origin https://github.com/<you>/config.git
291291
git pull --ff-only origin <your-branch>
292292
# re-seed the runtime copy from your preset
293-
cp settings.json mcp.json pi-defaults.json subagents.json ~/.pi/agent/ 2>/dev/null || true
293+
# Config repo IS ~/.pi/ — managed by start.sh at container startup
294294
```
295295

296296
### Repointing extensions at runtime (no rebuild)

doc/config-utility.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ container).
66

77
## Scope & operating model
88

9-
- The utility's **only domain is the runtime config copy** `~/.pi/agent/`
10-
(`settings.json`, `mcp.json`, `pi-defaults.json`, `subagents.json`,
11-
`lpb-memory-config.json`, `skills/`, `agents/`).
12-
- `localpibox/config` is a **preset** — a bootstrap seed copied to
13-
`~/.pi/agent/` at first run. The utility **never commits back to it**. Drift
9+
- The utility **manages the config repo** (git-cloned to `~/.pi/` by start.sh).
10+
Key files: `mcp.json`, `settings.json`, `AGENTS.md`, `lpb-memory-config.json`, `agents/`, `skills/`.
11+
12+
- `localpibox/config` is **git-cloned at startup** to `~/.pi/` — Pi reads directly from the repo root. The utility **never commits back to it**. Drift
1413
from the preset is expected (runtime is the authoritative, user-specific
1514
copy).
1615
- The utility lives **inside the container** (baked at

lpb.conf.env

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,26 +55,6 @@ LPB_AGENT_BROWSER_CONFIRM_ACTIONS=delete,download,cookie_delete,file_access
5555
# Daemon idle timeout (5 minutes) — auto-cleanup after inactivity
5656
LPB_AGENT_BROWSER_IDLE_TIMEOUT_MS=300000
5757

58-
# ─── MCP Server Versions ──────────────────────────────────────────────
59-
# These versions are resolved at container startup by start.sh.
60-
# Change them here to update — no image rebuild needed.
61-
LPB_GITHUB_MCP_SERVER_VERSION=v1.8.0
62-
LPB_AGENT_BROWSER_VERSION=latest
63-
LPB_EXA_MCP_SERVER_VERSION=latest
64-
LPB_CONTEXT7_MCP_SERVER_VERSION=latest
65-
66-
# ─── API Keys ───────────────────────────────────────────────────────────
67-
# Exa MCP search API key
68-
LPB_EXA_API_KEY=
69-
# Context7 MCP server API key (optional — improves rate limits)
70-
LPB_CONTEXT7_API_KEY=
71-
# ─── GitHub MCP Server Token ──────────────────────────────────────────────
72-
# Set to your GitHub token. start.sh will also try `gh auth token` as a
73-
# fallback if this is empty. Do NOT use command substitution here — it is
74-
# never evaluated by the config loader.
75-
LPB_GITHUB_TOKEN=
76-
LPB_GITHUB_TOOLSETS=all
77-
7858
# ─── Misc ───────────────────────────────────────────────────────────────
7959
# Persist gh CLI auth to ~/.config/gh inside container
8060
LPB_PERSIST_GH_CONFIG=true

support/lpb-config

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/env bash
2+
# lpb-config — Manage the LocalPibox Pi config repo inside the container
3+
#
4+
# Usage:
5+
# lpb-config status — Show current commit, remote HEAD, local changes
6+
# lpb-config update — Fetch + fast-forward (safe: refuses on local changes)
7+
# lpb-config reset — Re-clone, destroy local changes (with confirmation)
8+
# lpb-config merge — Open git merge flow for advanced users
9+
10+
set -euo pipefail
11+
12+
AGENT_DIR="${1:-/home/lpb/.pi}"
13+
CONFIG_REMOTE="${2:-https://github.com/localpibox/config.git}"
14+
CONFIG_REF="${3:-main}"
15+
16+
# ─── Helpers ────────────────────────────────────────────────────────────────
17+
info() { echo "[lpb-config] $*"; }
18+
warn() { echo "[lpb-config] $*" >&2; }
19+
err() { echo "[lpb-config] ERROR: $*" >&2; exit 1; }
20+
21+
# ─── status ─────────────────────────────────────────────────────────────────
22+
cmd_status() {
23+
if [[ ! -d "${AGENT_DIR}/.git" ]]; then
24+
warn "No config repo at ${AGENT_DIR}"
25+
echo " Run 'lpb-config update' to clone it."
26+
exit 0
27+
fi
28+
29+
local cur_head remote_head local_changes
30+
cur_head=$(git -C "${AGENT_DIR}" rev-parse HEAD 2>/dev/null || echo "?")
31+
cur_short="${cur_head:0:8}"
32+
33+
info "Current: ${cur_short} ($(git -C "${AGENT_DIR}" log --oneline -1 2>/dev/null || echo '?'))"
34+
35+
if ! git -C "${AGENT_DIR}" remote get-url origin &>/dev/null; then
36+
warn "No remote configured"
37+
exit 0
38+
fi
39+
40+
remote_head=$(git -C "${AGENT_DIR}" rev-parse "origin/${CONFIG_REF}" 2>/dev/null || echo "")
41+
if [[ -n "$remote_head" ]]; then
42+
remote_short="${remote_head:0:8}"
43+
info "Remote: ${remote_short} origin/${CONFIG_REF}"
44+
fi
45+
46+
# Check for uncommitted local changes
47+
local_changes=$(git -C "${AGENT_DIR}" status --porcelain 2>/dev/null || echo "")
48+
if [[ -n "$local_changes" ]]; then
49+
warn "Uncommitted changes detected (${#local_changes} lines):"
50+
echo "$local_changes" | head -10
51+
else
52+
info "Working tree: clean"
53+
fi
54+
55+
# Check if behind remote
56+
if [[ -n "$remote_head" ]]; then
57+
if git -C "${AGENT_DIR}" merge-base --is-ancestor "$cur_head" "$remote_head" 2>/dev/null; then
58+
info "Status: up to date (or ahead)"
59+
else
60+
warn "Status: behind remote — run 'lpb-config update' to fetch"
61+
fi
62+
fi
63+
}
64+
65+
# ─── update ─────────────────────────────────────────────────────────────────
66+
cmd_update() {
67+
if [[ ! -d "${AGENT_DIR}/.git" ]]; then
68+
info "Cloning config repo from ${CONFIG_REMOTE}..."
69+
git clone --depth=1 --branch "${CONFIG_REF}" "${CONFIG_REMOTE}" "${AGENT_DIR}"
70+
info "Cloned. Run 'lpb-config status' to verify."
71+
exit 0
72+
fi
73+
74+
info "Fetching updates from ${CONFIG_REMOTE}..."
75+
if ! git -C "${AGENT_DIR}" fetch origin "${CONFIG_REF}" 2>/dev/null; then
76+
err "Failed to fetch from ${CONFIG_REMOTE}"
77+
fi
78+
79+
remote_head=$(git -C "${AGENT_DIR}" rev-parse "origin/${CONFIG_REF}" 2>/dev/null)
80+
cur_head=$(git -C "${AGENT_DIR}" rev-parse HEAD 2>/dev/null)
81+
82+
if [[ "$remote_head" = "$cur_head" ]]; then
83+
info "Already up to date."
84+
exit 0
85+
fi
86+
87+
# Check for local changes — refuse to overwrite
88+
local_changes=$(git -C "${AGENT_DIR}" status --porcelain 2>/dev/null || echo "")
89+
if [[ -n "$local_changes" ]]; then
90+
warn "Uncommitted changes detected:"
91+
echo "$local_changes"
92+
warn "Aborting — local changes would be lost."
93+
echo ""
94+
echo "Options:"
95+
echo " 1. Commit your changes first, then re-run 'lpb-config update'"
96+
echo " 2. Use 'lpb-config reset' to discard local changes (destructive)"
97+
echo " 3. Use 'lpb-config merge' for an interactive git merge"
98+
exit 1
99+
fi
100+
101+
# Fast-forward
102+
if git -C "${AGENT_DIR}" merge-base --is-ancestor "$cur_head" "$remote_head" 2>/dev/null; then
103+
git -C "${AGENT_DIR}" reset --hard "origin/${CONFIG_REF}"
104+
info "Updated to $(git -C "${AGENT_DIR}" log --oneline -1)"
105+
else
106+
warn "Cannot fast-forward — conflicts detected."
107+
echo ""
108+
echo "Run 'lpb-config merge' for an interactive merge, or"
109+
echo "use 'lpb-config reset' to discard local changes (destructive)."
110+
exit 1
111+
fi
112+
}
113+
114+
# ─── reset ──────────────────────────────────────────────────────────────────
115+
cmd_reset() {
116+
if [[ ! -d "${AGENT_DIR}/.git" ]]; then
117+
info "Cloning config repo from ${CONFIG_REMOTE}..."
118+
git clone --depth=1 --branch "${CONFIG_REF}" "${CONFIG_REMOTE}" "${AGENT_DIR}"
119+
info "Cloned."
120+
exit 0
121+
fi
122+
123+
warn "This will destroy ALL local changes in ${AGENT_DIR}."
124+
echo " Current: $(git -C "${AGENT_DIR}" rev-parse HEAD 2>/dev/null | head -c 8)"
125+
echo " Remote: $(git -C "${AGENT_DIR}" rev-parse origin/${CONFIG_REF} 2>/dev/null | head -c 8)"
126+
127+
if [[ "${FORCE:-}" != "1" ]]; then
128+
echo -n " Continue? [y/N]: "
129+
read -r answer
130+
if [[ "$answer" != "y" && "$answer" != "Y" ]]; then
131+
info "Aborted."
132+
exit 0
133+
fi
134+
fi
135+
136+
rm -rf "${AGENT_DIR}"
137+
git clone --depth=1 --branch "${CONFIG_REF}" "${CONFIG_REMOTE}" "${AGENT_DIR}"
138+
info "Reset complete. Re-run Pi to apply the new config."
139+
}
140+
141+
# ─── merge ──────────────────────────────────────────────────────────────────
142+
cmd_merge() {
143+
if [[ ! -d "${AGENT_DIR}/.git" ]]; then
144+
err "No config repo at ${AGENT_DIR}. Run 'lpb-config update' first."
145+
fi
146+
147+
info "Fetching latest from ${CONFIG_REMOTE}..."
148+
git -C "${AGENT_DIR}" fetch origin "${CONFIG_REF}"
149+
150+
remote_head=$(git -C "${AGENT_DIR}" rev-parse "origin/${CONFIG_REF}" 2>/dev/null)
151+
cur_head=$(git -C "${AGENT_DIR}" rev-parse HEAD 2>/dev/null)
152+
153+
if [[ "$remote_head" = "$cur_head" ]]; then
154+
info "Already up to date."
155+
exit 0
156+
fi
157+
158+
info "Starting merge: ${cur_head:0:8}${remote_head:0:8}"
159+
if git -C "${AGENT_DIR}" merge --no-edit "origin/${CONFIG_REF}"; then
160+
info "Merge complete."
161+
else
162+
warn "Merge conflicts detected. Resolve them with git, then:"
163+
echo ""
164+
echo " cd ${AGENT_DIR}"
165+
echo " git add <resolved-files>"
166+
echo " git commit"
167+
echo " lpb-config status"
168+
exit 1
169+
fi
170+
}
171+
172+
# ─── Dispatcher ─────────────────────────────────────────────────────────────
173+
case "${1:-help}" in
174+
status) cmd_status ;;
175+
update) cmd_update ;;
176+
reset) cmd_reset ;;
177+
merge) cmd_merge ;;
178+
help|--help|-h)
179+
echo "Usage: lpb-config [command]"
180+
echo ""
181+
echo "Commands:"
182+
echo " status — Show current commit, remote HEAD, local changes"
183+
echo " update — Fetch + fast-forward (safe: refuses on local changes)"
184+
echo " reset — Re-clone, destroy local changes (with confirmation)"
185+
echo " merge — Open git merge flow for advanced users"
186+
echo ""
187+
echo "Environment:"
188+
echo " AGENT_DIR — Config repo path (default: /home/lpb/.pi)"
189+
echo " CONFIG_REMOTE — Git remote URL (default: https://github.com/localpibox/config.git)"
190+
echo " CONFIG_REF — Branch to track (default: main)"
191+
;;
192+
*)
193+
err "Unknown command: $1. Use 'lpb-config help' for usage."
194+
;;
195+
esac

0 commit comments

Comments
 (0)