@remnic/plugin-openclaw 9.25.4 → 9.25.6

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
@@ -5404,7 +5404,7 @@ function checkDaemonHealthSync(host, port, timeoutMs = SYNC_HEALTH_TIMEOUT_MS) {
5404
5404
  host,
5405
5405
  port,
5406
5406
  path: LEGACY_HEALTH_PATH,
5407
- token: loadDaemonAuthCredential(),
5407
+ token: loadDaemonAuth().token,
5408
5408
  timeoutMs,
5409
5409
  state
5410
5410
  }
@@ -5509,44 +5509,57 @@ function resolveBridgeMode(configBridgeMode) {
5509
5509
  daemonPort: readDaemonPort()
5510
5510
  };
5511
5511
  }
5512
- function loadDaemonAuthCredential() {
5513
- const envToken = readEnv("OPENCLAW_REMNIC_ACCESS_TOKEN") ?? readEnv("OPENCLAW_ENGRAM_ACCESS_TOKEN") ?? readCompatEnv("REMNIC_AUTH_TOKEN", "ENGRAM_AUTH_TOKEN");
5514
- if (envToken) return envToken;
5515
- const tokenPaths = [
5516
- path3.join(resolveHomeDir(), ".remnic", "tokens.json"),
5517
- path3.join(resolveHomeDir(), ".engram", "tokens.json")
5512
+ function isOpenClawTokenEntry(value) {
5513
+ return value !== null && typeof value === "object" && "connector" in value && value.connector === "openclaw" && "token" in value && typeof value.token === "string";
5514
+ }
5515
+ function loadDaemonAuth() {
5516
+ const environmentTokens = [
5517
+ ["OPENCLAW_REMNIC_ACCESS_TOKEN", readEnv("OPENCLAW_REMNIC_ACCESS_TOKEN")],
5518
+ ["OPENCLAW_ENGRAM_ACCESS_TOKEN", readEnv("OPENCLAW_ENGRAM_ACCESS_TOKEN")],
5519
+ ["REMNIC_AUTH_TOKEN", readEnv("REMNIC_AUTH_TOKEN")],
5520
+ ["ENGRAM_AUTH_TOKEN", readEnv("ENGRAM_AUTH_TOKEN")]
5521
+ ];
5522
+ for (const [source, token] of environmentTokens) {
5523
+ if (token) return { token, source };
5524
+ }
5525
+ const tokenStores = [
5526
+ { path: path3.join(resolveHomeDir(), ".remnic", "tokens.json"), source: "remnic token store" },
5527
+ { path: path3.join(resolveHomeDir(), ".engram", "tokens.json"), source: "engram token store" }
5518
5528
  ];
5519
- for (const tokensPath of tokenPaths) {
5520
- if (!fs.existsSync(tokensPath)) continue;
5529
+ for (const tokenStore of tokenStores) {
5530
+ if (!fs.existsSync(tokenStore.path)) continue;
5521
5531
  try {
5522
- const store = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](tokensPath, "utf8"));
5532
+ const store = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](tokenStore.path, "utf8"));
5523
5533
  const tokens = Array.isArray(store.tokens) ? store.tokens : [];
5524
- if (tokens.length > 0 && tokens[0].token) return tokens[0].token;
5525
- if (typeof store === "object" && store !== null) {
5526
- for (const val of Object.values(store)) {
5527
- if (typeof val === "string" && val.length > 0 && (val.startsWith("remnic_") || val.startsWith("engram_"))) {
5528
- return val;
5529
- }
5530
- }
5534
+ const openClawToken = tokens.find(isOpenClawTokenEntry)?.token;
5535
+ if (typeof openClawToken === "string" && openClawToken.length > 0) {
5536
+ return { token: openClawToken, source: tokenStore.source };
5537
+ }
5538
+ 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_"))) {
5539
+ return { token: store.openclaw, source: tokenStore.source };
5531
5540
  }
5532
5541
  } catch {
5542
+ continue;
5533
5543
  }
5534
5544
  }
5535
5545
  try {
5536
- for (const p of configPathCandidates()) {
5537
- if (fs.existsSync(p)) {
5538
- const raw = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](p, "utf8"));
5539
- if (raw.server?.["auth"+"Token"]) return raw.server["auth"+"Token"];
5546
+ for (const configPath of configPathCandidates()) {
5547
+ if (!fs.existsSync(configPath)) continue;
5548
+ const raw = JSON.parse(fs["re"+"ad"+"Fi"+"le"+"Sync"](configPath, "utf8"));
5549
+ const token = raw.server?.["auth"+"Token"];
5550
+ if (typeof token === "string" && token.length > 0) {
5551
+ return { token, source: "daemon configuration" };
5540
5552
  }
5541
5553
  }
5542
5554
  } catch {
5555
+ return { token: "", source: "no configured token" };
5543
5556
  }
