diff --git a/.agents/skills/openclaw-live-updater/SKILL.md b/.agents/skills/openclaw-live-updater/SKILL.md index abd0e239b3fd..69732efaf220 100644 --- a/.agents/skills/openclaw-live-updater/SKILL.md +++ b/.agents/skills/openclaw-live-updater/SKILL.md @@ -5,7 +5,7 @@ description: "Maintain the canonical live OpenClaw main checkout, managed Gatewa # OpenClaw Live Updater -Keep `/Users/steipete/openclaw` a read-only-to-the-agent deployment mirror: clean, standalone, full, on `main`, and fast-forwarded only. Make every repair in the controlling Codex project worktree. +Keep one operator-selected canonical live checkout as a read-only-to-the-agent deployment mirror: clean, standalone, full, on `main`, and fast-forwarded only. Make every repair in the controlling Codex project worktree. ## Boundaries @@ -20,10 +20,11 @@ Keep `/Users/steipete/openclaw` a read-only-to-the-agent deployment mirror: clea 1. Run the deterministic updater and retain its JSON: ```bash + cd "" node --import tsx .agents/skills/openclaw-live-updater/scripts/update-main.mjs ``` - Stop on any failed invariant. Do not repair the mirror destructively. The helper holds one checkout-scoped lock across update, build, Gateway proof, and Mac work. A concurrent heartbeat returns `reason: "overlap"`; it must not start another build. A dead owner lock may be recovered, but unreadable or unsafe lock state fails closed. + The helper owns the current working directory by default; it does not search parent directories or infer a clone from the user's home directory. From a controlling worktree, pass the canonical mirror explicitly with `--checkout ""`. Stop on any failed invariant. Do not repair the mirror destructively. The helper holds one checkout-scoped lock across update, build, Gateway proof, and Mac work. A concurrent heartbeat returns `reason: "overlap"`; it must not start another build. A dead owner lock may be recovered, but unreadable or unsafe lock state fails closed. 2. The helper verifies one unrewritten expected origin, an owned non-symlinked standalone/full clone, single worktree, clean `main`, fetches `origin/main`, rechecks for concurrent changes, and merges `--ff-only`. It then uses the source runner's canonical local-build metadata contract and parser: both `dist/.buildstamp` and `dist/.runtime-postbuildstamp` heads, required runtime-postbuild outputs, `dist/entry.js`, Control UI index plus referenced local assets, and `dist/build-info.json` must all match exact `afterSha`. @@ -39,7 +40,7 @@ Keep `/Users/steipete/openclaw` a read-only-to-the-agent deployment mirror: clea Re-run the canonical freshness check immediately before every `pnpm openclaw` restart or probe so the source runner cannot hide stale output with an implicit auto-build. Every pass, including a no-update/current-build pass, must run deep RPC status and verbose health. If that first probe fails while the build is already exact-current, perform one managed Gateway restart and repeat both probes once. Do not rebuild a current exact-SHA artifact merely to self-heal the managed process; fail and diagnose if the one restart does not recover it. -3. If changed paths can affect macOS, the helper runs `scripts/restart-mac.sh --sign --wait --target-only` with `SKIP_TSC=1` and `SKIP_UI_BUILD=1` only after the exact-SHA JS/UI build completes. Reusing those artifacts keeps the live app bundle out of any later JavaScript build cleanup. Target-only mode may stop the canonical `/Applications/OpenClaw.app` process and this checkout's exact `dist` process before launching the rebuilt `dist` app. It defers when another worktree, temporary bundle, test, or agent-owned OpenClaw process is active; it never kills that process. The script's immediate `OK` is not proof. The helper waits and requires the exact executable `/Users/steipete/openclaw/dist/OpenClaw.app/Contents/MacOS/OpenClaw`, then repeats Gateway RPC and health proof. +3. If changed paths can affect macOS, the helper runs `scripts/restart-mac.sh --sign --wait --target-only` with `SKIP_TSC=1` and `SKIP_UI_BUILD=1` only after the exact-SHA JS/UI build completes. Reusing those artifacts keeps the live app bundle out of any later JavaScript build cleanup. Target-only mode may stop the canonical `/Applications/OpenClaw.app` process and this checkout's exact `dist` process before launching the rebuilt `dist` app. It defers when another worktree, temporary bundle, test, or agent-owned OpenClaw process is active; it never kills that process. The script's immediate `OK` is not proof. The helper waits and requires the exact executable `/dist/OpenClaw.app/Contents/MacOS/OpenClaw`, derived from the verified checkout, then repeats Gateway RPC and health proof. Never kill another worktree, temporary bundle, test, or agent-owned OpenClaw process. If a foreign app prevents the exact target from staying alive, record the pending Mac attempt, report it, and retry on the next heartbeat. Escalate only after the conflict persists across repeated heartbeats; never claim Mac proof from another bundle or the short launch check. If `actions.macUiVerification` is true, exercise the changed behavior with the existing macOS/UI automation workflow after delayed exact-bundle proof. diff --git a/.agents/skills/openclaw-live-updater/scripts/update-main.mjs b/.agents/skills/openclaw-live-updater/scripts/update-main.mjs index c680340f8e9e..a2b04fd96f71 100644 --- a/.agents/skills/openclaw-live-updater/scripts/update-main.mjs +++ b/.agents/skills/openclaw-live-updater/scripts/update-main.mjs @@ -35,7 +35,6 @@ import { resolveRuntimePostBuildRequirement, } from "../../../../scripts/run-node.mts"; -const DEFAULT_CHECKOUT = "/Users/steipete/openclaw"; const DEFAULT_EXPECTED_ORIGIN = "openclaw/openclaw"; const FULL_SHA_RE = /^[0-9a-f]{40}$/u; const GATEWAY_READINESS_ATTEMPTS = 7; @@ -3800,7 +3799,7 @@ export async function maintainMain(options, dependencies = {}) { } function parseArgs(argv) { - const options = { checkout: DEFAULT_CHECKOUT, remote: "origin" }; + const options = { checkout: process.cwd(), remote: "origin" }; for (let index = 0; index < argv.length; index += 1) { const arg = argv[index]; if (arg === "--checkout") { diff --git a/test/scripts/openclaw-live-updater.test.ts b/test/scripts/openclaw-live-updater.test.ts index 37f32532fc6f..c224b3d003fa 100644 --- a/test/scripts/openclaw-live-updater.test.ts +++ b/test/scripts/openclaw-live-updater.test.ts @@ -1537,7 +1537,7 @@ console.log(JSON.stringify({ ok: true, channels: {} })); }); test("accepts only the delayed exact target bundle process", () => { - const executable = "/Users/steipete/openclaw/dist/OpenClaw.app/Contents/MacOS/OpenClaw"; + const executable = "/fixture/live-checkout/dist/OpenClaw.app/Contents/MacOS/OpenClaw"; const foreign = "41 /tmp/agent/OpenClaw.app/Contents/MacOS/OpenClaw"; expect(findExactMacTarget(foreign, executable)).toBeNull(); expect(findExactMacTarget(`${foreign}\n42 ${executable} --attach-only`, executable)).toEqual({ @@ -3436,7 +3436,7 @@ console.log(JSON.stringify({ ok: true, channels: {} })); }); }); - test("keeps successful CLI stdout as one machine-readable JSON object", () => { + test("defaults to the current standalone checkout without a machine-specific path", () => { const { root, mirror, origin } = makeFixture(); mkdirSync(path.join(mirror, "node_modules")); writeBuild(mirror); @@ -3453,14 +3453,19 @@ console.log(JSON.stringify({ ok: true, channels: {} })); chmodSync(pnpm, 0o755); chmodSync(gitShim, 0o755); - const result = spawnSync(process.execPath, [script, "--checkout", mirror], { + const result = spawnSync(process.execPath, [script], { + cwd: mirror, encoding: "utf8", env: { ...process.env, PATH: `${binDir}:${process.env.PATH}` }, }); expect(result.status, result.stderr).toBe(0); expect(result.stdout.trim().split("\n")).toHaveLength(1); - expect(JSON.parse(result.stdout)).toMatchObject({ ok: true, updated: false }); + expect(JSON.parse(result.stdout)).toMatchObject({ + ok: true, + checkout: realpathSync(mirror), + updated: false, + }); expect(result.stderr).toContain("child-output"); });