docs: document gateway shutdown session helpers · openclaw/openclaw@aa9cc80
steipete
·
2026-06-05
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Gateway shutdown and restart close orchestration. |
| 2 | +// Coordinates hooks, drains, sockets, sidecars, plugins, and runtime cleanup. |
1 | 3 | import type { Server as HttpServer } from "node:http"; |
2 | 4 | import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; |
3 | 5 | import type { WebSocketServer } from "ws"; |
@@ -18,8 +20,6 @@ import {
|
18 | 20 | import type { ChatRunEntry, ChatRunState } from "./server-chat-state.js"; |
19 | 21 | import type { GatewayPostReadySidecarHandle } from "./server-startup-post-attach.js"; |
20 | 22 | |
21 | | -// Gateway shutdown/restart close orchestration with bounded drain and cleanup steps. |
22 | | - |
23 | 23 | const shutdownLog = createSubsystemLogger("gateway/shutdown"); |
24 | 24 | const GATEWAY_SHUTDOWN_HOOK_TIMEOUT_MS = 5_000; |
25 | 25 | const GATEWAY_PRE_RESTART_HOOK_TIMEOUT_MS = 10_000; |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Gateway Control UI root resolver. |
| 2 | +// Finds configured, bundled, or source-built UI assets during startup. |
1 | 3 | import path from "node:path"; |
2 | 4 | import { |
3 | 5 | ensureControlUiAssetsBuilt, |
@@ -8,9 +10,6 @@ import {
|
8 | 10 | import type { RuntimeEnv } from "../runtime.js"; |
9 | 11 | import type { ControlUiRootState } from "./control-ui.js"; |
10 | 12 | |
11 | | -// Control UI root resolution prefers explicit config, then bundled/proven |
12 | | -// assets. Missing bundled assets trigger an async build attempt without blocking |
13 | | -// gateway startup. |
14 | 13 | function startControlUiAssetsBuild(params: { |
15 | 14 | gatewayRuntime: RuntimeEnv; |
16 | 15 | log: { warn: (message: string) => void }; |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Gateway cron lazy loader. |
| 2 | +// Defers scheduler startup until cron is touched by runtime or API handlers. |
1 | 3 | import type { CliDeps } from "../cli/deps.types.js"; |
2 | 4 | import type { OpenClawConfig } from "../config/types.openclaw.js"; |
3 | 5 | import type { CronServiceContract } from "../cron/service-contract.js"; |
4 | 6 | import { resolveCronJobsStorePath } from "../cron/store.js"; |
5 | 7 | import type { GatewayCronState } from "./server-cron.js"; |
6 | 8 | |
7 | | -// Gateway cron is loaded lazily so startup/tests that never touch cron do not |
8 | | -// materialize scheduler loops or bundled plugin runtime. |
9 | 9 | type LazyGatewayCronParams = { |
10 | 10 | cfg: OpenClawConfig; |
11 | 11 | deps: CliDeps; |
@@ -29,6 +29,8 @@ export function createLazyGatewayCronState(params: LazyGatewayCronParams): Gatew
|
29 | 29 | if (loaded) { |
30 | 30 | return loaded; |
31 | 31 | } |
| 32 | +// Share the same import promise across concurrent API calls so only one |
| 33 | +// scheduler instance is built for a Gateway process. |
32 | 34 | loading ??= import("./server-cron.js").then(({ buildGatewayCronService }) => { |
33 | 35 | loaded = { |
34 | 36 | state: buildGatewayCronService(params), |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Gateway maintenance timers. |
| 2 | +// Starts periodic health, dedupe, abort, and media cleanup loops. |
1 | 3 | import { isFutureDateTimestampMs } from "@openclaw/normalization-core/number-coercion"; |
2 | 4 | import type { HealthSummary } from "../commands/health.js"; |
3 | 5 | import { sweepStaleRunContexts } from "../infra/agent-events.js"; |
@@ -128,6 +130,8 @@ export function startGatewayMaintenanceTimers(params: {
|
128 | 130 | return isFutureDateTimestampMs(expiresAtMs, { nowMs: now }); |
129 | 131 | }; |
130 | 132 | const isActiveRunDedupeKey = (key: string, dedupeEntry: DedupeEntry) => { |
| 133 | +// Keep idempotency records for active runs so retries cannot create |
| 134 | +// duplicate chat/agent work while a command is still draining. |
131 | 135 | if (!key.startsWith("agent:") && !key.startsWith("chat:")) { |
132 | 136 | return false; |
133 | 137 | } |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Gateway session event broadcaster. |
| 2 | +// Projects transcript and lifecycle updates to websocket subscribers. |
1 | 3 | import { asPositiveSafeInteger } from "@openclaw/normalization-core/number-coercion"; |
2 | 4 | import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce"; |
3 | 5 | import { resolveDefaultAgentId } from "../agents/agent-scope.js"; |
@@ -20,10 +22,6 @@ import {
|
20 | 22 | type GatewaySessionRow, |
21 | 23 | } from "./session-utils.js"; |
22 | 24 | |
23 | | -// Session event broadcasting bridges transcript/lifecycle stores to live |
24 | | -// Gateway websocket subscribers. Message updates go to session-specific |
25 | | -// subscribers plus broad session listeners; non-display messages still trigger |
26 | | -// sessions.changed so lists refresh. |
27 | 25 | type SessionEventSubscribers = Pick<SessionEventSubscriberRegistry, "getAll">; |
28 | 26 | type SessionMessageSubscribers = Pick<SessionMessageSubscriberRegistry, "get">; |
29 | 27 | |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Gateway shared-auth generation enforcement. |
| 2 | +// Disconnects clients when config writes invalidate shared credentials. |
1 | 3 | import type { OpenClawConfig } from "../config/types.openclaw.js"; |
2 | 4 | import { resolveGatewayReloadSettings } from "./config-reload-settings.js"; |
3 | 5 | |
4 | | -// Tracks shared Gateway auth generations across config writes and reload modes. |
5 | | - |
6 | 6 | /** Gateway client subset relevant to shared auth generation enforcement. */ |
7 | 7 | export type SharedGatewayAuthClient = { |
8 | 8 | usesSharedGatewayAuth?: boolean; |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Filesystem session transcript helpers. |
| 2 | +// Resolves, archives, and cleans up transcript files owned by Gateway sessions. |
1 | 3 | import fs from "node:fs"; |
2 | 4 | import os from "node:os"; |
3 | 5 | import path from "node:path"; |
@@ -118,6 +120,8 @@ export function resolveSessionTranscriptCandidates(
|
118 | 120 | } |
119 | 121 | } |
120 | 122 | |
| 123 | +// Keep the legacy global sessions directory as a final candidate so tagged |
| 124 | +// upgrades can still find transcripts created before per-agent paths. |
121 | 125 | const home = resolveRequiredHomeDir(process.env, os.homedir); |
122 | 126 | const legacyDir = path.join(home, ".openclaw", "sessions"); |
123 | 127 | pushCandidate(() => resolveSessionTranscriptPathInDir(sessionId, legacyDir)); |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Session transcript key resolver. |
| 2 | +// Maps transcript file paths back to Gateway session keys for live broadcasts. |
1 | 3 | import { getRuntimeConfig } from "../config/io.js"; |
2 | 4 | import type { SessionEntry } from "../config/sessions/types.js"; |
3 | 5 | import type { OpenClawConfig } from "../config/types.openclaw.js"; |
@@ -38,10 +40,12 @@ function sessionKeyMatchesTranscriptPath(params: {
|
38 | 40 | ).some((candidate) => resolveTranscriptPathForComparison(candidate) === params.targetPath); |
39 | 41 | } |
40 | 42 | |
| 43 | +/** Clears the transcript path lookup cache for isolated tests. */ |
41 | 44 | export function clearSessionTranscriptKeyCacheForTests(): void { |
42 | 45 | TRANSCRIPT_SESSION_KEY_CACHE.clear(); |
43 | 46 | } |
44 | 47 | |
| 48 | +/** Resolve the most likely Gateway session key for a transcript file path. */ |
45 | 49 | export function resolveSessionKeyForTranscriptFile(sessionFile: string): string | undefined { |
46 | 50 | const targetPath = resolveTranscriptPathForComparison(sessionFile); |
47 | 51 | if (!targetPath) { |
@@ -81,6 +85,8 @@ export function resolveSessionKeyForTranscriptFile(sessionFile: string): string
|
81 | 85 | } |
82 | 86 | |
83 | 87 | if (matchingEntries.length > 0) { |
| 88 | +// Multiple keys can point at copied/forked transcript paths. Prefer the |
| 89 | +// freshest unambiguous session ID group; ties stay unresolved. |
84 | 90 | const matchesBySessionId = new Map<string, Array<[string, SessionEntry]>>(); |
85 | 91 | for (const entry of matchingEntries) { |
86 | 92 | const sessionId = entry[1].sessionId; |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。