openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
226 lines (225 loc) • 8.46 kB
JavaScript
import { C as parseStrictNonNegativeInteger } from "./number-coercion-CLj0HTDM.js";
import { u as isSecureWebSocketUrl } from "./net-DbNPs6Xm.js";
import { t as gatewayOriginScope } from "./gateway-origin-scope-D4zHFrov.js";
import { t as resolveSecretInputModeForEnvSelection } from "./provider-auth-mode-7FOSjRoY.js";
import { n as t } from "./i18n-hynzGFbD.js";
import { t as detectBinary } from "./detect-binary-CNBv2s1v.js";
import "./onboard-helpers-BDY-6Z-t.js";
import { t as maskApiKey } from "./secret-mask-BEdLuCrN.js";
import { a as resolveWideAreaDiscoveryDomain } from "./widearea-dns-rDnHQdmm.js";
import { t as discoverGatewayBeacons } from "./bonjour-discovery-BY76Gpf8.js";
import { n as buildGatewayDiscoveryTarget, t as buildGatewayDiscoveryLabel } from "./gateway-discovery-targets-DNcma2ND.js";
import { n as promptSecretRefForSetup } from "./provider-auth-ref-BgqISDYR.js";
//#region src/commands/onboard-remote.ts
const DEFAULT_GATEWAY_URL = "ws://127.0.0.1:18789";
function buildLabel(beacon) {
return buildGatewayDiscoveryLabel(beacon);
}
function ensureWsUrl(value) {
const trimmed = value.trim();
if (!trimmed) return DEFAULT_GATEWAY_URL;
return trimmed;
}
function validateGatewayWebSocketUrl(value) {
const trimmed = value.trim();
if (!trimmed.startsWith("ws://") && !trimmed.startsWith("wss://")) return t("wizard.remote.validWebSocketUrl");
if (!isSecureWebSocketUrl(trimmed, { allowPrivateWs: process.env.OPENCLAW_ALLOW_INSECURE_PRIVATE_WS === "1" })) return t("wizard.remote.insecureRemoteUrl");
}
/** Prompts for remote gateway connection and auth settings. */
async function promptRemoteGatewayConfig(cfg, prompter, options) {
let selectedBeacon = null;
let suggestedUrl = cfg.gateway?.remote?.url ?? DEFAULT_GATEWAY_URL;
let discoveryRemote;
const hasBonjourTool = await detectBinary("dns-sd") || await detectBinary("avahi-browse");
const wantsDiscover = hasBonjourTool ? await prompter.confirm({
message: t("wizard.remote.bonjour"),
initialValue: true
}) : false;
if (!hasBonjourTool) await prompter.note(["Bonjour discovery requires dns-sd (macOS) or avahi-browse (Linux).", "Docs: https://docs.openclaw.ai/gateway/discovery"].join("\n"), "Discovery");
if (wantsDiscover) {
const wideAreaDomain = resolveWideAreaDiscoveryDomain({ configDomain: cfg.discovery?.wideArea?.domain });
const spin = prompter.progress(t("wizard.remote.searchProgress"));
const beacons = await discoverGatewayBeacons({
timeoutMs: 2e3,
wideAreaDomain
});
spin.stop(beacons.length > 0 ? t("wizard.remote.foundGateways", { count: beacons.length }) : t("wizard.remote.noGatewaysFound"));
if (beacons.length > 0) {
const selection = await prompter.select({
message: t("wizard.remote.selectGateway"),
options: [...beacons.map((beacon, index) => ({
value: String(index),
label: buildLabel(beacon)
})), {
value: "manual",
label: t("wizard.remote.enterUrlManually")
}]
});
if (selection !== "manual") {
const idx = parseStrictNonNegativeInteger(selection);
selectedBeacon = idx === void 0 ? null : beacons[idx] ?? null;
}
}
}
if (selectedBeacon) {
const target = buildGatewayDiscoveryTarget(selectedBeacon);
if (target.endpoint) {
const { host, port } = target.endpoint;
if (await prompter.select({
message: t("wizard.remote.connectionMethod"),
options: [{
value: "direct",
label: `Direct gateway WS (${host}:${port})`
}, {
value: "ssh",
label: t("wizard.remote.sshTunnel")
}]
}) === "direct") {
suggestedUrl = `wss://${host}:${port}`;
const fingerprint = target.endpoint.gatewayTlsFingerprintSha256;
if (await prompter.confirm({
message: t("wizard.remote.trustGateway", {
host: `${host}:${port}`,
fingerprint: fingerprint ?? t("wizard.remote.fingerprintMissing")
}),
initialValue: false
})) {
discoveryRemote = {
url: suggestedUrl,
transport: "direct",
...fingerprint ? { tlsFingerprint: fingerprint } : {}
};
await prompter.note([
t("wizard.remote.directDefaultsTls"),
`Using: ${suggestedUrl}`,
...fingerprint ? [`TLS pin: ${fingerprint}`] : [],
t("wizard.remote.loopbackSshHint")
].join("\n"), t("wizard.remote.directAccessTitle"));
} else suggestedUrl = DEFAULT_GATEWAY_URL;
} else {
suggestedUrl = DEFAULT_GATEWAY_URL;
discoveryRemote = {
url: suggestedUrl,
transport: "ssh"
};
await prompter.note([
"Start a tunnel before using the CLI:",
`ssh -N -L 18789:127.0.0.1:18789 <user>@${host}${target.sshPort ? ` -p ${target.sshPort}` : ""}`,
"Docs: https://docs.openclaw.ai/gateway/remote"
].join("\n"), t("wizard.remote.sshTunnelTitle"));
}
}
}
const url = ensureWsUrl(await prompter.text({
message: t("wizard.remote.websocketUrl"),
initialValue: suggestedUrl,
validate: (value) => validateGatewayWebSocketUrl(value)
}));
const selectedDiscovery = discoveryRemote?.url === url ? discoveryRemote : void 0;
const authChoice = await prompter.select({
message: t("wizard.remote.auth"),
options: [
{
value: "token",
label: t("common.tokenRecommended")
},
{
value: "password",
label: t("common.password")
},
{
value: "off",
label: t("common.noAuth")
}
]
});
let token = cfg.gateway?.remote?.token;
let password = cfg.gateway?.remote?.password;
if (authChoice === "token") {
if (await resolveSecretInputModeForEnvSelection({
prompter,
explicitMode: options?.secretInputMode,
copy: {
modeMessage: t("wizard.gateway.remoteTokenMode"),
plaintextLabel: t("wizard.remote.plaintextTokenLabel"),
plaintextHint: t("wizard.remote.plaintextTokenHint")
}
}) === "ref") token = (await promptSecretRefForSetup({
provider: "gateway-remote-token",
config: cfg,
prompter,
preferredEnvVar: "OPENCLAW_GATEWAY_TOKEN",
copy: {
sourceMessage: t("wizard.remote.gatewayTokenStoredMessage"),
envVarPlaceholder: "OPENCLAW_GATEWAY_TOKEN"
}
})).ref;
else {
const existingToken = typeof token === "string" ? token : void 0;
if (existingToken && await prompter.confirm({
message: t("wizard.gateway.existingTokenConfirm", { token: maskApiKey(existingToken) }),
initialValue: true
})) token = existingToken;
else token = (await prompter.text({
message: t("wizard.remote.tokenPrompt"),
validate: (value) => value?.trim() ? void 0 : t("common.required"),
sensitive: true
})).trim();
}
password = void 0;
} else if (authChoice === "password") {
if (await resolveSecretInputModeForEnvSelection({
prompter,
explicitMode: options?.secretInputMode,
copy: {
modeMessage: t("wizard.gateway.remotePasswordMode"),
plaintextLabel: t("wizard.remote.plaintextPasswordLabel"),
plaintextHint: t("wizard.remote.plaintextPasswordHint")
}
}) === "ref") password = (await promptSecretRefForSetup({
provider: "gateway-remote-password",
config: cfg,
prompter,
preferredEnvVar: "OPENCLAW_GATEWAY_PASSWORD",
copy: {
sourceMessage: t("wizard.remote.gatewayPasswordStoredMessage"),
envVarPlaceholder: "OPENCLAW_GATEWAY_PASSWORD"
}
})).ref;
else {
const existingPassword = typeof password === "string" ? password : void 0;
if (existingPassword && await prompter.confirm({
message: t("wizard.gateway.existingPasswordConfirm", { password: maskApiKey(existingPassword) }),
initialValue: true
})) password = existingPassword;
else password = (await prompter.text({
message: t("wizard.remote.passwordPrompt"),
validate: (value) => value?.trim() ? void 0 : t("common.required"),
sensitive: true
})).trim();
}
token = void 0;
} else {
token = void 0;
password = void 0;
}
const remoteOriginUrl = options && "remoteOriginUrl" in options ? options.remoteOriginUrl : cfg.gateway?.remote?.url;
const edgeAuth = remoteOriginUrl && gatewayOriginScope(url) === gatewayOriginScope(remoteOriginUrl) ? cfg.gateway?.remote?.edgeAuth : void 0;
return {
...cfg,
gateway: {
...cfg.gateway,
mode: "remote",
remote: {
...url === remoteOriginUrl?.trim() && selectedDiscovery?.transport !== "ssh" ? cfg.gateway?.remote : {},
url,
edgeAuth,
token,
password,
...selectedDiscovery?.transport === "direct" ? selectedDiscovery : {}
}
}
};
}
//#endregion
export { validateGatewayWebSocketUrl as n, promptRemoteGatewayConfig as t };