feat(openclaw): add task-plane session-broker-client.js

This commit is contained in:
AIOS Candidate Writer
2026-07-25 18:35:20 +08:00
parent 56d675166a
commit 8f55eff11d
@@ -0,0 +1,76 @@
import fs from "node:fs";
import path from "node:path";
const ALLOWED_BROKER_HOSTS = new Set(["127.0.0.1", "localhost"]);
const DEFAULT_TIMEOUT_MS = 3000;
export function brokerCredentialFromPath(credentialPath) {
if (!path.isAbsolute(credentialPath)) throw new Error("broker_credential_path_invalid");
const stat = fs.lstatSync(credentialPath);
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error("broker_credential_path_invalid");
if (process.platform !== "win32" && (stat.mode & 0o077) !== 0) {
throw new Error("broker_credential_permissions_invalid");
}
const token = fs.readFileSync(credentialPath, "utf8").trim();
if (!token) throw new Error("broker_credential_empty");
return token;
}
export function assertLocalBrokerEndpoint(endpoint) {
const safe = new URL(endpoint);
if (safe.protocol !== "http:") throw new Error("broker_endpoint_protocol_invalid");
if (!ALLOWED_BROKER_HOSTS.has(safe.hostname)) throw new Error("broker_endpoint_host_invalid");
if (safe.username || safe.password || safe.search || safe.hash) throw new Error("broker_endpoint_invalid");
if (safe.pathname === "/") safe.pathname = "/v1/sessions/provision";
return safe;
}
export async function callBrokerProvision({
endpoint,
credentialPath,
payload,
timeoutMs = DEFAULT_TIMEOUT_MS,
}) {
const brokerUrl = assertLocalBrokerEndpoint(endpoint);
const credential = brokerCredentialFromPath(credentialPath);
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), Math.min(Math.max(Number(timeoutMs) || DEFAULT_TIMEOUT_MS, 500), 15_000));
try {
const response = await fetch(brokerUrl, {
method: "POST",
signal: controller.signal,
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${credential}`,
},
body: JSON.stringify(payload),
});
const responseBody = await response.text();
let body;
try {
body = responseBody ? JSON.parse(responseBody) : {};
} catch {
throw new Error("broker_response_not_json");
}
if (!response.ok) {
const reason = typeof body?.error === "string" ? body.error : `http_${response.status}`;
const error = new Error(reason);
error.code = "broker_http_error";
error.status = response.status;
throw error;
}
return body;
} catch (error) {
if (error.name === "AbortError") {
const timeout = new Error("broker_timeout");
timeout.code = "broker_timeout";
throw timeout;
}
if (error.code) throw error;
const transport = new Error("broker_unreachable");
transport.code = "broker_unreachable";
throw transport;
} finally {
clearTimeout(timer);
}
}