@remnic/plugin-openclaw 9.57.3 → 9.57.4

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 CHANGED
@@ -5412,10 +5412,10 @@ function runHealthWorker(request, data) {
5412
5412
  const READINESS_RETRY_MS = 250;
5413
5413
  const view = new Int32Array(data.state);
5414
5414
  let completed = false;
5415
- function finish(ok, rejectedAuth = false) {
5415
+ function finish(ok, rejectedAuth = false, httpFailure = false) {
5416
5416
  if (completed) return;
5417
5417
  completed = true;
5418
- Atomics.store(view, 0, ok ? 1 : rejectedAuth ? 3 : 2);
5418
+ Atomics.store(view, 0, ok ? 1 : rejectedAuth ? 3 : httpFailure ? 4 : 2);
5419
5419
  Atomics.notify(view, 0);
5420
5420
  }
5421
5421
  function probe(pathname, fallbackPath) {
@@ -5480,7 +5480,7 @@ function runHealthWorker(request, data) {
5480
5480
  } else if (statusCode === 401 || statusCode === 403) {
5481
5481
  finish(false, true);
5482
5482
  } else {
5483
- finish(false);
5483
+ finish(false, false, true);
5484
5484
  }
5485
5485
  }
5486
5486
  );
@@ -6256,8 +6256,11 @@ function coerceDaemonPort(value) {
6256
6256
  }
6257
6257
  var DAEMON_CAPTURE_BYTES = 1024;
6258
6258
  function probeDaemonSync(options) {
6259
+ const auth = options.authToken === void 0 ? loadDaemonAuth(options.configPath) : { token: options.authToken, source: "daemon configuration" };
6259
6260
  const { host, port, timeoutMs } = options;
6260
- if (!host || !Number.isInteger(port) || port <= 0 || port > 65535) return { ok: false };
6261
+ if (!host || !Number.isInteger(port) || port <= 0 || port > 65535) {
6262
+ return { ok: false, failure: "network" };
6263
+ }
6261
6264
  const deadline = Date.now() + timeoutMs;
6262
6265
  let worker;
6263
6266
  try {
@@ -6272,7 +6275,7 @@ function probeDaemonSync(options) {
6272
6275
  port,
6273
6276
  path: options.path,
6274
6277
  fallbackPath: options.fallbackPath,
6275
- token: options.authToken ?? loadDaemonAuth(options.configPath).token,
6278
+ token: auth.token,
6276
6279
  deadline,
6277
6280
  state,
6278
6281
  ...capture ? { capture, captureField: options.captureField } : {}
@@ -6282,7 +6285,14 @@ function probeDaemonSync(options) {
6282
6285
  Atomics.wait(view, 0, 0, Math.max(0, deadline - Date.now()));
6283
6286
  const status = Atomics.load(view, 0);
6284
6287
  if (status === 0) void worker.terminate();
6285
- if (status !== 1) return { ok: false, ...status === 3 ? { rejectedAuth: true } : {} };
6288
+ if (status !== 1) {
6289
+ const failure = status === 3 ? "auth" : status === 4 ? "http" : "network";
6290
+ return {
6291
+ ok: false,
6292
+ failure,
6293
+ ...status === 3 ? { rejectedAuth: true, authSource: auth.source } : {}
6294
+ };
6295
+ }
6286
6296
  if (!capture) return { ok: true };
6287
6297
  const length = new DataView(capture).getUint32(0);
6288
6298
  if (length === 0) return { ok: true };
@@ -6318,9 +6328,9 @@ function readDaemonMemoryDirSync(host, port, timeoutMs = DEFAULT_DAEMON_HEALTH_T
6318
6328
  return {
6319
6329
  healthy: probe.ok,
6320
6330
  memoryDir: probe.captured,
6321
- // Present only when true, so the result shape is unchanged for callers
6322
- // that compare it structurally.
6323
- ...probe.rejectedAuth === true ? { rejectedAuth: true } : {}
6331
+ ...probe.failure === void 0 ? {} : { failure: probe.failure },
6332
+ ...probe.rejectedAuth === true ? { rejectedAuth: true } : {},
6333
+ ...probe.authSource === void 0 ? {} : { authSource: probe.authSource }
6324
6334
  };
6325
6335
  }
6326
6336
  function shouldProbeDaemonHealth(host) {
@@ -6509,7 +6519,8 @@ function detectDaemonBridgeMode(options) {
6509
6519
  }
6510
6520
  }
6511
6521
  if (!health.healthy) {
6512
- options.onSkip?.(`no healthy daemon at ${daemonHost}:${daemonPort}`);
6522
+ const reason = health.failure === "auth" ? `daemon authentication failed at ${daemonHost}:${daemonPort} using ${health.authSource ?? "the configured credential"}` : health.failure === "http" ? `daemon health at ${daemonHost}:${daemonPort} returned an HTTP error; no healthy daemon` : `daemon network probe failed at ${daemonHost}:${daemonPort}; no healthy daemon`;
6523
+ options.onSkip?.(reason);
6513
6524
  continue;
6514
6525
  }
6515
6526
  if (health.memoryDir === void 0) {
@@ -6609,7 +6620,9 @@ function selectedDaemonConfigPath() {
6609
6620
  function isOpenClawTokenEntry(value) {
6610
6621
  return value !== null && typeof value === "object" && "connector" in value && value.connector === "openclaw" && "token" in value && typeof value.token === "string";
6611
6622
  }
6612
- function loadDaemonAuth(configPath) {
6623
+ var daemonAuthKey = (source, token) => `${source}\0${token}`;
6624
+ function loadDaemonAuth(configPath, excludedSources) {
6625
+ const excluded = (source, token) => excludedSources?.has(source) === true || excludedSources?.has(daemonAuthKey(source, token)) === true;
6613
6626
  const environmentTokens = [
6614
6627
  ["OPENCLAW_REMNIC_ACCESS_TOKEN", readEnv("OPENCLAW_REMNIC_ACCESS_TOKEN")],
6615
6628
  ["REMNIC_AUTH_TOKEN", readEnv("REMNIC_AUTH_TOKEN")],
@@ -6617,7 +6630,7 @@ function loadDaemonAuth(configPath) {
6617
6630
  ["ENGRAM_AUTH_TOKEN", readEnv("ENGRAM_AUTH_TOKEN")]
6618
6631
  ];
6619
6632
  for (const [source, token] of environmentTokens) {
6620
- if (token) return { token, source };
6633
+ if (token && !excluded(source, token)) return { token, source };
6621
6634
  }
6622
6635
  const tokenStores = [
6623
6636
  { path: path6.join(resolveHomeDir2(), ".remnic", "tokens.json"), source: "remnic token store" },
@@ -6629,10 +6642,10 @@ function loadDaemonAuth(configPath) {
6629
6642
  const store = JSON.parse(fs3.readFileSync(tokenStore.path, "utf8"));
6630
6643
  const tokens = Array.isArray(store.tokens) ? store.tokens : [];
6631
6644
  const openClawToken = tokens.find(isOpenClawTokenEntry)?.token;
6632
- if (typeof openClawToken === "string" && openClawToken.length > 0) {
6645
+ if (typeof openClawToken === "string" && openClawToken.length > 0 && !excluded(tokenStore.source, openClawToken)) {
6633
6646
  return { token: openClawToken, source: tokenStore.source };
6634
6647
  }
6635
- if (typeof store === "object" && store !== null && "openclaw" in store && typeof store.openclaw === "string" && store.openclaw.length > 0 && (store.openclaw.startsWith("remnic_") || store.openclaw.startsWith("engram_"))) {
6648
+ if (typeof store === "object" && store !== null && "openclaw" in store && typeof store.openclaw === "string" && store.openclaw.length > 0 && (store.openclaw.startsWith("remnic_") || store.openclaw.startsWith("engram_")) && !excluded(tokenStore.source, store.openclaw)) {
6636
6649
  return { token: store.openclaw, source: tokenStore.source };
6637
6650
  }
6638
6651
  } catch {
@@ -6645,7 +6658,7 @@ function loadDaemonAuth(configPath) {
6645
6658
  const raw = JSON.parse(fs3.readFileSync(candidate, "utf8"));
6646
6659
  const server = raw?.server;
6647
6660
  const token = server?.authToken;
6648
- if (typeof token === "string" && token.length > 0) {
6661
+ if (typeof token === "string" && token.length > 0 && !excluded("daemon configuration", token)) {
6649
6662
  return { token, source: "daemon configuration" };
6650
6663
  }
6651
6664
  } catch {
@@ -6655,15 +6668,24 @@ function loadDaemonAuth(configPath) {
6655
6668
  }
6656
6669
  return { token: "", source: "no configured token" };
6657
6670
  }
6658
- async function checkDaemonHealth(host, port, timeoutMs = DEFAULT_DAEMON_HEALTH_TIMEOUT_MS) {
6659
- if (!host || !Number.isInteger(port) || port <= 0 || port > 65535) return false;
6671
+ function classifyDaemonHealthStatus(status, tokenSource) {
6672
+ if (status === 200) return { ok: true };
6673
+ if (status === 401 || status === 403) {
6674
+ return { ok: false, failure: "auth", status, tokenSource };
6675
+ }
6676
+ if (status === void 0) return { ok: false, failure: "network" };
6677
+ return { ok: false, failure: "http", status };
6678
+ }
6679
+ async function checkDaemonHealthDetailed(host, port, timeoutMs = DEFAULT_DAEMON_HEALTH_TIMEOUT_MS) {
6680
+ if (!host || !Number.isInteger(port) || port <= 0 || port > 65535) {
6681
+ return { ok: false, failure: "network" };
6682
+ }
6660
6683
  const deadline = Date.now() + timeoutMs;
6661
6684
  try {
6662
6685
  const { request } = await import("http");
6663
- const token = loadDaemonAuth().token;
6664
- const headers = {};
6665
- if (token) headers["Authorization"] = `Bearer ${token}`;
6666
- const probe = (requestPath) => new Promise((resolve) => {
6686
+ let auth = loadDaemonAuth();
6687
+ const fallbackAuth = auth.token === "" ? void 0 : loadDaemonAuth(void 0, /* @__PURE__ */ new Set([auth.source]));
6688
+ const probe = (requestPath, token) => new Promise((resolve) => {
6667
6689
  const remainingMs = deadline - Date.now();
6668
6690
  if (remainingMs <= 0) {
6669
6691
  resolve(void 0);
@@ -6675,6 +6697,8 @@ async function checkDaemonHealth(host, port, timeoutMs = DEFAULT_DAEMON_HEALTH_T
6675
6697
  settled = true;
6676
6698
  resolve(statusCode);
6677
6699
  };
6700
+ const headers = {};
6701
+ if (token) headers.Authorization = `Bearer ${token}`;
6678
6702
  const req = request(
6679
6703
  { hostname: host, port, path: requestPath, method: "GET", timeout: remainingMs, headers },
6680
6704
  (res) => {
@@ -6689,14 +6713,28 @@ async function checkDaemonHealth(host, port, timeoutMs = DEFAULT_DAEMON_HEALTH_T
6689
6713
  });
6690
6714
  req.end();
6691
6715
  });
6692
- const livenessStatus = await probe(LIVENESS_PATH);
6693
- if (livenessStatus === 200) return true;
6694
- if (livenessStatus !== 404) return false;
6695
- return await probe(LEGACY_HEALTH_PATH) === 200;
6716
+ const probeWithFallback = async (requestPath) => {
6717
+ let status = await probe(requestPath, auth.token);
6718
+ if ((status === 401 || status === 403) && fallbackAuth?.token && fallbackAuth.token !== auth.token) {
6719
+ auth = fallbackAuth;
6720
+ status = await probe(requestPath, auth.token);
6721
+ }
6722
+ return status;
6723
+ };
6724
+ const livenessStatus = await probeWithFallback(LIVENESS_PATH);
6725
+ if (livenessStatus === 200) return { ok: true };
6726
+ if (livenessStatus !== 404) return classifyDaemonHealthStatus(livenessStatus, auth.source);
6727
+ return classifyDaemonHealthStatus(
6728
+ await probeWithFallback(LEGACY_HEALTH_PATH),
6729
+ auth.source
6730
+ );
6696
6731
  } catch {
6697
- return false;
6732
+ return { ok: false, failure: "network" };
6698
6733
  }
6699
6734
  }
6735
+ async function checkDaemonHealth(host, port, timeoutMs = DEFAULT_DAEMON_HEALTH_TIMEOUT_MS) {
6736
+ return (await checkDaemonHealthDetailed(host, port, timeoutMs)).ok;
6737
+ }
6700
6738
 
6701
6739
  // src/delegate-authorization.ts
6702
6740
  var DEFAULT_DELEGATE_AUTHORIZATION_OPERATIONS = [
@@ -6824,32 +6862,37 @@ async function lifecycleSessionNamespacesFrom(sessionKey, event, ctx, fallback,
6824
6862
 
6825
6863
  // src/delegate-daemon-target.ts
6826
6864
  function daemonTargetFor(bridge) {
6865
+ const invalidatedCredentials = /* @__PURE__ */ new Set();
6866
+ const invalidated = (token) => invalidatedCredentials.has(`daemon configuration\0${token}`);
6827
6867
  return {
6828
6868
  host: bridge.daemonHost,
6829
6869
  port: bridge.daemonPort,
6870
+ invalidateAuthToken: (auth) => {
6871
+ invalidatedCredentials.add(`${auth.source}\0${auth.token}`);
6872
+ },
6830
6873
  resolveAuthToken: () => {
6831
- if (bridge.daemonAuthTokenOverride !== void 0) {
6874
+ if (bridge.daemonAuthTokenOverride !== void 0 && !invalidated(bridge.daemonAuthTokenOverride)) {
6832
6875
  const unit = bridge.daemonAuthUnit === void 0 ? void 0 : readUnitAuthToken(bridge.daemonAuthUnit);
6833
- if (unit?.readable === true && unit.token !== void 0) {
6876
+ if (unit?.readable === true && unit.token !== void 0 && !invalidated(unit.token)) {
6834
6877
  return { token: unit.token, source: "daemon configuration" };
6835
6878
  }
6836
- if (unit === void 0 || unit.readable === false) {
6879
+ if ((unit === void 0 || unit.readable === false) && !invalidated(bridge.daemonAuthTokenOverride)) {
6837
6880
  return { token: bridge.daemonAuthTokenOverride, source: "daemon configuration" };
6838
6881
  }
6839
6882
  if (bridge.daemonConfigPath !== void 0) {
6840
6883
  const configToken = readDaemonConfigAuthToken(bridge.daemonConfigPath);
6841
- if (configToken !== void 0) {
6884
+ if (configToken !== void 0 && !invalidated(configToken)) {
6842
6885
  return { token: configToken, source: "daemon configuration" };
6843
6886
  }
6844
6887
  }
6845
6888
  }
6846
6889
  if (bridge.daemonAuthPrefersConfig && bridge.daemonConfigPath !== void 0) {
6847
6890
  const configToken = readDaemonConfigAuthToken(bridge.daemonConfigPath);
6848
- if (configToken !== void 0) {
6891
+ if (configToken !== void 0 && !invalidated(configToken)) {
6849
6892
  return { token: configToken, source: "daemon configuration" };
6850
6893
  }
6851
6894
  }
6852
- return loadDaemonAuth(bridge.daemonConfigPath);
6895
+ return loadDaemonAuth(bridge.daemonConfigPath, invalidatedCredentials);
6853
6896
  }
6854
6897
  };
6855
6898
  }
@@ -6892,15 +6935,33 @@ async function noteRefusal(res, serviceId, pathname, source) {
6892
6935
  }
6893
6936
  return { status: res.status, body: null };
6894
6937
  }
6895
- async function postJsonWithStatus(target, serviceId, pathname, body, timeoutMs) {
6896
- const headers = { "Content-Type": "application/json" };
6938
+ async function fetchWithAuthRetry(target, pathname, timeoutMs, init = {}) {
6939
+ const deadline = Date.now() + timeoutMs;
6940
+ const request = async (auth2) => {
6941
+ const remainingMs = deadline - Date.now();
6942
+ if (remainingMs <= 0) throw new Error("daemon request deadline exceeded");
6943
+ const headers = new Headers(init.headers);
6944
+ if (auth2.token) headers.set("Authorization", `Bearer ${auth2.token}`);
6945
+ return fetch(daemonUrl(target, pathname), {
6946
+ ...init,
6947
+ headers,
6948
+ signal: AbortSignal.timeout(remainingMs)
6949
+ });
6950
+ };
6897
6951
  const auth = target.resolveAuthToken();
6898
- if (auth.token) headers.Authorization = `Bearer ${auth.token}`;
6899
- const res = await fetch(daemonUrl(target, pathname), {
6952
+ const response = await request(auth);
6953
+ if (response.status !== 401) return { response, auth };
6954
+ await response.body?.cancel();
6955
+ target.invalidateAuthToken?.(auth);
6956
+ const refreshedAuth = target.resolveAuthToken();
6957
+ if (refreshedAuth.token === auth.token) return { response, auth };
6958
+ return { response: await request(refreshedAuth), auth: refreshedAuth };
6959
+ }
6960
+ async function postJsonWithStatus(target, serviceId, pathname, body, timeoutMs) {
6961
+ const { response: res, auth } = await fetchWithAuthRetry(target, pathname, timeoutMs, {
6900
6962
  method: "POST",
6901
- headers,
6902
- body: JSON.stringify(body),
6903
- signal: AbortSignal.timeout(timeoutMs)
6963
+ headers: { "Content-Type": "application/json" },
6964
+ body: JSON.stringify(body)
6904
6965
  });
6905
6966
  if (!res.ok) return noteRefusal(res, serviceId, pathname, auth.source);
6906
6967
  return { status: res.status, body: await readJsonObject(res) };
@@ -6914,13 +6975,7 @@ async function postJson(target, serviceId, pathname, body, timeoutMs) {
6914
6975
  return response.body;
6915
6976
  }
6916
6977
  async function getJson(target, serviceId, pathname, timeoutMs) {
6917
- const headers = {};
6918
- const auth = target.resolveAuthToken();
6919
- if (auth.token) headers.Authorization = `Bearer ${auth.token}`;
6920
- const res = await fetch(daemonUrl(target, pathname), {
6921
- headers,
6922
- signal: AbortSignal.timeout(timeoutMs)
6923
- });
6978
+ const { response: res, auth } = await fetchWithAuthRetry(target, pathname, timeoutMs);
6924
6979
  if (!res.ok) return noteRefusal(res, serviceId, pathname, auth.source);
6925
6980
  return { status: res.status, body: await readJsonObject(res) };
6926
6981
  }