feat: add system.which bin probe

This commit is contained in:
Peter Steinberger
2026-01-16 07:31:26 +00:00
parent e479c870fd
commit e96b939732
6 changed files with 96 additions and 21 deletions

View File

@@ -37,6 +37,10 @@ function supportsSystemRun(commands?: string[]): boolean {
return Array.isArray(commands) && commands.includes("system.run");
}
function supportsSystemWhich(commands?: string[]): boolean {
return Array.isArray(commands) && commands.includes("system.which");
}
function upsertNode(record: {
nodeId: string;
displayName?: string;
@@ -136,6 +140,27 @@ function buildBinProbeScript(bins: string[]): string {
return `for b in ${escaped}; do if command -v "$b" >/dev/null 2>&1; then echo "$b"; fi; done`;
}
function parseBinProbePayload(payloadJSON: string | null | undefined): string[] {
if (!payloadJSON) return [];
try {
const parsed = JSON.parse(payloadJSON) as { stdout?: unknown; bins?: unknown };
if (Array.isArray(parsed.bins)) {
return parsed.bins
.map((bin) => String(bin).trim())
.filter(Boolean);
}
if (typeof parsed.stdout === "string") {
return parsed.stdout
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean);
}
} catch {
return [];
}
return [];
}
export async function refreshRemoteNodeBins(params: {
nodeId: string;
platform?: string;
@@ -146,7 +171,9 @@ export async function refreshRemoteNodeBins(params: {
}) {
if (!remoteBridge) return;
if (!isMacPlatform(params.platform, params.deviceFamily)) return;
if (!supportsSystemRun(params.commands)) return;
const canWhich = supportsSystemWhich(params.commands);
const canRun = supportsSystemRun(params.commands);
if (!canWhich && !canRun) return;
const workspaceDirs = listWorkspaceDirs(params.cfg);
const requiredBins = new Set<string>();
@@ -158,31 +185,30 @@ export async function refreshRemoteNodeBins(params: {
}
if (requiredBins.size === 0) return;
const script = buildBinProbeScript([...requiredBins]);
const payload = {
command: ["/bin/sh", "-lc", script],
};
try {
const res = await remoteBridge.invoke({
nodeId: params.nodeId,
command: "system.run",
paramsJSON: JSON.stringify(payload),
timeoutMs: params.timeoutMs ?? 15_000,
});
const binsList = [...requiredBins];
const res = await remoteBridge.invoke(
canWhich
? {
nodeId: params.nodeId,
command: "system.which",
paramsJSON: JSON.stringify({ bins: binsList }),
timeoutMs: params.timeoutMs ?? 15_000,
}
: {
nodeId: params.nodeId,
command: "system.run",
paramsJSON: JSON.stringify({
command: ["/bin/sh", "-lc", buildBinProbeScript(binsList)],
}),
timeoutMs: params.timeoutMs ?? 15_000,
},
);
if (!res.ok) {
log.warn(`remote bin probe failed (${params.nodeId}): ${res.error?.message ?? "unknown"}`);
return;
}
const raw = typeof res.payloadJSON === "string" ? res.payloadJSON : "";
const parsed =
raw && raw.trim().length > 0
? (JSON.parse(raw) as { stdout?: string })
: ({ stdout: "" } as { stdout?: string });
const stdout = typeof parsed.stdout === "string" ? parsed.stdout : "";
const bins = stdout
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean);
const bins = parseBinProbePayload(res.payloadJSON);
recordRemoteNodeBins(params.nodeId, bins);
await updatePairedNodeMetadata(params.nodeId, { bins });
bumpSkillsSnapshotVersion({ reason: "remote-node" });