From 5152641c4b564857f23f46b1ea81f9319ef3fd4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=81=E5=9C=A8=E5=90=B5=E7=9D=80=E5=90=83=E7=B3=96?= Date: Wed, 17 Jun 2026 20:29:58 +0800 Subject: [PATCH 1/2] feat: add config UI update check --- clawdbot.plugin.json | 2 +- lib/check-update.js | 63 +++++++++------ lib/config-ui-server.js | 90 ++++++++++++++++----- lib/config-ui/app.css | 102 ++++++++++++++++++++++++ lib/config-ui/app.js | 168 ++++++++++++++++++++++++++++++++++++++- lib/config-ui/index.html | 1 + moltbot.plugin.json | 2 +- openclaw.plugin.json | 2 +- package.json | 2 +- 9 files changed, 383 insertions(+), 49 deletions(-) diff --git a/clawdbot.plugin.json b/clawdbot.plugin.json index 2df9c68..ea4d4b8 100644 --- a/clawdbot.plugin.json +++ b/clawdbot.plugin.json @@ -2,7 +2,7 @@ "id": "memos-cloud-openclaw-plugin", "name": "MemOS Cloud OpenClaw Plugin", "description": "MemOS Cloud recall + add memory via lifecycle hooks", - "version": "0.1.17", + "version": "0.1.18-beta.1", "kind": "lifecycle", "main": "./index.js", "activation": { diff --git a/lib/check-update.js b/lib/check-update.js index 906fdf3..75cb71e 100644 --- a/lib/check-update.js +++ b/lib/check-update.js @@ -19,7 +19,7 @@ const ANSI = { }; -function getPackageVersion() { +export function getPackageVersion() { try { const pkgPath = path.join(__dirname, "..", "package.json"); const pkgData = fs.readFileSync(pkgPath, "utf-8"); @@ -30,7 +30,7 @@ function getPackageVersion() { } } -function getLatestVersion(log) { +export function getLatestVersion() { return new Promise((resolve, reject) => { const req = https.get( `https://registry.npmjs.org/${PLUGIN_NAME}/latest`, @@ -68,7 +68,7 @@ function getLatestVersion(log) { }); } -function compareVersions(v1, v2) { +export function compareVersions(v1, v2) { // Split pre-release tags (e.g. 0.1.8-beta.1 -> "0.1.8" and "beta.1") const split1 = v1.split("-"); const split2 = v2.split("-"); @@ -101,6 +101,37 @@ function compareVersions(v1, v2) { return 0; } +function detectCliName() { + // Check the full path of the entry script (e.g., .../moltbot/bin/index.js) or the executable + const scriptPath = process.argv[1] ? process.argv[1].toLowerCase() : ""; + const execPath = process.execPath ? process.execPath.toLowerCase() : ""; + + if (scriptPath.includes("moltbot") || execPath.includes("moltbot")) return "moltbot"; + if (scriptPath.includes("clawdbot") || execPath.includes("clawdbot")) return "clawdbot"; + return "openclaw"; +} + +export async function checkForPluginUpdate() { + const currentVersion = getPackageVersion(); + if (!currentVersion) { + throw new Error("Could not read current version from package.json"); + } + + const latestVersion = await getLatestVersion(); + const updateAvailable = compareVersions(latestVersion, currentVersion) > 0; + const cliName = detectCliName(); + + return { + pluginName: PLUGIN_NAME, + currentVersion, + latestVersion, + updateAvailable, + cliName, + updateCommand: `${cliName} plugins update memos-cloud-openclaw-plugin`, + checkedAt: new Date().toISOString(), + }; +} + export function startUpdateChecker(log) { // Only start the interval if we are in the gateway const isGateway = process.argv.includes("gateway"); @@ -118,39 +149,23 @@ export function startUpdateChecker(log) { log.warn?.(`${ANSI.RED}[memos-cloud] Failed to write timestamp file: ${e.message}${ANSI.RESET}`); } - const currentVersion = getPackageVersion(); - if (!currentVersion) { - log.warn?.(`${ANSI.RED}[memos-cloud] Could not read current version from package.json${ANSI.RESET}`); - return; - } - try { - const latestVersion = await getLatestVersion(log); + const updateStatus = await checkForPluginUpdate(); // Normal version check - if (compareVersions(latestVersion, currentVersion) <= 0) { + if (!updateStatus.updateAvailable) { return; } - const cliName = (() => { - // Check the full path of the entry script (e.g., .../moltbot/bin/index.js) or the executable - const scriptPath = process.argv[1] ? process.argv[1].toLowerCase() : ""; - const execPath = process.execPath ? process.execPath.toLowerCase() : ""; - - if (scriptPath.includes("moltbot") || execPath.includes("moltbot")) return "moltbot"; - if (scriptPath.includes("clawdbot") || execPath.includes("clawdbot")) return "clawdbot"; - return "openclaw"; - })(); - const border = "=".repeat(64); log.info?.(""); log.info?.(`${ANSI.GREEN}${border}${ANSI.RESET}`); log.info?.(`${ANSI.YELLOW}🚀 [memos-cloud] NEW VERSION AVAILABLE!${ANSI.RESET}`); - log.info?.(`${ANSI.CYAN}📦 Current version : ${currentVersion}${ANSI.RESET}`); - log.info?.(`${ANSI.GREEN}✨ Latest version : ${latestVersion}${ANSI.RESET}`); + log.info?.(`${ANSI.CYAN}📦 Current version : ${updateStatus.currentVersion}${ANSI.RESET}`); + log.info?.(`${ANSI.GREEN}✨ Latest version : ${updateStatus.latestVersion}${ANSI.RESET}`); log.info?.(`${ANSI.CYAN}────────────────────────────────────────────────────────────────${ANSI.RESET}`); log.info?.(`${ANSI.GREEN}Please run the following command to update manually:${ANSI.RESET}`); - log.info?.(`${ANSI.YELLOW}${cliName} plugins update memos-cloud-openclaw-plugin${ANSI.RESET}`); + log.info?.(`${ANSI.YELLOW}${updateStatus.updateCommand}${ANSI.RESET}`); log.info?.(`${ANSI.GREEN}${border}${ANSI.RESET}`); log.info?.(""); diff --git a/lib/config-ui-server.js b/lib/config-ui-server.js index 1564c0d..dba4214 100644 --- a/lib/config-ui-server.js +++ b/lib/config-ui-server.js @@ -1,12 +1,13 @@ -import { createHash, randomBytes } from "node:crypto"; -import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; +import { createHash, randomBytes } from "node:crypto"; +import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; import { createServer } from "node:http"; import { homedir } from "node:os"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import { Script } from "node:vm"; -import { getConfigResolution } from "./memos-cloud-api.js"; +import { Script } from "node:vm"; +import { checkForPluginUpdate, getPackageVersion } from "./check-update.js"; +import { getConfigResolution } from "./memos-cloud-api.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -21,7 +22,8 @@ const ANSI_CYAN = "\x1b[36m"; const ANSI_GREEN = "\x1b[32m"; const ANSI_YELLOW = "\x1b[33m"; const ANSI_RESET = "\x1b[0m"; -const DEFAULT_GATEWAY_READY_PORT = 18789; +const DEFAULT_GATEWAY_READY_PORT = 18789; +const UPDATE_STATUS_CACHE_MS = 30 * 60 * 1000; // Hook policies that the gateway requires non-bundled plugins to opt into. // Without these, the gateway blocks `agent_end` (and other conversation-access @@ -517,9 +519,10 @@ function buildStatePayload(service) { const state = readGatewayConfig(service.profile); const resolution = getConfigResolution(state.config); return { - runtime: state.profile.id, - runtimeDisplayName: state.profile.displayName, - configPath: state.configPath, + runtime: state.profile.id, + runtimeDisplayName: state.profile.displayName, + pluginVersion: getPackageVersion(), + configPath: state.configPath, entryExists: state.entryExists, fileExists: state.fileExists, enabled: state.enabled, @@ -548,7 +551,7 @@ function getCachedStatePayload(service, maxAgeMs = 1200) { return payload; } -function getCachedHeartbeatPayload(service, maxAgeMs = 1200) { +function getCachedHeartbeatPayload(service, maxAgeMs = 1200) { const now = Date.now(); if (service.heartbeatCache && now - service.heartbeatCache.createdAt < maxAgeMs) { return { @@ -570,9 +573,41 @@ function getCachedHeartbeatPayload(service, maxAgeMs = 1200) { ...payload, timestamp: now, }; -} - -function loadAssetTemplate(name) { +} + +async function getCachedUpdateStatusPayload(service, maxAgeMs = UPDATE_STATUS_CACHE_MS, options = {}) { + const now = Date.now(); + if (!options.force && service.updateStatusCache && now - service.updateStatusCache.createdAt < maxAgeMs) { + return { + ...service.updateStatusCache.payload, + cached: true, + }; + } + + try { + const status = await checkForPluginUpdate(); + const payload = { + ok: true, + ...status, + cached: false, + }; + service.updateStatusCache = { + createdAt: now, + payload, + }; + return payload; + } catch (error) { + return { + ok: false, + currentVersion: getPackageVersion(), + error: String(error?.message || error), + checkedAt: new Date().toISOString(), + cached: false, + }; + } +} + +function loadAssetTemplate(name) { return readFileSync(join(ASSET_DIR, name), "utf8"); } @@ -827,7 +862,17 @@ async function createService(log) { const token = randomBytes(24).toString("hex"); const bootId = randomBytes(10).toString("hex"); - const service = { profile, token, bootId, port: 0, url: "", server: null, stateCache: null, heartbeatCache: null }; + const service = { + profile, + token, + bootId, + port: 0, + url: "", + server: null, + stateCache: null, + heartbeatCache: null, + updateStatusCache: null, + }; const server = createServer(async (req, res) => { try { @@ -872,12 +917,19 @@ async function createService(log) { return; } - if (requestUrl.pathname === "/api/state" && req.method === "GET") { - sendJson(res, 200, getCachedStatePayload(service)); - return; - } - - if (requestUrl.pathname === "/api/save" && req.method === "POST") { + if (requestUrl.pathname === "/api/state" && req.method === "GET") { + sendJson(res, 200, getCachedStatePayload(service)); + return; + } + + if (requestUrl.pathname === "/api/update-status" && req.method === "GET") { + sendJson(res, 200, await getCachedUpdateStatusPayload(service, UPDATE_STATUS_CACHE_MS, { + force: requestUrl.searchParams.get("force") === "1", + })); + return; + } + + if (requestUrl.pathname === "/api/save" && req.method === "POST") { let parsed = {}; try { parsed = JSON.parse((await readRequestBody(req)) || "{}"); diff --git a/lib/config-ui/app.css b/lib/config-ui/app.css index 58ca635..f636d37 100644 --- a/lib/config-ui/app.css +++ b/lib/config-ui/app.css @@ -337,6 +337,99 @@ h1 { color: var(--warn); } +.update-notice { + display: none; + margin-top: 16px; + padding: 14px 16px; + border: 1px solid rgba(181, 71, 8, 0.2); + border-radius: 18px; + background: rgba(181, 71, 8, 0.08); + color: var(--warn); +} + +.update-notice.show { + display: block; +} + +.update-notice.error { + border-color: rgba(180, 35, 24, 0.18); + background: rgba(180, 35, 24, 0.08); + color: var(--danger); +} + +.update-notice.ok, +.update-notice.info { + border-color: rgba(15, 118, 110, 0.18); + background: rgba(15, 118, 110, 0.08); + color: var(--accent2); +} + +.update-check-button { + appearance: none; + min-height: 38px; + padding: 8px 14px; + border: 1px solid rgba(15, 23, 42, 0.08); + background: rgba(255, 255, 255, 0.74); + color: var(--strong); + font-weight: 700; + cursor: pointer; + transition: border-color 160ms ease, box-shadow 160ms ease, color 160ms ease, transform 160ms ease; +} + +.update-check-button:hover { + border-color: rgba(15, 118, 110, 0.2); + color: var(--accent2); + transform: translateY(-1px); +} + +.update-check-button:focus { + outline: none; + border-color: rgba(15, 118, 110, 0.34); + box-shadow: 0 0 0 4px rgba(15, 118, 110, 0.12); +} + +.update-check-button:disabled { + cursor: wait; + opacity: 0.7; + transform: none; +} + +.update-copy { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 14px; +} + +.update-title { + margin-bottom: 4px; + color: var(--strong); + font-weight: 800; +} + +.update-body { + font-size: 13px; + line-height: 1.55; +} + +.update-command { + display: block; + margin-top: 12px; + padding: 10px 12px; + border-radius: 14px; + background: rgba(255, 255, 255, 0.72); + color: var(--strong); + font-family: "Cascadia Code", "Consolas", monospace; + font-size: 12px; + line-height: 1.45; + overflow-x: auto; +} + +.update-copy-button { + flex: 0 0 auto; + white-space: nowrap; +} + .hero-actions { display: grid; gap: 12px; @@ -609,6 +702,11 @@ h1 { cursor: pointer; } +.inline-btn:disabled { + opacity: 0.58; + cursor: not-allowed; +} + .error { color: var(--danger); font-size: 12px; @@ -868,6 +966,10 @@ body.nav-collapsed .floating-nav { padding: 18px; } + .update-copy { + display: grid; + } + .card, .panel { padding: 18px; diff --git a/lib/config-ui/app.js b/lib/config-ui/app.js index 576bc64..d5f68fa 100644 --- a/lib/config-ui/app.js +++ b/lib/config-ui/app.js @@ -6,6 +6,9 @@ const APP = { const knownKeys = new Set(APP.fieldDefinitions.map((field) => field.key)); let remoteState = null; +let updateStatus = null; +let updateStatusChecked = false; +let updateStatusLoading = false; let draft = null; let baselineSnapshot = ""; @@ -41,6 +44,8 @@ const elements = { languageOptionZh: document.getElementById("languageOptionZh"), statusRow: document.getElementById("statusRow"), metaRow: document.getElementById("metaRow"), + updateCheckButton: document.getElementById("updateCheckButton") || document.createElement("button"), + updateNotice: document.getElementById("updateNotice"), banner: document.getElementById("banner"), floatingTools: document.getElementById("floatingTools"), floatingNavLabel: document.getElementById("floatingNavLabel"), @@ -128,6 +133,7 @@ const UI_TEXT = { bannerAuthWaiting: "Config session expired. Waiting for the gateway to finish restarting before retrying...", bannerAuthFailed: "Config session expired. Refresh this page to reconnect.", bannerCopied: "The config file path was copied to your clipboard.", + bannerUpdateCommandCopied: "The update command was copied to your clipboard.", bannerClipboardFailed: "Clipboard access failed. Copy the address from your browser bar instead.", bannerSaved: "The plugin config was saved.", bannerRestartLaunched: "Restart requested. Refresh this page in a few seconds if it does not recover automatically.", @@ -135,12 +141,21 @@ const UI_TEXT = { bannerWaitingBack: "Restart requested. Refresh this page in a few seconds if it does not recover automatically.", bannerRestartRefreshHint: "Restart requested. Refresh this page in a few seconds if it does not recover automatically.", pillPlugin: "Plugin", + pillVersion: "Version", pillRuntime: "Runtime", pillEntry: "Entry", pillPageUrl: "Page URL", - pillRevision: "Revision", + pillRevision: "Config revision", pillConfigFile: "Config file", pillInclude: "Include", + checkUpdates: "Check for updates", + checkingUpdates: "Checking...", + updateTitle: "Plugin update available", + updateBody: "Current version {current}; latest version {latest}. Update and restart with:", + updateCopyCommand: "Copy Update And Restart Command", + upToDateTitle: "Already up to date", + upToDateBody: "Current version {current} is the latest version.", + updateCheckFailed: "Version check failed: {error}", pillEntryPresent: "present", pillEntryMissing: "missing", pillConfigFound: "found", @@ -209,6 +224,7 @@ const UI_TEXT = { bannerAuthWaiting: "配置页会话已过期,正在等待 Gateway 完成重启后再恢复...", bannerAuthFailed: "配置页会话已过期,请刷新页面后重新连接。", bannerCopied: "配置文件路径已复制到剪贴板。", + bannerUpdateCommandCopied: "更新命令已复制到剪贴板。", bannerClipboardFailed: "复制失败,请直接从浏览器地址栏复制。", bannerSaved: "插件配置已保存。", bannerRestartLaunched: "已请求重启。如果页面没有自动恢复,请过几秒刷新重试。", @@ -216,12 +232,21 @@ const UI_TEXT = { bannerWaitingBack: "已请求重启。如果页面没有自动恢复,请过几秒刷新重试。", bannerRestartRefreshHint: "已请求重启。如果页面没有自动恢复,请过几秒刷新重试。", pillPlugin: "插件", + pillVersion: "版本", pillRuntime: "运行时", pillEntry: "配置项", pillPageUrl: "页面地址", - pillRevision: "版本标识", + pillRevision: "配置标识", pillConfigFile: "配置文件", pillInclude: "包含", + checkUpdates: "检查更新", + checkingUpdates: "检查中...", + updateTitle: "插件有新版本", + updateBody: "当前版本 {current};最新版本 {latest}。可使用以下命令更新并重启:", + updateCopyCommand: "复制更新并重启命令", + upToDateTitle: "已是最新版本", + upToDateBody: "当前版本 {current} 已经是最新版本。", + updateCheckFailed: "版本检查失败:{error}", pillEntryPresent: "已存在", pillEntryMissing: "不存在", pillConfigFound: "已找到", @@ -307,6 +332,14 @@ function uiText(key) { return UI_TEXT[lang][key] ?? UI_TEXT.en[key] ?? key; } +function formatUiText(key, values) { + let text = uiText(key); + for (const [name, value] of Object.entries(values || {})) { + text = text.split(`{${name}}`).join(String(value ?? "")); + } + return text; +} + function localizedGroup(group) { const lang = getCurrentLanguage(); const translated = GROUP_TRANSLATIONS[lang]?.[group.id]; @@ -340,6 +373,10 @@ function applyLanguageUi() { elements.copyPathButton.setAttribute("aria-label", uiText("copyPath")); elements.copyPathButton.setAttribute("title", uiText("copyPath")); elements.saveButton.textContent = uiText("save"); + elements.updateCheckButton.id = "updateCheckButton"; + elements.updateCheckButton.type = "button"; + elements.updateCheckButton.className = "pill update-check-button"; + updateCheckButtonUi(); elements.reloadButton.textContent = uiText("reload"); elements.overlayTitle.textContent = uiText("overlayTitle"); @@ -350,6 +387,22 @@ function applyLanguageUi() { elements.languageOptionEn.textContent = "EN"; elements.languageOptionZh.textContent = "中文"; updateLanguageDropdownUi(); + renderUpdateNotice(); +} + +function updateCheckButtonUi() { + if (!elements.updateCheckButton) return; + const version = remoteState?.pluginVersion || "unknown"; + const action = updateStatusLoading ? uiText("checkingUpdates") : uiText("checkUpdates"); + elements.updateCheckButton.innerHTML = + "" + + escapeHtml(uiText("pillVersion")) + + "" + + escapeHtml(version + " · " + action) + + ""; + elements.updateCheckButton.setAttribute("aria-label", uiText("pillVersion") + " " + version + ", " + action); + elements.updateCheckButton.setAttribute("title", action); + elements.updateCheckButton.disabled = updateStatusLoading; } function updateFloatingNavToggleUi() { @@ -722,11 +775,16 @@ function renderStatus() { elements.statusRow.innerHTML = ""; elements.metaRow.innerHTML = ""; elements.pathBox.textContent = remoteState ? remoteState.configPath : ""; + if (elements.updateCheckButton && !elements.updateCheckButton.parentElement) { + elements.updateCheckButton.remove(); + } if (!remoteState) return; elements.statusRow.appendChild( createPill(remoteState.enabled ? "ok" : "warn", uiText("pillPlugin"), remoteState.enabled ? uiText("enabled") : uiText("disabled")), ); + updateCheckButtonUi(); + elements.statusRow.appendChild(elements.updateCheckButton); elements.statusRow.appendChild(createPill("", uiText("pillRuntime"), remoteState.runtimeDisplayName)); elements.statusRow.appendChild( createPill(remoteState.entryExists ? "" : "warn", uiText("pillEntry"), remoteState.entryExists ? uiText("pillEntryPresent") : uiText("pillEntryMissing")), @@ -743,6 +801,70 @@ function renderStatus() { } } +function renderUpdateNotice() { + if (!elements.updateNotice) return; + + if (updateStatusLoading) { + elements.updateNotice.hidden = false; + elements.updateNotice.className = "update-notice info show"; + elements.updateNotice.innerHTML = + '
' + + escapeHtml(uiText("checkingUpdates")) + + "
"; + return; + } + + if (!updateStatusChecked || !updateStatus) { + elements.updateNotice.hidden = true; + elements.updateNotice.className = "update-notice"; + elements.updateNotice.innerHTML = ""; + return; + } + + elements.updateNotice.hidden = false; + elements.updateNotice.className = "update-notice " + (updateStatus.ok === false ? "error show" : updateStatus.updateAvailable ? "show" : "ok show"); + + if (updateStatus.updateAvailable) { + const command = getUpdateAndRestartCommand(updateStatus); + elements.updateNotice.innerHTML = + '
' + + '
' + + escapeHtml(uiText("updateTitle")) + + '
' + + escapeHtml(formatUiText("updateBody", { current: updateStatus.currentVersion, latest: updateStatus.latestVersion })) + + '
' + + '
" + + '' + + escapeHtml(command) + + ""; + + elements.updateNotice.querySelector(".update-copy-button")?.addEventListener("click", () => { + void copyUpdateCommand(command); + }); + return; + } + + if (updateStatus.ok === false) { + elements.updateNotice.innerHTML = escapeHtml(formatUiText("updateCheckFailed", { error: updateStatus.error || "Unknown error" })); + return; + } + + elements.updateNotice.innerHTML = + '
' + + escapeHtml(uiText("upToDateTitle")) + + '
' + + escapeHtml(formatUiText("upToDateBody", { current: updateStatus.currentVersion || remoteState?.pluginVersion || "unknown" })) + + "
"; +} + +function getUpdateAndRestartCommand(status) { + const cliName = status?.cliName || "openclaw"; + const updateCommand = status?.updateCommand || `${cliName} plugins update memos-cloud-openclaw-plugin`; + return `${updateCommand} && ${cliName} gateway restart`; +} + function renderEnabledField() { const field = document.createElement("div"); field.className = "field"; @@ -1096,6 +1218,35 @@ async function checkHeartbeat() { return response.json(); } +async function loadUpdateStatus(options = {}) { + const manual = options.manual === true; + if (manual) { + updateStatusChecked = true; + updateStatusLoading = true; + updateCheckButtonUi(); + renderUpdateNotice(); + } + + try { + updateStatus = await api("/api/update-status" + (options.force ? "?force=1" : "")); + updateStatusChecked = true; + } catch (error) { + if (error?.status === 403) { + void recoverFromAuthError(); + return; + } + updateStatusChecked = true; + updateStatus = { + ok: false, + error: String(error.message || error), + }; + } finally { + updateStatusLoading = false; + updateCheckButtonUi(); + renderUpdateNotice(); + } +} + function handleHeartbeatState(heartbeat) { if (!heartbeat || typeof heartbeat !== "object") return; @@ -1240,6 +1391,16 @@ async function copyConfigPath() { } } +async function copyUpdateCommand(command) { + if (!command) return; + try { + await navigator.clipboard.writeText(command); + setBanner("info", uiText("bannerUpdateCommandCopied")); + } catch { + setBanner("error", uiText("bannerClipboardFailed")); + } +} + elements.languageSelectButton.addEventListener("click", () => { setLanguageMenuOpen(!languageMenuOpen); }); @@ -1272,6 +1433,9 @@ elements.overlayRefreshButton.addEventListener("click", () => { window.location.reload(); }); elements.copyPathButton.addEventListener("click", () => void copyConfigPath()); +elements.updateCheckButton?.addEventListener("click", () => { + void loadUpdateStatus({ manual: true, force: true }); +}); elements.floatingToggleButton.addEventListener("click", () => { setNavCollapsed(!navCollapsed); }); diff --git a/lib/config-ui/index.html b/lib/config-ui/index.html index f4ce1f7..97f5743 100644 --- a/lib/config-ui/index.html +++ b/lib/config-ui/index.html @@ -48,6 +48,7 @@

MemOS Cloud OpenClaw Plugin Config

+
diff --git a/moltbot.plugin.json b/moltbot.plugin.json index 2df9c68..ea4d4b8 100644 --- a/moltbot.plugin.json +++ b/moltbot.plugin.json @@ -2,7 +2,7 @@ "id": "memos-cloud-openclaw-plugin", "name": "MemOS Cloud OpenClaw Plugin", "description": "MemOS Cloud recall + add memory via lifecycle hooks", - "version": "0.1.17", + "version": "0.1.18-beta.1", "kind": "lifecycle", "main": "./index.js", "activation": { diff --git a/openclaw.plugin.json b/openclaw.plugin.json index 2df9c68..ea4d4b8 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -2,7 +2,7 @@ "id": "memos-cloud-openclaw-plugin", "name": "MemOS Cloud OpenClaw Plugin", "description": "MemOS Cloud recall + add memory via lifecycle hooks", - "version": "0.1.17", + "version": "0.1.18-beta.1", "kind": "lifecycle", "main": "./index.js", "activation": { diff --git a/package.json b/package.json index e78c5f4..30b7479 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@memtensor/memos-cloud-openclaw-plugin", - "version": "0.1.17", + "version": "0.1.18-beta.1", "description": "OpenClaw lifecycle plugin for MemOS Cloud (add + recall memory)", "scripts": { "sync-version": "node scripts/sync-version.js", From e1e7fb0f20d549253a762c1bea4ed4d9b4477ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=81=E5=9C=A8=E5=90=B5=E7=9D=80=E5=90=83=E7=B3=96?= Date: Mon, 22 Jun 2026 17:45:56 +0800 Subject: [PATCH 2/2] 0.1.18 --- clawdbot.plugin.json | 2 +- moltbot.plugin.json | 2 +- openclaw.plugin.json | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clawdbot.plugin.json b/clawdbot.plugin.json index ea4d4b8..5367322 100644 --- a/clawdbot.plugin.json +++ b/clawdbot.plugin.json @@ -2,7 +2,7 @@ "id": "memos-cloud-openclaw-plugin", "name": "MemOS Cloud OpenClaw Plugin", "description": "MemOS Cloud recall + add memory via lifecycle hooks", - "version": "0.1.18-beta.1", + "version": "0.1.18", "kind": "lifecycle", "main": "./index.js", "activation": { diff --git a/moltbot.plugin.json b/moltbot.plugin.json index ea4d4b8..5367322 100644 --- a/moltbot.plugin.json +++ b/moltbot.plugin.json @@ -2,7 +2,7 @@ "id": "memos-cloud-openclaw-plugin", "name": "MemOS Cloud OpenClaw Plugin", "description": "MemOS Cloud recall + add memory via lifecycle hooks", - "version": "0.1.18-beta.1", + "version": "0.1.18", "kind": "lifecycle", "main": "./index.js", "activation": { diff --git a/openclaw.plugin.json b/openclaw.plugin.json index ea4d4b8..5367322 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -2,7 +2,7 @@ "id": "memos-cloud-openclaw-plugin", "name": "MemOS Cloud OpenClaw Plugin", "description": "MemOS Cloud recall + add memory via lifecycle hooks", - "version": "0.1.18-beta.1", + "version": "0.1.18", "kind": "lifecycle", "main": "./index.js", "activation": { diff --git a/package.json b/package.json index 30b7479..77d0789 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@memtensor/memos-cloud-openclaw-plugin", - "version": "0.1.18-beta.1", + "version": "0.1.18", "description": "OpenClaw lifecycle plugin for MemOS Cloud (add + recall memory)", "scripts": { "sync-version": "node scripts/sync-version.js",