@remnic/plugin-openclaw 9.39.0 → 9.41.0
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/index.js +124 -55
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -5294,48 +5294,84 @@ import { Worker } from "worker_threads";
|
|
|
5294
5294
|
import { expandTildePath } from "@remnic/core";
|
|
5295
5295
|
var DEFAULT_HOST = "127.0.0.1";
|
|
5296
5296
|
var DEFAULT_PORT = 4318;
|
|
5297
|
+
var LIVENESS_PATH = "/engram/v1/live";
|
|
5297
5298
|
var LEGACY_HEALTH_PATH = "/engram/v1/health";
|
|
5298
|
-
var
|
|
5299
|
+
var DEFAULT_DAEMON_HEALTH_TIMEOUT_MS = 1e4;
|
|
5300
|
+
function parseBridgeHealthTimeoutMs(value) {
|
|
5301
|
+
if (value === void 0) return DEFAULT_DAEMON_HEALTH_TIMEOUT_MS;
|
|
5302
|
+
const parsed = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
|
|
5303
|
+
if (typeof parsed !== "number" || !Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < 1 || parsed > 12e4) {
|
|
5304
|
+
throw new Error(
|
|
5305
|
+
`bridgeHealthTimeoutMs must be an integer in [1, 120000]; got ${String(value)}`
|
|
5306
|
+
);
|
|
5307
|
+
}
|
|
5308
|
+
return parsed;
|
|
5309
|
+
}
|
|
5310
|
+
function parseOpenClawBridgeConfig(config) {
|
|
5311
|
+
return {
|
|
5312
|
+
healthTimeoutMs: parseBridgeHealthTimeoutMs(config.bridgeHealthTimeoutMs)
|
|
5313
|
+
};
|
|
5314
|
+
}
|
|
5315
|
+
function runHealthWorker(request, data) {
|
|
5316
|
+
const view = new Int32Array(data.state);
|
|
5317
|
+
let completed = false;
|
|
5318
|
+
function finish(ok) {
|
|
5319
|
+
if (completed) return;
|
|
5320
|
+
completed = true;
|
|
5321
|
+
Atomics.store(view, 0, ok ? 1 : 2);
|
|
5322
|
+
Atomics.notify(view, 0);
|
|
5323
|
+
}
|
|
5324
|
+
function probe(pathname, fallbackPath) {
|
|
5325
|
+
const remainingMs = data.deadline - Date.now();
|
|
5326
|
+
if (remainingMs <= 0) {
|
|
5327
|
+
finish(false);
|
|
5328
|
+
return;
|
|
5329
|
+
}
|
|
5330
|
+
let responseReceived = false;
|
|
5331
|
+
try {
|
|
5332
|
+
const headers = {};
|
|
5333
|
+
if (data.token) headers.Authorization = `Bearer ${data.token}`;
|
|
5334
|
+
const req = request(
|
|
5335
|
+
{
|
|
5336
|
+
hostname: data.host,
|
|
5337
|
+
port: data.port,
|
|
5338
|
+
path: pathname,
|
|
5339
|
+
method: "GET",
|
|
5340
|
+
timeout: remainingMs,
|
|
5341
|
+
headers
|
|
5342
|
+
},
|
|
5343
|
+
(res) => {
|
|
5344
|
+
responseReceived = true;
|
|
5345
|
+
const statusCode = res.statusCode;
|
|
5346
|
+
res.resume();
|
|
5347
|
+
if (statusCode === 200) {
|
|
5348
|
+
finish(true);
|
|
5349
|
+
} else if (statusCode === 404 && fallbackPath) {
|
|
5350
|
+
probe(fallbackPath, null);
|
|
5351
|
+
} else {
|
|
5352
|
+
finish(false);
|
|
5353
|
+
}
|
|
5354
|
+
}
|
|
5355
|
+
);
|
|
5356
|
+
req.on("error", () => {
|
|
5357
|
+
if (!responseReceived) finish(false);
|
|
5358
|
+
});
|
|
5359
|
+
req.on("timeout", () => {
|
|
5360
|
+
req.destroy();
|
|
5361
|
+
if (!responseReceived) finish(false);
|
|
5362
|
+
});
|
|
5363
|
+
req.end();
|
|
5364
|
+
} catch {
|
|
5365
|
+
finish(false);
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
probe(data.path, data.fallbackPath);
|
|
5369
|
+
}
|
|
5299
5370
|
var HEALTH_WORKER_SOURCE = `
|
|
5300
5371
|
import { request } from "node:http";
|
|
5301
5372
|
import { workerData } from "node:worker_threads";
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
let completed = false;
|
|
5305
|
-
|
|
5306
|
-
function finish(ok) {
|
|
5307
|
-
if (completed) return;
|
|
5308
|
-
completed = true;
|
|
5309
|
-
Atomics.store(view, 0, ok ? 1 : 2);
|
|
5310
|
-
Atomics.notify(view, 0);
|
|
5311
|
-
}
|
|
5312
|
-
|
|
5313
|
-
try {
|
|
5314
|
-
const headers = {};
|
|
5315
|
-
if (workerData.token) headers.Authorization = "Bearer " + workerData.token;
|
|
5316
|
-
const req = request(
|
|
5317
|
-
{
|
|
5318
|
-
hostname: workerData.host,
|
|
5319
|
-
port: workerData.port,
|
|
5320
|
-
path: workerData.path,
|
|
5321
|
-
method: "GET",
|
|
5322
|
-
timeout: workerData.timeoutMs,
|
|
5323
|
-
headers,
|
|
5324
|
-
},
|
|
5325
|
-
(res) => {
|
|
5326
|
-
finish(res.statusCode === 200);
|
|
5327
|
-
res.resume();
|
|
5328
|
-
},
|
|
5329
|
-
);
|
|
5330
|
-
req.on("error", () => finish(false));
|
|
5331
|
-
req.on("timeout", () => {
|
|
5332
|
-
req.destroy();
|
|
5333
|
-
finish(false);
|
|
5334
|
-
});
|
|
5335
|
-
req.end();
|
|
5336
|
-
} catch {
|
|
5337
|
-
finish(false);
|
|
5338
|
-
}
|
|
5373
|
+
const __name = (target) => target;
|
|
5374
|
+
(${runHealthWorker.toString()})(request, workerData);
|
|
5339
5375
|
`;
|
|
5340
5376
|
var LAUNCHD_SERVICE_PATHS = [
|
|
5341
5377
|
["Library", "LaunchAgents", "ai.remnic.daemon.plist"],
|
|
@@ -5402,8 +5438,9 @@ function coerceDaemonPort(value) {
|
|
|
5402
5438
|
const parsed = typeof value === "string" && value.trim() !== "" ? Number(value.trim()) : value;
|
|
5403
5439
|
return typeof parsed === "number" && Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : void 0;
|
|
5404
5440
|
}
|
|
5405
|
-
function checkDaemonHealthSync(host, port, timeoutMs =
|
|
5441
|
+
function checkDaemonHealthSync(host, port, timeoutMs = DEFAULT_DAEMON_HEALTH_TIMEOUT_MS) {
|
|
5406
5442
|
if (!host || !Number.isInteger(port) || port <= 0 || port > 65535) return false;
|
|
5443
|
+
const deadline = Date.now() + timeoutMs;
|
|
5407
5444
|
let worker;
|
|
5408
5445
|
try {
|
|
5409
5446
|
const state = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
|
|
@@ -5414,14 +5451,15 @@ function checkDaemonHealthSync(host, port, timeoutMs = SYNC_HEALTH_TIMEOUT_MS) {
|
|
|
5414
5451
|
workerData: {
|
|
5415
5452
|
host,
|
|
5416
5453
|
port,
|
|
5417
|
-
path:
|
|
5454
|
+
path: LIVENESS_PATH,
|
|
5455
|
+
fallbackPath: LEGACY_HEALTH_PATH,
|
|
5418
5456
|
token: loadDaemonAuth().token,
|
|
5419
|
-
|
|
5457
|
+
deadline,
|
|
5420
5458
|
state
|
|
5421
5459
|
}
|
|
5422
5460
|
};
|
|
5423
5461
|
worker = new Worker(workerUrl, workerOptions);
|
|
5424
|
-
Atomics.wait(view, 0, 0,
|
|
5462
|
+
Atomics.wait(view, 0, 0, Math.max(0, deadline - Date.now()));
|
|
5425
5463
|
const status = Atomics.load(view, 0);
|
|
5426
5464
|
if (status === 0) void worker.terminate();
|
|
5427
5465
|
return status === 1;
|
|
@@ -5567,27 +5605,44 @@ function loadDaemonAuth() {
|
|
|
5567
5605
|
}
|
|
5568
5606
|
return { token: "", source: "no configured token" };
|
|
5569
5607
|
}
|
|
5570
|
-
async function checkDaemonHealth(host, port) {
|
|
5608
|
+
async function checkDaemonHealth(host, port, timeoutMs = DEFAULT_DAEMON_HEALTH_TIMEOUT_MS) {
|
|
5609
|
+
if (!host || !Number.isInteger(port) || port <= 0 || port > 65535) return false;
|
|
5610
|
+
const deadline = Date.now() + timeoutMs;
|
|
5571
5611
|
try {
|
|
5572
5612
|
const { request } = await import("http");
|
|
5573
5613
|
const token = loadDaemonAuth().token;
|
|
5574
5614
|
const headers = {};
|
|
5575
5615
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
5576
|
-
|
|
5616
|
+
const probe = (requestPath) => new Promise((resolve) => {
|
|
5617
|
+
const remainingMs = deadline - Date.now();
|
|
5618
|
+
if (remainingMs <= 0) {
|
|
5619
|
+
resolve(void 0);
|
|
5620
|
+
return;
|
|
5621
|
+
}
|
|
5622
|
+
let settled = false;
|
|
5623
|
+
const finish = (statusCode) => {
|
|
5624
|
+
if (settled) return;
|
|
5625
|
+
settled = true;
|
|
5626
|
+
resolve(statusCode);
|
|
5627
|
+
};
|
|
5577
5628
|
const req = request(
|
|
5578
|
-
{ hostname: host, port, path:
|
|
5629
|
+
{ hostname: host, port, path: requestPath, method: "GET", timeout: remainingMs, headers },
|
|
5579
5630
|
(res) => {
|
|
5580
|
-
|
|
5631
|
+
finish(res.statusCode);
|
|
5581
5632
|
res.resume();
|
|
5582
5633
|
}
|
|
5583
5634
|
);
|
|
5584
|
-
req.on("error", () =>
|
|
5635
|
+
req.on("error", () => finish(void 0));
|
|
5585
5636
|
req.on("timeout", () => {
|
|
5586
5637
|
req.destroy();
|
|
5587
|
-
|
|
5638
|
+
finish(void 0);
|
|
5588
5639
|
});
|
|
5589
5640
|
req.end();
|
|
5590
5641
|
});
|
|
5642
|
+
const livenessStatus = await probe(LIVENESS_PATH);
|
|
5643
|
+
if (livenessStatus === 200) return true;
|
|
5644
|
+
if (livenessStatus !== 404) return false;
|
|
5645
|
+
return await probe(LEGACY_HEALTH_PATH) === 200;
|
|
5591
5646
|
} catch {
|
|
5592
5647
|
return false;
|
|
5593
5648
|
}
|
|
@@ -6231,7 +6286,21 @@ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkD
|
|
|
6231
6286
|
);
|
|
6232
6287
|
return true;
|
|
6233
6288
|
}
|
|
6234
|
-
|
|
6289
|
+
let bridgeHealthTimeoutMs;
|
|
6290
|
+
try {
|
|
6291
|
+
bridgeHealthTimeoutMs = parseOpenClawBridgeConfig({
|
|
6292
|
+
bridgeHealthTimeoutMs: options.bridgeHealthTimeoutMs
|
|
6293
|
+
}).healthTimeoutMs;
|
|
6294
|
+
} catch (err) {
|
|
6295
|
+
log2.error(`${String(err)} \u2014 falling back to the embedded runtime`);
|
|
6296
|
+
delegateEmbeddedFallbackApis.add(api);
|
|
6297
|
+
return false;
|
|
6298
|
+
}
|
|
6299
|
+
if (!deps.checkHealth(
|
|
6300
|
+
bridge.daemonHost,
|
|
6301
|
+
bridge.daemonPort,
|
|
6302
|
+
bridgeHealthTimeoutMs
|
|
6303
|
+
)) {
|
|
6235
6304
|
delegateEmbeddedFallbackApis.add(api);
|
|
6236
6305
|
log2.error(
|
|
6237
6306
|
`bridge mode delegate requested but no healthy daemon at ${bridge.daemonHost}:${bridge.daemonPort} \u2014 falling back to the embedded runtime`
|
|
@@ -7236,12 +7305,13 @@ var pluginDefinition = {
|
|
|
7236
7305
|
const fileConfig = loadPluginConfigFromFile(serviceId);
|
|
7237
7306
|
const openclawFlushPlanProcessingEnabled = resolveOpenClawFlushPlanProcessingEnabledFromConfig(fileConfig, api.pluginConfig);
|
|
7238
7307
|
const runtimeSet = /* @__PURE__ */ new Set();
|
|
7308
|
+
const rawPluginConfig = {
|
|
7309
|
+
...fileConfig,
|
|
7310
|
+
...api.pluginConfig
|
|
7311
|
+
};
|
|
7239
7312
|
const cfg = (0, config_exports.parseConfig)(
|
|
7240
7313
|
{
|
|
7241
|
-
...
|
|
7242
|
-
// File-backed fallback for runtimes that omit pluginConfig
|
|
7243
|
-
...api.pluginConfig,
|
|
7244
|
-
// Runtime/plugin-supplied config must win
|
|
7314
|
+
...rawPluginConfig,
|
|
7245
7315
|
gatewayConfig: api.config
|
|
7246
7316
|
// Pass gateway config for fallback AI
|
|
7247
7317
|
},
|
|
@@ -7293,9 +7363,8 @@ var pluginDefinition = {
|
|
|
7293
7363
|
const delegateApi = api;
|
|
7294
7364
|
const delegateHandled = maybeRegisterDelegateRuntime(delegateApi, {
|
|
7295
7365
|
serviceId,
|
|
7296
|
-
// cfg.bridgeMode is parseConfig's merged file+runtime passthrough, so
|
|
7297
|
-
// file-config-only deployments activate delegate too (round-7 High).
|
|
7298
7366
|
configBridgeMode: cfg.bridgeMode,
|
|
7367
|
+
bridgeHealthTimeoutMs: rawPluginConfig.bridgeHealthTimeoutMs,
|
|
7299
7368
|
passive: passiveMode,
|
|
7300
7369
|
allowPromptInjection: coerceRawConfigBoolean(
|
|
7301
7370
|
readPluginHooksPolicy(api.config, serviceId)?.allowPromptInjection
|