mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 12:34:47 +00:00
fix: consolidate gateway doctor service notes (#78688)
Fixes #80287. Co-authored-by: YB0y <brianandez6@gmail.com>
This commit is contained in:
@@ -71,6 +71,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Channels: cache selected channel registry lookups against the active fallback snapshot so pinned-empty registries refresh native command and alias routing after active registry swaps. (#80333) Thanks @samzong.
|
||||
- Gateway: scope `sessions.resolve` sessionId and label store loads to the requested agent so large unrelated agent stores are not parsed for scoped lookups. Fixes #51264. (#79474) Thanks @samzong.
|
||||
- Gateway: share serialized streaming event envelopes across eligible WebSocket and node subscribers while preserving per-client sequence numbers. (#80299) Thanks @samzong.
|
||||
- Gateway: consolidate duplicate `openclaw doctor` service config panels while preserving the declined-repair `--force` hint. Fixes #80287. (#78688) Thanks @YB0y.
|
||||
- Browser: report Chrome MCP existing-session page readiness in browser status without letting status probes exceed the client timeout. Fixes #80268. (#80280) Thanks @ai-hpc.
|
||||
- WhatsApp: route opening-phase Baileys 428 connectionClosed through the WhatsApp reconnect policy and keep post-open 428 closes retryable, so transient setup socket closes retry with WhatsApp diagnostics instead of escaping as a bare `channel exited` error. Fixes #75736; mitigates #77443. Thanks @dataCenter430.
|
||||
- Agents: disable Pi's default filesystem resource discovery for embedded runs while keeping OpenClaw inline extension factories active, avoiding Windows event-loop stalls during first WhatsApp-triggered agent startup. Fixes #77443. Thanks @dataCenter430.
|
||||
|
||||
@@ -998,6 +998,102 @@ describe("maybeRepairGatewayServiceConfig", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("does not duplicate Gateway service config panels for a source-checkout entrypoint with audit findings", async () => {
|
||||
await withEnvAsync({}, async () => {
|
||||
const root = await fs.mkdtemp(
|
||||
path.join(os.tmpdir(), "openclaw-doctor-service-config-dedup-"),
|
||||
);
|
||||
try {
|
||||
await fs.mkdir(path.join(root, ".git"), { recursive: true });
|
||||
await fs.mkdir(path.join(root, "src"), { recursive: true });
|
||||
await fs.mkdir(path.join(root, "extensions"), { recursive: true });
|
||||
await fs.mkdir(path.join(root, "dist"), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(root, "package.json"),
|
||||
JSON.stringify({ name: "openclaw", version: "0.0.0-test" }),
|
||||
"utf8",
|
||||
);
|
||||
const sourceCheckoutEntrypoint = path.join(root, "dist", "index.js");
|
||||
await fs.writeFile(sourceCheckoutEntrypoint, "export {};\n", "utf8");
|
||||
const installEntrypoint = "/usr/local/lib/node_modules/openclaw/dist/index.js";
|
||||
setupGatewayEntrypointRepairScenario({
|
||||
currentEntrypoint: sourceCheckoutEntrypoint,
|
||||
installEntrypoint,
|
||||
installWorkingDirectory: "/tmp",
|
||||
});
|
||||
|
||||
await runRepair({ gateway: {} });
|
||||
|
||||
const gatewayServiceConfigNotes = mocks.note.mock.calls.filter(
|
||||
([, title]) => title === "Gateway service config",
|
||||
);
|
||||
expect(gatewayServiceConfigNotes).toHaveLength(1);
|
||||
const consolidated = gatewayServiceConfigNotes[0]?.[0] ?? "";
|
||||
expect(consolidated).toContain(
|
||||
"Gateway service entrypoint does not match the current install.",
|
||||
);
|
||||
expect(consolidated).not.toContain("resolves to a source checkout");
|
||||
const forceMatches = consolidated.match(/openclaw gateway install --force/g) ?? [];
|
||||
expect(forceMatches).toHaveLength(0);
|
||||
} finally {
|
||||
await fs.rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the gateway install force hint when a source-checkout warning is suppressed and repair is declined", async () => {
|
||||
await withEnvAsync({}, async () => {
|
||||
const root = await fs.mkdtemp(
|
||||
path.join(os.tmpdir(), "openclaw-doctor-service-config-force-hint-"),
|
||||
);
|
||||
try {
|
||||
await fs.mkdir(path.join(root, ".git"), { recursive: true });
|
||||
await fs.mkdir(path.join(root, "src"), { recursive: true });
|
||||
await fs.mkdir(path.join(root, "extensions"), { recursive: true });
|
||||
await fs.mkdir(path.join(root, "dist"), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(root, "package.json"),
|
||||
JSON.stringify({ name: "openclaw", version: "0.0.0-test" }),
|
||||
"utf8",
|
||||
);
|
||||
const sourceCheckoutEntrypoint = path.join(root, "dist", "index.js");
|
||||
await fs.writeFile(sourceCheckoutEntrypoint, "export {};\n", "utf8");
|
||||
const installEntrypoint = "/usr/local/lib/node_modules/openclaw/dist/index.js";
|
||||
setupGatewayEntrypointRepairScenario({
|
||||
currentEntrypoint: sourceCheckoutEntrypoint,
|
||||
installEntrypoint,
|
||||
installWorkingDirectory: "/tmp",
|
||||
});
|
||||
|
||||
const declinePrompts = {
|
||||
...makeDoctorPrompts(),
|
||||
confirmAutoFix: vi.fn().mockResolvedValue(false),
|
||||
confirmAggressiveAutoFix: vi.fn().mockResolvedValue(false),
|
||||
confirmRuntimeRepair: vi.fn().mockResolvedValue(false),
|
||||
};
|
||||
await maybeRepairGatewayServiceConfig(
|
||||
{ gateway: {} },
|
||||
"local",
|
||||
makeDoctorIo(),
|
||||
declinePrompts,
|
||||
);
|
||||
|
||||
const gatewayServiceConfigNotes = mocks.note.mock.calls.filter(
|
||||
([, title]) => title === "Gateway service config",
|
||||
);
|
||||
expect(gatewayServiceConfigNotes).toHaveLength(2);
|
||||
const auditNote = gatewayServiceConfigNotes[0]?.[0] ?? "";
|
||||
expect(auditNote).toContain(
|
||||
"Gateway service entrypoint does not match the current install.",
|
||||
);
|
||||
expect(auditNote).not.toContain("resolves to a source checkout");
|
||||
expect(gatewayServiceConfigNotes[1]?.[0]).toContain("openclaw gateway install --force");
|
||||
} finally {
|
||||
await fs.rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("maybeScanExtraGatewayServices", () => {
|
||||
|
||||
@@ -381,15 +381,12 @@ export async function maybeRepairGatewayServiceConfig(
|
||||
note(`Gateway service invokes ${OPENCLAW_WRAPPER_ENV_KEY}: ${serviceWrapperPath}`, "Gateway");
|
||||
}
|
||||
const serviceLayout = await summarizeGatewayServiceLayout(command);
|
||||
if (serviceLayout?.entrypointSourceCheckout) {
|
||||
note(
|
||||
[
|
||||
const sourceCheckoutWarning = serviceLayout?.entrypointSourceCheckout
|
||||
? [
|
||||
`Gateway service entrypoint resolves to a source checkout: ${serviceLayout.packageRootReal ?? serviceLayout.packageRoot ?? serviceLayout.entrypointReal ?? serviceLayout.entrypoint}.`,
|
||||
"Run `openclaw doctor --fix` from the intended package install, or reinstall the gateway service with `openclaw gateway install --force`.",
|
||||
].join("\n"),
|
||||
"Gateway service config",
|
||||
);
|
||||
}
|
||||
].join("\n")
|
||||
: null;
|
||||
|
||||
const tokenRefConfigured = Boolean(
|
||||
resolveSecretInputRef({
|
||||
@@ -483,21 +480,34 @@ export async function maybeRepairGatewayServiceConfig(
|
||||
issues: audit.issues,
|
||||
});
|
||||
|
||||
const hasEntrypointMismatch = audit.issues.some(
|
||||
(issue) => issue.code === SERVICE_AUDIT_CODES.gatewayEntrypointMismatch,
|
||||
);
|
||||
const showSourceCheckoutWarning = sourceCheckoutWarning !== null && !hasEntrypointMismatch;
|
||||
|
||||
if (audit.issues.length === 0) {
|
||||
if (sourceCheckoutWarning !== null && !hasEntrypointMismatch) {
|
||||
note(sourceCheckoutWarning, "Gateway service config");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const serviceRepairPolicy = resolveServiceRepairPolicy();
|
||||
const serviceRepairExternal = isServiceRepairExternallyManaged(serviceRepairPolicy);
|
||||
|
||||
note(
|
||||
audit.issues
|
||||
.map((issue) =>
|
||||
issue.detail ? `- ${issue.message} (${issue.detail})` : `- ${issue.message}`,
|
||||
)
|
||||
.join("\n"),
|
||||
"Gateway service config",
|
||||
const consolidatedLines: string[] = [];
|
||||
let emittedSourceCheckoutWarning = false;
|
||||
if (sourceCheckoutWarning !== null && showSourceCheckoutWarning) {
|
||||
consolidatedLines.push(sourceCheckoutWarning);
|
||||
consolidatedLines.push("");
|
||||
emittedSourceCheckoutWarning = true;
|
||||
}
|
||||
consolidatedLines.push(
|
||||
...audit.issues.map((issue) =>
|
||||
issue.detail ? `- ${issue.message} (${issue.detail})` : `- ${issue.message}`,
|
||||
),
|
||||
);
|
||||
note(consolidatedLines.join("\n"), "Gateway service config");
|
||||
|
||||
const aggressiveIssues = audit.issues.filter((issue) => issue.level === "aggressive");
|
||||
const needsAggressive = aggressiveIssues.length > 0;
|
||||
@@ -555,10 +565,12 @@ export async function maybeRepairGatewayServiceConfig(
|
||||
requiresInteractiveConfirmation: true,
|
||||
});
|
||||
if (!repair) {
|
||||
note(
|
||||
"Run `openclaw gateway install --force` when you want to replace the gateway service definition.",
|
||||
"Gateway service config",
|
||||
);
|
||||
if (!emittedSourceCheckoutWarning) {
|
||||
note(
|
||||
"Run `openclaw gateway install --force` when you want to replace the gateway service definition.",
|
||||
"Gateway service config",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const serviceEmbeddedToken = readEmbeddedGatewayToken(command);
|
||||
|
||||
Reference in New Issue
Block a user