@memtensor/memos-cloud-openclaw-plugin 0.1.8-beta.7 → 0.1.8-beta.9

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,14 +2,31 @@ import https from "https";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
  import { fileURLToPath } from "url";
5
- import { spawn } from "child_process";
5
+ import { spawn, exec } from "child_process";
6
6
  import os from "os";
7
7
 
8
+ /**
9
+ * Kill a spawned child process and its entire process tree.
10
+ */
11
+ function killProcessTree(child) {
12
+ try {
13
+ if (process.platform === "win32") {
14
+ exec(`taskkill /pid ${child.pid} /T /F`, () => {});
15
+ } else {
16
+ // On Unix, kill the process group
17
+ process.kill(-child.pid, "SIGKILL");
18
+ }
19
+ } catch (e) {
20
+ // Fallback: try the basic kill
21
+ try { child.kill("SIGKILL"); } catch (_) {}
22
+ }
23
+ }
24
+
8
25
  let isUpdating = false;
9
26
 
10
27
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
28
 
12
- const CHECK_INTERVAL = 5 * 60 * 1000; // 12 hours check interval
29
+ const CHECK_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours check interval
13
30
  const UPDATE_TIMEOUT = 3 * 60 * 1000; // 3 minutes timeout for the CLI update command to finish
14
31
  const PLUGIN_NAME = "@memtensor/memos-cloud-openclaw-plugin";
15
32
  const CHECK_FILE = path.join(os.tmpdir(), "memos_openclaw_update_check.json");
@@ -22,6 +39,36 @@ const ANSI = {
22
39
  RED: "\x1b[31m"
23
40
  };
24
41
 
42
+ function canExecuteCli(cliName, timeoutMs = 8000) {
43
+ return new Promise((resolve) => {
44
+ let done = false;
45
+ const child = spawn(cliName, ["--version"], { shell: true });
46
+ const timer = setTimeout(() => {
47
+ if (done) return;
48
+ done = true;
49
+ killProcessTree(child);
50
+ resolve({ ok: false, reason: `timeout after ${timeoutMs}ms` });
51
+ }, timeoutMs);
52
+
53
+ child.on("error", (err) => {
54
+ if (done) return;
55
+ done = true;
56
+ clearTimeout(timer);
57
+ resolve({ ok: false, reason: err?.message || String(err) });
58
+ });
59
+
60
+ child.on("close", (code) => {
61
+ if (done) return;
62
+ done = true;
63
+ clearTimeout(timer);
64
+ resolve({
65
+ ok: code === 0,
66
+ reason: code === 0 ? "" : `exit code ${code}`,
67
+ });
68
+ });
69
+ });
70
+ }
71
+
25
72
 
26
73
  function getPackageVersion() {
27
74
  try {
@@ -141,6 +188,15 @@ export function startUpdateChecker(log) {
141
188
  return;
142
189
  }
143
190
 
191
+ // Check if we have write permission to the plugin directory before attempting update
192
+ const pluginDir = path.join(__dirname, "..");
193
+ try {
194
+ fs.accessSync(pluginDir, fs.constants.W_OK);
195
+ } catch (err) {
196
+ log.warn?.(`${ANSI.YELLOW}[memos-cloud] Update available (${latestVersion}), but skipping auto-update due to missing write permissions in ${pluginDir}. Please run manually with sudo.${ANSI.RESET}`);
197
+ return;
198
+ }
199
+
144
200
  log.info?.(`${ANSI.YELLOW}[memos-cloud] Update available: ${currentVersion} -> ${latestVersion}. Updating in background...${ANSI.RESET}`);
145
201
 
146
202
  let dotCount = 0;
@@ -160,19 +216,41 @@ export function startUpdateChecker(log) {
160
216
  return "openclaw";
161
217
  })();
162
218
 
219
+ const preflight = await canExecuteCli(cliName);
220
+ if (!preflight.ok) {
221
+ log.warn?.(
222
+ `${ANSI.YELLOW}[memos-cloud] Update available (${latestVersion}), but cannot execute CLI '${cliName}' (${preflight.reason}). Please update manually: ${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`,
223
+ );
224
+ return;
225
+ }
226
+
163
227
  isUpdating = true;
164
- const child = spawn(cliName, ["plugins", "update", "memos-cloud-openclaw-plugin"], {
165
- shell: true
166
- });
228
+ const spawnOpts = { shell: true };
229
+ // On Unix, detach the process so we can kill the entire process group on timeout
230
+ if (process.platform !== "win32") {
231
+ spawnOpts.detached = true;
232
+ }
233
+ const child = spawn(cliName, ["plugins", "update", "memos-cloud-openclaw-plugin"], spawnOpts);
234
+ let finished = false;
235
+ const finishUpdateSequence = () => {
236
+ if (finished) return;
237
+ finished = true;
238
+ clearTimeout(updateTimeout);
239
+ clearInterval(progressInterval);
240
+ isUpdating = false;
241
+ };
167
242
 
168
243
  // Timeout mechanism: forcefully kill the update process if it hangs for more than the configured timeout
169
244
  const updateTimeout = setTimeout(() => {
170
245
  log.warn?.(`${ANSI.RED}[memos-cloud] Update process timed out. Please try manually running: ${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`);
171
- try {
172
- child.kill("SIGKILL");
173
- } catch (e) {
174
- // Process might already be dead
175
- }
246
+ killProcessTree(child);
247
+
248
+ // Fallback: if kill failed and the close event never fires, forcefully release the lock after 5 seconds
249
+ setTimeout(() => {
250
+ if (isUpdating) {
251
+ finishUpdateSequence();
252
+ }
253
+ }, 5000);
176
254
  }, UPDATE_TIMEOUT);
177
255
 
178
256
  child.stdout.on("data", (data) => {
@@ -196,9 +274,7 @@ export function startUpdateChecker(log) {
196
274
  });
197
275
 
198
276
  child.on("close", (code) => {
199
- clearTimeout(updateTimeout);
200
- clearInterval(progressInterval);
201
- isUpdating = false;
277
+ finishUpdateSequence();
202
278
 
203
279
  // Wait for a brief moment to let file system sync if needed
204
280
  setTimeout(() => {
@@ -213,6 +289,13 @@ export function startUpdateChecker(log) {
213
289
  }, 1000); // Small 1-second buffer for file systems
214
290
  });
215
291
 
292
+ child.on("error", (err) => {
293
+ finishUpdateSequence();
294
+ log.warn?.(
295
+ `${ANSI.RED}[memos-cloud] Failed to start auto-update process: ${err?.message || String(err)}. Please run manually: ${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`,
296
+ );
297
+ });
298
+
216
299
  } catch (error) {
217
300
  log.warn?.(`${ANSI.RED}[memos-cloud] Update check failed entirely: ${error.message}${ANSI.RESET}`);
218
301
  }