惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MyScale Blog
MyScale Blog
F
Fortinet All Blogs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
V
Visual Studio Blog
Last Week in AI
Last Week in AI
WordPress大学
WordPress大学
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
L
LangChain Blog
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
IT之家
IT之家
P
Proofpoint News Feed
博客园_首页
D
DataBreaches.Net
T
The Blog of Author Tim Ferriss
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Check Point Blog
Engineering at Meta
Engineering at Meta
Microsoft Azure Blog
Microsoft Azure Blog

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(deadcode): move restart intents to sqlite · openclaw/...
vincentkoc · 2026-06-21 · via Recent Commits to openclaw:main
11

// Coordinates gateway restart requests across supported supervisors.

22

import { spawnSync } from "node:child_process";

3-

import fs from "node:fs";

43

import os from "node:os";

54

import path from "node:path";

65

import { getRuntimeConfig } from "../config/config.js";

7-

import { resolveStateDir } from "../config/paths.js";

86

import {

97

resolveGatewayLaunchAgentLabel,

108

resolveGatewaySystemdServiceName,

119

} from "../daemon/constants.js";

1210

import { createSubsystemLogger } from "../logging/subsystem.js";

1311

import { resolveTimerTimeoutMs } from "../shared/number-coercion.js";

14-

import { replaceFileAtomicSync } from "./replace-file.js";

12+

import type { DB as OpenClawStateKyselyDatabase } from "../state/openclaw-state-db.generated.js";

13+

import {

14+

openOpenClawStateDatabase,

15+

runOpenClawStateWriteTransaction,

16+

} from "../state/openclaw-state-db.js";

17+

import {

18+

executeSqliteQuerySync,

19+

executeSqliteQueryTakeFirstSync,

20+

getNodeSqliteKysely,

21+

} from "./kysely-sync.js";

1522

import { cleanStaleGatewayProcessesSync, findGatewayPidsOnPortSync } from "./restart-stale-pids.js";

1623

import type { RestartAttempt } from "./restart.types.js";

1724

import { relaunchGatewayScheduledTask } from "./windows-task-restart.js";

@@ -25,11 +32,11 @@ const DEFAULT_DEFERRAL_STILL_PENDING_WARN_MS = 30_000;

2532

export const DEFAULT_RESTART_DEFERRAL_TIMEOUT_MS = 300_000;

2633

const RESTART_COOLDOWN_MS = 30_000;

2734

const LAUNCHCTL_ALREADY_LOADED_EXIT_CODE = 37;

28-

const GATEWAY_RESTART_INTENT_FILENAME = "gateway-restart-intent.json";

35+

const GATEWAY_RESTART_INTENT_KEY = "gateway-restart";

2936

const GATEWAY_RESTART_INTENT_TTL_MS = 60_000;

30-

const GATEWAY_RESTART_INTENT_MAX_BYTES = 1024;

31373238

const restartLog = createSubsystemLogger("restart");

39+

type GatewayRestartIntentDatabase = Pick<OpenClawStateKyselyDatabase, "gateway_restart_intent">;

33403441

export { findGatewayPidsOnPortSync };

3542

@@ -137,23 +144,6 @@ export type GatewayRestartIntent = {

137144

waitMs?: number;

138145

};

139146140-

function resolveGatewayRestartIntentPath(env: NodeJS.ProcessEnv = process.env): string {

141-

return path.join(resolveStateDir(env), GATEWAY_RESTART_INTENT_FILENAME);

142-

}

143-144-

function unlinkGatewayRestartIntentFileSync(intentPath: string): boolean {

145-

try {

146-

const stat = fs.lstatSync(intentPath);

147-

if (!stat.isFile() || stat.nlink > 1) {

148-

return false;

149-

}

150-

fs.unlinkSync(intentPath);

151-

return true;

152-

} catch {

153-

return false;

154-

}

155-

}

156-157147

function normalizeRestartIntentPid(pid: number | undefined): number | null {

158148

return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : null;

159149

}

