fix(gateway): ignore malformed node catalog capabilities (#79205)

This commit is contained in:
Peter Steinberger
2026-05-08 06:47:36 +01:00
committed by GitHub
parent eebbe41da2
commit e7391fc2b6
3 changed files with 32 additions and 2 deletions

View File

@@ -186,6 +186,7 @@ Docs: https://docs.openclaw.ai
- Agents/PI: route PI-native OpenAI-compatible default streams through OpenClaw boundary-aware transports so local-compatible model runs keep API-key injection and transport policy.
- Gateway/media: require authenticated owner or admin context for managed outgoing image bytes instead of trusting requester-session headers.
- Doctor/gateway: avoid duplicate Node runtime warnings when the daemon install plan already selected a supported Node runtime.
- Gateway/nodes: ignore malformed non-string capability entries from live nodes instead of throwing while listing the node catalog.
- Gateway/watch: leave `OPENCLAW_TRACE_SYNC_IO` disabled by default in `pnpm gateway:watch:raw` so watch mode avoids noisy Node sync-I/O stack traces unless explicitly requested.
- Codex app-server: close stdio stdin before force-killing the managed app-server, matching Codex single-client shutdown behavior and avoiding unsettled CLI exits after successful runs.
- CLI/Codex: dispose registered agent harnesses during short-lived CLI shutdown so successful Codex-backed `agent --local` runs do not leave app-server child processes alive.

View File

@@ -289,4 +289,30 @@ describe("gateway/node-catalog", () => {
}),
);
});
it("ignores malformed node capability entries instead of throwing", () => {
const catalog = createKnownNodeCatalog({
pairedDevices: [],
pairedNodes: [],
connectedNodes: [
{
nodeId: "bad-node",
connId: "conn-1",
client: {} as never,
displayName: "Bad Node",
caps: ["camera", undefined],
commands: ["system.run", null],
connectedAtMs: 1,
} as never,
],
});
expect(listKnownNodes(catalog)).toEqual([
expect.objectContaining({
nodeId: "bad-node",
caps: ["camera"],
commands: ["system.run"],
}),
]);
});
});

View File

@@ -47,13 +47,16 @@ type KnownNodeCatalog = {
entriesById: Map<string, KnownNodeEntry>;
};
function uniqueSortedStrings(...items: Array<readonly string[] | undefined>): string[] {
function uniqueSortedStrings(...items: Array<readonly unknown[] | undefined>): string[] {
const values = new Set<string>();
for (const item of items) {
if (!item) {
if (!Array.isArray(item)) {
continue;
}
for (const value of item) {
if (typeof value !== "string") {
continue;
}
const trimmed = value.trim();
if (trimmed) {
values.add(trimmed);