openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
94 lines (93 loc) • 5.26 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { o as listAgentIds } from "./agent-scope-config-DcbEhP0R.js";
import { l as normalizePluginsConfig, u as resolveEffectiveEnableState } from "./config-state-BkU1frVq.js";
import "./agent-runtime-id-BpfvnoN1.js";
import { t as resolveDefaultModelForAgent } from "./model-selection-config-BrdmmqKD.js";
import { u as getActivePluginRegistry } from "./runtime-BL4wZfTq.js";
import { o as resolveEffectiveAgentRuntime } from "./thinking-runtime-BiFbTVdI.js";
import "./model-selection-di2kjKCB.js";
import { n as hasConfiguredGatewayAuthSecretInput } from "./auth-config-utils-mqnk38xp.js";
//#region src/commands/doctor-node-hosting-preconditions.ts
const CHECK_ID = "core/doctor/node-hosting-preconditions";
const LOOPBACK_JOIN_CODE_MESSAGE = "Gateway is only bound to loopback. Set gateway.bind=lan, enable tailscale serve, or configure plugins.entries.device-pair.config.publicUrl.";
function usesIdentityHeadersWithoutMachineCredentials(cfg) {
const hasToken = hasConfiguredGatewayAuthSecretInput(cfg, "gateway.auth.token");
const hasPassword = hasConfiguredGatewayAuthSecretInput(cfg, "gateway.auth.password");
if (hasToken || hasPassword) return false;
if (cfg.gateway?.auth?.mode === "trusted-proxy") return true;
return cfg.gateway?.tailscale?.mode === "serve" && cfg.gateway?.auth?.mode !== "password" && cfg.gateway?.auth?.mode !== "none" && cfg.gateway?.auth?.allowTailscale !== false;
}
function lacksNodeOnboardingUrl(cfg) {
const bind = cfg.gateway?.bind ?? "loopback";
if (bind !== "loopback" && bind !== "auto") return false;
const publicUrl = cfg.plugins?.entries?.["device-pair"]?.config?.["publicUrl"];
const remoteUrl = cfg.gateway?.remote?.url;
const tailscaleMode = cfg.gateway?.tailscale?.mode ?? "off";
return !normalizeOptionalString(publicUrl) && !normalizeOptionalString(remoteUrl) && tailscaleMode !== "serve" && tailscaleMode !== "funnel";
}
function lacksNodeOnboardingPlugin(cfg) {
return !resolveEffectiveEnableState({
id: "device-pair",
origin: "bundled",
config: normalizePluginsConfig(cfg.plugins),
rootConfig: cfg,
enabledByDefault: true
}).enabled;
}
function lacksDeviceCapableRuntimeRoute(cfg) {
const registry = getActivePluginRegistry();
return listAgentIds(cfg).every((agentId) => {
const model = resolveDefaultModelForAgent({
cfg,
agentId
});
const runtime = resolveEffectiveAgentRuntime({
cfg,
provider: model.provider,
modelId: model.model,
agentId
});
if (runtime === "openclaw") return false;
const harness = registry?.agentHarnesses.find((entry) => entry.harness.id === runtime)?.harness;
return harness !== void 0 && harness.cloudPlacement?.devicePlacement === void 0;
});
}
/** Collects config-only warnings for node authentication, onboarding, and worker ingress. */
function collectNodeHostingPreconditionFindings(cfg) {
const findings = [];
if (lacksNodeOnboardingPlugin(cfg)) findings.push({
checkId: CHECK_ID,
severity: "warning",
message: "The device-pair plugin is not enabled; node onboarding join codes and openclaw connect are unavailable.",
path: "plugins.entries.device-pair.enabled",
requirement: "node-onboarding-plugin",
fixHint: "Set plugins.entries.device-pair.enabled: true, ensure device-pair is not denied or excluded by plugins.allow, then restart the Gateway."
});
if (lacksDeviceCapableRuntimeRoute(cfg)) findings.push({
checkId: CHECK_ID,
severity: "warning",
message: "No configured agent/model route resolves to a runtime that supports paired-device placement.",
path: "agents",
requirement: "device-session-runtime",
fixHint: "Select an agent/model route whose runtime supports paired-device placement, then ensure its plugin is enabled and its required node commands are explicitly allowed. Runtime policy is model/provider-scoped; whole-agent runtime keys are ignored. For a multi-agent roster, set agents.ownership: \"explicit\"."
});
if (usesIdentityHeadersWithoutMachineCredentials(cfg)) findings.push({
checkId: CHECK_ID,
severity: "warning",
message: "Gateway identity-header auth has no configured token/password path for machine clients; new node hosts cannot authenticate or become worker hosts.",
path: "gateway.auth",
requirement: "machine-client-auth",
fixHint: "Switch gateway.auth.mode to token and configure gateway.auth.token as a SecretRef so machine clients can authenticate as devices. Keep trusted-proxy only if machine clients use a clean loopback/direct gateway.auth.password path. For Access-fronted gateways, configure the node gateway.cloudflareAccess.clientId / clientSecret SecretInputs or set CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET before openclaw connect."
});
if (lacksNodeOnboardingUrl(cfg)) findings.push({
checkId: CHECK_ID,
severity: "warning",
message: LOOPBACK_JOIN_CODE_MESSAGE,
path: "gateway.bind",
requirement: "node-onboarding-url",
fixHint: "If an edge proxy fronts node onboarding, allow /j/* and /__openclaw__/worker without edge identity auth, and preserve WebSocket upgrade on /__openclaw__/worker. Both routes enforce their own credentials."
});
return findings;
}
//#endregion
export { collectNodeHostingPreconditionFindings };