Files
openclaw/src/commands/gateway-status.ts
2026-03-23 00:38:31 -07:00

137 lines
3.8 KiB
TypeScript

import { withProgress } from "../cli/progress.js";
import { readBestEffortConfig, resolveGatewayPort } from "../config/config.js";
import { resolveWideAreaDiscoveryDomain } from "../infra/widearea-dns.js";
import type { RuntimeEnv } from "../runtime.js";
import { isRich } from "../terminal/theme.js";
import { inferSshTargetFromRemoteUrl, resolveSshTarget } from "./gateway-status/discovery.js";
import {
buildNetworkHints,
parseTimeoutMs,
resolveTargets,
sanitizeSshTarget,
} from "./gateway-status/helpers.js";
import {
buildGatewayStatusWarnings,
pickPrimaryProbedTarget,
writeGatewayStatusJson,
writeGatewayStatusText,
} from "./gateway-status/output.js";
import { runGatewayStatusProbePass } from "./gateway-status/probe-run.js";
let sshConfigModulePromise: Promise<typeof import("../infra/ssh-config.js")> | undefined;
let sshTunnelModulePromise: Promise<typeof import("../infra/ssh-tunnel.js")> | undefined;
function loadSshConfigModule() {
sshConfigModulePromise ??= import("../infra/ssh-config.js");
return sshConfigModulePromise;
}
function loadSshTunnelModule() {
sshTunnelModulePromise ??= import("../infra/ssh-tunnel.js");
return sshTunnelModulePromise;
}
export async function gatewayStatusCommand(
opts: {
url?: string;
token?: string;
password?: string;
timeout?: unknown;
json?: boolean;
ssh?: string;
sshIdentity?: string;
sshAuto?: boolean;
},
runtime: RuntimeEnv,
) {
const startedAt = Date.now();
const cfg = await readBestEffortConfig();
const rich = isRich() && opts.json !== true;
const overallTimeoutMs = parseTimeoutMs(opts.timeout, 3000);
const wideAreaDomain = resolveWideAreaDiscoveryDomain({
configDomain: cfg.discovery?.wideArea?.domain,
});
const baseTargets = resolveTargets(cfg, opts.url);
const network = buildNetworkHints(cfg);
const remotePort = resolveGatewayPort(cfg);
const discoveryTimeoutMs = Math.min(1200, overallTimeoutMs);
let sshTarget = sanitizeSshTarget(opts.ssh) ?? sanitizeSshTarget(cfg.gateway?.remote?.sshTarget);
let sshIdentity =
sanitizeSshTarget(opts.sshIdentity) ?? sanitizeSshTarget(cfg.gateway?.remote?.sshIdentity);
if (!sshTarget) {
sshTarget = inferSshTargetFromRemoteUrl(cfg.gateway?.remote?.url);
}
if (sshTarget) {
const resolved = await resolveSshTarget({
rawTarget: sshTarget,
identity: sshIdentity,
overallTimeoutMs,
loadSshConfigModule,
loadSshTunnelModule,
});
if (resolved) {
sshTarget = resolved.target;
if (!sshIdentity && resolved.identity) {
sshIdentity = resolved.identity;
}
}
}
const probePass = await withProgress(
{
label: "Inspecting gateways…",
indeterminate: true,
enabled: opts.json !== true,
},
async () =>
await runGatewayStatusProbePass({
cfg,
opts,
overallTimeoutMs,
discoveryTimeoutMs,
wideAreaDomain,
baseTargets,
remotePort,
sshTarget,
sshIdentity,
loadSshTunnelModule,
}),
);
const warnings = buildGatewayStatusWarnings({
probed: probePass.probed,
sshTarget: probePass.sshTarget,
sshTunnelStarted: probePass.sshTunnelStarted,
sshTunnelError: probePass.sshTunnelError,
});
const primary = pickPrimaryProbedTarget(probePass.probed);
if (opts.json) {
writeGatewayStatusJson({
runtime,
startedAt,
overallTimeoutMs,
discoveryTimeoutMs,
network,
discovery: probePass.discovery,
probed: probePass.probed,
warnings,
primaryTargetId: primary?.target.id ?? null,
});
return;
}
writeGatewayStatusText({
runtime,
rich,
overallTimeoutMs,
wideAreaDomain,
discovery: probePass.discovery,
probed: probePass.probed,
warnings,
});
}