diff --git a/CHANGELOG.md b/CHANGELOG.md index 7847e2cf5..b23c6fad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ - Fix: allow local Tailscale Serve hostnames without treating tailnet clients as direct. (#885) — thanks @oswalpalash. - Fix: reset sessions after role-ordering conflicts to recover from consecutive user turns. (#998) - Fix: repair orphaned user turns before embedded prompts to avoid role-ordering lockouts. (#1026) — thanks @odrobnik. +- Dev: auto-sync Peekaboo submodule on install when clean to avoid dirty checkouts. - Fix: keep background exec aborts from killing backgrounded sessions while honoring timeouts. - Fix: use local auth for gateway security probe unless remote mode has a URL. (#1011) — thanks @ivanrvpereira. - Discord: truncate skill command descriptions for slash command limits. (#1018) — thanks @evalexpr. diff --git a/scripts/postinstall.js b/scripts/postinstall.js index c14686f55..0f3f902c7 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -1,5 +1,6 @@ import fs from "node:fs"; import path from "node:path"; +import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; function detectPackageManager(ua = process.env.npm_config_user_agent ?? "") { @@ -37,6 +38,52 @@ function ensureExecutable(targetPath) { } } +function hasGit(repoRoot) { + const result = spawnSync("git", ["--version"], { cwd: repoRoot, stdio: "ignore" }); + return result.status === 0; +} + +function isSubmoduleDirty(submodulePath, repoRoot) { + const result = spawnSync("git", ["-C", submodulePath, "status", "--porcelain"], { + cwd: repoRoot, + encoding: "utf-8", + }); + if (result.status !== 0) return false; + return result.stdout.trim().length > 0; +} + +function shouldSyncSubmodule(name, repoRoot) { + const result = spawnSync("git", ["submodule", "status", "--recursive", "--", name], { + cwd: repoRoot, + encoding: "utf-8", + }); + if (result.status !== 0) return false; + const line = result.stdout.trim().split("\n")[0]; + if (!line) return false; + const marker = line[0]; + return marker === "-" || marker === "+"; +} + +function syncPeekabooSubmodule(repoRoot) { + if (!hasGit(repoRoot)) return; + if (!fs.existsSync(path.join(repoRoot, ".gitmodules"))) return; + const peekabooPath = path.join(repoRoot, "Peekaboo"); + if (fs.existsSync(peekabooPath) && isSubmoduleDirty(peekabooPath, repoRoot)) { + console.warn("[postinstall] Peekaboo submodule dirty; skipping update"); + return; + } + if (!shouldSyncSubmodule("Peekaboo", repoRoot)) return; + const result = spawnSync( + "git", + ["submodule", "update", "--init", "--recursive", "--", "Peekaboo"], + { cwd: repoRoot, encoding: "utf-8" }, + ); + if (result.status !== 0) { + const stderr = result.stderr?.toString().trim(); + console.warn(`[postinstall] Peekaboo submodule update failed: ${stderr || "unknown error"}`); + } +} + function extractPackageName(key) { if (key.startsWith("@")) { const idx = key.indexOf("@", 1); @@ -246,6 +293,7 @@ function main() { process.chdir(repoRoot); ensureExecutable(path.join(repoRoot, "dist", "entry.js")); + syncPeekabooSubmodule(repoRoot); if (!shouldApplyPnpmPatchedDependenciesFallback()) { return;