Skip to content

Commit 84646cc

Browse files
authored
Merge branch 'main' into main
2 parents 36ff921 + 64417f1 commit 84646cc

File tree

1 file changed

+85
-4
lines changed

1 file changed

+85
-4
lines changed

examples/openclaw-plugin/index.ts

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import {
2323
IS_WIN,
2424
waitForHealth,
25+
quickHealthCheck,
2526
quickRecallPrecheck,
2627
withTimeout,
2728
resolvePythonCommand,
@@ -707,7 +708,11 @@ const contextEnginePlugin = {
707708
async execute(_toolCallId: string, params: Record<string, unknown>) {
708709
rememberSessionAgentId(ctx);
709710
const archiveId = String((params as { archiveId?: string }).archiveId ?? "").trim();
711+
const sessionId = ctx.sessionId ?? "";
712+
api.logger.info?.(`openviking: ov_archive_expand invoked (archiveId=${archiveId || "(empty)"}, sessionId=${sessionId || "(empty)"})`);
713+
710714
if (!archiveId) {
715+
api.logger.warn?.(`openviking: ov_archive_expand missing archiveId`);
711716
return {
712717
content: [{ type: "text", text: "Error: archiveId is required." }],
713718
details: { error: "missing_param", param: "archiveId" },
@@ -747,6 +752,7 @@ const contextEnginePlugin = {
747752
.map((m: OVMessage) => formatMessageFaithful(m))
748753
.join("\n\n");
749754

755+
api.logger.info?.(`openviking: ov_archive_expand expanded ${detail.archive_id}, messages=${detail.messages.length}, chars=${body.length}, sessionId=${sessionId}`);
750756
return {
751757
content: [{ type: "text", text: `${header}\n${body}` }],
752758
details: {
@@ -759,6 +765,7 @@ const contextEnginePlugin = {
759765
};
760766
} catch (err) {
761767
const msg = err instanceof Error ? err.message : String(err);
768+
api.logger.warn?.(`openviking: ov_archive_expand failed (archiveId=${archiveId}, sessionId=${sessionId}): ${msg}`);
762769
return {
763770
content: [{ type: "text", text: `Failed to expand ${archiveId}: ${msg}` }],
764771
details: { error: msg, archiveId, sessionId, ovSessionId },
@@ -1072,10 +1079,8 @@ const contextEnginePlugin = {
10721079
localProcess = null;
10731080
localClientCache.delete(localCacheKey);
10741081
}
1075-
if (code != null && code !== 0 || signal) {
1076-
const out = formatStderrOutput();
1077-
api.logger.warn(`openviking: subprocess exited (code=${code}, signal=${signal})${out}`);
1078-
}
1082+
const out = formatStderrOutput();
1083+
api.logger.warn(`openviking: subprocess exited (code=${code}, signal=${signal})${out}`);
10791084
});
10801085
try {
10811086
await waitForHealth(baseUrl, timeoutMs, intervalMs);
@@ -1105,6 +1110,82 @@ const contextEnginePlugin = {
11051110
}
11061111
throw err;
11071112
}
1113+
} else if (cfg.mode === "local") {
1114+
// Defensive re-spawn: if we're not the designated spawner but there's
1115+
// no valid local process, trigger a fresh spawn to recover from
1116+
// scenarios like Gateway force-restart where the child process was
1117+
// orphaned or exited silently.
1118+
const cached = localClientCache.get(localCacheKey);
1119+
const processAlive = cached?.process && cached.process.exitCode === null && !cached.process.killed;
1120+
if (!processAlive) {
1121+
const healthOk = await quickHealthCheck(`http://127.0.0.1:${cfg.port}`, 2000);
1122+
if (!healthOk) {
1123+
api.logger.warn(
1124+
`openviking: no valid local process detected (isSpawner=false), triggering defensive re-spawn`,
1125+
);
1126+
const timeoutMs = 60_000;
1127+
const intervalMs = 500;
1128+
const actualPort = await prepareLocalPort(cfg.port, api.logger);
1129+
const baseUrl = `http://127.0.0.1:${actualPort}`;
1130+
const pythonCmd = resolvePythonCommand(api.logger);
1131+
const pathSep = IS_WIN ? ";" : ":";
1132+
const env = {
1133+
...process.env,
1134+
PYTHONUNBUFFERED: "1",
1135+
PYTHONWARNINGS: "ignore::RuntimeWarning",
1136+
OPENVIKING_CONFIG_FILE: cfg.configPath,
1137+
OPENVIKING_START_CONFIG: cfg.configPath,
1138+
OPENVIKING_START_HOST: "127.0.0.1",
1139+
OPENVIKING_START_PORT: String(actualPort),
1140+
...(process.env.OPENVIKING_GO_PATH && { PATH: `${process.env.OPENVIKING_GO_PATH}${pathSep}${process.env.PATH || ""}` }),
1141+
...(process.env.OPENVIKING_GOPATH && { GOPATH: process.env.OPENVIKING_GOPATH }),
1142+
...(process.env.OPENVIKING_GOPROXY && { GOPROXY: process.env.OPENVIKING_GOPROXY }),
1143+
};
1144+
const runpyCode = `import sys,os,warnings; warnings.filterwarnings('ignore', category=RuntimeWarning, message='.*sys.modules.*'); sys.argv=['openviking.server.bootstrap','--config',os.environ['OPENVIKING_START_CONFIG'],'--host',os.environ.get('OPENVIKING_START_HOST','127.0.0.1'),'--port',os.environ['OPENVIKING_START_PORT']]; import runpy, importlib.util; spec=importlib.util.find_spec('openviking.server.bootstrap'); (runpy.run_path(spec.origin, run_name='__main__') if spec and getattr(spec,'origin',None) else runpy.run_module('openviking.server.bootstrap', run_name='__main__', alter_sys=True))`;
1145+
const child = spawn(
1146+
pythonCmd,
1147+
["-c", runpyCode],
1148+
{ env, cwd: IS_WIN ? tmpdir() : "/tmp", stdio: ["ignore", "pipe", "pipe"] },
1149+
);
1150+
localProcess = child;
1151+
child.on("error", (err: Error) => api.logger.warn(`openviking: local server error (re-spawn): ${String(err)}`));
1152+
child.stderr?.on("data", (chunk: Buffer) => {
1153+
api.logger.debug?.(`[openviking-respawn] ${String(chunk).trim()}`);
1154+
});
1155+
child.on("exit", (code: number | null, signal: string | null) => {
1156+
if (localProcess === child) {
1157+
localProcess = null;
1158+
localClientCache.delete(localCacheKey);
1159+
}
1160+
api.logger.warn(`openviking: re-spawned subprocess exited (code=${code}, signal=${signal})`);
1161+
});
1162+
try {
1163+
await waitForHealth(baseUrl, timeoutMs, intervalMs);
1164+
const client = new OpenVikingClient(baseUrl, cfg.apiKey, cfg.agentId, cfg.timeoutMs);
1165+
localClientCache.set(localCacheKey, { client, process: child });
1166+
if (resolveLocalClient) {
1167+
resolveLocalClient(client);
1168+
rejectLocalClient = null;
1169+
}
1170+
api.logger.info(
1171+
`openviking: local server re-spawned successfully (${baseUrl}, config: ${cfg.configPath})`,
1172+
);
1173+
} catch (err) {
1174+
localProcess = null;
1175+
child.kill("SIGTERM");
1176+
markLocalUnavailable("re-spawn failed", err);
1177+
api.logger.warn(`openviking: defensive re-spawn failed: ${String(err)}`);
1178+
throw err;
1179+
}
1180+
} else {
1181+
api.logger.info(`openviking: local process healthy on port ${cfg.port} (isSpawner=false)`);
1182+
}
1183+
} else {
1184+
await (await getClient()).healthCheck().catch(() => {});
1185+
api.logger.info(
1186+
`openviking: initialized via cache (url: ${cfg.baseUrl}, targetUri: ${cfg.targetUri})`,
1187+
);
1188+
}
11081189
} else {
11091190
await (await getClient()).healthCheck().catch(() => {});
11101191
api.logger.info(

0 commit comments

Comments
 (0)