@memtensor/memos-cloud-openclaw-plugin 0.1.8-beta.1 → 0.1.8-beta.3

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.
@@ -2,7 +2,7 @@
2
2
  "id": "memos-cloud-openclaw-plugin",
3
3
  "name": "MemOS Cloud OpenClaw Plugin",
4
4
  "description": "MemOS Cloud recall + add memory via lifecycle hooks",
5
- "version": "0.1.8-beta.1",
5
+ "version": "0.1.8-beta.3",
6
6
  "kind": "lifecycle",
7
7
  "main": "./index.js",
8
8
  "configSchema": {
package/index.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  USER_QUERY_MARKER,
8
8
  searchMemory,
9
9
  } from "./lib/memos-cloud-api.js";
10
+ import { checkUpdate } from "./lib/check-update.js";
10
11
  let lastCaptureTime = 0;
11
12
  const conversationCounters = new Map();
12
13
  const API_KEY_HELP_URL = "https://memos-dashboard.openmem.net/cn/apikeys/";
@@ -205,6 +206,9 @@ export default {
205
206
  const cfg = buildConfig(api.pluginConfig);
206
207
  const log = api.logger ?? console;
207
208
 
209
+ // Call update check asynchronously. The interval control is inside checkUpdate
210
+ checkUpdate(log);
211
+
208
212
  if (!cfg.envFileStatus?.found) {
209
213
  const searchPaths = cfg.envFileStatus?.searchPaths?.join(", ") ?? ENV_FILE_SEARCH_HINTS.join(", ");
210
214
  log.warn?.(`[memos-cloud] No .env found in ${searchPaths}; falling back to process env or plugin config.`);
@@ -229,6 +233,7 @@ export default {
229
233
  }
230
234
 
231
235
  api.on("before_agent_start", async (event, ctx) => {
236
+ checkUpdate(log);
232
237
  if (!cfg.recallEnabled) return;
233
238
  if (!event?.prompt || event.prompt.length < 3) return;
234
239
  if (!cfg.apiKey) {
@@ -0,0 +1,197 @@
1
+ import https from "https";
2
+ import fs from "fs";
3
+ import { exec } from "child_process";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+ import os from "os";
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+
10
+ const CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
11
+ const PLUGIN_NAME = "@memtensor/memos-cloud-openclaw-plugin";
12
+ const CHECK_FILE = path.join(os.tmpdir(), "memos_openclaw_update_check.json");
13
+
14
+ const ANSI = {
15
+ RESET: "\x1b[0m",
16
+ GREEN: "\x1b[32m",
17
+ YELLOW: "\x1b[33m",
18
+ CYAN: "\x1b[36m",
19
+ RED: "\x1b[31m"
20
+ };
21
+
22
+
23
+ function getPackageVersion() {
24
+ try {
25
+ const pkgPath = path.join(__dirname, "..", "package.json");
26
+ const pkgData = fs.readFileSync(pkgPath, "utf-8");
27
+ const pkg = JSON.parse(pkgData);
28
+ return pkg.version;
29
+ } catch (err) {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ function getLatestVersion(log) {
35
+ return new Promise((resolve, reject) => {
36
+ const req = https.get(
37
+ `https://registry.npmjs.org/${PLUGIN_NAME}/latest`,
38
+ { timeout: 5000 },
39
+ (res) => {
40
+ if (res.statusCode !== 200) {
41
+ req.destroy();
42
+ return reject(new Error(`Failed to fetch version, status: ${res.statusCode}`));
43
+ }
44
+
45
+ let body = "";
46
+ res.on("data", (chunk) => {
47
+ body += chunk;
48
+ });
49
+
50
+ res.on("end", () => {
51
+ try {
52
+ const data = JSON.parse(body);
53
+ resolve(data.version);
54
+ } catch (err) {
55
+ reject(err);
56
+ }
57
+ });
58
+ }
59
+ );
60
+
61
+ req.on("error", (err) => {
62
+ reject(err);
63
+ });
64
+
65
+ req.on("timeout", () => {
66
+ req.destroy();
67
+ reject(new Error("Timeout getting latest version"));
68
+ });
69
+ });
70
+ }
71
+
72
+ function compareVersions(v1, v2) {
73
+ // Split pre-release tags (e.g. 0.1.8-beta.1 -> "0.1.8" and "beta.1")
74
+ const split1 = v1.split("-");
75
+ const split2 = v2.split("-");
76
+ const parts1 = split1[0].split(".").map(Number);
77
+ const parts2 = split2[0].split(".").map(Number);
78
+
79
+ // Compare major.minor.patch
80
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
81
+ const p1 = parts1[i] || 0;
82
+ const p2 = parts2[i] || 0;
83
+ if (p1 > p2) return 1;
84
+ if (p1 < p2) return -1;
85
+ }
86
+
87
+ // If base versions are equal, compare pre-release tags.
88
+ // A version WITH a pre-release tag is LOWER than a version WITHOUT one.
89
+ // e.g. 0.1.8-beta is less than 0.1.8. 0.1.8 is the final release.
90
+ const hasPre1 = split1.length > 1;
91
+ const hasPre2 = split2.length > 1;
92
+
93
+ if (hasPre1 && !hasPre2) return -1; // v1 is a beta, v2 is a full release
94
+ if (!hasPre1 && hasPre2) return 1; // v1 is a full release, v2 is a beta
95
+ if (!hasPre1 && !hasPre2) return 0; // both are full releases and equal
96
+
97
+ // If both are pre-releases, do a basic string compare on the tag
98
+ // "alpha" < "beta" < "rc"
99
+ if (split1[1] > split2[1]) return 1;
100
+ if (split1[1] < split2[1]) return -1;
101
+
102
+ return 0;
103
+ }
104
+
105
+ export async function checkUpdate(log) {
106
+ // Only check for updates when the gateway is starting.
107
+ // When starting the gateway, 'gateway' is in process.argv.
108
+ log.warn(JSON.stringify(process.argv))
109
+ const isGateway = process.argv.includes("gateway");
110
+
111
+ if (!isGateway) {
112
+ return;
113
+ }
114
+
115
+ const now = Date.now();
116
+ let lastCheckTime = 0;
117
+ try {
118
+ if (fs.existsSync(CHECK_FILE)) {
119
+ const data = JSON.parse(fs.readFileSync(CHECK_FILE, "utf-8"));
120
+ lastCheckTime = data.time || 0;
121
+ }
122
+ } catch (e) {}
123
+
124
+ if (now - lastCheckTime < CHECK_INTERVAL) {
125
+ return;
126
+ }
127
+
128
+ const currentVersion = getPackageVersion();
129
+ if (!currentVersion) {
130
+ return;
131
+ }
132
+
133
+ try {
134
+ const latestVersion = await getLatestVersion(log);
135
+
136
+ // Normal version check
137
+ if (compareVersions(latestVersion, currentVersion) <= 0) {
138
+ return;
139
+ }
140
+
141
+ log.info?.(`${ANSI.YELLOW}[memos-cloud] Update available: ${currentVersion} -> ${latestVersion}. Updating in background...${ANSI.RESET}`);
142
+
143
+
144
+ let dotCount = 0;
145
+ const progressInterval = setInterval(() => {
146
+ dotCount++;
147
+ const dots = ".".repeat(dotCount % 4);
148
+ log.info?.(`${ANSI.YELLOW}[memos-cloud] Update in progress for memos-cloud-openclaw-plugin${dots}${ANSI.RESET}`);
149
+ }, 5000); // Log every 5 seconds to show it's still alive
150
+
151
+ const cliName = (() => {
152
+ // Check the full path of the entry script (e.g., .../moltbot/bin/index.js) or the executable
153
+ const scriptPath = process.argv[1] ? process.argv[1].toLowerCase() : "";
154
+ const execPath = process.execPath ? process.execPath.toLowerCase() : "";
155
+
156
+ if (scriptPath.includes("moltbot") || execPath.includes("moltbot")) return "moltbot";
157
+ if (scriptPath.includes("clawdbot") || execPath.includes("clawdbot")) return "clawdbot";
158
+ return "openclaw";
159
+ })();
160
+
161
+ exec(`${cliName} plugins update memos-cloud-openclaw-plugin`, (error, stdout, stderr) => {
162
+ clearInterval(progressInterval);
163
+
164
+ const combinedOutput = `${outText} ${errText}`.toLowerCase();
165
+ const requiresRestart = combinedOutput.includes("restart") ||
166
+ combinedOutput.includes("already at");
167
+
168
+ // ONLY write the 24-hour throttle if the CLI actually tells us to restart the gateway
169
+ // This is the condition that indicates a loop might happen (because the CLI modified openclaw.json)
170
+ // Or if the CLI outputs 'already at', meaning the update was suppressed due to fixed spec.
171
+ if (requiresRestart) {
172
+ try {
173
+ fs.writeFileSync(CHECK_FILE, JSON.stringify({ time: now }));
174
+ } catch (e) {}
175
+ }
176
+
177
+ if (outText) log.info?.(`${ANSI.CYAN}[${cliName}-cli]${ANSI.RESET}\n${outText}`);
178
+ if (errText) log.warn?.(`${ANSI.RED}[${cliName}-cli]${ANSI.RESET}\n${errText}`);
179
+
180
+ // Wait for a brief moment to let file system sync if needed
181
+ setTimeout(() => {
182
+ const postUpdateVersion = getPackageVersion();
183
+ const actuallyUpdated = (postUpdateVersion === latestVersion) && (postUpdateVersion !== currentVersion);
184
+
185
+ if (error || !actuallyUpdated) {
186
+ const reason = error ? "Command exited with error" : "Version did not change after update command";
187
+ log.warn?.(`${ANSI.RED}[memos-cloud] Auto-update failed (${reason}). Please refer to the CLI logs above, or run manually: ${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`);
188
+ } else {
189
+ log.info?.(`${ANSI.GREEN}[memos-cloud] Successfully updated to version ${latestVersion}. Please restart the gateway to apply changes.${ANSI.RESET}`);
190
+ }
191
+ }, 1000); // Small 1-second buffer for file systems
192
+ });
193
+
194
+ } catch (error) {
195
+ // Silently handle errors
196
+ }
197
+ }
@@ -2,7 +2,7 @@
2
2
  "id": "memos-cloud-openclaw-plugin",
3
3
  "name": "MemOS Cloud OpenClaw Plugin",
4
4
  "description": "MemOS Cloud recall + add memory via lifecycle hooks",
5
- "version": "0.1.8-beta.1",
5
+ "version": "0.1.8-beta.3",
6
6
  "kind": "lifecycle",
7
7
  "main": "./index.js",
8
8
  "configSchema": {
@@ -2,7 +2,7 @@
2
2
  "id": "memos-cloud-openclaw-plugin",
3
3
  "name": "MemOS Cloud OpenClaw Plugin",
4
4
  "description": "MemOS Cloud recall + add memory via lifecycle hooks",
5
- "version": "0.1.8-beta.1",
5
+ "version": "0.1.8-beta.3",
6
6
  "kind": "lifecycle",
7
7
  "main": "./index.js",
8
8
  "configSchema": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memtensor/memos-cloud-openclaw-plugin",
3
- "version": "0.1.8-beta.1",
3
+ "version": "0.1.8-beta.3",
4
4
  "description": "OpenClaw lifecycle plugin for MemOS Cloud (add + recall memory)",
5
5
  "scripts": {
6
6
  "sync-version": "node scripts/sync-version.js",