@memtensor/memos-cloud-openclaw-plugin 0.1.11-beta.1 → 0.1.12-beta.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.
@@ -1,11 +1,11 @@
1
1
  export const CONFIG_RESOLUTION_FIELDS = [
2
2
  { key: "baseUrl", configMode: "truthy", envVar: "MEMOS_BASE_URL", envMode: "truthy", fallbackSource: "default", uiDefaultValue: "https://memos.memtensor.cn/api/openmem/v1" },
3
3
  { key: "apiKey", configMode: "truthy", envVar: "MEMOS_API_KEY", envMode: "truthy", fallbackSource: "empty", inheritedFrom: "env", inheritedFallback: "" },
4
- { key: "userId", configMode: "truthy", envVar: "MEMOS_USER_ID", envMode: "truthy", fallbackSource: "default", uiDefaultValue: "openclaw-user" },
4
+ { key: "userId", configMode: "truthy", envVar: "MEMOS_USER_ID", envMode: "truthy", fallbackSource: "default", uiDefaultValue: "openclaw-user", inheritedFrom: "env", inheritedFallback: "openclaw-user" },
5
5
  { key: "useDirectSessionUserId", configMode: "nullish", envVar: "MEMOS_USE_DIRECT_SESSION_USER_ID", envMode: "defined", fallbackSource: "default", uiDefaultValue: false },
6
- { key: "conversationId", configMode: "truthy", envVar: "MEMOS_CONVERSATION_ID", envMode: "truthy", fallbackSource: "empty" },
7
- { key: "conversationIdPrefix", configMode: "nullish", envVar: "MEMOS_CONVERSATION_PREFIX", envMode: "defined", fallbackSource: "empty" },
8
- { key: "conversationIdSuffix", configMode: "nullish", envVar: "MEMOS_CONVERSATION_SUFFIX", envMode: "defined", fallbackSource: "empty" },
6
+ { key: "conversationId", configMode: "truthy", envVar: "MEMOS_CONVERSATION_ID", envMode: "truthy", fallbackSource: "empty", inheritedFrom: "env", inheritedFallback: "" },
7
+ { key: "conversationIdPrefix", configMode: "nullish", envVar: "MEMOS_CONVERSATION_PREFIX", envMode: "defined", fallbackSource: "empty", inheritedFrom: "env", inheritedFallback: "" },
8
+ { key: "conversationIdSuffix", configMode: "nullish", envVar: "MEMOS_CONVERSATION_SUFFIX", envMode: "defined", fallbackSource: "empty", inheritedFrom: "env", inheritedFallback: "" },
9
9
  { key: "conversationSuffixMode", configMode: "nullish", envVar: "MEMOS_CONVERSATION_SUFFIX_MODE", envMode: "truthy", fallbackSource: "default", uiDefaultValue: "none" },
10
10
  { key: "resetOnNew", configMode: "nullish", envVar: "MEMOS_CONVERSATION_RESET_ON_NEW", envMode: "defined", fallbackSource: "default", uiDefaultValue: true },
11
11
  { key: "queryPrefix", configMode: "nullish", envMode: "none", fallbackSource: "empty", inheritedValue: "" },
@@ -23,7 +23,7 @@ export const CONFIG_RESOLUTION_FIELDS = [
23
23
  { key: "knowledgebaseIds", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: [] },
24
24
  { key: "addEnabled", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: true },
25
25
  { key: "captureStrategy", configMode: "nullish", envVar: "MEMOS_CAPTURE_STRATEGY", envMode: "truthy", fallbackSource: "default", uiDefaultValue: "last_turn" },
26
- { key: "maxMessageChars", configMode: "nullish", envVar: "MEMOS_MAX_MESSAGE_CHARS", envMode: "defined", fallbackSource: "default", uiDefaultValue: 20000 },
26
+ { key: "maxMessageChars", configMode: "nullish", envVar: "MEMOS_MAX_MESSAGE_CHARS", envMode: "defined", fallbackSource: "default", uiDefaultValue: 20000, inheritedFrom: "env", inheritedFallback: 20000 },
27
27
  { key: "includeAssistant", configMode: "nullish", envVar: "MEMOS_INCLUDE_ASSISTANT", envMode: "defined", fallbackSource: "default", uiDefaultValue: true },
28
28
  { key: "tags", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: ["openclaw"] },
29
29
  { key: "info", configMode: "nullish", envMode: "none", fallbackSource: "default", uiDefaultValue: {} },
@@ -244,7 +244,7 @@ const GROUP_TRANSLATIONS = {
244
244
  const FIELD_TRANSLATIONS = {
245
245
  zh: {
246
246
  baseUrl: { label: "MemOS 地址", description: "MemOS OpenMem API 的基础地址。" },
247
- apiKey: { label: "MemOS API Key", description: "Token 鉴权密钥。选择默认时会优先读取 env 文件或进程环境变量。" },
247
+ apiKey: { label: "MemOS API Key", description: "Token 鉴权密钥。未在此处填写时,将从 .env 文件中读取。" },
248
248
  userId: { label: "用户 ID", description: "消息添加与记忆查询关联的用户唯一标识符。" },
249
249
  useDirectSessionUserId: { label: "使用 Direct 会话用户 ID", description: "可用时从 session key 的 direct 段提取用户 ID,覆盖默认 userId。" },
250
250
  conversationId: { label: "会话 ID 覆盖", description: "消息添加与记忆查询关联的会话唯一标识符;同一 ID 会被视为同一上下文。" },
@@ -20,10 +20,6 @@ const ANSI_BOLD = "\x1b[1m";
20
20
  const ANSI_CYAN = "\x1b[36m";
21
21
  const ANSI_GREEN = "\x1b[32m";
22
22
  const ANSI_RESET = "\x1b[0m";
23
- const CHILD_ENTRY = fileURLToPath(new URL("./config-ui-child.js", import.meta.url));
24
- const CHILD_ENV_KEY = "MEMOS_CLOUD_CONFIG_UI_CHILD";
25
- const HOST_EXEC_PATH_ENV_KEY = "MEMOS_CLOUD_HOST_EXEC_PATH";
26
- const HOST_ARGV_ENV_KEY = "MEMOS_CLOUD_HOST_ARGV_JSON";
27
23
  const DEFAULT_GATEWAY_READY_PORT = 18789;
28
24
 
29
25
  const FIELD_GROUPS = [
@@ -37,7 +33,7 @@ const FIELD_GROUPS = [
37
33
 
38
34
  const FIELD_DEFINITIONS = [
39
35
  { key: "baseUrl", group: "connection", type: "string", label: "MemOS Base URL", description: "Base URL for the MemOS OpenMem API.", placeholder: "https://memos.memtensor.cn/api/openmem/v1" },
40
- { key: "apiKey", group: "connection", type: "secret", label: "MemOS API Key", description: "Token auth key. Leave inherited to use env files or process env.", placeholder: "mpg-..." },
36
+ { key: "apiKey", group: "connection", type: "secret", label: "MemOS API Key", description: "Token auth key. Leave inherited to use env files.", placeholder: "mpg-..." },
41
37
  { key: "userId", group: "connection", type: "string", label: "User ID", description: "Unique identifier of the user associated with added messages and queried memories.", placeholder: "openclaw-user" },
42
38
  { key: "useDirectSessionUserId", group: "connection", type: "boolean", label: "Use Direct Session User ID", description: "Use direct-session user id from session key when available." },
43
39
  { key: "conversationId", group: "session", type: "string", label: "Conversation ID Override", description: "Unique identifier of the conversation. Reusing the same value keeps turns in the same context." },
@@ -155,12 +151,12 @@ function detectRuntimeProfile() {
155
151
  const execPath = String(process.execPath || "").toLowerCase();
156
152
 
157
153
  if (scriptPath.includes("moltbot") || execPath.includes("moltbot")) {
158
- return { id: "moltbot", displayName: "Moltbot", cliName: "moltbot", configPath: process.env.MOLTBOT_CONFIG_PATH || join(homedir(), ".moltbot", "moltbot.json") };
154
+ return { id: "moltbot", displayName: "Moltbot", cliName: "moltbot", configPath: join(homedir(), ".moltbot", "moltbot.json") };
159
155
  }
160
156
  if (scriptPath.includes("clawdbot") || execPath.includes("clawdbot")) {
161
- return { id: "clawdbot", displayName: "ClawDBot", cliName: "clawdbot", configPath: process.env.CLAWDBOT_CONFIG_PATH || join(homedir(), ".clawdbot", "clawdbot.json") };
157
+ return { id: "clawdbot", displayName: "ClawDBot", cliName: "clawdbot", configPath: join(homedir(), ".clawdbot", "clawdbot.json") };
162
158
  }
163
- return { id: "openclaw", displayName: "OpenClaw", cliName: "openclaw", configPath: process.env.OPENCLAW_CONFIG_PATH || join(homedir(), ".openclaw", "openclaw.json") };
159
+ return { id: "openclaw", displayName: "OpenClaw", cliName: "openclaw", configPath: join(homedir(), ".openclaw", "openclaw.json") };
164
160
  }
165
161
 
166
162
  function parsePositiveInteger(value, fallback) {
@@ -171,11 +167,7 @@ function parsePositiveInteger(value, fallback) {
171
167
 
172
168
  function resolveGatewayReadyProbeTarget(rootConfig = {}) {
173
169
  const gateway = isPlainObject(rootConfig?.gateway) ? rootConfig.gateway : {};
174
- const envPort =
175
- process.env.OPENCLAW_GATEWAY_PORT ||
176
- process.env.MOLTBOT_GATEWAY_PORT ||
177
- process.env.CLAWDBOT_GATEWAY_PORT;
178
- const port = parsePositiveInteger(gateway.port ?? envPort, DEFAULT_GATEWAY_READY_PORT);
170
+ const port = parsePositiveInteger(gateway.port, DEFAULT_GATEWAY_READY_PORT);
179
171
  const bind = typeof gateway.bind === "string" ? gateway.bind.trim().toLowerCase() : "";
180
172
  const customBindHost = typeof gateway.customBindHost === "string" ? gateway.customBindHost.trim() : "";
181
173
  const host = bind === "custom" && customBindHost ? customBindHost : "127.0.0.1";
@@ -492,194 +484,7 @@ function renderAppJs(service) {
492
484
  });
493
485
  }
494
486
 
495
- function installCleanupHooks() {
496
- const globalState = getGlobalState();
497
- if (globalState.cleanupInstalled) return;
498
- globalState.cleanupInstalled = true;
499
-
500
- const terminateChildImmediately = () => {
501
- const { child } = getGlobalState();
502
- if (!child?.pid) return;
503
-
504
- if (process.platform === "win32") {
505
- exec(`taskkill /pid ${child.pid} /T /F`, () => {});
506
- return;
507
- }
508
-
509
- try {
510
- process.kill(-child.pid, "SIGKILL");
511
- } catch {
512
- try {
513
- child.kill("SIGKILL");
514
- } catch {
515
- // Ignore kill races during shutdown.
516
- }
517
- }
518
- };
519
-
520
- const closeSynchronously = () => {
521
- const globalState = getGlobalState();
522
- globalState.shuttingDown = true;
523
- const { service, child } = globalState;
524
- if (service?.server) {
525
- try {
526
- service.server.close();
527
- } catch {
528
- // Ignore close races during shutdown.
529
- }
530
- }
531
- if (child?.pid) {
532
- terminateChildImmediately();
533
- }
534
- };
535
-
536
- for (const signal of ["SIGINT", "SIGTERM", "SIGBREAK"]) {
537
- process.once(signal, closeSynchronously);
538
- }
539
- process.once("beforeExit", closeSynchronously);
540
- process.once("exit", closeSynchronously);
541
- }
542
-
543
- function installGatewayRestartHook(log = console) {
544
- const globalState = getGlobalState();
545
- if (globalState.restartHookInstalled || process.env[CHILD_ENV_KEY] === "1") return;
546
- globalState.restartHookInstalled = true;
547
-
548
- process.on("SIGUSR1", () => {
549
- const state = getGlobalState();
550
- if (state.shuttingDown) return;
551
- if (state.recyclePromise) return;
552
-
553
- log.info?.("[memos-cloud] Gateway restart detected; recycling config UI service.");
554
- state.recyclePromise = closeConfigUiService({ clearRestartPending: false })
555
- .catch((error) => {
556
- log.warn?.(`[memos-cloud] Failed to close config UI during gateway restart: ${String(error?.message || error)}`);
557
- })
558
- .then(
559
- () =>
560
- new Promise((resolve) => {
561
- setTimeout(resolve, 500);
562
- }),
563
- )
564
- .then(() => {
565
- const latestState = getGlobalState();
566
- if (latestState.shuttingDown) return null;
567
- latestState.recyclePromise = null;
568
- return waitForGatewayReady({}, log, { timeoutMs: 15000, intervalMs: 250 });
569
- })
570
- .then((ready) => {
571
- if (!ready) return null;
572
- log.info?.("[memos-cloud] Restarting config UI service after gateway recycle.");
573
- return ensureConfigUiService(log);
574
- })
575
- .catch((error) => {
576
- log.warn?.(`[memos-cloud] Failed to restart config UI after gateway recycle: ${String(error?.message || error)}`);
577
- return null;
578
- })
579
- .finally(() => {
580
- const latestState = getGlobalState();
581
- latestState.recyclePromise = null;
582
- });
583
- });
584
- }
585
-
586
- function markRestartPending(durationMs = 15000) {
587
- const globalState = getGlobalState();
588
- globalState.restartPending = true;
589
- if (globalState.restartTimer) {
590
- clearTimeout(globalState.restartTimer);
591
- }
592
- globalState.restartTimer = setTimeout(() => {
593
- const latestState = getGlobalState();
594
- latestState.restartPending = false;
595
- latestState.restartTimer = null;
596
- }, durationMs);
597
- }
598
-
599
- function clearRestartPending() {
600
- const globalState = getGlobalState();
601
- globalState.restartPending = false;
602
- if (globalState.restartTimer) {
603
- clearTimeout(globalState.restartTimer);
604
- globalState.restartTimer = null;
605
- }
606
- }
607
-
608
- function readHostLaunchContext() {
609
- const execPath = process.env[HOST_EXEC_PATH_ENV_KEY] || process.execPath;
610
- const fallbackArgv = Array.isArray(process.argv) ? [...process.argv] : [];
611
-
612
- try {
613
- const parsed = JSON.parse(process.env[HOST_ARGV_ENV_KEY] || "null");
614
- if (Array.isArray(parsed) && parsed.length > 0) {
615
- return { execPath, argv: parsed.map((value) => String(value ?? "")) };
616
- }
617
- } catch {
618
- // Ignore malformed env and fall back to the current process argv.
619
- }
620
-
621
- return { execPath, argv: fallbackArgv.map((value) => String(value ?? "")) };
622
- }
623
-
624
- function buildGatewayRestartLaunch() {
625
- const { execPath, argv } = readHostLaunchContext();
626
- const args = argv.slice(1);
627
- const gatewayIndex = args.findIndex((value) => value.toLowerCase() === "gateway");
628
- const prefixArgs = gatewayIndex >= 0 ? args.slice(0, gatewayIndex) : args;
629
- return {
630
- filePath: execPath,
631
- args: [...prefixArgs, "gateway", "restart"],
632
- };
633
- }
634
-
635
- function requestGatewayRestartFromParent(log) {
636
- if (process.env[CHILD_ENV_KEY] !== "1") return false;
637
- if (typeof process.send !== "function") return false;
638
-
639
- try {
640
- process.send({ type: "request-restart" });
641
- log.info?.("[memos-cloud] Requested in-process gateway restart from parent process.");
642
- return true;
643
- } catch (error) {
644
- log.warn?.(`[memos-cloud] Failed to request in-process gateway restart: ${String(error?.message || error)}`);
645
- return false;
646
- }
647
- }
648
-
649
- function launchGatewayRestartInBackground(service, log) {
650
- try {
651
- const launch = buildGatewayRestartLaunch();
652
- const child = spawn(launch.filePath, launch.args, {
653
- detached: true,
654
- stdio: "ignore",
655
- windowsHide: process.platform === "win32",
656
- shell: false,
657
- cwd: process.cwd(),
658
- env: { ...process.env, [CHILD_ENV_KEY]: "0" },
659
- });
660
- child.once("error", (error) => {
661
- clearRestartPending();
662
- log.warn?.(
663
- `[memos-cloud] Restart launcher failed for ${service.profile.displayName} gateway: ${String(error?.message || error)}`,
664
- );
665
- });
666
- child.once("exit", (code, signal) => {
667
- if (code && code !== 0) {
668
- clearRestartPending();
669
- log.warn?.(
670
- `[memos-cloud] Restart launcher exited early for ${service.profile.displayName} gateway (code=${code}, signal=${signal ?? "null"}).`,
671
- );
672
- }
673
- });
674
- child.unref();
675
- log.info?.(
676
- `[memos-cloud] Background restart command launched for ${service.profile.displayName} gateway using ${launch.filePath} ${launch.args.join(" ")}`,
677
- );
678
- } catch (error) {
679
- log.warn?.(`[memos-cloud] Failed to launch restart for ${service.profile.displayName} gateway: ${String(error)}`);
680
- throw error;
681
- }
682
- }
487
+ // Legacy restart functions removed for sandbox compliance
683
488
 
684
489
  function listenOnPort(server, host, port) {
685
490
  return new Promise((resolve, reject) => {
@@ -745,7 +550,6 @@ function isAuthorized(req, service) {
745
550
  }
746
551
 
747
552
  async function createService(log) {
748
- installCleanupHooks();
749
553
  const profile = detectRuntimeProfile();
750
554
  const token = randomBytes(24).toString("hex");
751
555
  const bootId = randomBytes(10).toString("hex");
@@ -828,27 +632,7 @@ async function createService(log) {
828
632
  return;
829
633
  }
830
634
 
831
- if (requestUrl.pathname === "/api/restart" && req.method === "POST") {
832
- const globalState = getGlobalState();
833
- if (globalState.restartPending) {
834
- sendJson(res, 200, { ok: true, deduped: true });
835
- return;
836
- }
837
-
838
- markRestartPending();
839
- sendJson(res, 200, { ok: true });
840
- setTimeout(() => {
841
- try {
842
- if (requestGatewayRestartFromParent(log)) return;
843
- launchGatewayRestartInBackground(service, log);
844
- } catch (error) {
845
- clearRestartPending();
846
- log.warn?.(`[memos-cloud] Restart launch failed: ${String(error?.message || error)}`);
847
- }
848
- }, 120);
849
- return;
850
- }
851
-
635
+ // Legacy `/api/restart` has been entirely removed
852
636
  sendText(res, 404, "Not found");
853
637
  return;
854
638
  }
@@ -880,19 +664,11 @@ async function createService(log) {
880
664
  }
881
665
 
882
666
  export function ensureConfigUiService(log = console) {
883
- if (process.env[CHILD_ENV_KEY] === "1") {
884
- return Promise.resolve(null);
885
- }
886
667
  if (!shouldStartConfigUi()) {
887
668
  return Promise.resolve(null);
888
669
  }
889
- installCleanupHooks();
890
- installGatewayRestartHook(log);
891
670
 
892
671
  const globalState = getGlobalState();
893
- if (globalState.recyclePromise) {
894
- return globalState.recyclePromise.then(() => ensureConfigUiService(log));
895
- }
896
672
  if (globalState.promise) return globalState.promise;
897
673
 
898
674
  globalState.promise = createService(log)
@@ -911,14 +687,6 @@ export function ensureConfigUiService(log = console) {
911
687
 
912
688
  export async function closeConfigUiService(options = {}) {
913
689
  const globalState = getGlobalState();
914
- const clearRestartPending = options.clearRestartPending !== false;
915
- if (clearRestartPending) {
916
- if (globalState.restartTimer) {
917
- clearTimeout(globalState.restartTimer);
918
- globalState.restartTimer = null;
919
- }
920
- globalState.restartPending = false;
921
- }
922
690
 
923
691
  if (!globalState.service) {
924
692
  globalState.promise = null;