UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

226 lines (225 loc) 8.46 kB
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 };