mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
fix(parallels): recover migration-refused gateway startup
This commit is contained in:
@@ -26,6 +26,8 @@ interface NpmUpdateScriptInput {
|
||||
}
|
||||
|
||||
const windowsStalePostSwapImportRegex = String.raw`node_modules\\openclaw\\dist\\[^\\]+-[A-Za-z0-9_-]+\.js`;
|
||||
const startupMigrationRestartPrefix =
|
||||
"OpenClaw plugin migration inputs changed during startup convergence;";
|
||||
const macosGuestPath =
|
||||
"/opt/homebrew/bin:/opt/homebrew/opt/node/bin:/usr/local/bin:/usr/local/sbin:/opt/homebrew/sbin:/usr/bin:/bin:/usr/sbin:/sbin";
|
||||
const macosOpenClawCommand = '"$OPENCLAW_BIN"';
|
||||
@@ -155,25 +157,65 @@ $updateExit = $script:OpenClawUpdateExit
|
||||
$updateOutput`;
|
||||
}
|
||||
|
||||
function windowsGatewayReadyScript(): string {
|
||||
return `function Wait-OpenClawGateway {
|
||||
function windowsGatewayReadyScript(input: NpmUpdateScriptInput): string {
|
||||
return `$gatewayLogRoot = Join-Path ([System.IO.Path]::GetTempPath()) 'openclaw-parallels-windows-gateway'
|
||||
$gatewayLaunch = 0
|
||||
$gatewayRestartCount = 0
|
||||
function Start-OpenClawGateway {
|
||||
$script:gatewayLaunch += 1
|
||||
$script:gatewayLogPath = "$gatewayLogRoot-$($script:gatewayLaunch).log"
|
||||
Remove-Item $script:gatewayLogPath -Force -ErrorAction SilentlyContinue
|
||||
$gatewayCommand = Resolve-OpenClawCommand
|
||||
$gatewayCommandPath = $gatewayCommand.Path.Replace("'", "''")
|
||||
$gatewayInvocation = if ($gatewayCommand.Kind -eq 'node') {
|
||||
"& node.exe '$gatewayCommandPath' gateway run --bind loopback --port 18789 --force"
|
||||
} else {
|
||||
"& '$gatewayCommandPath' gateway run --bind loopback --port 18789 --force"
|
||||
}
|
||||
$gatewayScript = "\`$ErrorActionPreference = 'Continue'\`n$gatewayInvocation *>> \`$env:OPENCLAW_PARALLELS_GATEWAY_LOG\`nexit \`$LASTEXITCODE"
|
||||
$gatewayEncodedScript = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($gatewayScript))
|
||||
$gatewayPowerShell = (Get-Process -Id $PID).Path
|
||||
Invoke-WithScopedEnv @{
|
||||
OPENCLAW_HOME = $env:USERPROFILE
|
||||
OPENCLAW_STATE_DIR = (Join-Path $env:USERPROFILE '.openclaw')
|
||||
OPENCLAW_CONFIG_PATH = (Join-Path $env:USERPROFILE '.openclaw\\openclaw.json')
|
||||
OPENCLAW_PARALLELS_GATEWAY_LOG = $script:gatewayLogPath
|
||||
${input.auth.apiKeyEnv} = ${psSingleQuote(input.auth.apiKeyValue)}
|
||||
} {
|
||||
$script:gatewayProcess = Start-Process -FilePath $gatewayPowerShell -ArgumentList @('-NoProfile', '-NonInteractive', '-EncodedCommand', $gatewayEncodedScript) -WindowStyle Hidden -PassThru
|
||||
}
|
||||
}
|
||||
function Write-CurrentGatewayLog {
|
||||
if (Test-Path $script:gatewayLogPath) {
|
||||
Get-Content $script:gatewayLogPath -ErrorAction SilentlyContinue | Out-Host
|
||||
}
|
||||
}
|
||||
function Test-CurrentGatewayStartupMigrationRefusal {
|
||||
if (-not (Test-Path $script:gatewayLogPath)) { return $false }
|
||||
return Select-String -Path $script:gatewayLogPath -SimpleMatch ${psSingleQuote(startupMigrationRestartPrefix)} -Quiet
|
||||
}
|
||||
function Wait-OpenClawGateway {
|
||||
$deadline = (Get-Date).AddSeconds(180)
|
||||
$attempt = 0
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
Invoke-OpenClaw gateway status --deep --require-rpc --timeout 15000
|
||||
if ($LASTEXITCODE -eq 0) { return }
|
||||
$attempt += 1
|
||||
if ($attempt -eq 4) {
|
||||
Invoke-OpenClaw gateway start *>&1 | Out-Host
|
||||
if ($script:gatewayProcess.HasExited) {
|
||||
$script:gatewayProcess.WaitForExit()
|
||||
if ($script:gatewayRestartCount -eq 0 -and (Test-CurrentGatewayStartupMigrationRefusal)) {
|
||||
$script:gatewayRestartCount = 1
|
||||
Write-Host 'gateway exited after startup migration convergence refusal; restarting once'
|
||||
Start-OpenClawGateway
|
||||
continue
|
||||
}
|
||||
Write-CurrentGatewayLog
|
||||
throw "gateway exited before becoming ready after update with code $($script:gatewayProcess.ExitCode)"
|
||||
}
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
Write-CurrentGatewayLog
|
||||
throw "gateway did not become ready after update"
|
||||
}
|
||||
Invoke-OpenClaw gateway restart *>&1 | Out-Host
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
"gateway restart exited with code $LASTEXITCODE; probing readiness before failing" | Out-Host
|
||||
}
|
||||
Start-OpenClawGateway
|
||||
Wait-OpenClawGateway`;
|
||||
}
|
||||
|
||||
@@ -259,27 +301,44 @@ stop_openclaw_gateway_processes() {
|
||||
fi
|
||||
fi
|
||||
}
|
||||
gateway_log=/tmp/openclaw-parallels-macos-gateway.log
|
||||
rm -f "$gateway_log"
|
||||
touch "$gateway_log"
|
||||
gateway_pid=
|
||||
gateway_launch_log_offset=0
|
||||
gateway_restart_count=0
|
||||
start_openclaw_gateway() {
|
||||
stop_openclaw_gateway_processes
|
||||
rm -f /tmp/openclaw-parallels-macos-gateway.log
|
||||
gateway_launch_log_offset="$(wc -c <"$gateway_log" 2>/dev/null | tr -d '[:space:]' || echo 0)"
|
||||
trap '' HUP
|
||||
with_provider_api_key /usr/bin/env OPENCLAW_HOME="$HOME" OPENCLAW_STATE_DIR="$HOME/.openclaw" OPENCLAW_CONFIG_PATH="$HOME/.openclaw/openclaw.json" "$OPENCLAW_BIN" gateway run --bind loopback --port 18789 --force >/tmp/openclaw-parallels-macos-gateway.log 2>&1 </dev/null &
|
||||
with_provider_api_key /usr/bin/env OPENCLAW_HOME="$HOME" OPENCLAW_STATE_DIR="$HOME/.openclaw" OPENCLAW_CONFIG_PATH="$HOME/.openclaw/openclaw.json" "$OPENCLAW_BIN" gateway run --bind loopback --port 18789 --force >>"$gateway_log" 2>&1 </dev/null &
|
||||
gateway_pid=$!
|
||||
sleep 1
|
||||
}
|
||||
wait_for_gateway() {
|
||||
deadline=$((SECONDS + 240))
|
||||
attempt=0
|
||||
while [ "$SECONDS" -lt "$deadline" ]; do
|
||||
if "$OPENCLAW_BIN" gateway status --deep --require-rpc --timeout 15000; then
|
||||
return
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
if [ "$attempt" -eq 4 ]; then
|
||||
start_openclaw_gateway
|
||||
if ! kill -0 "$gateway_pid" 2>/dev/null; then
|
||||
if wait "$gateway_pid"; then gateway_exit_status=0; else gateway_exit_status=$?; fi
|
||||
if [ "$gateway_exit_status" -le 128 ] && [ "$gateway_restart_count" -eq 0 ]; then
|
||||
if tail -c +"$((gateway_launch_log_offset + 1))" "$gateway_log" 2>/dev/null | grep -F -- ${shellQuote(startupMigrationRestartPrefix)} >/dev/null; then
|
||||
gateway_restart_count=1
|
||||
echo "gateway exited after startup migration convergence refusal; restarting once"
|
||||
start_openclaw_gateway
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
print_log_tail "$gateway_log" >&2
|
||||
echo "gateway exited before becoming ready after update (exit $gateway_exit_status)" >&2
|
||||
if [ "$gateway_exit_status" -eq 0 ]; then exit 1; fi
|
||||
exit "$gateway_exit_status"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
print_log_tail /tmp/openclaw-parallels-macos-gateway.log >&2
|
||||
print_log_tail "$gateway_log" >&2
|
||||
echo "gateway did not become ready after update" >&2
|
||||
exit 1
|
||||
}
|
||||
@@ -368,7 +427,7 @@ if ($updateExit -ne 0) {
|
||||
Write-Host "openclaw update returned a stale post-swap module import; continuing to post-update health checks"
|
||||
}
|
||||
${windowsVersionCheck(input.expectedNeedle)}
|
||||
${windowsGatewayReadyScript()}
|
||||
${windowsGatewayReadyScript(input)}
|
||||
${windowsAssertAgentOkScript(input)}`;
|
||||
}
|
||||
|
||||
@@ -403,27 +462,44 @@ stop_openclaw_gateway_processes() {
|
||||
OPENCLAW_DISABLE_BUNDLED_PLUGINS=1 OPENCLAW_ALLOW_ROOT=1 openclaw gateway stop || true
|
||||
pkill -f 'openclaw.*gateway' >/dev/null 2>&1 || true
|
||||
}
|
||||
gateway_log=/tmp/openclaw-parallels-linux-gateway.log
|
||||
rm -f "$gateway_log"
|
||||
touch "$gateway_log"
|
||||
gateway_pid=
|
||||
gateway_launch_log_offset=0
|
||||
gateway_restart_count=0
|
||||
start_openclaw_gateway() {
|
||||
pkill -f "openclaw gateway run" >/dev/null 2>&1 || true
|
||||
rm -f /tmp/openclaw-parallels-linux-gateway.log
|
||||
gateway_launch_log_offset="$(wc -c <"$gateway_log" 2>/dev/null | tr -d '[:space:]' || echo 0)"
|
||||
with_provider_api_key setsid sh -lc ${shellQuote(
|
||||
"exec env OPENCLAW_HOME=/root OPENCLAW_STATE_DIR=/root/.openclaw OPENCLAW_CONFIG_PATH=/root/.openclaw/openclaw.json OPENCLAW_DISABLE_BONJOUR=1 OPENCLAW_ALLOW_ROOT=1 openclaw gateway run --bind loopback --port 18789 --force >/tmp/openclaw-parallels-linux-gateway.log 2>&1",
|
||||
"exec env OPENCLAW_HOME=/root OPENCLAW_STATE_DIR=/root/.openclaw OPENCLAW_CONFIG_PATH=/root/.openclaw/openclaw.json OPENCLAW_DISABLE_BONJOUR=1 OPENCLAW_ALLOW_ROOT=1 openclaw gateway run --bind loopback --port 18789 --force >>/tmp/openclaw-parallels-linux-gateway.log 2>&1",
|
||||
)} >/dev/null 2>&1 < /dev/null &
|
||||
gateway_pid=$!
|
||||
}
|
||||
wait_for_gateway() {
|
||||
deadline=$((SECONDS + 240))
|
||||
attempt=0
|
||||
while [ "$SECONDS" -lt "$deadline" ]; do
|
||||
if openclaw gateway status --deep --require-rpc --timeout 15000; then
|
||||
return
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
if [ "$attempt" -eq 4 ]; then
|
||||
start_openclaw_gateway
|
||||
if ! kill -0 "$gateway_pid" 2>/dev/null; then
|
||||
if wait "$gateway_pid"; then gateway_exit_status=0; else gateway_exit_status=$?; fi
|
||||
if [ "$gateway_exit_status" -le 128 ] && [ "$gateway_restart_count" -eq 0 ]; then
|
||||
if tail -c +"$((gateway_launch_log_offset + 1))" "$gateway_log" 2>/dev/null | grep -F -- ${shellQuote(startupMigrationRestartPrefix)} >/dev/null; then
|
||||
gateway_restart_count=1
|
||||
echo "gateway exited after startup migration convergence refusal; restarting once"
|
||||
start_openclaw_gateway
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
print_log_tail "$gateway_log" >&2
|
||||
echo "gateway exited before becoming ready after update (exit $gateway_exit_status)" >&2
|
||||
if [ "$gateway_exit_status" -eq 0 ]; then exit 1; fi
|
||||
exit "$gateway_exit_status"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
print_log_tail /tmp/openclaw-parallels-linux-gateway.log >&2
|
||||
print_log_tail "$gateway_log" >&2
|
||||
echo "gateway did not become ready after update" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ exit 1
|
||||
expect(windowsUpdateScript(input)).toContain(`NPM_CONFIG_REGISTRY = '${registry}'`);
|
||||
});
|
||||
|
||||
it("relaunches POSIX gateways after a transient post-update startup failure", () => {
|
||||
it("restarts POSIX gateways only after an exact current-launch migration refusal", () => {
|
||||
const input = {
|
||||
auth: TEST_AUTH,
|
||||
expectedNeedle: "2026.7.2-beta.5",
|
||||
@@ -379,11 +379,41 @@ exit 1
|
||||
};
|
||||
|
||||
for (const script of [macosUpdateScript(input), linuxUpdateScript(input)]) {
|
||||
expect(script).toContain("attempt=$((attempt + 1))");
|
||||
expect(script).toContain('if [ "$attempt" -eq 4 ]; then\n start_openclaw_gateway');
|
||||
expect(script).toContain(
|
||||
"OpenClaw plugin migration inputs changed during startup convergence;",
|
||||
);
|
||||
expect(script).toContain("gateway_launch_log_offset=");
|
||||
expect(script).toContain("gateway_pid=$!");
|
||||
expect(script).toContain('if ! kill -0 "$gateway_pid" 2>/dev/null; then');
|
||||
expect(script).toContain('tail -c +"$((gateway_launch_log_offset + 1))" "$gateway_log"');
|
||||
expect(script).toContain(
|
||||
'if [ "$gateway_exit_status" -le 128 ] && [ "$gateway_restart_count" -eq 0 ]; then',
|
||||
);
|
||||
expect(script).toContain("gateway_restart_count=1");
|
||||
expect(script).not.toContain('if [ "$attempt" -eq 4 ]');
|
||||
}
|
||||
});
|
||||
|
||||
it("restarts the Windows gateway only after its current launch exits with the exact refusal", () => {
|
||||
const script = windowsUpdateScript({
|
||||
auth: TEST_AUTH,
|
||||
expectedNeedle: "2026.7.2-beta.5",
|
||||
updateTarget: "2026.7.2-beta.5",
|
||||
});
|
||||
|
||||
expect(script).toContain(
|
||||
"OpenClaw plugin migration inputs changed during startup convergence;",
|
||||
);
|
||||
expect(script).toContain("$script:gatewayProcess.HasExited");
|
||||
expect(script).toContain("$script:gatewayProcess.WaitForExit()");
|
||||
expect(script).toContain("$script:gatewayRestartCount -eq 0");
|
||||
expect(script).toContain("Test-CurrentGatewayStartupMigrationRefusal");
|
||||
expect(script).toContain("Select-String -Path $script:gatewayLogPath -SimpleMatch");
|
||||
expect(script).toContain("$script:gatewayRestartCount = 1");
|
||||
expect(script).not.toContain("$attempt -eq 4");
|
||||
expect(script).not.toContain("Invoke-OpenClaw gateway restart");
|
||||
});
|
||||
|
||||
it("keeps POSIX provider secrets out of executable command lines", () => {
|
||||
const input = {
|
||||
auth: TEST_AUTH,
|
||||
@@ -515,8 +545,7 @@ exit 1
|
||||
expect(scripts).toContain("print_log_tail()");
|
||||
expect(scripts).toContain("OPENCLAW_PARALLELS_NPM_UPDATE_LOG_TAIL_BYTES");
|
||||
expect(scripts).toContain('print_log_tail "$output_file"');
|
||||
expect(scripts).toContain("print_log_tail /tmp/openclaw-parallels-macos-gateway.log >&2");
|
||||
expect(scripts).toContain("print_log_tail /tmp/openclaw-parallels-linux-gateway.log >&2");
|
||||
expect(scripts).toContain('print_log_tail "$gateway_log" >&2');
|
||||
expect(scripts).not.toContain('cat "$output_file"');
|
||||
expect(scripts).not.toContain("cat /tmp/openclaw-parallels-");
|
||||
});
|
||||
@@ -1222,14 +1251,14 @@ exit 7
|
||||
"Invoke-WithScopedEnv @{ OPENCLAW_ALLOW_OLDER_BINARY_DESTRUCTIVE_ACTIONS",
|
||||
);
|
||||
const versionIndex = script.indexOf("Invoke-OpenClaw --version", scopedIndex);
|
||||
const restartIndex = script.indexOf("Invoke-OpenClaw gateway restart");
|
||||
const startIndex = script.indexOf("\nStart-OpenClawGateway\n", updateIndex);
|
||||
const agentIndex = script.indexOf("Invoke-OpenClaw agent --local");
|
||||
|
||||
expect(updateIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(scopedIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(updateIndex).toBeGreaterThan(scopedIndex);
|
||||
expect(versionIndex).toBeGreaterThan(updateIndex);
|
||||
expect(restartIndex).toBeGreaterThan(updateIndex);
|
||||
expect(startIndex).toBeGreaterThan(updateIndex);
|
||||
expect(agentIndex).toBeGreaterThan(updateIndex);
|
||||
expect(script).not.toContain("OPENCLAW_DISABLE_BUNDLED_PLUGINS");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user