@@ -170,26 +160,46 @@ export function writeGatewayRestartIntentSync(opts: {

170160

}

171161

const env = opts.env ?? process.env;

172162

try {

173-

const intentPath = resolveGatewayRestartIntentPath(env);

174163

const reason = normalizeRestartIntentReason(opts.reason ?? opts.intent?.reason);

175-

const payload: GatewayRestartIntentPayload = {

176-

kind: "gateway-restart",

177-

pid: targetPid,

178-

createdAt: Date.now(),

179-

...(reason ? { reason } : {}),

180-

...(opts.intent?.force ? { force: true } : {}),

181-

...(typeof opts.intent?.waitMs === "number" &&

164+

const waitMs =

165+

typeof opts.intent?.waitMs === "number" &&

182166

Number.isFinite(opts.intent.waitMs) &&

183167

opts.intent.waitMs >= 0

184-

? { waitMs: Math.floor(opts.intent.waitMs) }

185-

: {}),

186-

};

187-

replaceFileAtomicSync({

188-

filePath: intentPath,

189-

content: `${JSON.stringify(payload)}\n`,

190-

mode: 0o600,

191-

tempPrefix: ".gateway-restart-intent",

192-

});

168+

? Math.floor(opts.intent.waitMs)

169+

: null;

170+

const createdAt = Date.now();

171+

runOpenClawStateWriteTransaction(

172+

({ db }) => {

173+

const stateDb = getNodeSqliteKysely<GatewayRestartIntentDatabase>(db);

174+

executeSqliteQuerySync(

175+

db,

176+

stateDb

177+

.insertInto("gateway_restart_intent")

178+

.values({

179+

intent_key: GATEWAY_RESTART_INTENT_KEY,

180+

kind: "gateway-restart",

181+

pid: targetPid,

182+

created_at: createdAt,

183+

reason: reason ?? null,

184+

force: opts.intent?.force ? 1 : null,

185+

wait_ms: waitMs,

186+

updated_at_ms: createdAt,

187+

})

188+

.onConflict((conflict) =>

189+

conflict.column("intent_key").doUpdateSet({

190+

kind: (eb) => eb.ref("excluded.kind"),

191+

pid: (eb) => eb.ref("excluded.pid"),

192+

created_at: (eb) => eb.ref("excluded.created_at"),

193+

reason: (eb) => eb.ref("excluded.reason"),

194+

force: (eb) => eb.ref("excluded.force"),

195+

wait_ms: (eb) => eb.ref("excluded.wait_ms"),

196+

updated_at_ms: (eb) => eb.ref("excluded.updated_at_ms"),

197+

}),

198+

),

199+

);

200+

},

201+

{ env },

202+

);

193203

return true;

194204

} catch (err) {

195205

restartLog.warn(`failed to write gateway restart intent: ${String(err)}`);

@@ -198,31 +208,57 @@ export function writeGatewayRestartIntentSync(opts: {

198208

}

199209200210

export function clearGatewayRestartIntentSync(env: NodeJS.ProcessEnv = process.env): void {

201-

unlinkGatewayRestartIntentFileSync(resolveGatewayRestartIntentPath(env));

211+

try {

212+

runOpenClawStateWriteTransaction(

213+

({ db }) => {

214+

const stateDb = getNodeSqliteKysely<GatewayRestartIntentDatabase>(db);

215+

executeSqliteQuerySync(

216+

db,

217+

stateDb

218+

.deleteFrom("gateway_restart_intent")

219+

.where("intent_key", "=", GATEWAY_RESTART_INTENT_KEY),

220+

);

221+

},

222+

{ env },

223+

);

224+

} catch {}

202225

}

203226204-

function parseGatewayRestartIntent(raw: string): GatewayRestartIntentPayload | null {

227+

function readGatewayRestartIntentPayloadSync(

228+

env: NodeJS.ProcessEnv,

229+

): GatewayRestartIntentPayload | null {

205230

try {

206-

const parsed = JSON.parse(raw) as Partial<GatewayRestartIntentPayload>;

231+

const { db } = openOpenClawStateDatabase({ env });

232+

const stateDb = getNodeSqliteKysely<GatewayRestartIntentDatabase>(db);

233+

const parsed = executeSqliteQueryTakeFirstSync(

234+

db,

235+

stateDb

236+

.selectFrom("gateway_restart_intent")

237+

.select(["kind", "pid", "created_at", "reason", "force", "wait_ms"])

238+

.where("intent_key", "=", GATEWAY_RESTART_INTENT_KEY),

239+

);

207240

if (

208-

parsed.kind === "gateway-restart" &&

241+

parsed?.kind === "gateway-restart" &&

209242

typeof parsed.pid === "number" &&

210243

Number.isFinite(parsed.pid) &&

211-

typeof parsed.createdAt === "number" &&

212-

Number.isFinite(parsed.createdAt) &&

213-

(parsed.reason === undefined || typeof parsed.reason === "string") &&

214-

(parsed.force === undefined || typeof parsed.force === "boolean") &&

215-

(parsed.waitMs === undefined ||

216-

(typeof parsed.waitMs === "number" && Number.isFinite(parsed.waitMs) && parsed.waitMs >= 0))

244+

typeof parsed.created_at === "number" &&

245+

Number.isFinite(parsed.created_at) &&

246+

(parsed.reason === null || typeof parsed.reason === "string") &&

247+

(parsed.force === null ||

248+

(typeof parsed.force === "number" && Number.isFinite(parsed.force))) &&

249+

(parsed.wait_ms === null ||

250+

(typeof parsed.wait_ms === "number" &&

251+

Number.isFinite(parsed.wait_ms) &&

252+

parsed.wait_ms >= 0))

217253

) {

218-

const reason = normalizeRestartIntentReason(parsed.reason);

254+

const reason = normalizeRestartIntentReason(parsed.reason ?? undefined);

219255

return {

220256

kind: "gateway-restart",

221257

pid: parsed.pid,

222-

createdAt: parsed.createdAt,

258+

createdAt: parsed.created_at,

223259

...(reason ? { reason } : {}),

224260

...(parsed.force ? { force: true } : {}),

225-

...(typeof parsed.waitMs === "number" ? { waitMs: Math.floor(parsed.waitMs) } : {}),

261+

...(typeof parsed.wait_ms === "number" ? { waitMs: Math.floor(parsed.wait_ms) } : {}),

226262

};

227263

}

228264

} catch {

@@ -240,20 +276,8 @@ export function consumeGatewayRestartIntentPayloadSync(

240276

env: NodeJS.ProcessEnv = process.env,

241277

now = Date.now(),

242278

): GatewayRestartIntent | null {

243-

const intentPath = resolveGatewayRestartIntentPath(env);

244-

let raw: string;

245-

try {

246-

const stat = fs.lstatSync(intentPath);

247-

if (!stat.isFile() || stat.size > GATEWAY_RESTART_INTENT_MAX_BYTES) {

248-

return null;

249-

}

250-

raw = fs.readFileSync(intentPath, "utf8");

251-

} catch {

252-

return null;

253-

} finally {

254-

clearGatewayRestartIntentSync(env);

255-

}

256-

const payload = parseGatewayRestartIntent(raw);

279+

const payload = readGatewayRestartIntentPayloadSync(env);

280+

clearGatewayRestartIntentSync(env);

257281

if (!payload) {

258282

return null;

259283

}