docs: document gateway reload helpers

This commit is contained in:
Peter Steinberger
2026-06-04 17:30:08 -04:00
parent 8e371cfea1
commit c8ac4c8aea
8 changed files with 36 additions and 1 deletions

View File

@@ -1,3 +1,5 @@
// Gateway config reload planner.
// Maps changed config paths to hot-reload actions, no-ops, or full restarts.
import { type ChannelId, listChannelPlugins } from "../channels/plugins/index.js";
import {
getActivePluginChannelRegistryVersion,
@@ -146,6 +148,8 @@ function listReloadRules(): ReloadRule[] {
const registry = getActivePluginRegistry();
const activeRegistryVersion = getActivePluginRegistryVersion();
const channelRegistryVersion = getActivePluginChannelRegistryVersion();
// Plugin/channel reload rules are process-stable until the active registry
// version changes; cache them to keep every config diff cheap.
if (
registry !== cachedRegistry ||
activeRegistryVersion !== cachedActiveRegistryVersion ||

View File

@@ -1,3 +1,5 @@
// Gateway config hot-reload watcher.
// Diffs config/plugin install snapshots and dispatches hot reload or restart plans.
import chokidar from "chokidar";
import type { ConfigWriteNotification } from "../config/io.js";
import { formatConfigIssueLines } from "../config/issue-format.js";
@@ -126,6 +128,8 @@ export function startGatewayConfigReloader(opts: {
if (stopped) {
return;
}
// Coalesce filesystem/write-listener bursts into one reload pass. Config
// writes often touch temp and final paths in quick succession.
if (debounceTimer) {
clearTimeout(debounceTimer);
}

View File

@@ -1,3 +1,5 @@
// Gateway credential planning helpers.
// Classifies local/remote auth inputs before SecretRef resolution.
import { normalizeOptionalString } from "../../packages/normalization-core/src/string-coerce.js";
import { containsEnvVarReference } from "../config/env-substitution.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
@@ -145,6 +147,8 @@ export function createGatewayCredentialPlan(params: {
const remoteUrlConfigured = Boolean(trimToUndefined(remote?.url));
const tailscaleRemoteExposure =
gateway?.tailscale?.mode === "serve" || gateway?.tailscale?.mode === "funnel";
// Remote credential surfaces are considered active when the gateway is used
// remotely or when local auth may be borrowed for a published Tailscale URL.
const remoteConfiguredSurface = remoteMode || remoteUrlConfigured || tailscaleRemoteExposure;
// Remote credentials may borrow local auth credentials only when the remote
// surface exists but no explicit remote/env candidate can satisfy the mode.

View File

@@ -1,3 +1,5 @@
// Gateway iOS exec-approval push delivery.
// Sends APNs request/resolution wakes to paired operator devices.
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
import { getRuntimeConfig } from "../config/io.js";
import {
@@ -149,6 +151,8 @@ async function resolveDeliveryPlan(params: {
isTargetVisible?: (target: ApprovalPushTarget) => boolean;
log: GatewayLikeLogger;
}): Promise<DeliveryPlan> {
// Request delivery requires current approval scope; resolution delivery may
// target prior node ids so existing notification badges can be cleared.
const targets = params.explicitNodeIds?.length
? await loadRegisteredTargets({ deviceIds: params.explicitNodeIds })
: await resolvePairedTargets({

View File

@@ -1,3 +1,5 @@
// Gateway exec approval manager.
// Tracks pending operator decisions and short-lived resolved approval records.
import { randomUUID } from "node:crypto";
import { resolveExpiresAtMsFromDurationMs } from "@openclaw/normalization-core/number-coercion";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
@@ -18,6 +20,8 @@ function unrefTimer(timer: ReturnType<typeof setTimeout>): void {
}
function scheduleResolvedEntryCleanup(cleanup: () => void): void {
// Resolved approvals stay visible briefly so node.invoke sanitizers can
// consume a just-approved id after the UI decision races the command retry.
const timer = setTimeout(cleanup, RESOLVED_ENTRY_GRACE_MS);
unrefTimer(timer);
}

View File

@@ -1,3 +1,5 @@
// Gateway model-pricing cache state.
// Stores normalized pricing rows and source-health failures for runtime reads.
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce";
import { normalizeModelRef } from "../agents/model-selection.js";
@@ -42,6 +44,8 @@ const sourceFailures = new Map<
>();
function modelPricingCacheKey(provider: string, model: string): string {
// Keys accept both provider/model and provider-prefixed model ids so external
// catalogs can be queried without double-prefixing.
const providerId = normalizeProviderId(provider);
const modelId = model.trim();
if (!providerId || !modelId) {

View File

@@ -1,4 +1,10 @@
// Gateway model-pricing refresh and normalization.
// Fetches, normalizes, and schedules cached pricing for model usage estimates.
import type { ModelCatalogCost } from "@openclaw/model-catalog-core/model-catalog-types";
import {
normalizeOptionalString,
resolvePrimaryStringValue,
} from "../../packages/normalization-core/src/string-coerce.js";
import { DEFAULT_PROVIDER } from "../agents/defaults.js";
import {
buildModelAliasIndex,
@@ -26,7 +32,6 @@ import {
} from "../plugins/plugin-metadata-snapshot.js";
import type { PluginMetadataRegistryView } from "../plugins/plugin-metadata-snapshot.types.js";
import type { PluginRegistrySnapshot } from "../plugins/plugin-registry.js";
import { normalizeOptionalString, resolvePrimaryStringValue } from "../../packages/normalization-core/src/string-coerce.js";
import {
clearGatewayModelPricingCacheState,
clearGatewayModelPricingFailures,
@@ -180,6 +185,8 @@ function isTimeoutError(error: unknown): boolean {
}
function createPricingFetchSignal(signal: AbortSignal | undefined): AbortSignal {
// Pricing fetches are background refreshes; bound them so startup/reload
// cannot leave an unbounded network request alive.
const timeoutSignal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
}

View File

@@ -1,3 +1,5 @@
// Gateway hot-reload handlers.
// Applies config reload plans to hooks, cron, heartbeat, plugins, channels, and restarts.
import { disposeAllSessionMcpRuntimes } from "../agents/agent-bundle-mcp-tools.js";
import {
getActiveEmbeddedRunCount,
@@ -102,6 +104,8 @@ async function disposeMcpRuntimesWithTimeout(params: {
onWarn: (message: string) => void;
label: string;
}) {
// MCP runtime disposal may need async provider cleanup. Bound it so config
// reload can proceed and report the stale runtime risk.
let timer: ReturnType<typeof setTimeout> | undefined;
const disposePromise = params.dispose().catch((error: unknown) => {
params.onWarn(`${params.label} failed: ${String(error)}`);