fix: harden gateway recovery diagnostics and media delivery

Harden gateway recovery diagnostics and media delivery.\n\n- Accept gateway send asVoice and map it to outbound audioAsVoice.\n- Preserve generated Swift protocol models for the gateway send schema.\n- Keep the broader recovery hardening for install/update/status/vector/TTS paths in one reviewed PR.\n\nProof:\n- Focused local gateway/outbound/update/status/doctor/sqlite-vec tests passed.\n- oxfmt --check and git diff --check passed.\n- Testbox OPENCLAW_TESTBOX=1 pnpm check:changed passed at 2f5ef650e97763a61ff43c28e61707db84c50060.\n- GitHub required checks are green at the merge SHA; the qa-lab parity gate is optional/surface-only and was still pending.
This commit is contained in:
Val Alexander
2026-04-30 21:46:22 -05:00
committed by GitHub
parent 98d87b06e0
commit df0ee092f0
27 changed files with 647 additions and 15 deletions

View File

@@ -142,6 +142,45 @@ exit 0
await cleanupScript(scriptPath);
});
it("fails with sudo systemd guidance when the gateway unit is system-scoped", async () => {
Object.defineProperty(process, "platform", { value: "linux" });
const tmpDir = await makeTempDir("openclaw-restart-helper-");
const fakeBinDir = path.join(tmpDir, "bin");
const callsPath = path.join(tmpDir, "systemctl-calls.log");
await fs.mkdir(fakeBinDir, { recursive: true });
await fs.writeFile(
path.join(fakeBinDir, "systemctl"),
`#!/bin/sh
printf '%s\\n' "$*" >> "$OPENCLAW_SYSTEMCTL_CALLS"
if [ "$1" = "--user" ] && [ "$2" = "is-active" ]; then exit 3; fi
if [ "$1" = "--user" ] && [ "$2" = "is-enabled" ]; then exit 1; fi
if [ "$1" = "is-active" ] && [ "$2" = "--quiet" ]; then exit 0; fi
if [ "$1" = "is-enabled" ] && [ "$2" = "--quiet" ]; then exit 0; fi
if [ "$1" = "--user" ] && [ "$2" = "restart" ]; then exit 99; fi
exit 1
`,
{ mode: 0o755 },
);
const { scriptPath } = await prepareAndReadScript({
OPENCLAW_PROFILE: "default",
HOME: path.join(tmpDir, "home"),
OPENCLAW_STATE_DIR: path.join(tmpDir, "state"),
});
const result = await executeScript(scriptPath, {
PATH: `${fakeBinDir}:${process.env.PATH ?? ""}`,
OPENCLAW_SYSTEMCTL_CALLS: callsPath,
});
const calls = await fs.readFile(callsPath, "utf-8");
expect(result.code).toBe(78);
expect(result.stderr).toContain("system-scoped openclaw gateway unit detected");
expect(result.stderr).toContain("sudo systemctl restart openclaw-gateway.service");
expect(calls).toContain("--user is-active --quiet openclaw-gateway.service");
expect(calls).toContain("is-active --quiet openclaw-gateway.service");
expect(calls).not.toContain("--user restart openclaw-gateway.service");
});
it("creates a launchd restart script on macOS", async () => {
Object.defineProperty(process, "platform", { value: "darwin" });
process.getuid = () => 501;

View File

@@ -85,16 +85,32 @@ export async function prepareRestartScript(
# Standalone restart script — survives parent process termination.
# Wait briefly to ensure file locks are released after update.
sleep 1
exec 3>&2
${logSetup}
printf '[%s] openclaw restart attempt source=update target=%s\\n' "$(date -u +%FT%TZ)" '${escaped}' >&2
if systemctl --user restart '${escaped}'; then
status=0
printf '[%s] openclaw restart done source=update\\n' "$(date -u +%FT%TZ)" >&2
if systemctl --user is-active --quiet '${escaped}' || systemctl --user is-enabled --quiet '${escaped}'; then
if systemctl --user restart '${escaped}'; then
status=0
printf '[%s] openclaw restart done source=update\\n' "$(date -u +%FT%TZ)" >&2
else
status=$?
printf '[%s] openclaw restart failed source=update status=%s\\n' "$(date -u +%FT%TZ)" "$status" >&2
fi
elif systemctl is-active --quiet '${escaped}' || systemctl is-enabled --quiet '${escaped}'; then
status=78
printf '[%s] system-scoped openclaw gateway unit detected; update cannot restart it without sudo. Run: sudo systemctl restart %s\\n' "$(date -u +%FT%TZ)" '${escaped}' >&2
printf '[%s] system-scoped openclaw gateway unit detected; update cannot restart it without sudo. Run: sudo systemctl restart %s\\n' "$(date -u +%FT%TZ)" '${escaped}' >&3 2>/dev/null || true
else
status=$?
printf '[%s] openclaw restart failed source=update status=%s\\n' "$(date -u +%FT%TZ)" "$status" >&2
if systemctl --user restart '${escaped}'; then
status=0
printf '[%s] openclaw restart done source=update\\n' "$(date -u +%FT%TZ)" >&2
else
status=$?
printf '[%s] openclaw restart failed source=update status=%s\\n' "$(date -u +%FT%TZ)" "$status" >&2
fi
fi
# Self-cleanup
exec 3>&-
rm -f "$0"
exit "$status"
`;