mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:40:44 +00:00
fix: stabilize Parallels plugin smoke paths
This commit is contained in:
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Gateway/Bonjour: keep @homebridge/ciao cancellation handlers registered across advertiser restarts so late probing cancellations cannot crash Linux and other mDNS-churned gateways. Thanks @codex.
|
||||
- Plugins/startup: load the default `memory-core` slot during Gateway startup when permitted so active-memory recall can call `memory_search` and `memory_get` without requiring an explicit `plugins.slots.memory` entry, while preserving `plugins.slots.memory: "none"`. Thanks @codex.
|
||||
- Plugins/CLI: prefer native require for compiled bundled plugin JavaScript before jiti so read-only config, status, device, and node commands avoid unnecessary transform overhead on slow hosts. Fixes #62842. Thanks @Effet.
|
||||
- Plugins/compat: inventory doctor-side deprecation migrations separately from runtime plugin compatibility so release sweeps preserve needed repairs while enforcing dated removal windows. Thanks @vincentkoc.
|
||||
|
||||
@@ -490,6 +490,8 @@ describe("gateway bonjour advertiser", () => {
|
||||
|
||||
const stateRef = { value: "announcing" };
|
||||
const events: string[] = [];
|
||||
const cleanupException = vi.fn();
|
||||
const cleanupRejection = vi.fn();
|
||||
let advertiseCount = 0;
|
||||
const destroy = vi.fn().mockImplementation(async () => {
|
||||
events.push("destroy");
|
||||
@@ -505,6 +507,8 @@ describe("gateway bonjour advertiser", () => {
|
||||
return Promise.resolve();
|
||||
});
|
||||
mockCiaoService({ advertise, destroy, stateRef });
|
||||
registerUncaughtExceptionHandler.mockImplementation(() => cleanupException);
|
||||
registerUnhandledRejectionHandler.mockImplementation(() => cleanupRejection);
|
||||
|
||||
const started = await startAdvertiser({
|
||||
gatewayPort: 18789,
|
||||
@@ -513,6 +517,8 @@ describe("gateway bonjour advertiser", () => {
|
||||
|
||||
expect(createService).toHaveBeenCalledTimes(1);
|
||||
expect(advertise).toHaveBeenCalledTimes(1);
|
||||
expect(registerUncaughtExceptionHandler).toHaveBeenCalledTimes(1);
|
||||
expect(registerUnhandledRejectionHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(15_000);
|
||||
|
||||
@@ -521,11 +527,15 @@ describe("gateway bonjour advertiser", () => {
|
||||
expect(advertise).toHaveBeenCalledTimes(2);
|
||||
expect(destroy).toHaveBeenCalledTimes(1);
|
||||
expect(shutdown).not.toHaveBeenCalled();
|
||||
expect(cleanupException).not.toHaveBeenCalled();
|
||||
expect(cleanupRejection).not.toHaveBeenCalled();
|
||||
expect(events).toEqual(["advertise:1", "destroy", "advertise:2"]);
|
||||
|
||||
await started.stop();
|
||||
expect(destroy).toHaveBeenCalledTimes(2);
|
||||
expect(shutdown).toHaveBeenCalledTimes(1);
|
||||
expect(cleanupException).toHaveBeenCalledTimes(1);
|
||||
expect(cleanupRejection).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("treats probing-to-announcing churn as one unhealthy window", async () => {
|
||||
|
||||
@@ -50,8 +50,6 @@ type CiaoModule = {
|
||||
type BonjourCycle = {
|
||||
responder: BonjourResponder;
|
||||
services: Array<{ label: string; svc: BonjourService }>;
|
||||
cleanupUncaughtException?: () => void;
|
||||
cleanupUnhandledRejection?: () => void;
|
||||
};
|
||||
|
||||
type ServiceStateTracker = {
|
||||
@@ -179,6 +177,18 @@ export async function startGatewayBonjourAdvertiser(
|
||||
const { getResponder, Protocol } = await loadCiaoModule();
|
||||
const restoreConsoleLog = installCiaoConsoleNoiseFilter();
|
||||
let requestCiaoRecovery: ((classification: CiaoProcessErrorClassification) => void) | undefined;
|
||||
let cleanupUnhandledRejection: (() => void) | undefined;
|
||||
let cleanupUncaughtException: (() => void) | undefined;
|
||||
let processHandlersCleaned = false;
|
||||
|
||||
function cleanupProcessHandlers() {
|
||||
if (processHandlersCleaned) {
|
||||
return;
|
||||
}
|
||||
processHandlersCleaned = true;
|
||||
cleanupUncaughtException?.();
|
||||
cleanupUnhandledRejection?.();
|
||||
}
|
||||
|
||||
const handleCiaoProcessError = (reason: unknown): boolean => {
|
||||
const classification = classifyCiaoProcessError(reason);
|
||||
@@ -196,6 +206,8 @@ export async function startGatewayBonjourAdvertiser(
|
||||
}
|
||||
return true;
|
||||
};
|
||||
cleanupUnhandledRejection = deps.registerUnhandledRejectionHandler?.(handleCiaoProcessError);
|
||||
cleanupUncaughtException = deps.registerUncaughtExceptionHandler?.(handleCiaoProcessError);
|
||||
|
||||
try {
|
||||
const hostnameRaw = process.env.OPENCLAW_MDNS_HOSTNAME?.trim() || "openclaw";
|
||||
@@ -259,16 +271,7 @@ export async function startGatewayBonjourAdvertiser(
|
||||
svc: gateway as unknown as BonjourService,
|
||||
});
|
||||
|
||||
const cleanupUnhandledRejection =
|
||||
services.length > 0 && deps.registerUnhandledRejectionHandler
|
||||
? deps.registerUnhandledRejectionHandler(handleCiaoProcessError)
|
||||
: undefined;
|
||||
const cleanupUncaughtException =
|
||||
services.length > 0 && deps.registerUncaughtExceptionHandler
|
||||
? deps.registerUncaughtExceptionHandler(handleCiaoProcessError)
|
||||
: undefined;
|
||||
|
||||
return { responder, services, cleanupUncaughtException, cleanupUnhandledRejection };
|
||||
return { responder, services };
|
||||
}
|
||||
|
||||
async function stopCycle(cycle: BonjourCycle | null, opts?: { shutdownResponder?: boolean }) {
|
||||
@@ -288,9 +291,6 @@ export async function startGatewayBonjourAdvertiser(
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
} finally {
|
||||
cycle.cleanupUncaughtException?.();
|
||||
cycle.cleanupUnhandledRejection?.();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,10 +483,12 @@ export async function startGatewayBonjourAdvertiser(
|
||||
}
|
||||
await stopCycle(cycle, { shutdownResponder: true });
|
||||
restoreConsoleLog();
|
||||
cleanupProcessHandlers();
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
restoreConsoleLog();
|
||||
cleanupProcessHandlers();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ TIMEOUT_ONBOARD_S=180
|
||||
TIMEOUT_AGENT_S="${OPENCLAW_PARALLELS_LINUX_AGENT_TIMEOUT_S:-300}"
|
||||
TIMEOUT_GATEWAY_S=240
|
||||
PHASE_STALE_WARN_S=60
|
||||
DISABLE_BONJOUR_FOR_GATEWAY=0
|
||||
|
||||
FRESH_MAIN_STATUS="skip"
|
||||
FRESH_MAIN_VERSION="skip"
|
||||
@@ -230,6 +231,11 @@ esac
|
||||
|
||||
API_KEY_VALUE="${!API_KEY_ENV:-}"
|
||||
[[ -n "$API_KEY_VALUE" ]] || die "$API_KEY_ENV is required"
|
||||
case "${OPENCLAW_PARALLELS_LINUX_DISABLE_BONJOUR:-}" in
|
||||
1|true|TRUE|yes|YES|on|ON)
|
||||
DISABLE_BONJOUR_FOR_GATEWAY=1
|
||||
;;
|
||||
esac
|
||||
|
||||
resolve_vm_name() {
|
||||
local json requested explicit
|
||||
@@ -725,12 +731,16 @@ EOF
|
||||
}
|
||||
|
||||
start_gateway_background() {
|
||||
local cmd api_key_value_q
|
||||
local cmd api_key_value_q bonjour_env
|
||||
api_key_value_q="$(shell_quote "$API_KEY_VALUE")"
|
||||
bonjour_env=""
|
||||
if [[ "$DISABLE_BONJOUR_FOR_GATEWAY" -eq 1 ]]; then
|
||||
bonjour_env=" OPENCLAW_DISABLE_BONJOUR=1"
|
||||
fi
|
||||
cmd="$(cat <<EOF
|
||||
pkill -f "openclaw gateway run" >/dev/null 2>&1 || true
|
||||
rm -f /tmp/openclaw-parallels-linux-gateway.log
|
||||
setsid sh -lc 'exec env OPENCLAW_HOME=/root OPENCLAW_STATE_DIR=/root/.openclaw OPENCLAW_CONFIG_PATH=/root/.openclaw/openclaw.json ${API_KEY_ENV}=${api_key_value_q} openclaw gateway run --bind loopback --port 18789 --force >/tmp/openclaw-parallels-linux-gateway.log 2>&1' >/dev/null 2>&1 < /dev/null &
|
||||
setsid sh -lc 'exec env OPENCLAW_HOME=/root OPENCLAW_STATE_DIR=/root/.openclaw OPENCLAW_CONFIG_PATH=/root/.openclaw/openclaw.json${bonjour_env} ${API_KEY_ENV}=${api_key_value_q} openclaw gateway run --bind loopback --port 18789 --force >/tmp/openclaw-parallels-linux-gateway.log 2>&1' >/dev/null 2>&1 < /dev/null &
|
||||
EOF
|
||||
)"
|
||||
guest_exec bash -lc "$cmd"
|
||||
|
||||
@@ -1944,7 +1944,7 @@ if platform_enabled windows; then
|
||||
fi
|
||||
|
||||
if platform_enabled linux; then
|
||||
bash "$ROOT_DIR/scripts/e2e/parallels-linux-smoke.sh" \
|
||||
OPENCLAW_PARALLELS_LINUX_DISABLE_BONJOUR=1 bash "$ROOT_DIR/scripts/e2e/parallels-linux-smoke.sh" \
|
||||
--mode fresh \
|
||||
--provider "$PROVIDER" \
|
||||
--model "$MODEL_ID" \
|
||||
|
||||
@@ -610,6 +610,9 @@ function buildInstalledPluginIndex(
|
||||
if (record.setupSource) {
|
||||
indexRecord.setupSource = record.setupSource;
|
||||
}
|
||||
if (record.syntheticAuthRefs && record.syntheticAuthRefs.length > 0) {
|
||||
indexRecord.syntheticAuthRefs = record.syntheticAuthRefs;
|
||||
}
|
||||
if (candidate?.packageName) {
|
||||
indexRecord.packageName = candidate.packageName;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user