mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-24 22:31:12 +00:00
* feat(linux): add node device capabilities * fix(linux-node): actionable pending-approval error + node-host advertise integration test * fix(linux-node): map geoclue access-denied to LOCATION_DISABLED; floor camera maxWidth to avoid zero-height scale * fix(linux-node): clamp small camera maxWidth to 2 instead of default * docs(linux-node): clarify where-am-i -t is a process timeout, not update throttle * refactor(gateway): extract legacy-node filter + rejection hint to fit LOC ratchet; docs-map + deadcode baseline * fix(gateway): drop now-unused DEFAULT_DANGEROUS_NODE_COMMANDS import after hint extraction * test(node-host): drop imports orphaned by removed error-code test
42 lines
1.1 KiB
TypeScript
42 lines
1.1 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
export type ExecutableResolver = (
|
|
command: string,
|
|
env: NodeJS.ProcessEnv,
|
|
extraCandidates?: readonly string[],
|
|
) => string | null;
|
|
|
|
export function createCachedExecutableResolver(
|
|
isExecutable: (candidate: string) => boolean = (candidate) => {
|
|
try {
|
|
fs.accessSync(candidate, fs.constants.X_OK);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
},
|
|
): ExecutableResolver {
|
|
const cache = new Map<string, string | null>();
|
|
return (command, env, extraCandidates = []) => {
|
|
const pathValue = env.PATH ?? "";
|
|
const key = `${command}\0${pathValue}\0${extraCandidates.join("\0")}`;
|
|
if (cache.has(key)) {
|
|
return cache.get(key) ?? null;
|
|
}
|
|
|
|
const pathCandidates = pathValue
|
|
.split(path.delimiter)
|
|
.filter(Boolean)
|
|
.map((dir) => path.join(dir, command));
|
|
const candidates = path.isAbsolute(command)
|
|
? [command]
|
|
: [...pathCandidates, ...extraCandidates];
|
|
const found = candidates.find(isExecutable) ?? null;
|
|
cache.set(key, found);
|
|
return found;
|
|
};
|
|
}
|
|
|
|
export const resolveExecutable = createCachedExecutableResolver();
|