fix(gateway): ignore malformed node catalog capabilities (#79205) · openclaw/openclaw@e7391fc
steipete
·
2026-05-08
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -186,6 +186,7 @@ Docs: https://docs.openclaw.ai
|
186 | 186 | - 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. |
187 | 187 | - Gateway/media: require authenticated owner or admin context for managed outgoing image bytes instead of trusting requester-session headers. |
188 | 188 | - Doctor/gateway: avoid duplicate Node runtime warnings when the daemon install plan already selected a supported Node runtime. |
| 189 | +- Gateway/nodes: ignore malformed non-string capability entries from live nodes instead of throwing while listing the node catalog. |
189 | 190 | - 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. |
190 | 191 | - 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. |
191 | 192 | - 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. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -289,4 +289,30 @@ describe("gateway/node-catalog", () => {
|
289 | 289 | }), |
290 | 290 | ); |
291 | 291 | }); |
| 292 | + |
| 293 | +it("ignores malformed node capability entries instead of throwing", () => { |
| 294 | +const catalog = createKnownNodeCatalog({ |
| 295 | +pairedDevices: [], |
| 296 | +pairedNodes: [], |
| 297 | +connectedNodes: [ |
| 298 | +{ |
| 299 | +nodeId: "bad-node", |
| 300 | +connId: "conn-1", |
| 301 | +client: {} as never, |
| 302 | +displayName: "Bad Node", |
| 303 | +caps: ["camera", undefined], |
| 304 | +commands: ["system.run", null], |
| 305 | +connectedAtMs: 1, |
| 306 | +} as never, |
| 307 | +], |
| 308 | +}); |
| 309 | + |
| 310 | +expect(listKnownNodes(catalog)).toEqual([ |
| 311 | +expect.objectContaining({ |
| 312 | +nodeId: "bad-node", |
| 313 | +caps: ["camera"], |
| 314 | +commands: ["system.run"], |
| 315 | +}), |
| 316 | +]); |
| 317 | +}); |
292 | 318 | }); |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -47,13 +47,16 @@ type KnownNodeCatalog = {
|
47 | 47 | entriesById: Map<string, KnownNodeEntry>; |
48 | 48 | }; |
49 | 49 | |
50 | | -function uniqueSortedStrings(...items: Array<readonly string[] | undefined>): string[] { |
| 50 | +function uniqueSortedStrings(...items: Array<readonly unknown[] | undefined>): string[] { |
51 | 51 | const values = new Set<string>(); |
52 | 52 | for (const item of items) { |
53 | | -if (!item) { |
| 53 | +if (!Array.isArray(item)) { |
54 | 54 | continue; |
55 | 55 | } |
56 | 56 | for (const value of item) { |
| 57 | +if (typeof value !== "string") { |
| 58 | +continue; |
| 59 | +} |
57 | 60 | const trimmed = value.trim(); |
58 | 61 | if (trimmed) { |
59 | 62 | values.add(trimmed); |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。