mirror of
http://192.168.0.88:13333/lywsvip/openclaw-zero-token.git
synced 2026-06-04 17:40:05 +08:00
Major upgrade from e26988a38 to upstream v2026.3.28 (f9b107928).
Key changes:
- Upstream src/, ui/, extensions/ (89 bundled extensions)
- Zero-token web providers preserved in src/zero-token/
- AskOnce plugin restored and registered as CLI command
- Added missing packages: @anthropic-ai/vertex-sdk, @modelcontextprotocol/sdk
- Fixed tsconfig rootDir, skipLibCheck for plugin-sdk DTS build
- Added askonce to bundled plugin metadata and package.json exports
- Fixed AskOnce CLI command registration (missing commands metadata)
- Restored AskOnce adapter imports (correct 5-level relative paths)
- Removed stale migration artifacts from root directory
217 lines
7.7 KiB
TypeScript
217 lines
7.7 KiB
TypeScript
import type { RuntimeEnv } from "../../runtime.js";
|
||
import { writeRuntimeJson } from "../../runtime.js";
|
||
import { colorize, theme } from "../../terminal/theme.js";
|
||
import { serializeGatewayDiscoveryBeacon } from "./discovery.js";
|
||
import {
|
||
isProbeReachable,
|
||
isScopeLimitedProbeFailure,
|
||
renderProbeSummaryLine,
|
||
renderTargetHeader,
|
||
} from "./helpers.js";
|
||
import type { GatewayStatusProbedTarget } from "./probe-run.js";
|
||
|
||
export type GatewayStatusWarning = {
|
||
code: string;
|
||
message: string;
|
||
targetIds?: string[];
|
||
};
|
||
|
||
export function pickPrimaryProbedTarget(probed: GatewayStatusProbedTarget[]) {
|
||
const reachable = probed.filter((entry) => isProbeReachable(entry.probe));
|
||
return (
|
||
reachable.find((entry) => entry.target.kind === "explicit") ??
|
||
reachable.find((entry) => entry.target.kind === "sshTunnel") ??
|
||
reachable.find((entry) => entry.target.kind === "configRemote") ??
|
||
reachable.find((entry) => entry.target.kind === "localLoopback") ??
|
||
null
|
||
);
|
||
}
|
||
|
||
export function buildGatewayStatusWarnings(params: {
|
||
probed: GatewayStatusProbedTarget[];
|
||
sshTarget: string | null;
|
||
sshTunnelStarted: boolean;
|
||
sshTunnelError: string | null;
|
||
}): GatewayStatusWarning[] {
|
||
const reachable = params.probed.filter((entry) => isProbeReachable(entry.probe));
|
||
const degradedScopeLimited = params.probed.filter((entry) =>
|
||
isScopeLimitedProbeFailure(entry.probe),
|
||
);
|
||
const warnings: GatewayStatusWarning[] = [];
|
||
if (params.sshTarget && !params.sshTunnelStarted) {
|
||
warnings.push({
|
||
code: "ssh_tunnel_failed",
|
||
message: params.sshTunnelError
|
||
? `SSH tunnel failed: ${String(params.sshTunnelError)}`
|
||
: "SSH tunnel failed to start; falling back to direct probes.",
|
||
});
|
||
}
|
||
if (reachable.length > 1) {
|
||
warnings.push({
|
||
code: "multiple_gateways",
|
||
message:
|
||
"Unconventional setup: multiple reachable gateways detected. Usually one gateway per network is recommended unless you intentionally run isolated profiles, like a rescue bot (see docs: /gateway#multiple-gateways-same-host).",
|
||
targetIds: reachable.map((entry) => entry.target.id),
|
||
});
|
||
}
|
||
for (const result of params.probed) {
|
||
if (result.authDiagnostics.length === 0 || isProbeReachable(result.probe)) {
|
||
continue;
|
||
}
|
||
for (const diagnostic of result.authDiagnostics) {
|
||
warnings.push({
|
||
code: "auth_secretref_unresolved",
|
||
message: diagnostic,
|
||
targetIds: [result.target.id],
|
||
});
|
||
}
|
||
}
|
||
for (const result of degradedScopeLimited) {
|
||
warnings.push({
|
||
code: "probe_scope_limited",
|
||
message:
|
||
"Probe diagnostics are limited by gateway scopes (missing operator.read). Connection succeeded, but status details may be incomplete. Hint: pair device identity or use credentials with operator.read.",
|
||
targetIds: [result.target.id],
|
||
});
|
||
}
|
||
return warnings;
|
||
}
|
||
|
||
export function writeGatewayStatusJson(params: {
|
||
runtime: RuntimeEnv;
|
||
startedAt: number;
|
||
overallTimeoutMs: number;
|
||
discoveryTimeoutMs: number;
|
||
network: ReturnType<typeof import("./helpers.js").buildNetworkHints>;
|
||
discovery: Parameters<typeof serializeGatewayDiscoveryBeacon>[0][];
|
||
probed: GatewayStatusProbedTarget[];
|
||
warnings: GatewayStatusWarning[];
|
||
primaryTargetId: string | null;
|
||
}) {
|
||
const reachable = params.probed.filter((entry) => isProbeReachable(entry.probe));
|
||
const degraded = params.probed.some((entry) => isScopeLimitedProbeFailure(entry.probe));
|
||
writeRuntimeJson(params.runtime, {
|
||
ok: reachable.length > 0,
|
||
degraded,
|
||
ts: Date.now(),
|
||
durationMs: Date.now() - params.startedAt,
|
||
timeoutMs: params.overallTimeoutMs,
|
||
primaryTargetId: params.primaryTargetId,
|
||
warnings: params.warnings,
|
||
network: params.network,
|
||
discovery: {
|
||
timeoutMs: params.discoveryTimeoutMs,
|
||
count: params.discovery.length,
|
||
beacons: params.discovery.map((beacon) => serializeGatewayDiscoveryBeacon(beacon)),
|
||
},
|
||
targets: params.probed.map((entry) => ({
|
||
id: entry.target.id,
|
||
kind: entry.target.kind,
|
||
url: entry.target.url,
|
||
active: entry.target.active,
|
||
tunnel: entry.target.tunnel ?? null,
|
||
connect: {
|
||
ok: isProbeReachable(entry.probe),
|
||
rpcOk: entry.probe.ok,
|
||
scopeLimited: isScopeLimitedProbeFailure(entry.probe),
|
||
latencyMs: entry.probe.connectLatencyMs,
|
||
error: entry.probe.error,
|
||
close: entry.probe.close,
|
||
},
|
||
self: entry.self,
|
||
config: entry.configSummary,
|
||
health: entry.probe.health,
|
||
summary: entry.probe.status,
|
||
presence: entry.probe.presence,
|
||
})),
|
||
});
|
||
if (reachable.length === 0) {
|
||
params.runtime.exit(1);
|
||
}
|
||
}
|
||
|
||
export function writeGatewayStatusText(params: {
|
||
runtime: RuntimeEnv;
|
||
rich: boolean;
|
||
overallTimeoutMs: number;
|
||
wideAreaDomain?: string | null;
|
||
discovery: Parameters<typeof serializeGatewayDiscoveryBeacon>[0][];
|
||
probed: GatewayStatusProbedTarget[];
|
||
warnings: GatewayStatusWarning[];
|
||
}) {
|
||
const reachable = params.probed.filter((entry) => isProbeReachable(entry.probe));
|
||
const ok = reachable.length > 0;
|
||
params.runtime.log(colorize(params.rich, theme.heading, "Gateway Status"));
|
||
params.runtime.log(
|
||
ok
|
||
? `${colorize(params.rich, theme.success, "Reachable")}: yes`
|
||
: `${colorize(params.rich, theme.error, "Reachable")}: no`,
|
||
);
|
||
params.runtime.log(
|
||
colorize(params.rich, theme.muted, `Probe budget: ${params.overallTimeoutMs}ms`),
|
||
);
|
||
|
||
if (params.warnings.length > 0) {
|
||
params.runtime.log("");
|
||
params.runtime.log(colorize(params.rich, theme.warn, "Warning:"));
|
||
for (const warning of params.warnings) {
|
||
params.runtime.log(`- ${warning.message}`);
|
||
}
|
||
}
|
||
|
||
params.runtime.log("");
|
||
params.runtime.log(colorize(params.rich, theme.heading, "Discovery (this machine)"));
|
||
const discoveryDomains = params.wideAreaDomain ? `local. + ${params.wideAreaDomain}` : "local.";
|
||
params.runtime.log(
|
||
params.discovery.length > 0
|
||
? `Found ${params.discovery.length} gateway(s) via Bonjour (${discoveryDomains})`
|
||
: `Found 0 gateways via Bonjour (${discoveryDomains})`,
|
||
);
|
||
if (params.discovery.length === 0) {
|
||
params.runtime.log(
|
||
colorize(
|
||
params.rich,
|
||
theme.muted,
|
||
"Tip: if the gateway is remote, mDNS won’t cross networks; use Wide-Area Bonjour (split DNS) or SSH tunnels.",
|
||
),
|
||
);
|
||
}
|
||
|
||
params.runtime.log("");
|
||
params.runtime.log(colorize(params.rich, theme.heading, "Targets"));
|
||
for (const result of params.probed) {
|
||
params.runtime.log(renderTargetHeader(result.target, params.rich));
|
||
params.runtime.log(` ${renderProbeSummaryLine(result.probe, params.rich)}`);
|
||
if (result.target.tunnel?.kind === "ssh") {
|
||
params.runtime.log(
|
||
` ${colorize(params.rich, theme.muted, "ssh")}: ${colorize(params.rich, theme.command, result.target.tunnel.target)}`,
|
||
);
|
||
}
|
||
if (result.probe.ok && result.self) {
|
||
const host = result.self.host ?? "unknown";
|
||
const ip = result.self.ip ? ` (${result.self.ip})` : "";
|
||
const platform = result.self.platform ? ` · ${result.self.platform}` : "";
|
||
const version = result.self.version ? ` · app ${result.self.version}` : "";
|
||
params.runtime.log(
|
||
` ${colorize(params.rich, theme.info, "Gateway")}: ${host}${ip}${platform}${version}`,
|
||
);
|
||
}
|
||
if (result.configSummary) {
|
||
const wideArea =
|
||
result.configSummary.discovery.wideAreaEnabled === true
|
||
? "enabled"
|
||
: result.configSummary.discovery.wideAreaEnabled === false
|
||
? "disabled"
|
||
: "unknown";
|
||
params.runtime.log(
|
||
` ${colorize(params.rich, theme.info, "Wide-area discovery")}: ${wideArea}`,
|
||
);
|
||
}
|
||
params.runtime.log("");
|
||
}
|
||
|
||
if (!ok) {
|
||
params.runtime.exit(1);
|
||
}
|
||
}
|