fix: publish an OpenClaw-specific ClawHub artifact (#513)

* fix: publish an OpenClaw-specific ClawHub artifact

* fix: enable ClawHub publication
This commit is contained in:
jinliyl 2026-09-01 20:27:47 +08:00 committed by GitHub
parent 65cb4ebdd6
commit f9a45a319a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 310 additions and 14 deletions

View file

@ -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 }}

View file

@ -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 -- <output-directory>` 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`.

View file

@ -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 `<reme-context>` 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.

View file

@ -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 触发的运行。
- 使用 `<reme-context>` 包裹召回内容,并将其标记为不可信历史数据。
## 环境要求
- 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 许可证发布。

View file

@ -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`

View file

@ -11,7 +11,7 @@
"contracts": {
"tools": ["reme_search"]
},
"configUiHints": {
"uiHints": {
"endpoint": {
"label": "ReMe endpoint",
"placeholder": "http://127.0.0.1:2333"

View file

@ -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"

View file

@ -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",

View file

@ -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 });
}

View file

@ -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 });
}

View file

@ -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);
});