mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:40:44 +00:00
fix(skills): parse remote which bin maps
This commit is contained in:
@@ -121,6 +121,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Providers/Azure OpenAI: give deployment-scoped image generation requests a longer 600s default timeout so slow `gpt-image-2` generations can complete without a per-call `timeoutMs`. Fixes #71705. Thanks @voytas75.
|
||||
- Gateway/plugins: link source-checkout bundled runtime dependency caches instead of recursively copying `node_modules` on the gateway main thread, preventing local status, node, and skill probes from timing out during startup cache restores. Thanks @steipete.
|
||||
- Skills/remote nodes: only expose remote macOS skill bins for connected nodes, clear stale bin matches when node probes fail, and include probe command, timeout, bin count, and connection state in timeout logs. Thanks @steipete.
|
||||
- Skills/remote nodes: recognize `system.which` object-map responses when probing connected macOS nodes, so Linux gateways can expose macOS-only skills such as Apple Notes when the required binaries are installed remotely. Fixes #71877. Thanks @miguelarios.
|
||||
- CLI/gateway: keep diagnostic probes from creating first-time read-only device pairings, while still reusing cached device tokens for detailed read probes. Fixes #71766. Thanks @SunboZ.
|
||||
- CLI/plugins: keep `message` startup, `channels logs`, `agents delete`, and `agents set-identity` off broad plugin preloading; message delivery still loads plugins when the action actually runs.
|
||||
- Image understanding: resolve configured image models such as local LM Studio vision entries before reporting `Unknown model` when the discovery registry has not registered that provider. Fixes #66486. Thanks @zhanggpcsu.
|
||||
|
||||
@@ -297,4 +297,69 @@ describe("skills-remote", () => {
|
||||
fs.rmSync(workspaceDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("records bins from system.which object-map responses", async () => {
|
||||
await resetSkillsRefreshForTest();
|
||||
const workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-remote-skills-"));
|
||||
const nodeId = `node-${randomUUID()}`;
|
||||
const bin = `bin-${randomUUID()}`;
|
||||
try {
|
||||
fs.mkdirSync(path.join(workspaceDir, "remote-skill"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(workspaceDir, "remote-skill", "SKILL.md"),
|
||||
[
|
||||
"---",
|
||||
"name: remote-skill",
|
||||
"description: Needs a remote bin",
|
||||
`metadata: { "openclaw": { "os": ["darwin"], "requires": { "bins": ["${bin}"] } } }`,
|
||||
"---",
|
||||
"# Remote Skill",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: workspaceDir,
|
||||
},
|
||||
},
|
||||
} satisfies OpenClawConfig;
|
||||
const invokeCalls: string[] = [];
|
||||
setSkillsRemoteRegistry({
|
||||
listConnected: () => [],
|
||||
get: () => undefined,
|
||||
invoke: async (params: { command: string }) => {
|
||||
invokeCalls.push(params.command);
|
||||
return {
|
||||
ok: true,
|
||||
payload: { bins: { [bin]: `/opt/homebrew/bin/${bin}`, missing: "" } },
|
||||
payloadJSON: JSON.stringify({ bins: { [bin]: `/opt/homebrew/bin/${bin}` } }),
|
||||
};
|
||||
},
|
||||
} as unknown as NodeRegistry);
|
||||
recordRemoteNodeInfo({
|
||||
nodeId,
|
||||
displayName: "Remote Mac",
|
||||
platform: "darwin",
|
||||
commands: ["system.run", "system.which"],
|
||||
});
|
||||
const before = getSkillsSnapshotVersion(workspaceDir);
|
||||
|
||||
await refreshRemoteNodeBins({
|
||||
nodeId,
|
||||
platform: "darwin",
|
||||
commands: ["system.run", "system.which"],
|
||||
cfg,
|
||||
timeoutMs: 10,
|
||||
});
|
||||
|
||||
expect(invokeCalls).toEqual(["system.which"]);
|
||||
expect(getRemoteSkillEligibility()?.hasBin(bin)).toBe(true);
|
||||
expect(getRemoteSkillEligibility()?.hasBin("missing")).toBe(false);
|
||||
expect(getSkillsSnapshotVersion(workspaceDir)).toBeGreaterThan(before);
|
||||
} finally {
|
||||
removeRemoteNodeInfo(nodeId);
|
||||
fs.rmSync(workspaceDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -254,6 +254,12 @@ function parseBinProbePayload(payloadJSON: string | null | undefined, payload?:
|
||||
if (Array.isArray(parsed.bins)) {
|
||||
return parsed.bins.map((bin) => normalizeOptionalString(String(bin)) ?? "").filter(Boolean);
|
||||
}
|
||||
if (parsed.bins && typeof parsed.bins === "object") {
|
||||
return Object.entries(parsed.bins)
|
||||
.filter(([, resolvedPath]) => normalizeOptionalString(resolvedPath) !== undefined)
|
||||
.map(([bin]) => normalizeOptionalString(bin) ?? "")
|
||||
.filter(Boolean);
|
||||
}
|
||||
if (typeof parsed.stdout === "string") {
|
||||
return parsed.stdout
|
||||
.split(/\r?\n/)
|
||||
|
||||
Reference in New Issue
Block a user