@2en/clawly-plugins 1.7.0 → 1.7.1

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.
package/channel.ts CHANGED
@@ -2,9 +2,10 @@
2
2
  * Clawly cron delivery channel — registers a minimal channel plugin so that
3
3
  * cron jobs using `delivery: { channel: "clawly-cron" }` have a valid target.
4
4
  *
5
- * When a cron job delivers text, the channel injects it into the main session
6
- * transcript via chat.inject (no LLM round-trip) and sends a push notification
7
- * if the mobile client is offline.
5
+ * When a cron job delivers text, the channel routes it through the agent via
6
+ * `openclaw agent --message` (LLM round-trip) so the response appears as a
7
+ * natural assistant message, then sends a push notification if the mobile
8
+ * client is offline.
8
9
  */
9
10
 
10
11
  import {$} from 'zx'
@@ -14,17 +15,13 @@ import {isClientOnline} from './gateway/presence'
14
15
 
15
16
  $.verbose = false
16
17
 
17
- async function injectAndNotify(text: string, api: PluginApi): Promise<void> {
18
+ async function deliverAndNotify(text: string, api: PluginApi): Promise<void> {
18
19
  try {
19
- // Resolve main session key from config
20
20
  const config = api.pluginConfig as Record<string, unknown> | undefined
21
21
  const agentId = typeof config?.agentId === 'string' ? config.agentId : 'clawly'
22
- const mainKey = typeof config?.mainKey === 'string' ? config.mainKey : 'main'
23
- const sessionKey = `agent:${agentId}:${mainKey}`
24
22
 
25
- const params = JSON.stringify({sessionKey, message: text})
26
- await $`openclaw gateway call chat.inject --json --params ${params}`
27
- api.logger.info(`clawly-cron: injected message (${text.length} chars) into ${sessionKey}`)
23
+ await $`openclaw agent --agent ${agentId} --message ${text}`
24
+ api.logger.info(`clawly-cron: delivered message (${text.length} chars) to agent ${agentId}`)
28
25
 
29
26
  // Push notification if client is offline
30
27
  const online = await isClientOnline()
@@ -34,7 +31,7 @@ async function injectAndNotify(text: string, api: PluginApi): Promise<void> {
34
31
  }
35
32
  } catch (err) {
36
33
  const msg = err instanceof Error ? err.message : String(err)
37
- api.logger.error(`clawly-cron: failed to inject message — ${msg}`)
34
+ api.logger.error(`clawly-cron: failed to deliver message — ${msg}`)
38
35
  }
39
36
  }
40
37
 
@@ -77,7 +74,7 @@ export function registerClawlyCronChannel(api: PluginApi) {
77
74
  api.logger.info(`clawly-cron sendText: text=${text.length} chars`)
78
75
  if (text) {
79
76
  // Fire-and-forget — delivery system may not await sendText
80
- injectAndNotify(text, api).catch(() => {})
77
+ deliverAndNotify(text, api).catch(() => {})
81
78
  }
82
79
  return {channel: 'clawly-cron', messageId: crypto.randomUUID()}
83
80
  },
@@ -0,0 +1,190 @@
1
+ # 插件版本管理
2
+
3
+ ## 产品需求
4
+
5
+ ### 背景
6
+
7
+ OpenClaw 插件以 npm 包形式安装在 gateway 的 `extensions/` 目录下。用户需要一种方式来查询已安装插件的版本、检测是否有更新、并执行更新操作——无需手动 SSH 到服务器执行 CLI 命令。
8
+
9
+ ### 目标
10
+
11
+ - 通过 Gateway RPC 远程查询任意插件的已安装版本和 npm 最新版本
12
+ - 支持多种更新策略(常规更新、全新安装、强制重装)
13
+ - 更新后可选自动重启 gateway,使新版本立即生效
14
+
15
+ ### 功能需求
16
+
17
+ 1. **版本查询** — 给定 `pluginId` 和 `npmPkgName`,返回已安装版本、npm 最新版本、所有可用版本列表,以及是否有更新。
18
+ 2. **插件更新** — 给定 `pluginId`、`npmPkgName` 和更新策略,执行安装或更新操作。支持指定目标版本和更新后自动重启。
19
+ 3. **强制重装** — `force` 策略会备份插件配置、删除扩展目录、全新安装、再恢复配置,解决损坏安装或缓存问题。
20
+
21
+ ### 约束
22
+
23
+ - 仅通过 Gateway WebSocket RPC 调用,不暴露 HTTP 端点。
24
+ - 版本比较仅支持标准 semver,预发布版本(如 `1.0.0-beta.1`)不会被标记为可用更新。
25
+ - npm registry 查询结果缓存 5 分钟(LRU,最多 5 个包),避免频繁请求。
26
+
27
+ ---
28
+
29
+ ## 技术设计
30
+
31
+ ### 概览
32
+
33
+ 插件版本管理通过 `gateway/plugins.ts` 注册两个 Gateway RPC 方法,分别负责版本查询和更新执行。
34
+
35
+ ### 数据流
36
+
37
+ ```
38
+ Mobile / Agent
39
+
40
+ ├─ clawly.plugins.version({ pluginId, npmPkgName })
41
+ │ ├─ 读取 extensions/<pluginId>/package.json → 已安装版本
42
+ │ ├─ npm view <npmPkgName> --json → 最新版本 + 所有版本
43
+ │ └─ isUpdateAvailable(current, latest) → 是否有更新
44
+
45
+ └─ clawly.plugins.update({ pluginId, npmPkgName, strategy, targetVersion?, restart? })
46
+ ├─ strategy=install → openclaw plugins install <pkg>
47
+ ├─ strategy=update → openclaw plugins update <pluginId>
48
+ └─ strategy=force → 备份配置 → 删除目录 → 安装 → 恢复配置
49
+ └─ restart=true → openclaw gateway restart
50
+ ```
51
+
52
+ ### Gateway RPC 方法
53
+
54
+ | 方法 | 参数 | 返回 |
55
+ |---|---|---|
56
+ | `clawly.plugins.version` | `{ pluginId, npmPkgName }` | `VersionResult` |
57
+ | `clawly.plugins.update` | `{ pluginId, npmPkgName, strategy, targetVersion?, restart? }` | `UpdateResult` |
58
+
59
+ ### 关键类型
60
+
61
+ ```typescript
62
+ // clawly.plugins.version 返回
63
+ interface VersionResult {
64
+ pluginVersion: string | null // 已安装版本(来自 package.json)
65
+ npmPackageVersion: string | null // 同上(兼容字段)
66
+ latestNpmVersion: string | null // npm registry 最新稳定版
67
+ allNpmVersions: string[] // npm registry 所有已发布版本
68
+ updateAvailable: boolean // 是否有可用更新
69
+ error?: string // 查询错误(如 npm 不可达)
70
+ }
71
+
72
+ // clawly.plugins.update 返回
73
+ interface UpdateResult {
74
+ ok: boolean // 操作是否成功
75
+ strategy: string // 实际使用的策略
76
+ output?: string // CLI 输出
77
+ restarted?: boolean // 是否已重启 gateway
78
+ error?: string // 失败时的错误信息
79
+ }
80
+ ```
81
+
82
+ ### 更新策略
83
+
84
+ | 策略 | 行为 | 适用场景 |
85
+ |---|---|---|
86
+ | `install` | `openclaw plugins install <pkg>[@version]` | 首次安装或指定版本安装 |
87
+ | `update` | `openclaw plugins update <pluginId>` | 常规更新到最新版 |
88
+ | `force` | 备份配置 → 删除扩展目录 → 全新安装 → 恢复配置 | 安装损坏、缓存问题、降级 |
89
+
90
+ ### `force` 策略详细流程
91
+
92
+ 1. 读取 `openclaw.json` 中 `plugins.entries.<pluginId>` 的用户配置并保存
93
+ 2. 从 `openclaw.json` 删除该插件条目(确保干净安装)
94
+ 3. 删除 `extensions/<pluginId>/` 目录
95
+ 4. 执行 `openclaw plugins install <pkg>[@version]`
96
+ 5. 将保存的用户配置合并回 `openclaw.json`
97
+
98
+ ### 版本检测逻辑
99
+
100
+ 已安装版本从 `<stateDir>/extensions/<pluginId>/package.json` 读取。npm 最新版本通过 `npm view <pkg> --json` 获取。版本比较使用内置 `isUpdateAvailable(current, latest)`:
101
+
102
+ - 仅比较标准 semver(`major.minor.patch`)
103
+ - `latest` 为预发布版本时返回 `false`(不推荐更新到预发布版)
104
+ - `latest > current` 时返回 `true`
105
+
106
+ ### 缓存
107
+
108
+ npm registry 查询结果通过 `LruCache` 缓存:
109
+
110
+ - **容量**: 5 个包
111
+ - **TTL**: 5 分钟
112
+ - 更新操作成功后自动失效对应包的缓存
113
+
114
+ ### 状态目录解析
115
+
116
+ `stateDir` 按以下优先级解析:
117
+
118
+ 1. `api.runtime.state.resolveStateDir(process.env)`(插件 API 提供)
119
+ 2. `OPENCLAW_STATE_DIR` 环境变量
120
+ 3. 空字符串(回退,版本查询将返回 `null`)
121
+
122
+ ### 调用示例
123
+
124
+ 查询版本:
125
+
126
+ ```json
127
+ {
128
+ "method": "clawly.plugins.version",
129
+ "params": {
130
+ "pluginId": "clawly-plugins",
131
+ "npmPkgName": "@AISomething/clawly-plugins"
132
+ }
133
+ }
134
+ ```
135
+
136
+ 常规更新:
137
+
138
+ ```json
139
+ {
140
+ "method": "clawly.plugins.update",
141
+ "params": {
142
+ "pluginId": "clawly-plugins",
143
+ "npmPkgName": "@AISomething/clawly-plugins",
144
+ "strategy": "update"
145
+ }
146
+ }
147
+ ```
148
+
149
+ 强制重装并重启 gateway:
150
+
151
+ ```json
152
+ {
153
+ "method": "clawly.plugins.update",
154
+ "params": {
155
+ "pluginId": "clawly-plugins",
156
+ "npmPkgName": "@AISomething/clawly-plugins",
157
+ "strategy": "force",
158
+ "restart": true
159
+ }
160
+ }
161
+ ```
162
+
163
+ 安装指定版本:
164
+
165
+ ```json
166
+ {
167
+ "method": "clawly.plugins.update",
168
+ "params": {
169
+ "pluginId": "clawly-plugins",
170
+ "npmPkgName": "@AISomething/clawly-plugins",
171
+ "strategy": "install",
172
+ "targetVersion": "1.2.3"
173
+ }
174
+ }
175
+ ```
176
+
177
+ ### 错误处理
178
+
179
+ - 参数缺失返回 `{ code: "invalid_params" }` 错误。
180
+ - 无效 strategy 返回 `{ code: "invalid_params" }` 错误。
181
+ - npm 查询失败时,版本查询仍返回成功,`error` 字段包含错误信息。
182
+ - 更新操作失败时返回 `{ ok: false, error: "..." }`。
183
+ - `force` 策略在恢复配置失败时仅打印警告日志,不影响整体操作结果。
184
+
185
+ ### 不在范围内
186
+
187
+ - 批量更新多个插件。
188
+ - 插件安装/卸载(使用 `clawhub2gateway` 或 CLI)。
189
+ - 自动定时检查更新。
190
+ - 版本回滚历史记录。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2en/clawly-plugins",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "repository": {