feat(spawn): share host spawn-pi mesh + models.json with spawned containers

- bind $LVMH_HOST_PI_RUNTIME_DIR/spawn-pi -> /root/.pi/spawn-pi (rw) so
  container pi's join the host pi's node mesh (spawn_pi/list_pi_nodes/
  send_pi_message now reach across the host/container boundary)
- bind host models.json (when present) over the stale image-baked
  fallback so newly added catalog models resolve in spawns
This commit is contained in:
buenosair
2026-09-07 13:21:49 +00:00
parent 6ef27e5cd6
commit 4755f6d1e3
3 changed files with 80 additions and 1 deletions
+19 -1
View File
@@ -47,11 +47,14 @@ const (
volumePiCache string = "lvmh-pi-cache" // pi package cache (git:/npm:), shared across spawns
cacheMount string = "/root/.pi/agent/cache"
authMountTarget string = "/root/.pi/agent/auth.json"
modelsMountTarget string = "/root/.pi/agent/models.json"
envHostPiAgentDir string = "LVMH_HOST_PI_AGENT_DIR"
envHostPiRuntimeDir string = "LVMH_HOST_PI_RUNTIME_DIR"
envSecretsDir string = "LVMH_SECRETS_DIR"
envCloakCacheDir string = "LVMH_CLOAK_CACHE_DIR"
envPlaywrightCacheDir string = "LVMH_PLAYWRIGHT_CACHE_DIR"
sshMountTarget string = "/root/.ssh"
meshMountTarget string = "/root/.pi/spawn-pi"
gitconfigMountTarget string = "/root/.gitconfig"
workspaceMount string = "/workspace"
sessionsMount string = "/pi-sessions"
@@ -573,9 +576,24 @@ func (s *Spawner) createAndStart(ctx context.Context, repo, slug, model string,
volumePiCache+":"+cacheMount,
)
// Host pi credentials (OAuth tokens for anthropic etc.), read-only, so
// spawned agents can use every model the catalog offers.
// spawned agents can use every model the catalog offers. The host
// models.json rides along when present: image fallbacks are baked at
// build time and go stale the moment the dotfiles catalog changes.
if hostAgent := os.Getenv(envHostPiAgentDir); hostAgent != "" {
binds = append(binds, hostAgent+"/auth.json:"+authMountTarget+":ro")
if _, err := os.Stat(filepath.Join(hostAgent, "models.json")); err == nil {
binds = append(binds, hostAgent+"/models.json:"+modelsMountTarget+":ro")
}
}
// Shared spawn-pi mesh directory (node registry + AF_UNIX sockets),
// read-write: each pi creates its own socket and node file. Sharing it
// with the host puts container pi's on the same mesh as the host pi —
// without this each container is an isolated island.
if hostRuntime := os.Getenv(envHostPiRuntimeDir); hostRuntime != "" {
mesh := filepath.Join(hostRuntime, "spawn-pi")
if _, err := os.Stat(mesh); err == nil {
binds = append(binds, mesh+":"+meshMountTarget)
}
}
// Shared playwright browser cache (host path, read-only); the env var
// below makes every playwright-based MCP use it instead of downloading.