























@@ -220,6 +220,155 @@ describe("skills-remote", () => {
220220}
221221});
222222223+it("skips remote bin probes when the node connectivity preflight fails", async () => {
224+await resetSkillsRefreshForTest();
225+const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-remote-skills-"));
226+const nodeId = `node-${randomUUID()}`;
227+const bin = `bin-${randomUUID()}`;
228+try {
229+fs.mkdirSync(path.join(workspaceDir, "remote-skill"), { recursive: true });
230+fs.writeFileSync(
231+path.join(workspaceDir, "remote-skill", "SKILL.md"),
232+[
233+"---",
234+"name: remote-skill",
235+"description: Needs a remote bin",
236+`metadata: { "openclaw": { "os": ["darwin"], "requires": { "bins": ["${bin}"] } } }`,
237+"---",
238+"# Remote Skill",
239+"",
240+].join("\n"),
241+);
242+const cfg = {
243+agents: {
244+defaults: {
245+workspace: workspaceDir,
246+},
247+},
248+} satisfies OpenClawConfig;
249+const invokeCalls: string[] = [];
250+setSkillsRemoteRegistry({
251+listConnected: () => [],
252+get: () => undefined,
253+checkConnectivity: async () => ({
254+ok: false,
255+error: { code: "TIMEOUT", message: "node connectivity probe timed out" },
256+}),
257+invoke: async (params: { command: string }) => {
258+invokeCalls.push(params.command);
259+return {
260+ok: true,
261+payloadJSON: JSON.stringify({ bins: [bin] }),
262+};
263+},
264+} as unknown as NodeRegistry);
265+recordRemoteNodeInfo({
266+ nodeId,
267+displayName: "Remote Mac",
268+platform: "darwin",
269+commands: ["system.run", "system.which"],
270+});
271+recordRemoteNodeBins(nodeId, [bin]);
272+const before = getSkillsSnapshotVersion(workspaceDir);
273+274+await refreshRemoteNodeBins({
275+ nodeId,
276+platform: "darwin",
277+commands: ["system.run", "system.which"],
278+ cfg,
279+timeoutMs: 10,
280+});
281+282+expect(invokeCalls).toEqual([]);
283+expect(getRemoteSkillEligibility()?.hasBin(bin) ?? false).toBe(false);
284+expect(getSkillsSnapshotVersion(workspaceDir)).toBeGreaterThan(before);
285+} finally {
286+removeRemoteNodeInfo(nodeId);
287+fs.rmSync(workspaceDir, { recursive: true, force: true });
288+}
289+});
290+291+it("retries the bin probe when the node reconnects during preflight", async () => {
292+await resetSkillsRefreshForTest();
293+const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-remote-skills-"));
294+const nodeId = `node-${randomUUID()}`;
295+const bin = `bin-${randomUUID()}`;
296+try {
297+fs.mkdirSync(path.join(workspaceDir, "remote-skill"), { recursive: true });
298+fs.writeFileSync(
299+path.join(workspaceDir, "remote-skill", "SKILL.md"),
300+[
301+"---",
302+"name: remote-skill",
303+"description: Needs a remote bin",
304+`metadata: { "openclaw": { "os": ["darwin"], "requires": { "bins": ["${bin}"] } } }`,
305+"---",
306+"# Remote Skill",
307+"",
308+].join("\n"),
309+);
310+const cfg = {
311+agents: {
312+defaults: {
313+workspace: workspaceDir,
314+},
315+},
316+} satisfies OpenClawConfig;
317+let connId = "conn-old";
318+const connectivityCalls: string[] = [];
319+const invokeCalls: string[] = [];
320+setSkillsRemoteRegistry({
321+listConnected: () => [],
322+get: () =>
323+({
324+ nodeId,
325+ connId,
326+platform: "darwin",
327+commands: ["system.run", "system.which"],
328+}) as unknown as ReturnType<NodeRegistry["get"]>,
329+checkConnectivity: async () => {
330+connectivityCalls.push(connId);
331+if (connectivityCalls.length === 1) {
332+connId = "conn-new";
333+return {
334+ok: false,
335+error: { code: "TIMEOUT", message: "node connectivity probe timed out" },
336+};
337+}
338+return { ok: true };
339+},
340+invoke: async (params: { command: string }) => {
341+invokeCalls.push(params.command);
342+return {
343+ok: true,
344+payloadJSON: JSON.stringify({ bins: [bin] }),
345+};
346+},
347+} as unknown as NodeRegistry);
348+recordRemoteNodeInfo({
349+ nodeId,
350+displayName: "Remote Mac",
351+platform: "darwin",
352+commands: ["system.run", "system.which"],
353+});
354+355+await refreshRemoteNodeBins({
356+ nodeId,
357+platform: "darwin",
358+commands: ["system.run", "system.which"],
359+ cfg,
360+timeoutMs: 10,
361+});
362+363+expect(connectivityCalls).toEqual(["conn-old", "conn-new"]);
364+expect(invokeCalls).toEqual(["system.which"]);
365+expect(getRemoteSkillEligibility()?.hasBin(bin)).toBe(true);
366+} finally {
367+removeRemoteNodeInfo(nodeId);
368+fs.rmSync(workspaceDir, { recursive: true, force: true });
369+}
370+});
371+223372it("coalesces overlapping bin probes for the same node", async () => {
224373const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-remote-skills-"));
225374const nodeId = `node-${randomUUID()}`;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。