@openviking/openclaw-plugin 2026.5.10-dev.3 → 2026.5.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js CHANGED
@@ -245,6 +245,36 @@ export class OpenVikingClient {
245
245
  async read(uri, agentId) {
246
246
  return this.request(`/api/v1/content/read?uri=${encodeURIComponent(uri)}`, {}, agentId);
247
247
  }
248
+ async readToolResult(sessionId, toolResultId, options, agentId) {
249
+ const params = new URLSearchParams();
250
+ if (options?.offset !== undefined)
251
+ params.set("offset", String(options.offset));
252
+ if (options?.limit !== undefined)
253
+ params.set("limit", String(options.limit));
254
+ if (options?.includeMetadata !== undefined) {
255
+ params.set("include_metadata", String(options.includeMetadata));
256
+ }
257
+ const query = params.toString();
258
+ return this.request(`/api/v1/sessions/${encodeURIComponent(sessionId)}/tool-results/${encodeURIComponent(toolResultId)}${query ? `?${query}` : ""}`, {}, agentId);
259
+ }
260
+ async searchToolResult(sessionId, toolResultId, queryText, options, agentId) {
261
+ const params = new URLSearchParams({ q: queryText });
262
+ if (options?.limit !== undefined)
263
+ params.set("limit", String(options.limit));
264
+ if (options?.contextChars !== undefined) {
265
+ params.set("context_chars", String(options.contextChars));
266
+ }
267
+ return this.request(`/api/v1/sessions/${encodeURIComponent(sessionId)}/tool-results/${encodeURIComponent(toolResultId)}/search?${params.toString()}`, {}, agentId);
268
+ }
269
+ async listToolResults(sessionId, options, agentId) {
270
+ const params = new URLSearchParams();
271
+ if (options?.toolName)
272
+ params.set("tool_name", options.toolName);
273
+ if (options?.limit !== undefined)
274
+ params.set("limit", String(options.limit));
275
+ const query = params.toString();
276
+ return this.request(`/api/v1/sessions/${encodeURIComponent(sessionId)}/tool-results${query ? `?${query}` : ""}`, {}, agentId);
277
+ }
248
278
  async uploadTempFile(filePath, agentId) {
249
279
  const fileBytes = await readFile(filePath);
250
280
  const form = new FormData();
@@ -29,9 +29,6 @@ function allocateContextBudget(totalBudget, instructionTokens = 0) {
29
29
  const sessionContext = Math.max(usableBudget - archiveMemory, 0);
30
30
  return { archiveMemory, sessionContext, reserved };
31
31
  }
32
- function estimateTokens(messages) {
33
- return Math.max(1, messages.length * 80);
34
- }
35
32
  function roughEstimate(messages) {
36
33
  return Math.ceil(JSON.stringify(messages).length / 4);
37
34
  }
@@ -265,6 +262,10 @@ export function convertToAgentMessages(msg) {
265
262
  const toolName = typeof p.tool_name === "string" ? p.tool_name : undefined;
266
263
  const status = typeof p.tool_status === "string" ? p.tool_status : "unknown";
267
264
  const output = typeof p.tool_output === "string" ? p.tool_output : "";
265
+ const ref = typeof p.tool_output_ref === "string" ? p.tool_output_ref : "";
266
+ const originalChars = typeof p.tool_output_original_chars === "number"
267
+ ? p.tool_output_original_chars
268
+ : undefined;
268
269
  if (toolId) {
269
270
  // Structured path: emit canonical toolCall + toolResult pair (works for any role)
270
271
  toolCallBlocks.push({
@@ -273,9 +274,15 @@ export function convertToAgentMessages(msg) {
273
274
  name: toolName ?? "unknown",
274
275
  arguments: p.tool_input ?? {},
275
276
  });
276
- const resultText = (status === "completed" || status === "error")
277
+ let resultText = (status === "completed" || status === "error")
277
278
  ? (output || "(no output)")
278
279
  : "(interrupted — tool did not complete)";
280
+ if (ref && !resultText.includes(ref)) {
281
+ const suffix = originalChars !== undefined
282
+ ? `\n[tool-result-ref] ${ref} original_chars=${originalChars}`
283
+ : `\n[tool-result-ref] ${ref}`;
284
+ resultText += suffix;
285
+ }
279
286
  const resultPayload = {
280
287
  role: "toolResult",
281
288
  toolCallId: toolId,
@@ -330,6 +337,32 @@ export function convertToAgentMessages(msg) {
330
337
  }
331
338
  return result;
332
339
  }
340
+ function isToolOnlyMessage(msg) {
341
+ return msg.role === "user" && msg.parts.length > 0 && msg.parts.every((part) => part.type === "tool");
342
+ }
343
+ function coalesceConsecutiveToolMessages(messages) {
344
+ const result = [];
345
+ let pendingTools;
346
+ const flush = () => {
347
+ if (pendingTools) {
348
+ result.push(pendingTools);
349
+ pendingTools = undefined;
350
+ }
351
+ };
352
+ for (const msg of messages) {
353
+ if (isToolOnlyMessage(msg)) {
354
+ if (!pendingTools) {
355
+ pendingTools = { role: "user", parts: [] };
356
+ }
357
+ pendingTools.parts.push(...msg.parts);
358
+ continue;
359
+ }
360
+ flush();
361
+ result.push(msg);
362
+ }
363
+ flush();
364
+ return result;
365
+ }
333
366
  function normalizeAssistantContent(messages) {
334
367
  for (let i = 0; i < messages.length; i++) {
335
368
  const msg = messages[i];
@@ -965,7 +998,8 @@ export function createMemoryOpenVikingContextEngine(params) {
965
998
  afterTurnParams.prePromptMessageCount >= 0
966
999
  ? afterTurnParams.prePromptMessageCount
967
1000
  : 0;
968
- const { messages: extractedMessages, newCount } = extractNewTurnMessages(messages, start);
1001
+ const { messages: extractedMessagesRaw, newCount } = extractNewTurnMessages(messages, start);
1002
+ const extractedMessages = coalesceConsecutiveToolMessages(extractedMessagesRaw);
969
1003
  if (extractedMessages.length === 0) {
970
1004
  diag("afterTurn_skip", OVSessionId, {
971
1005
  reason: "no_new_turn_messages",
@@ -1055,9 +1089,7 @@ export function createMemoryOpenVikingContextEngine(params) {
1055
1089
  if (commitResult.task_id && cfg.logFindRequests) {
1056
1090
  logger.info(`openviking: Phase2 memory extraction runs asynchronously on the server (task_id=${commitResult.task_id}). ` +
1057
1091
  "memories_extracted appears only after that task completes — not in this immediate response.");
1058
- if (cfg.logFindRequests) {
1059
- void pollPhase2ExtractionOutcome(getClient, commitResult.task_id, agentId, logger, OVSessionId);
1060
- }
1092
+ void pollPhase2ExtractionOutcome(getClient, commitResult.task_id, agentId, logger, OVSessionId);
1061
1093
  }
1062
1094
  }
1063
1095
  catch (err) {
package/dist/index.js CHANGED
@@ -4,10 +4,39 @@ import { registerSetupCli } from "./commands/setup.js";
4
4
  import { OpenVikingClient, isMemoryUri } from "./client.js";
5
5
  import { formatMessageFaithful, toRoleId } from "./context-engine.js";
6
6
  import { compileSessionPatterns, shouldBypassSession, } from "./text-utils.js";
7
- import { clampScore, postProcessMemories, formatMemoryLines, } from "./memory-ranking.js";
7
+ import { clampScore, postProcessMemories, pickMemoriesForInjection, } from "./memory-ranking.js";
8
8
  import { createMemoryOpenVikingContextEngine, openClawSessionToOvStorageId, openClawSessionRefToOvStorageId, } from "./context-engine.js";
9
9
  import { buildMemoryLines, buildMemoryLinesWithBudget, estimateTokenCount, prepareRecallQuery, } from "./auto-recall.js";
10
10
  export { buildMemoryLines, buildMemoryLinesWithBudget, estimateTokenCount, prepareRecallQuery, };
11
+ function parseToolResultRef(value) {
12
+ const raw = typeof value === "string" ? value.trim() : "";
13
+ if (!raw) {
14
+ return null;
15
+ }
16
+ const match = raw.match(/^viking:\/\/session\/([^/]+)\/tool-results\/([^/?#]+)(?:[?#].*)?$/);
17
+ if (!match) {
18
+ return null;
19
+ }
20
+ const sessionId = decodeURIComponent(match[1]);
21
+ const toolResultId = decodeURIComponent(match[2]);
22
+ if (!sessionId || !toolResultId) {
23
+ return null;
24
+ }
25
+ return {
26
+ sessionId,
27
+ toolResultId,
28
+ ref: `viking://session/${encodeURIComponent(sessionId)}/tool-results/${encodeURIComponent(toolResultId)}`,
29
+ };
30
+ }
31
+ function getOptionalInteger(value, fallback) {
32
+ if (typeof value !== "number" || !Number.isFinite(value)) {
33
+ return fallback;
34
+ }
35
+ return Math.floor(value);
36
+ }
37
+ function getPositiveInteger(value, fallback) {
38
+ return Math.max(1, getOptionalInteger(value, fallback));
39
+ }
11
40
  const DEFAULT_OPENCLAW_AGENT_ID = "main";
12
41
  /**
13
42
  * OpenViking `UserIdentifier` allows only [a-zA-Z0-9_-] for agent_id
@@ -764,29 +793,54 @@ const contextEnginePlugin = {
764
793
  total: leafOnly.length,
765
794
  };
766
795
  }
767
- const memories = postProcessMemories(result.memories ?? [], {
768
- limit,
796
+ const leafOnly = (result.memories ?? []).filter((m) => !m.level || m.level === 2);
797
+ const processed = postProcessMemories(leafOnly, {
798
+ limit: requestLimit,
769
799
  scoreThreshold,
770
800
  });
801
+ const memories = pickMemoriesForInjection(processed, limit, query);
771
802
  if (memories.length === 0) {
772
803
  return {
773
804
  content: [{ type: "text", text: "No relevant OpenViking memories found." }],
774
805
  details: { count: 0, total: result.total ?? 0, scoreThreshold },
775
806
  };
776
807
  }
808
+ const { lines: memoryLines } = await buildMemoryLinesWithBudget(memories, (uri) => recallClient.read(uri, session.agentId), {
809
+ recallPreferAbstract: false,
810
+ recallMaxInjectedChars: cfg.recallMaxInjectedChars,
811
+ });
812
+ if (memoryLines.length === 0) {
813
+ return {
814
+ content: [
815
+ {
816
+ type: "text",
817
+ text: `No complete OpenViking memories fit recallMaxInjectedChars=${cfg.recallMaxInjectedChars}.`,
818
+ },
819
+ ],
820
+ details: {
821
+ count: 0,
822
+ memories,
823
+ total: result.total ?? memories.length,
824
+ scoreThreshold,
825
+ requestLimit,
826
+ recallMaxInjectedChars: cfg.recallMaxInjectedChars,
827
+ },
828
+ };
829
+ }
777
830
  return {
778
831
  content: [
779
832
  {
780
833
  type: "text",
781
- text: `Found ${memories.length} memories:\n\n${formatMemoryLines(memories)}`,
834
+ text: `Found ${memoryLines.length} memories:\n\n${memoryLines.join("\n")}`,
782
835
  },
783
836
  ],
784
837
  details: {
785
- count: memories.length,
838
+ count: memoryLines.length,
786
839
  memories,
787
840
  total: result.total ?? memories.length,
788
841
  scoreThreshold,
789
842
  requestLimit,
843
+ recallMaxInjectedChars: cfg.recallMaxInjectedChars,
790
844
  },
791
845
  };
792
846
  },
@@ -1133,6 +1187,242 @@ const contextEnginePlugin = {
1133
1187
  }
1134
1188
  },
1135
1189
  }), { name: "ov_archive_expand" });
1190
+ api.registerTool((ctx) => ({
1191
+ name: "openviking_tool_result_read",
1192
+ label: "Tool Result Read (OpenViking)",
1193
+ description: "Restore the full original content of a tool result that was externalized by OpenViking. " +
1194
+ "Use when a previous tool result was externalized and only a preview is visible — " +
1195
+ "the preview contains a [tool-result-ref] or viking://session/.../tool-results/... URI. " +
1196
+ "\"Read\" tool returns the same truncated preview; this tool returns the complete content. " +
1197
+ "To read all content: pass offset=0 and a limit large enough to cover the whole result " +
1198
+ "(e.g. limit=100000). Use offset/limit for paging only when you need a specific section.",
1199
+ parameters: Type.Object({
1200
+ tool_output_ref: Type.String({
1201
+ description: "Exact OV URI from the preview, e.g. viking://session/<session_id>/tool-results/<tool_result_id>",
1202
+ }),
1203
+ offset: Type.Optional(Type.Number({ description: "Unicode character offset. Default: 0" })),
1204
+ limit: Type.Optional(Type.Number({ description: "Maximum Unicode characters to read. Default: 20000" })),
1205
+ }),
1206
+ async execute(_toolCallId, params) {
1207
+ if (isBypassedSession(ctx)) {
1208
+ return makeBypassedToolResult("openviking_tool_result_read");
1209
+ }
1210
+ const session = resolvePluginSessionRouting(ctx);
1211
+ if (!session.ovSessionId) {
1212
+ return {
1213
+ content: [{ type: "text", text: "Error: no active session." }],
1214
+ details: { error: "no_session" },
1215
+ };
1216
+ }
1217
+ const parsed = parseToolResultRef(params.tool_output_ref ?? params.ref ?? params.uri);
1218
+ if (!parsed) {
1219
+ return {
1220
+ content: [{ type: "text", text: "Error: tool_output_ref must be a viking://session/.../tool-results/... URI." }],
1221
+ details: { error: "invalid_tool_output_ref" },
1222
+ };
1223
+ }
1224
+ if (parsed.sessionId !== session.ovSessionId) {
1225
+ return {
1226
+ content: [{ type: "text", text: "Error: refusing to read a tool result from another session." }],
1227
+ details: {
1228
+ error: "session_mismatch",
1229
+ requestedSessionId: parsed.sessionId,
1230
+ currentSessionId: session.ovSessionId,
1231
+ },
1232
+ };
1233
+ }
1234
+ const offset = Math.max(0, getOptionalInteger(params.offset, 0));
1235
+ const limit = getOptionalInteger(params.limit, 20_000);
1236
+ if (limit < -1) {
1237
+ return {
1238
+ content: [{ type: "text", text: "Error: limit must be -1 or greater than or equal to 0." }],
1239
+ details: { error: "invalid_limit", limit },
1240
+ };
1241
+ }
1242
+ try {
1243
+ const client = await getClient();
1244
+ const result = await client.readToolResult(session.ovSessionId, parsed.toolResultId, { offset, limit, includeMetadata: true }, session.agentId);
1245
+ const returnedChars = result.content.length;
1246
+ const nextOffset = result.offset + returnedChars;
1247
+ const text = result.content || "(empty tool result chunk)";
1248
+ return {
1249
+ content: [{ type: "text", text }],
1250
+ details: {
1251
+ action: "read",
1252
+ tool_output_ref: parsed.ref,
1253
+ tool_result_id: result.tool_result_id,
1254
+ offset: result.offset,
1255
+ limit: result.limit,
1256
+ returned_chars: returnedChars,
1257
+ total_chars: result.total_chars,
1258
+ has_more: result.has_more,
1259
+ next_offset: result.has_more ? nextOffset : null,
1260
+ metadata: result.metadata ?? null,
1261
+ },
1262
+ };
1263
+ }
1264
+ catch (err) {
1265
+ const msg = err instanceof Error ? err.message : String(err);
1266
+ api.logger.warn?.(`openviking: openviking_tool_result_read failed: ${msg}`);
1267
+ return {
1268
+ content: [{ type: "text", text: `Failed to read tool result: ${msg}` }],
1269
+ details: { error: msg, tool_output_ref: parsed.ref },
1270
+ };
1271
+ }
1272
+ },
1273
+ }), { name: "openviking_tool_result_read" });
1274
+ api.registerTool((ctx) => ({
1275
+ name: "openviking_tool_result_search",
1276
+ label: "Tool Result Search (OpenViking)",
1277
+ description: "Search inside an externalized tool result for a keyword. " +
1278
+ "Use when you need to find specific content in a large externalized result, " +
1279
+ "before reading it with openviking_tool_result_read. " +
1280
+ "Returns matching snippets with their character offsets.",
1281
+ parameters: Type.Object({
1282
+ tool_output_ref: Type.String({
1283
+ description: "Exact OV URI from the preview, e.g. viking://session/<session_id>/tool-results/<tool_result_id>",
1284
+ }),
1285
+ query: Type.String({ description: "Keyword or exact text to search for" }),
1286
+ limit: Type.Optional(Type.Number({ description: "Maximum matches. Default: 20" })),
1287
+ context_chars: Type.Optional(Type.Number({ description: "Characters around each match. Default: 300" })),
1288
+ }),
1289
+ async execute(_toolCallId, params) {
1290
+ if (isBypassedSession(ctx)) {
1291
+ return makeBypassedToolResult("openviking_tool_result_search");
1292
+ }
1293
+ const session = resolvePluginSessionRouting(ctx);
1294
+ if (!session.ovSessionId) {
1295
+ return {
1296
+ content: [{ type: "text", text: "Error: no active session." }],
1297
+ details: { error: "no_session" },
1298
+ };
1299
+ }
1300
+ const parsed = parseToolResultRef(params.tool_output_ref ?? params.ref ?? params.uri);
1301
+ if (!parsed) {
1302
+ return {
1303
+ content: [{ type: "text", text: "Error: tool_output_ref must be a viking://session/.../tool-results/... URI." }],
1304
+ details: { error: "invalid_tool_output_ref" },
1305
+ };
1306
+ }
1307
+ if (parsed.sessionId !== session.ovSessionId) {
1308
+ return {
1309
+ content: [{ type: "text", text: "Error: refusing to search a tool result from another session." }],
1310
+ details: {
1311
+ error: "session_mismatch",
1312
+ requestedSessionId: parsed.sessionId,
1313
+ currentSessionId: session.ovSessionId,
1314
+ },
1315
+ };
1316
+ }
1317
+ const query = String(params.query ?? "").trim();
1318
+ if (!query) {
1319
+ return {
1320
+ content: [{ type: "text", text: "Error: query is required." }],
1321
+ details: { error: "missing_param", param: "query" },
1322
+ };
1323
+ }
1324
+ const limit = getPositiveInteger(params.limit, 20);
1325
+ const contextChars = Math.max(0, getOptionalInteger(params.context_chars ?? params.contextChars, 300));
1326
+ try {
1327
+ const client = await getClient();
1328
+ const result = await client.searchToolResult(session.ovSessionId, parsed.toolResultId, query, { limit, contextChars }, session.agentId);
1329
+ const matches = result.matches ?? [];
1330
+ const text = matches.length
1331
+ ? [
1332
+ `Found ${matches.length} match(es) for "${query}" in ${parsed.ref}:`,
1333
+ "",
1334
+ ...matches.map((match, index) => `## Match ${index + 1} (offset ${match.offset})\n${match.snippet}`),
1335
+ ].join("\n")
1336
+ : `No matches found for "${query}" in ${parsed.ref}.`;
1337
+ return {
1338
+ content: [{ type: "text", text }],
1339
+ details: {
1340
+ action: "searched",
1341
+ tool_output_ref: parsed.ref,
1342
+ tool_result_id: result.tool_result_id,
1343
+ query,
1344
+ match_count: matches.length,
1345
+ matches,
1346
+ },
1347
+ };
1348
+ }
1349
+ catch (err) {
1350
+ const msg = err instanceof Error ? err.message : String(err);
1351
+ api.logger.warn?.(`openviking: openviking_tool_result_search failed: ${msg}`);
1352
+ return {
1353
+ content: [{ type: "text", text: `Failed to search tool result: ${msg}` }],
1354
+ details: { error: msg, tool_output_ref: parsed.ref, query },
1355
+ };
1356
+ }
1357
+ },
1358
+ }), { name: "openviking_tool_result_search" });
1359
+ api.registerTool((ctx) => ({
1360
+ name: "openviking_tool_result_list",
1361
+ label: "Tool Result List (OpenViking)",
1362
+ description: "List externalized tool results for the current session. " +
1363
+ "Use to discover available refs before calling openviking_tool_result_read. " +
1364
+ "Optionally filter by tool_name to narrow down results.",
1365
+ parameters: Type.Object({
1366
+ tool_name: Type.Optional(Type.String({ description: "Optional exact tool name filter" })),
1367
+ limit: Type.Optional(Type.Number({ description: "Maximum results. Default: 50" })),
1368
+ }),
1369
+ async execute(_toolCallId, params) {
1370
+ if (isBypassedSession(ctx)) {
1371
+ return makeBypassedToolResult("openviking_tool_result_list");
1372
+ }
1373
+ const session = resolvePluginSessionRouting(ctx);
1374
+ if (!session.ovSessionId) {
1375
+ return {
1376
+ content: [{ type: "text", text: "Error: no active session." }],
1377
+ details: { error: "no_session" },
1378
+ };
1379
+ }
1380
+ const toolName = typeof params.tool_name === "string" && params.tool_name.trim()
1381
+ ? params.tool_name.trim()
1382
+ : typeof params.toolName === "string" && params.toolName.trim()
1383
+ ? params.toolName.trim()
1384
+ : undefined;
1385
+ const limit = getPositiveInteger(params.limit, 50);
1386
+ try {
1387
+ const client = await getClient();
1388
+ const result = await client.listToolResults(session.ovSessionId, { toolName, limit }, session.agentId);
1389
+ const items = result.tool_results ?? [];
1390
+ const text = items.length
1391
+ ? [
1392
+ `Found ${items.length} externalized tool result(s) in current session:`,
1393
+ "",
1394
+ ...items.map((item, index) => {
1395
+ const ref = typeof item.storage_uri === "string" ? item.storage_uri : "(missing ref)";
1396
+ const name = typeof item.tool_name === "string" ? item.tool_name : "tool";
1397
+ const chars = typeof item.original_chars === "number" ? item.original_chars : "unknown";
1398
+ const created = typeof item.created_at === "string" ? ` created_at=${item.created_at}` : "";
1399
+ return `${index + 1}. ${name} original_chars=${chars}${created}\nref: ${ref}`;
1400
+ }),
1401
+ ].join("\n")
1402
+ : toolName
1403
+ ? `No externalized tool results found for tool "${toolName}" in current session.`
1404
+ : "No externalized tool results found in current session.";
1405
+ return {
1406
+ content: [{ type: "text", text }],
1407
+ details: {
1408
+ action: "listed",
1409
+ session_id: session.ovSessionId,
1410
+ tool_name: toolName ?? null,
1411
+ count: items.length,
1412
+ tool_results: items,
1413
+ },
1414
+ };
1415
+ }
1416
+ catch (err) {
1417
+ const msg = err instanceof Error ? err.message : String(err);
1418
+ api.logger.warn?.(`openviking: openviking_tool_result_list failed: ${msg}`);
1419
+ return {
1420
+ content: [{ type: "text", text: `Failed to list tool results: ${msg}` }],
1421
+ details: { error: msg, session_id: session.ovSessionId, tool_name: toolName ?? null },
1422
+ };
1423
+ }
1424
+ },
1425
+ }), { name: "openviking_tool_result_list" });
1136
1426
  let contextEngineRef = null;
1137
1427
  const sessionAgentResolver = createSessionAgentResolver(cfg.agent_prefix);
1138
1428
  const rememberSessionAgentId = (ctx) => {
@@ -1,66 +1,3 @@
1
- import { Socket } from "node:net";
2
- export function waitForHealth(baseUrl, timeoutMs, intervalMs) {
3
- const deadline = Date.now() + timeoutMs;
4
- return new Promise((resolve, reject) => {
5
- const tick = () => {
6
- if (Date.now() > deadline) {
7
- reject(new Error(`OpenViking health check timeout at ${baseUrl}`));
8
- return;
9
- }
10
- fetch(`${baseUrl}/health`)
11
- .then((r) => r.json())
12
- .then((body) => {
13
- if (body?.status === "ok") {
14
- resolve();
15
- return;
16
- }
17
- setTimeout(tick, intervalMs);
18
- })
19
- .catch(() => setTimeout(tick, intervalMs));
20
- };
21
- tick();
22
- });
23
- }
24
- export function waitForHealthOrExit(baseUrl, timeoutMs, intervalMs, child) {
25
- const exited = child.killed || child.exitCode !== null || child.signalCode !== null;
26
- if (exited) {
27
- return Promise.reject(new Error(`OpenViking subprocess exited before health check ` +
28
- `(code=${child.exitCode}, signal=${child.signalCode})`));
29
- }
30
- return new Promise((resolve, reject) => {
31
- let settled = false;
32
- const cleanup = () => {
33
- child.off?.("error", onError);
34
- child.off?.("exit", onExit);
35
- };
36
- const finishResolve = () => {
37
- if (settled) {
38
- return;
39
- }
40
- settled = true;
41
- cleanup();
42
- resolve();
43
- };
44
- const finishReject = (err) => {
45
- if (settled) {
46
- return;
47
- }
48
- settled = true;
49
- cleanup();
50
- reject(err instanceof Error ? err : new Error(String(err)));
51
- };
52
- const onError = (err) => {
53
- finishReject(err);
54
- };
55
- const onExit = (code, signal) => {
56
- finishReject(new Error(`OpenViking subprocess exited before health check ` +
57
- `(code=${code}, signal=${signal})`));
58
- };
59
- child.once("error", onError);
60
- child.once("exit", onExit);
61
- waitForHealth(baseUrl, timeoutMs, intervalMs).then(finishResolve, finishReject);
62
- });
63
- }
64
1
  export function withTimeout(promise, timeoutMs, timeoutMessage) {
65
2
  return new Promise((resolve, reject) => {
66
3
  const timer = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs);
@@ -73,30 +10,6 @@ export function withTimeout(promise, timeoutMs, timeoutMessage) {
73
10
  });
74
11
  });
75
12
  }
76
- export function quickTcpProbe(host, port, timeoutMs) {
77
- return new Promise((resolve) => {
78
- const socket = new Socket();
79
- let done = false;
80
- const finish = (ok) => {
81
- if (done) {
82
- return;
83
- }
84
- done = true;
85
- socket.destroy();
86
- resolve(ok);
87
- };
88
- socket.setTimeout(timeoutMs);
89
- socket.once("connect", () => finish(true));
90
- socket.once("timeout", () => finish(false));
91
- socket.once("error", () => finish(false));
92
- try {
93
- socket.connect(port, host);
94
- }
95
- catch {
96
- finish(false);
97
- }
98
- });
99
- }
100
13
  export async function quickHealthCheck(baseUrl, timeoutMs) {
101
14
  const controller = new AbortController();
102
15
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -1,7 +1,4 @@
1
1
  const _env = globalThis["process"];
2
- export function sysEnv() {
3
- return _env.env;
4
- }
5
2
  export function getEnv(key) {
6
3
  return _env.env[key];
7
4
  }