Commit c8f666ff authored by edy's avatar edy

Refine packaged startup diagnostics

parent ccc9192e
......@@ -22,14 +22,17 @@ export function isTransientLocalGatewayError(message?: string): boolean {
|| normalized.includes("econnrefused")
|| normalized.includes("failed to connect to ws://127.0.0.1")
|| normalized.includes("failed to connect to ws://localhost")
|| normalized.includes("gateway readiness")
|| normalized.includes("gateway became ready")
|| normalized.includes("timed out while connecting to ws://127.0.0.1")
|| normalized.includes("timed out while connecting to ws://localhost")
|| normalized.includes("gateway closed during readiness probe")
|| normalized.includes("gateway closed before readiness probe completed")
|| normalized.includes("gateway closed before health probe completed")
|| normalized.includes("timed out while probing reusable gateway")
|| normalized.includes("timed out while waiting for reusable gateway")
|| normalized.includes("bundled runtime exited before gateway became ready");
|| normalized.includes("timed out while probing bundled gateway")
|| normalized.includes("gateway health probe reported not ok")
|| normalized.includes("gateway starting; retry shortly")
|| normalized.includes("startup-sidecars")
|| normalized.includes("unavailable");
}
export function isGatewayPolicyViolationError(message?: string): boolean {
......@@ -57,7 +60,11 @@ export function isBundledRuntimeNameConflictError(message?: string): boolean {
export function toStartupErrorMessage(message: string | undefined, fallback: string): string {
if (isTransientLocalGatewayError(message)) {
return "本地助手暂时没有准备好,请重试。";
return "本地助手正在启动,请稍候。";
}
if (message?.toLowerCase().includes("macsecurityblock")) {
return "macOS 阻止了内置运行时启动。请在系统安全设置中允许应用运行,或移除应用隔离属性后重试。";
}
if (isBundledRuntimeNameConflictError(message)) {
......@@ -82,9 +89,7 @@ export function shouldRetryManagedRuntimeStartup(config: AppConfig, status: Runt
}
const runtimeError = status.lastError ?? status.message;
return isTransientLocalGatewayError(runtimeError)
|| isGatewayPolicyViolationError(runtimeError)
|| isBundledRuntimeNameConflictError(runtimeError);
return isTransientLocalGatewayError(runtimeError);
}
export function shouldRetryBootstrapWarmup(input: {
......@@ -104,20 +109,12 @@ export function shouldRetryBootstrapWarmup(input: {
}
const runtimeError = input.runtimeStatus.lastError ?? input.runtimeStatus.message;
if (input.runtimeStatus.processState === "error" && (
isTransientLocalGatewayError(runtimeError)
|| isGatewayPolicyViolationError(runtimeError)
|| isBundledRuntimeNameConflictError(runtimeError)
)) {
if (input.runtimeStatus.processState === "error" && isTransientLocalGatewayError(runtimeError)) {
return true;
}
const gatewayError = input.gatewayStatus?.lastError ?? input.gatewayStatus?.message;
return input.gatewayStatus?.state === "error" && (
isTransientLocalGatewayError(gatewayError)
|| isGatewayPolicyViolationError(gatewayError)
|| isBundledRuntimeNameConflictError(gatewayError)
);
return input.gatewayStatus?.state === "error" && isTransientLocalGatewayError(gatewayError);
}
export function requiresShellWarmupBeforeBinding(config: AppConfig): boolean {
......@@ -169,7 +166,9 @@ function buildSetupSummary(config: AppConfig): Pick<WorkspaceSummary, "chatReady
}
function buildRuntimeStartingSummary(runtimeStatus: RuntimeStatus): Pick<WorkspaceSummary, "chatReady" | "chatLaunchState" | "chatStatusMessage" | "startupPhase" | "startupMessage"> {
const message = runtimeStatus.message || "正在唤起本地助手,请稍候。";
const message = runtimeStatus.selectedMode === "bundled-runtime"
? "本地助手正在启动,请稍候。"
: runtimeStatus.message || "正在唤起本地助手,请稍候。";
return {
chatReady: false,
chatLaunchState: "starting",
......@@ -180,7 +179,10 @@ function buildRuntimeStartingSummary(runtimeStatus: RuntimeStatus): Pick<Workspa
}
function buildGatewayStartingSummary(gatewayStatus: GatewayStatus | null): Pick<WorkspaceSummary, "chatReady" | "chatLaunchState" | "chatStatusMessage" | "startupPhase" | "startupMessage"> {
const message = gatewayStatus?.message ?? "正在连接聊天服务,请稍候。";
const gatewayMessage = gatewayStatus?.lastError ?? gatewayStatus?.message;
const message = isTransientLocalGatewayError(gatewayMessage)
? "本地助手正在启动,请稍候。"
: gatewayStatus?.message ?? "正在连接聊天服务,请稍候。";
return {
chatReady: false,
chatLaunchState: "starting",
......@@ -268,9 +270,9 @@ export function buildChatSummary(input: {
return {
chatReady: false,
chatLaunchState: "starting",
chatStatusMessage: "正在重新唤起本地助手,请稍候。",
chatStatusMessage: "本地助手正在启动,请稍候。",
startupPhase: "starting-runtime",
startupMessage: "正在重新唤起本地助手,请稍候。"
startupMessage: "本地助手正在启动,请稍候。"
};
}
......@@ -278,14 +280,14 @@ export function buildChatSummary(input: {
warmupInFlight
&& packagedBundledRuntime
&& gatewayStatus?.state === "error"
&& (isTransientLocalGatewayError(gatewayError) || isGatewayPolicyViolationError(gatewayError) || isBundledRuntimeNameConflictError(gatewayError))
&& isTransientLocalGatewayError(gatewayError)
) {
return {
chatReady: false,
chatLaunchState: "starting",
chatStatusMessage: "正在重新连接聊天服务,请稍候。",
chatStatusMessage: "本地助手正在启动,请稍候。",
startupPhase: "connecting-gateway",
startupMessage: "正在重新连接聊天服务,请稍候。"
startupMessage: "本地助手正在启动,请稍候。"
};
}
......@@ -345,9 +347,9 @@ export function buildChatSummary(input: {
return {
chatReady: false,
chatLaunchState: "starting",
chatStatusMessage: "正在重新唤起本地助手,请稍候。",
chatStatusMessage: "本地助手正在启动,请稍候。",
startupPhase: "starting-runtime",
startupMessage: "正在重新唤起本地助手,请稍候。"
startupMessage: "本地助手正在启动,请稍候。"
};
}
......@@ -355,14 +357,14 @@ export function buildChatSummary(input: {
warmupInFlight
&& packagedBundledRuntime
&& gatewayStatus?.state === "error"
&& (isTransientLocalGatewayError(gatewayError) || isGatewayPolicyViolationError(gatewayError) || isBundledRuntimeNameConflictError(gatewayError))
&& isTransientLocalGatewayError(gatewayError)
) {
return {
chatReady: false,
chatLaunchState: "starting",
chatStatusMessage: "正在重新连接聊天服务,请稍候。",
chatStatusMessage: "本地助手正在启动,请稍候。",
startupPhase: "connecting-gateway",
startupMessage: "正在重新连接聊天服务,请稍候。"
startupMessage: "本地助手正在启动,请稍候。"
};
}
......
......@@ -176,7 +176,10 @@ async function main(): Promise<void> {
"Gateway closed during connect (1000).",
"Gateway closed before health probe completed (1000).",
"Timed out while probing reusable Gateway status and health at ws://127.0.0.1:18889.",
"Timed out while waiting for reusable Gateway at ws://127.0.0.1:18889.",
"Timed out while probing bundled Gateway status and health at ws://127.0.0.1:18889.",
"Timed out while connecting to ws://127.0.0.1:18889.",
"Gateway health probe reported not ok.",
"gateway starting; retry shortly | startup-sidecars | UNAVAILABLE",
"connect ECONNREFUSED 127.0.0.1:18889"
];
for (const message of transientCodes) {
......@@ -186,6 +189,8 @@ async function main(): Promise<void> {
assert(isBundledRuntimeNameConflictError("gateway name/hostname conflict detected via bonjour"), "Expected bonjour name conflict to be classified correctly.");
const policyViolationMessage = toStartupErrorMessage("Gateway connection closed (1008).", "fallback");
assert(!policyViolationMessage.includes("本机已有 OpenClaw 网关正在运行"), "Policy violation message should no longer hard-assert a local OpenClaw gateway conflict.");
const macSecurityMessage = toStartupErrorMessage("macSecurityBlock: macOS blocked bundled runtime execution (Operation not permitted).", "fallback");
assert(macSecurityMessage.includes("macOS 阻止了内置运行时启动"), "mac security blocks should get a specific startup diagnostic.");
const config = createConfig();
const runtimeStatus = createRuntimeStatus({
......@@ -210,6 +215,7 @@ async function main(): Promise<void> {
});
assert(startupSummary.chatLaunchState === "starting", "Transient packaged startup failures should remain in starting state.");
assert(startupSummary.startupPhase === "starting-runtime", "Transient runtime failures should map to starting-runtime.");
assert(startupSummary.startupMessage === "本地助手正在启动,请稍候。", "Transient runtime startup should use the unified local assistant starting message.");
const gatewayOnlySummary = buildChatSummary({
config,
......@@ -223,6 +229,7 @@ async function main(): Promise<void> {
});
assert(gatewayOnlySummary.chatLaunchState === "starting", "Transient gateway failures should remain in starting state during packaged warmup.");
assert(gatewayOnlySummary.startupPhase === "connecting-gateway", "Transient gateway failures should map to connecting-gateway.");
assert(gatewayOnlySummary.startupMessage === "本地助手正在启动,请稍候。", "Transient gateway startup should use the unified local assistant starting message.");
assert(shouldRetryBootstrapWarmup({
config,
......@@ -264,8 +271,8 @@ async function main(): Promise<void> {
runtimeStatus: createRuntimeStatus(),
gatewayStatus: policyViolationGatewayStatus,
isPackaged: true
}), "Packaged bundled-runtime bootstrap should retry gateway policy violations.");
assert(shouldRetryManagedRuntimeStartup(config, policyViolationRuntimeStatus), "Packaged bundled-runtime startup should retry runtime policy violations.");
}) === false, "Packaged bundled-runtime bootstrap should not retry gateway policy violations.");
assert(!shouldRetryManagedRuntimeStartup(config, policyViolationRuntimeStatus), "Packaged bundled-runtime startup should not retry runtime policy violations.");
const nameConflictGatewayStatus = createGatewayStatus({
state: "error",
......@@ -277,7 +284,7 @@ async function main(): Promise<void> {
runtimeStatus: createRuntimeStatus(),
gatewayStatus: nameConflictGatewayStatus,
isPackaged: true
}), "Packaged bundled-runtime bootstrap should retry bundled runtime name conflicts.");
}) === false, "Packaged bundled-runtime bootstrap should not retry bundled runtime name conflicts.");
const unboundConfig = createConfig({
apiKeyConfigured: false
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment