From f9a45a319a784a0327a920eb5590df593cecf46e Mon Sep 17 00:00:00 2001 From: jinliyl <6469360+jinliyl@users.noreply.github.com> Date: Tue, 1 Sep 2026 20:27:47 +0800 Subject: [PATCH] fix: publish an OpenClaw-specific ClawHub artifact (#513) * fix: publish an OpenClaw-specific ClawHub artifact * fix: enable ClawHub publication --- .github/workflows/release-typescript.yml | 26 ++++++-- typescript/README.md | 3 +- typescript/README_OPENCLAW.md | 72 +++++++++++++++++++++ typescript/README_OPENCLAW_ZH.md | 71 ++++++++++++++++++++ typescript/README_ZH.md | 2 + typescript/openclaw.plugin.json | 2 +- typescript/package-lock.json | 4 +- typescript/package.json | 3 +- typescript/scripts/pack-clawhub.mjs | 82 ++++++++++++++++++++++++ typescript/scripts/test-package.mjs | 57 +++++++++++++++- typescript/tests/openclaw.test.mjs | 2 +- 11 files changed, 310 insertions(+), 14 deletions(-) create mode 100644 typescript/README_OPENCLAW.md create mode 100644 typescript/README_OPENCLAW_ZH.md create mode 100644 typescript/scripts/pack-clawhub.mjs diff --git a/.github/workflows/release-typescript.yml b/.github/workflows/release-typescript.yml index a58325e2..208fa8d5 100644 --- a/.github/workflows/release-typescript.yml +++ b/.github/workflows/release-typescript.yml @@ -2,7 +2,7 @@ # 1. Update typescript/package.json and package-lock.json to the release version and merge them. # 2. Configure npm Trusted Publishing for agentscope-ai/ReMe and this workflow file. # 3. Run this workflow manually with the exact package version (an optional v prefix is accepted). -# 4. Configure ClawHub Trusted Publishing or CLAWHUB_TOKEN before enabling ClawHub publication. +# 4. Configure ClawHub Trusted Publishing for agentscope-ai/ReMe and this workflow file. # 5. Use the `next` tag for prereleases and `latest` only for stable releases. name: Release / TypeScript integrations @@ -25,7 +25,7 @@ on: - next - latest publish_clawhub: - description: Also publish the verified tarball to ClawHub + description: Also publish the OpenClaw-specific ClawPack to ClawHub required: true default: false type: boolean @@ -54,7 +54,7 @@ jobs: - name: Set up Node uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: - node-version: '22.22.3' + node-version: "22.22.3" - name: Validate package name and release version id: validate @@ -109,6 +109,19 @@ jobs: path: ${{ runner.temp }}/reme-typescript-package/*.tgz if-no-files-found: error + - name: Pack ClawHub tarball + working-directory: typescript + run: | + mkdir -p "${RUNNER_TEMP}/reme-clawhub-package" + npm run pack:clawhub -- "${RUNNER_TEMP}/reme-clawhub-package" + + - name: Upload ClawHub tarball + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: agentscope-ai-reme-clawhub-${{ inputs.version }} + path: ${{ runner.temp }}/reme-clawhub-package/*.tgz + if-no-files-found: error + publish: needs: build runs-on: ubuntu-latest @@ -120,7 +133,7 @@ jobs: - name: Set up Node for npm uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: - node-version: '24' + node-version: "24" registry-url: https://registry.npmjs.org - name: Download npm tarball @@ -161,7 +174,6 @@ jobs: source_commit: ${{ github.sha }} source_ref: ${{ github.ref }} source_path: typescript - package_artifact_name: agentscope-ai-reme-${{ inputs.version }} + package_artifact_name: agentscope-ai-reme-clawhub-${{ inputs.version }} + dry_run: false wait_for_publication: true - secrets: - clawhub_token: ${{ secrets.CLAWHUB_TOKEN }} diff --git a/typescript/README.md b/typescript/README.md index 56c7d09c..19ec084c 100644 --- a/typescript/README.md +++ b/typescript/README.md @@ -140,6 +140,7 @@ npm run test:package ``` `npm pack` runs the TypeScript build and includes only `dist`, the DSH patch, the OpenClaw manifest, and the English and -Chinese READMEs. +Chinese READMEs. `npm run pack:clawhub -- ` creates the ClawHub artifact with the same runtime files +and the OpenClaw-only `README_OPENCLAW.md` and `README_OPENCLAW_ZH.md` content at the package README paths. Stable releases use npm's `latest` distribution tag; prereleases use `next`. diff --git a/typescript/README_OPENCLAW.md b/typescript/README_OPENCLAW.md new file mode 100644 index 00000000..8ee632c0 --- /dev/null +++ b/typescript/README_OPENCLAW.md @@ -0,0 +1,72 @@ +# ReMe memory for OpenClaw + +[中文说明](./README_ZH.md) + +ReMe gives OpenClaw file-native long-term memory while keeping durable memory in a workspace you own. The plugin uses +OpenClaw's native lifecycle, hooks, tools, and memory slot. + +## What the plugin does + +- Registers `reme_search` for explicit memory lookup. +- Recalls relevant memory before conversational root-agent runs. +- Captures completed user/assistant turns in background batches. +- Runs optional daily `auto_dream` memory consolidation in the workspace timezone. +- Excludes subagent, cron, heartbeat, memory, and overflow runs by default. +- Wraps recalled content in `` and marks it as untrusted historical data. + +## Requirements + +- OpenClaw `2026.7.1` or later. +- Node.js `22.22.3+`, `24.15.0+`, or `25.9.0+` on the corresponding supported major-version line. +- A running ReMe HTTP service with the `search`, `auto_memory`, and `auto_dream` jobs. + +## Start ReMe + +Install ReMe and start its local HTTP service: + +```bash +pip install "reme-ai[core]" +reme start workspace_dir=/absolute/path/to/workspace +``` + +The default endpoint is `http://127.0.0.1:2333`. ReMe's HTTP service does not use API-key authentication, so keep it +on loopback or another trusted network unless you provide a protected proxy boundary. + +## Install the OpenClaw plugin + +Install explicitly from ClawHub: + +```bash +openclaw plugins install clawhub:@agentscope-ai/reme +``` + +When another memory plugin is enabled, select `reme` for `plugins.slots.memory`. OpenClaw remains authoritative for +conversation access and prompt-injection permissions; grant them to ReMe when your policy requires explicit consent. +The plugin does not rewrite Gateway configuration. + +## Configuration + +| Option | Default | Meaning | +| --------------------- | ----------------------- | --------------------------------------------- | +| `endpoint` | `http://127.0.0.1:2333` | ReMe HTTP service URL | +| `language` | `en` | Memory guidance language: `en` or `zh` | +| `autoRecall` | `true` | Recall before conversational root-agent runs | +| `searchLimit` | `5` | Maximum search results | +| `recallMinScore` | `0` | Minimum automatic-recall score | +| `autoMemoryEnabled` | `true` | Capture completed conversational turns | +| `autoMemoryInterval` | `5` | Submit after this many completed turns | +| `autoDreamEnabled` | `true` | Enable daily memory consolidation | +| `dreamCron` | `0 23 * * *` | Daily schedule in the workspace timezone | +| `dreamHint` | empty | Optional guidance sent to `auto_dream` | +| `rootAgentsOnly` | `true` | Exclude subagents from guidance and capture | +| `timezone` | `Asia/Shanghai` | IANA timezone used for batches and scheduling | +| `requestTimeoutMs` | `10000` | Recall and explicit-search timeout | +| `backgroundTimeoutMs` | `3600000` | Automatic-memory and dream timeout | +| `shutdownTimeoutMs` | `5000` | Best-effort Gateway shutdown drain budget | + +Configuration changes apply to subsequent runs. Failed automatic-memory batches are retained for retry, and the +Gateway shutdown hook attempts to flush pending work within `shutdownTimeoutMs`. + +## Source and license + +ReMe is developed at [agentscope-ai/ReMe](https://github.com/agentscope-ai/ReMe) and released under Apache-2.0. diff --git a/typescript/README_OPENCLAW_ZH.md b/typescript/README_OPENCLAW_ZH.md new file mode 100644 index 00000000..d518b506 --- /dev/null +++ b/typescript/README_OPENCLAW_ZH.md @@ -0,0 +1,71 @@ +# OpenClaw 的 ReMe 长期记忆插件 + +[English](./README.md) + +ReMe 为 OpenClaw 提供文件原生的长期记忆,并将持久记忆保存在用户拥有的 workspace 中。插件使用 OpenClaw +原生的生命周期、Hook、工具接口和 memory slot。 + +## 插件能力 + +- 注册 `reme_search`,支持显式检索记忆。 +- 在根 Agent 的对话运行前自动召回相关记忆。 +- 在后台分批捕获已完成的用户/助手对话。 +- 按 workspace 时区运行可选的每日 `auto_dream` 记忆整理。 +- 默认排除子 Agent、Cron、Heartbeat、Memory 和 Overflow 触发的运行。 +- 使用 `` 包裹召回内容,并将其标记为不可信历史数据。 + +## 环境要求 + +- OpenClaw `2026.7.1` 或更高版本。 +- 对应受支持主版本线上的 Node.js `22.22.3+`、`24.15.0+` 或 `25.9.0+`。 +- 已启动 ReMe HTTP 服务,并提供 `search`、`auto_memory` 和 `auto_dream` Job。 + +## 启动 ReMe + +安装 ReMe 并启动本地 HTTP 服务: + +```bash +pip install "reme-ai[core]" +reme start workspace_dir=/absolute/path/to/workspace +``` + +默认地址为 `http://127.0.0.1:2333`。ReMe HTTP 服务不使用 API Key 认证,因此除非前面部署了受保护的代理边界, +否则应仅监听 loopback 或其他可信网络。 + +## 安装 OpenClaw 插件 + +明确指定从 ClawHub 安装: + +```bash +openclaw plugins install clawhub:@agentscope-ai/reme +``` + +如果已经启用了其他记忆插件,请将 `plugins.slots.memory` 设为 `reme`。对话访问和 Prompt 注入权限仍由 +OpenClaw 管理;策略要求显式授权时,需要为 ReMe 开启相应权限。插件不会修改 Gateway 配置。 + +## 配置 + +| 配置项 | 默认值 | 作用 | +| --------------------- | ----------------------- | ------------------------------------ | +| `endpoint` | `http://127.0.0.1:2333` | ReMe HTTP 服务地址 | +| `language` | `en` | 记忆指引语言:`en` 或 `zh` | +| `autoRecall` | `true` | 在根 Agent 对话运行前自动召回 | +| `searchLimit` | `5` | 搜索结果上限 | +| `recallMinScore` | `0` | 自动召回的最低搜索分数 | +| `autoMemoryEnabled` | `true` | 捕获已完成的对话 | +| `autoMemoryInterval` | `5` | 每完成多少轮提交一次 | +| `autoDreamEnabled` | `true` | 启用每日记忆整理 | +| `dreamCron` | `0 23 * * *` | workspace 时区下的每日计划 | +| `dreamHint` | 空字符串 | 传给 `auto_dream` 的可选指引 | +| `rootAgentsOnly` | `true` | 不为子 Agent 注入指引或捕获对话 | +| `timezone` | `Asia/Shanghai` | 每日批次和计划使用的 IANA 时区 | +| `requestTimeoutMs` | `10000` | 自动召回和显式搜索超时 | +| `backgroundTimeoutMs` | `3600000` | 自动记忆和 Auto Dream 超时 | +| `shutdownTimeoutMs` | `5000` | Gateway 退出时尽力排空任务的时间预算 | + +配置修改会从后续运行开始生效。失败的自动记忆批次会保留等待重试,Gateway 退出时会在 `shutdownTimeoutMs` 范围内 +尝试刷新未完成任务。 + +## 源码与许可证 + +ReMe 在 [agentscope-ai/ReMe](https://github.com/agentscope-ai/ReMe) 开发,并使用 Apache-2.0 许可证发布。 diff --git a/typescript/README_ZH.md b/typescript/README_ZH.md index 515a8fe9..3e81cf29 100644 --- a/typescript/README_ZH.md +++ b/typescript/README_ZH.md @@ -130,5 +130,7 @@ npm run test:package ``` `npm pack` 会先构建 TypeScript,并且只打包 `dist`、DSH patch、OpenClaw manifest 和中英文 README。 +`npm run pack:clawhub -- <输出目录>` 会使用相同的运行时文件生成 ClawHub 产物,并将仅包含 OpenClaw +说明的 `README_OPENCLAW.md` 和 `README_OPENCLAW_ZH.md` 放到包内 README 路径。 稳定版本使用 npm 的 `latest` distribution tag 发布,预发布版本使用 `next`。 diff --git a/typescript/openclaw.plugin.json b/typescript/openclaw.plugin.json index 14015d4c..3524d788 100644 --- a/typescript/openclaw.plugin.json +++ b/typescript/openclaw.plugin.json @@ -11,7 +11,7 @@ "contracts": { "tools": ["reme_search"] }, - "configUiHints": { + "uiHints": { "endpoint": { "label": "ReMe endpoint", "placeholder": "http://127.0.0.1:2333" diff --git a/typescript/package-lock.json b/typescript/package-lock.json index 8fca1497..911e3f93 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -1,12 +1,12 @@ { "name": "@agentscope-ai/reme", - "version": "0.1.1", + "version": "0.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@agentscope-ai/reme", - "version": "0.1.1", + "version": "0.1.2", "license": "Apache-2.0", "dependencies": { "typebox": "1.3.19" diff --git a/typescript/package.json b/typescript/package.json index 8c20d7c7..e5093de9 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@agentscope-ai/reme", - "version": "0.1.1", + "version": "0.1.2", "description": "ReMe client and memory integrations for TypeScript agents", "type": "module", "main": "./dist/index.js", @@ -64,6 +64,7 @@ "format": "prettier --write .", "format:check": "prettier --check .", "lint": "eslint .", + "pack:clawhub": "node scripts/pack-clawhub.mjs", "pretest": "npm run build", "test": "node --test tests/*.test.mjs", "test:package": "node scripts/test-package.mjs", diff --git a/typescript/scripts/pack-clawhub.mjs b/typescript/scripts/pack-clawhub.mjs new file mode 100644 index 00000000..d42e8970 --- /dev/null +++ b/typescript/scripts/pack-clawhub.mjs @@ -0,0 +1,82 @@ +import { execFile } from "node:child_process"; +import { + copyFile, + cp, + mkdir, + mkdtemp, + readFile, + rm, + writeFile, +} from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { promisify } from "node:util"; + +const execFileAsync = promisify(execFile); +const packageDirectory = fileURLToPath(new URL("..", import.meta.url)); +const destinationDirectory = path.resolve(process.argv[2] ?? packageDirectory); +const temporaryDirectory = await mkdtemp( + path.join(tmpdir(), "reme-clawhub-pack-"), +); +const stagingDirectory = path.join(temporaryDirectory, "package"); + +try { + await mkdir(stagingDirectory, { recursive: true }); + await mkdir(destinationDirectory, { recursive: true }); + + const packageManifest = JSON.parse( + await readFile(path.join(packageDirectory, "package.json"), "utf8"), + ); + delete packageManifest.scripts?.prepare; + + await Promise.all([ + cp( + path.join(packageDirectory, "dist"), + path.join(stagingDirectory, "dist"), + { + recursive: true, + }, + ), + cp(path.join(packageDirectory, "dsh"), path.join(stagingDirectory, "dsh"), { + recursive: true, + }), + writeFile( + path.join(stagingDirectory, "package.json"), + `${JSON.stringify(packageManifest, null, 2)}\n`, + ), + copyFile( + path.join(packageDirectory, "openclaw.plugin.json"), + path.join(stagingDirectory, "openclaw.plugin.json"), + ), + copyFile( + path.join(packageDirectory, "README_OPENCLAW.md"), + path.join(stagingDirectory, "README.md"), + ), + copyFile( + path.join(packageDirectory, "README_OPENCLAW_ZH.md"), + path.join(stagingDirectory, "README_ZH.md"), + ), + ]); + + const { stdout } = await execFileAsync( + "npm", + [ + "pack", + "--json", + "--ignore-scripts", + "--pack-destination", + destinationDirectory, + ], + { cwd: stagingDirectory }, + ); + const [{ filename }] = JSON.parse(stdout); + console.log( + JSON.stringify({ + filename, + tarball: path.join(destinationDirectory, filename), + }), + ); +} finally { + await rm(temporaryDirectory, { force: true, recursive: true }); +} diff --git a/typescript/scripts/test-package.mjs b/typescript/scripts/test-package.mjs index 731c8012..3a9e7654 100644 --- a/typescript/scripts/test-package.mjs +++ b/typescript/scripts/test-package.mjs @@ -1,7 +1,9 @@ import { execFile } from "node:child_process"; -import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import assert from "node:assert/strict"; +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; +import { fileURLToPath } from "node:url"; import { promisify } from "node:util"; const execFileAsync = promisify(execFile); @@ -57,6 +59,59 @@ try { ].join("\n"), ); await execFileAsync("node", [consumerEntry], { cwd: temporaryDirectory }); + + const clawHubOutputDirectory = path.join(temporaryDirectory, "clawhub-pack"); + await mkdir(clawHubOutputDirectory, { recursive: true }); + const packClawHubScript = fileURLToPath( + new URL("./pack-clawhub.mjs", import.meta.url), + ); + const clawHubPackResult = await execFileAsync( + "node", + [packClawHubScript, clawHubOutputDirectory], + { cwd: packageDirectory }, + ); + const { tarball: clawHubTarball } = JSON.parse(clawHubPackResult.stdout); + const clawHubConsumerDirectory = path.join( + temporaryDirectory, + "clawhub-consumer", + ); + await mkdir(clawHubConsumerDirectory, { recursive: true }); + await writeFile( + path.join(clawHubConsumerDirectory, "package.json"), + JSON.stringify({ private: true, type: "module" }), + ); + await execFileAsync( + "npm", + ["install", "--ignore-scripts", "--no-audit", "--no-fund", clawHubTarball], + { cwd: clawHubConsumerDirectory }, + ); + const clawHubPackageDirectory = path.join( + clawHubConsumerDirectory, + "node_modules", + "@agentscope-ai", + "reme", + ); + const clawHubReadme = await readFile( + path.join(clawHubPackageDirectory, "README.md"), + "utf8", + ); + const clawHubReadmeZh = await readFile( + path.join(clawHubPackageDirectory, "README_ZH.md"), + "utf8", + ); + assert.match(clawHubReadme, /^# ReMe memory for OpenClaw/m); + assert.doesNotMatch(clawHubReadme, /DeepSeek Harness/); + assert.match(clawHubReadmeZh, /^# OpenClaw 的 ReMe 长期记忆插件/m); + assert.doesNotMatch(clawHubReadmeZh, /DeepSeek Harness/); + assert.equal( + JSON.parse( + await readFile( + path.join(clawHubPackageDirectory, "package.json"), + "utf8", + ), + ).version, + sourceManifest.version, + ); } finally { await rm(temporaryDirectory, { force: true, recursive: true }); } diff --git a/typescript/tests/openclaw.test.mjs b/typescript/tests/openclaw.test.mjs index e29dfff2..e41ef8f0 100644 --- a/typescript/tests/openclaw.test.mjs +++ b/typescript/tests/openclaw.test.mjs @@ -41,7 +41,7 @@ test("keeps the runtime schema aligned with the OpenClaw manifest", async () => await readFile(new URL("../openclaw.plugin.json", import.meta.url), "utf8"), ); assert.deepEqual(manifest.configSchema, OPENCLAW_CONFIG_SCHEMA); - assert.deepEqual(manifest.configUiHints, OPENCLAW_CONFIG_UI_HINTS); + assert.deepEqual(manifest.uiHints, OPENCLAW_CONFIG_UI_HINTS); assert.deepEqual(manifest.contracts.tools, ["reme_search"]); assert.equal(manifest.activation.onStartup, true); });