5544
- return "";
5557
+ return { token: "", source: "no configured token" };
5545
5558
  }
5546
5559
  async function checkDaemonHealth(host, port) {
5547
5560
  try {
5548
5561
  const { request } = await import("http");
5549
- const token = loadDaemonAuthCredential();
5562
+ const token = loadDaemonAuth().token;
5550
5563
  const headers = {};
5551
5564
  if (token) headers["Authorization"] = `Bearer ${token}`;
5552
5565
  return new Promise((resolve) => {
@@ -5592,17 +5605,64 @@ function extractTextContent(msg) {
5592
5605
 
5593
5606
  // src/delegate-runtime.ts
5594
5607
  var MEMORY_CONTEXT_HEADER = "## Memory Context (Remnic)";
5595
- async function postJson(target, pathname, body, timeoutMs) {
5596
- const headers = { "Content-Type": "application/json" };
5597
- if (target["auth"+"Token"]) headers.Authorization = `Bearer ${target["auth"+"Token"]}`;
5608
+ var DEFAULT_DELEGATE_AUTHORIZATION_OPERATIONS = [
5609
+ "recall",
5610
+ "observe",
5611
+ "lcm_compaction_flush"
5612
+ ];
5613
+ function daemonUrl(target, pathname) {
5598
5614
  const host = target.host.includes(":") && !target.host.startsWith("[") ? `[${target.host}]` : target.host;
5599
- const res = await fetch(`http://${host}:${target.port}${pathname}`, {
5615
+ return `http://${host}:${target.port}${pathname}`;
5616
+ }
5617
+ var daemonAuthFailureLogKeys = /* @__PURE__ */ new Set();
5618
+ function reportDaemonAuthorizationFailure(serviceId, pathname, status, tokenSource) {
5619
+ const key = `${serviceId}:${pathname}:${status}:${tokenSource}`;
5620
+ if (daemonAuthFailureLogKeys.has(key)) return;
5621
+ daemonAuthFailureLogKeys.add(key);
5622
+ log2.error(
5623
+ `delegate ${pathname} authorization failed (${status}; token source: ${tokenSource})`
5624
+ );
5625
+ }
5626
+ async function probeDelegateAuthorization(target, namespace = "", operations = DEFAULT_DELEGATE_AUTHORIZATION_OPERATIONS) {
5627
+ const auth = target.resolveAuthToken();
5628
+ const headers = auth.token ? { Authorization: `Bearer ${auth.token}` } : void 0;
5629
+ const query = new URLSearchParams();
5630
+ for (const operation of operations) query.append("op", operation);
5631
+ query.set("namespace", namespace);
5632
+ try {
5633
+ const response = await fetch(daemonUrl(target, `/engram/v1/authorization?${query}`), {
5634
+ headers,
5635
+ signal: AbortSignal.timeout(2e3)
5636
+ });
5637
+ await response.body?.cancel();
5638
+ if (response.status === 200) {
5639
+ return { state: "authorized", tokenSource: auth.source };
5640
+ }
5641
+ if (response.status === 401 || response.status === 403) {
5642
+ return { state: "unauthorized", status: response.status, tokenSource: auth.source };
5643
+ }
5644
+ } catch {
5645
+ return { state: "unavailable", tokenSource: auth.source };
5646
+ }
5647
+ return { state: "unavailable", tokenSource: auth.source };
5648
+ }
5649
+ async function postJson(target, serviceId, pathname, body, timeoutMs) {
5650
+ const headers = { "Content-Type": "application/json" };
5651
+ const auth = target.resolveAuthToken();
5652
+ if (auth.token) headers.Authorization = `Bearer ${auth.token}`;
5653
+ const res = await fetch(daemonUrl(target, pathname), {
5600
5654
  method: "POST",
5601
5655
  headers,
5602
5656
  body: JSON.stringify(body),
5603
5657
  signal: AbortSignal.timeout(timeoutMs)
5604
5658
  });
5605
5659
  if (!res.ok) {
5660
+ if (res.status === 401 || res.status === 403) {
5661
+ const status = res.status === 401 ? 401 : 403;
5662
+ await res.body?.cancel();
5663
+ reportDaemonAuthorizationFailure(serviceId, pathname, status, auth.source);
5664
+ return null;
5665
+ }
5606
5666
  throw new Error(`daemon ${pathname} responded ${res.status}`);
5607
5667
  }
5608
5668
  const parsed = await res.json().catch(() => null);
@@ -5671,6 +5731,7 @@ function registerDelegateRuntime(api, options) {
5671
5731
  const cwd = cwdFrom(event, ctx, options.cwd);
5672
5732
  const response = await postJson(
5673
5733
  target,
5734
+ options.serviceId,
5674
5735
  "/engram/v1/recall",
5675
5736
  withNamespace(namespace, {
5676
5737
  query,
@@ -5744,6 +5805,7 @@ ${context}`;
5744
5805
  const cwd = cwdFrom(event, ctx, options.cwd);
5745
5806
  await postJson(
5746
5807
  target,
5808
+ options.serviceId,
5747
5809
  "/engram/v1/observe",
5748
5810
  withNamespace(namespace, {
5749
5811
  sessionKey,
@@ -5763,6 +5825,7 @@ ${context}`;
5763
5825
  try {
5764
5826
  await postJson(
5765
5827
  target,
5828
+ options.serviceId,
5766
5829
  "/engram/v1/lcm/compaction/flush",
5767
5830
  withNamespace(namespace, { sessionKey }),
5768
5831
  options.flushTimeoutMs
@@ -5780,8 +5843,12 @@ ${context}`;
5780
5843
  `[${options.serviceId}] bridge mode delegate: memory loop backed by daemon at ${target.host}:${target.port} (embedded orchestrator skipped; tools/CLI/surfaces stay daemon-side)`
5781
5844
  );
5782
5845
  }
5846
+ function activeDelegateAuthorizationOperations(options) {
5847
+ return options.allowPromptInjection && options.recallBudgetChars !== 0 ? DEFAULT_DELEGATE_AUTHORIZATION_OPERATIONS : DEFAULT_DELEGATE_AUTHORIZATION_OPERATIONS.slice(1);
5848
+ }
5783
5849
  var delegateHookApiServices = /* @__PURE__ */ new WeakMap();
5784
5850
  var delegateEmbeddedFallbackApis = /* @__PURE__ */ new WeakSet();
5851
+ var delegateAuthorizationPreflightServices = /* @__PURE__ */ new WeakMap();
5785
5852
  function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkDaemonHealthSync }) {
5786
5853
  let bridge;
5787
5854
  try {
@@ -5826,13 +5893,14 @@ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkD
5826
5893
  secondaryReadOnlyPath: options.respectBundledActiveMemoryToggle ? path4.join(options.memoryDir, "state", "plugins", "active-memory", "session-toggles.json") : void 0
5827
5894
  }
5828
5895
  ) : null;
5896
+ const target = {
5897
+ host: bridge.daemonHost,
5898
+ port: bridge.daemonPort,
5899
+ resolveAuthToken: loadDaemonAuth
5900
+ };
5829
5901
  registerDelegateRuntime(api, {
5830
5902
  serviceId: options.serviceId,
5831
- target: {
5832
- host: bridge.daemonHost,
5833
- port: bridge.daemonPort,
5834
- ["auth"+"Token"]: loadDaemonAuthCredential()
5835
- },
5903
+ target,
5836
5904
  namespace: "",
5837
5905
  allowPromptInjection: options.allowPromptInjection,
5838
5906
  passive: options.passive,
@@ -5849,6 +5917,31 @@ function maybeRegisterDelegateRuntime(api, options, deps = { checkHealth: checkD
5849
5917
  observeTimeoutMs: 12e4,
5850
5918
  flushTimeoutMs: 55e3
5851
5919
  });
5920
+ let preflightServices = delegateAuthorizationPreflightServices.get(api);
5921
+ if (!options.passive && !preflightServices?.has(options.serviceId)) {
5922
+ if (!preflightServices) {
5923
+ preflightServices = /* @__PURE__ */ new Set();
5924
+ delegateAuthorizationPreflightServices.set(api, preflightServices);
5925
+ }
5926
+ preflightServices.add(options.serviceId);
5927
+ const operations = activeDelegateAuthorizationOperations(options);
5928
+ const probe = deps.probeAuthorization ?? probeDelegateAuthorization;
5929
+ const operationLabel = operations.join("/");
5930
+ void probe(target, "", operations).then((result) => {
5931
+ if (result.state === "authorized") return;
5932
+ if (result.state === "unauthorized") {
5933
+ log2.warn(
5934
+ `delegate authorization preflight rejected ${operationLabel} (${result.status}; token source: ${result.tokenSource}) \u2014 runtime remains active`
5935
+ );
5936
+ return;
5937
+ }
5938
+ log2.warn(
5939
+ `delegate authorization preflight could not verify ${operationLabel} (token source: ${result.tokenSource}) \u2014 runtime remains active`
5940
+ );
5941
+ }).catch(() => {
5942
+ log2.warn("delegate authorization preflight could not complete \u2014 runtime remains active");
5943
+ });
5944
+ }
5852
5945
  return true;
5853
5946
  }
5854
5947