feat(openclaw): add task-plane session-broker-client.js
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user