@workclaw/openclaw-workclaw 1.0.17 → 1.0.18

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.
Files changed (51) hide show
  1. package/README.md +21 -1
  2. package/index.ts +210 -210
  3. package/openclaw.plugin.json +1 -0
  4. package/package.json +11 -4
  5. package/setup-entry.ts +6 -0
  6. package/skills/openclaw-workclaw-cron/SKILL.md +45 -28
  7. package/src/accounts.ts +62 -37
  8. package/src/api/accounts-api.ts +88 -89
  9. package/src/api/prompts-api.ts +70 -77
  10. package/src/api/session-api.ts +99 -108
  11. package/src/api/skills-api.ts +35 -37
  12. package/src/api/workspace.ts +27 -29
  13. package/src/channel.ts +200 -202
  14. package/src/config-schema.ts +9 -9
  15. package/src/connection/workclaw-client.ts +554 -567
  16. package/src/gateway/agent-handlers.ts +392 -426
  17. package/src/gateway/config-writer.ts +228 -243
  18. package/src/gateway/message-context.ts +534 -362
  19. package/src/gateway/message-dispatcher.ts +529 -489
  20. package/src/gateway/reconnect.ts +217 -113
  21. package/src/gateway/skills-handler.ts +408 -472
  22. package/src/gateway/skills-list-handler.ts +9 -9
  23. package/src/gateway/tools-list-handler.ts +70 -72
  24. package/src/gateway/workclaw-gateway.ts +328 -486
  25. package/src/media/upload.ts +83 -94
  26. package/src/outbound/index.ts +57 -55
  27. package/src/outbound/workclaw-sender.ts +134 -133
  28. package/src/runtime.ts +291 -194
  29. package/src/send.ts +1 -1
  30. package/src/tools/openclaw-workclaw-cron/api/index.ts +6 -6
  31. package/src/tools/openclaw-workclaw-cron/src/add/params.ts +20 -19
  32. package/src/tools/openclaw-workclaw-cron/src/add/sync.ts +2 -2
  33. package/src/tools/openclaw-workclaw-cron/src/disable/params.ts +1 -1
  34. package/src/tools/openclaw-workclaw-cron/src/disable/sync.ts +3 -3
  35. package/src/tools/openclaw-workclaw-cron/src/enable/params.ts +1 -1
  36. package/src/tools/openclaw-workclaw-cron/src/enable/sync.ts +3 -3
  37. package/src/tools/openclaw-workclaw-cron/src/notify/sync.ts +2 -2
  38. package/src/tools/openclaw-workclaw-cron/src/remove/params.ts +1 -1
  39. package/src/tools/openclaw-workclaw-cron/src/remove/sync.ts +3 -3
  40. package/src/tools/openclaw-workclaw-cron/src/update/params.ts +195 -197
  41. package/src/tools/openclaw-workclaw-cron/src/update/sync.ts +4 -4
  42. package/src/tools/openclaw-workclaw-system/src/get/index.ts +2 -2
  43. package/src/tools/openclaw-workclaw-system/src/token/index.ts +4 -4
  44. package/src/types.ts +38 -40
  45. package/src/utils/content.ts +16 -21
  46. package/tests/accounts.test.ts +285 -0
  47. package/tests/message-context.test.ts +313 -0
  48. package/tests/reconnect.test.ts +257 -0
  49. package/tests/workclaw-client.test.ts +112 -0
  50. package/tsconfig.json +8 -5
  51. package/vitest.config.ts +8 -0
package/README.md CHANGED
@@ -224,7 +224,6 @@ openclaw-workclaw/
224
224
  调用:openclaw-workclaw-cron-add-params({ time: "5m", name: "喝水提醒", message: "该喝水了" })
225
225
 
226
226
  步骤2:使用返回的 cronParams 调用 OpenClaw cron 工具
227
-
228
227
  步骤3:从 cron 工具返回值中提取 jobId(如 "7382945612345")
229
228
 
230
229
  步骤4:调用后端同步
@@ -233,6 +232,27 @@ openclaw-workclaw/
233
232
  回复:"⏰ 好的,5分钟后提醒你喝水~"
234
233
  ```
235
234
 
235
+ ### add-params 参数说明
236
+
237
+ | 参数 | 类型 | 必填 | 说明 |
238
+ |------|------|------|------|
239
+ | time | string | 是 | 时间描述,支持相对时间(5m、1h)和 cron 表达式 |
240
+ | name | string | 是 | 任务名称 |
241
+ | message | string | 是 | 提醒内容 |
242
+
243
+ > **注意**:`account` 参数不需要传入,会自动从 `ctx.agentAccountId` 获取
244
+
245
+ ### update-params 参数说明
246
+
247
+ | 参数 | 类型 | 必填 | 说明 |
248
+ |------|------|------|------|
249
+ | jobId | string | 是 | OpenClaw 任务 ID |
250
+ | time | string | 是 | 时间描述,支持相对时间(5m、1h)和 cron 表达式 |
251
+ | name | string | 是 | 任务名称 |
252
+ | message | string | 是 | 提醒内容 |
253
+
254
+ > **注意**:`account` 参数不需要传入,会自动从 `ctx.agentAccountId` 获取
255
+
236
256
  ## 🌐 HTTP API
237
257
 
238
258
  插件提供以下 HTTP API 接口:
package/index.ts CHANGED
@@ -1,302 +1,302 @@
1
- import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
2
- import { mkdir, readFile, writeFile } from 'node:fs/promises'
3
- import path from 'node:path'
4
- import { fileURLToPath } from 'node:url'
5
-
6
- import { emptyPluginConfigSchema } from 'openclaw/plugin-sdk'
7
- import { createAccountsApiHandler } from './src/api/accounts-api.js'
8
- import { createPromptsApiHandler } from './src/api/prompts-api.js'
9
- import { createSessionApiHandler } from './src/api/session-api.js'
10
- import { createSkillsApiHandler } from './src/api/skills-api.js'
11
- import { isDefaultWorkspace, resolveWorkspaceDir } from './src/api/workspace.js'
12
- import { openclawWorkclawPlugin } from './src/channel.js'
13
- import { sendMessageOpenclawWorkclaw } from './src/outbound/index.js'
14
- import { deleteToolContext, getOpenclawWorkclawRuntime, getToolContext, getToolResultHint, setOpenclawWorkclawRuntime, setToolCallIdToRunIdMapping } from './src/runtime.js'
15
-
16
- import { registerAllOpenclawWorkclawCronTools } from './src/tools/openclaw-workclaw-cron/index.js'
17
- import { registerAllOpenclawWorkclawSystemTools } from './src/tools/openclaw-workclaw-system/index.js'
1
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
+ import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
3
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
4
+ // @ts-ignore - types from openclaw 4.2, local dev uses 3.13
5
+ import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
6
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
7
+ import path from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+
10
+ import { workclawPlugin } from "./src/channel.js";
11
+ import {
12
+ setWorkclawRuntime,
13
+ getWorkclawRuntime,
14
+ getToolResultHint,
15
+ } from "./src/runtime.js";
16
+ import { resolveWorkspaceDir, isDefaultWorkspace } from "./src/api/workspace.js";
17
+ import { createPromptsApiHandler } from "./src/api/prompts-api.js";
18
+ import { createAccountsApiHandler } from "./src/api/accounts-api.js";
19
+ import { createSessionApiHandler } from "./src/api/session-api.js";
20
+ import { createSkillsApiHandler } from "./src/api/skills-api.js";
21
+ import { registerAllOpenclawWorkclawCronTools } from "./src/tools/openclaw-workclaw-cron/index.js";
22
+ import {
23
+ getToolContext,
24
+ setToolCallIdToRunIdMapping,
25
+ deleteToolContext,
26
+ } from "./src/runtime.js";
27
+ import { sendMessageWorkclaw } from "./src/outbound/index.js";
28
+
29
+ // ============================================================================
30
+ // Tool output formatting helpers
31
+ // ============================================================================
18
32
 
19
- /**
20
- * 根据工具名称格式化参数字符串
21
- */
22
33
  function formatToolParams(toolName: string | undefined, params: unknown): string {
23
- if (!params || typeof params !== 'object') {
24
- return ''
34
+ if (!params || typeof params !== "object") {
35
+ return "";
25
36
  }
26
37
 
27
- const normalizedToolName = (toolName || '').toLowerCase().trim()
28
- const p = params as Record<string, unknown>
38
+ const normalizedToolName = (toolName || "").toLowerCase().trim();
39
+ const p = params as Record<string, unknown>;
29
40
 
30
41
  try {
31
- // exec/bash/shell: 显示 command
32
- if (normalizedToolName === 'exec' || normalizedToolName === 'bash' || normalizedToolName === 'shell') {
33
- const cmd = p.command || p.cmd
34
- if (cmd)
35
- return `command: ${String(cmd).slice(0, 200)}`
36
- }
37
- // read/file_read/read: 显示 path
38
- else if (normalizedToolName === 'read' || normalizedToolName === 'file_read' || normalizedToolName === 'view') {
39
- const path = p.path
40
- if (path)
41
- return `path: ${path}`
42
- }
43
- // write/file_write/write/edit/patch: 显示 path
44
- else if (normalizedToolName === 'write' || normalizedToolName === 'file_write' || normalizedToolName === 'edit' || normalizedToolName === 'patch') {
45
- const path = p.path
46
- if (path)
47
- return `path: ${path}`
48
- }
49
- // web_fetch/fetch/http_get/http_request/open_url/browser_open: 显示 url
50
- else if (
51
- normalizedToolName === 'web_fetch' || normalizedToolName === 'fetch'
52
- || normalizedToolName === 'http_get' || normalizedToolName === 'http_request'
53
- || normalizedToolName === 'open_url' || normalizedToolName === 'browser_open'
54
- || normalizedToolName === 'open'
42
+ if (normalizedToolName === "exec" || normalizedToolName === "bash" || normalizedToolName === "shell") {
43
+ const cmd = p.command || p.cmd;
44
+ if (cmd) return `command: ${String(cmd).slice(0, 200)}`;
45
+ } else if (
46
+ normalizedToolName === "read" ||
47
+ normalizedToolName === "file_read" ||
48
+ normalizedToolName === "view"
55
49
  ) {
56
- const url = p.url
57
- if (url)
58
- return `url: ${url}`
59
- }
60
- // search/web_search/google_search/browser_search: 显示 query
61
- else if (
62
- normalizedToolName === 'search' || normalizedToolName === 'web_search'
63
- || normalizedToolName === 'google_search' || normalizedToolName === 'browser_search'
64
- || normalizedToolName === 'browser.search'
50
+ const p2 = p.path;
51
+ if (p2) return `path: ${p2}`;
52
+ } else if (
53
+ normalizedToolName === "write" ||
54
+ normalizedToolName === "file_write" ||
55
+ normalizedToolName === "edit" ||
56
+ normalizedToolName === "patch"
65
57
  ) {
66
- const query = p.query || p.q || p.search
67
- if (query)
68
- return `query: ${query}`
69
- }
70
- // image_generate/generate_image/dalle/text_to_image: 显示 prompt
71
- else if (
72
- normalizedToolName === 'image_generate' || normalizedToolName === 'generate_image'
73
- || normalizedToolName === 'dalle' || normalizedToolName === 'text_to_image'
58
+ const p2 = p.path;
59
+ if (p2) return `path: ${p2}`;
60
+ } else if (
61
+ normalizedToolName === "web_fetch" ||
62
+ normalizedToolName === "fetch" ||
63
+ normalizedToolName === "http_get" ||
64
+ normalizedToolName === "http_request" ||
65
+ normalizedToolName === "open_url" ||
66
+ normalizedToolName === "browser_open" ||
67
+ normalizedToolName === "open"
74
68
  ) {
75
- const prompt = p.prompt
76
- if (prompt)
77
- return `prompt: ${String(prompt).slice(0, 200)}`
78
- }
79
- // code_interpreter/python/jupyter: 显示 code
80
- else if (
81
- normalizedToolName === 'code_interpreter' || normalizedToolName === 'python'
82
- || normalizedToolName === 'jupyter'
69
+ const u = p.url;
70
+ if (u) return `url: ${u}`;
71
+ } else if (
72
+ normalizedToolName === "search" ||
73
+ normalizedToolName === "web_search" ||
74
+ normalizedToolName === "google_search" ||
75
+ normalizedToolName === "browser_search" ||
76
+ normalizedToolName === "browser.search"
83
77
  ) {
84
- const code = p.code
85
- if (code)
86
- return `code: ${String(code).slice(0, 200)}`
87
- }
88
- // mcp_deliver/deliver: 显示 target
89
- else if (normalizedToolName === 'mcp_deliver' || normalizedToolName === 'deliver') {
90
- const target = p.target || p.to
91
- if (target)
92
- return `target: ${target}`
93
- }
94
- // 其他工具:显示所有 key=value
95
- else {
96
- const entries = Object.entries(p)
78
+ const q = p.query || p.q || p.search;
79
+ if (q) return `query: ${q}`;
80
+ } else if (
81
+ normalizedToolName === "image_generate" ||
82
+ normalizedToolName === "generate_image" ||
83
+ normalizedToolName === "dalle" ||
84
+ normalizedToolName === "text_to_image"
85
+ ) {
86
+ const prompt = p.prompt;
87
+ if (prompt) return `prompt: ${String(prompt).slice(0, 200)}`;
88
+ } else if (
89
+ normalizedToolName === "code_interpreter" ||
90
+ normalizedToolName === "python" ||
91
+ normalizedToolName === "jupyter"
92
+ ) {
93
+ const code = p.code;
94
+ if (code) return `code: ${String(code).slice(0, 200)}`;
95
+ } else if (normalizedToolName === "mcp_deliver" || normalizedToolName === "deliver") {
96
+ const target = p.target || p.to;
97
+ if (target) return `target: ${target}`;
98
+ } else {
99
+ const entries = Object.entries(p);
97
100
  if (entries.length > 0) {
98
101
  return entries
99
102
  .map(([k, v]) => {
100
- const vStr = typeof v === 'string' ? v : JSON.stringify(v)
101
- return vStr.length > 100 ? `${k}: ${vStr.slice(0, 100)}...` : `${k}: ${vStr}`
103
+ const vStr = typeof v === "string" ? v : JSON.stringify(v);
104
+ return vStr.length > 100 ? `${k}: ${vStr.slice(0, 100)}...` : `${k}: ${vStr}`;
102
105
  })
103
- .join('\n')
106
+ .join("\n");
104
107
  }
105
108
  }
106
- }
107
- catch {
108
- return String(params)
109
+ } catch {
110
+ return String(params);
109
111
  }
110
112
 
111
- return ''
113
+ return "";
112
114
  }
113
115
 
114
- /**
115
- * 组合工具输出内容(只显示参数)
116
- */
117
116
  function formatToolOutput(toolName: string | undefined, params: unknown, _result: unknown): string {
118
- return formatToolParams(toolName, params)
117
+ return formatToolParams(toolName, params);
119
118
  }
120
119
 
121
- // 注册工具执行相关的 hook
122
- function registerToolHooks(api: OpenClawPluginApi): void {
123
- // agent_end: 对话结束时清理 runId 映射
124
- api.on('agent_end', (event: any, ctx: any) => {
125
- const { runId } = ctx || {}
120
+ // ============================================================================
121
+ // Tool hooks
122
+ // ============================================================================
123
+
124
+ function registerToolHooks(api: OpenClawPluginApi) {
125
+ api.on("agent_end", (event: any, ctx: any) => {
126
+ const { runId } = ctx || {};
126
127
  if (runId) {
127
- deleteToolContext(runId)
128
- api.logger?.info?.(`[ToolHook] agent_end runId=${runId} cleaned up`)
128
+ deleteToolContext(runId);
129
+ api.logger?.info?.(`[ToolHook] agent_end runId=${runId} cleaned up`);
129
130
  }
130
- return event
131
- })
131
+ return event;
132
+ });
132
133
 
133
- api.on('before_tool_call', (event: any) => {
134
- api.logger?.info?.(`[ToolHook] before_tool_call event=${JSON.stringify(event)}`)
135
- })
134
+ api.on("before_tool_call", (event: any, _ctx: any) => {
135
+ api.logger?.info?.(`[ToolHook] before_tool_call event=${JSON.stringify(event)}`);
136
+ });
136
137
 
137
- // after_tool_call: 获取工具执行结果并推送
138
- api.on('after_tool_call', (event: any) => {
139
- api.logger?.info?.(`[ToolHook] after_tool_call event=${JSON.stringify(event)}`)
140
- const { runId, toolCallId, toolName, params, result } = event || {}
141
- const toolCtx = runId ? getToolContext(runId) : undefined
138
+ api.on("after_tool_call", (event: any, _ctx: any) => {
139
+ api.logger?.info?.(`[ToolHook] after_tool_call event=${JSON.stringify(event)}`);
140
+ const { runId, toolCallId, toolName, params, result } = event || {};
141
+ const toolCtx = runId ? getToolContext(runId) : undefined;
142
142
 
143
143
  if (toolCtx && result) {
144
- const content = formatToolOutput(toolName, params, result)
144
+ const content = formatToolOutput(toolName, params, result);
145
145
  if (content) {
146
146
  try {
147
- const cfg = getOpenclawWorkclawRuntime().config.loadConfig()
147
+ const cfg = getWorkclawRuntime().config.loadConfig();
148
148
  const formattedToolOutput = {
149
149
  name: getToolResultHint(toolName),
150
- toolName,
151
- content,
152
- state: 'result',
153
- }
154
- sendMessageOpenclawWorkclaw({
150
+ toolName: toolName,
151
+ content: content,
152
+ state: "result",
153
+ };
154
+ sendMessageWorkclaw({
155
155
  cfg,
156
156
  to: toolCtx.target,
157
157
  text: JSON.stringify(formattedToolOutput),
158
- msgType: '26',
158
+ msgType: "26",
159
159
  accountId: toolCtx.accountId,
160
160
  openConversationId: toolCtx.openConversationId,
161
161
  agentId: toolCtx.agentId,
162
162
  replyToMessageId: toolCtx.replyToMessageId,
163
163
  }).catch((err) => {
164
- api.logger?.error?.(`[ToolHook] after_tool_call push failed: ${err}`)
165
- })
166
- api.logger?.info?.(`[ToolHook] after_tool_call pushed for toolName=${toolName}`)
167
- }
168
- catch (err) {
169
- api.logger?.error?.(`[ToolHook] after_tool_call push failed: ${err}`)
164
+ api.logger?.error?.(`[ToolHook] after_tool_call push failed: ${err}`);
165
+ });
166
+ api.logger?.info?.(`[ToolHook] after_tool_call pushed for toolName=${toolName}`);
167
+ } catch (err) {
168
+ api.logger?.error?.(`[ToolHook] after_tool_call push failed: ${err}`);
170
169
  }
171
170
  }
172
171
  }
173
172
 
174
- // 建立 toolCallId -> runId 映射(备用,用于 tool_result_persist)
175
173
  if (runId && toolCallId) {
176
- setToolCallIdToRunIdMapping(toolCallId, runId)
174
+ setToolCallIdToRunIdMapping(toolCallId, runId);
177
175
  }
178
176
 
179
- return event
180
- })
177
+ return event;
178
+ });
181
179
 
182
- // tool_result_persist: 仅返回消息,不推送
183
- api.on('tool_result_persist', (event: any) => {
184
- return { message: event.message }
185
- })
180
+ api.on("tool_result_persist", (event: any, _ctx: any) => {
181
+ return { message: event.message };
182
+ });
186
183
 
187
- api.logger?.info?.(`[ToolHook] Tool execution hooks registered`)
184
+ api.logger?.info?.(`[ToolHook] Tool execution hooks registered`);
188
185
  }
189
186
 
190
- export { openclawWorkclawPlugin } from './src/channel.js'
191
- export {
192
- getMessageOpenclawWorkclaw,
193
- sendMessageOpenclawWorkclaw,
194
- } from './src/outbound/index.js'
187
+ // ============================================================================
188
+ // Templates
189
+ // ============================================================================
195
190
 
196
- const templateNames = ['SOUL.md', 'IDENTITY.md'] as const
191
+ const templateNames = ["SOUL.md", "IDENTITY.md"] as const;
197
192
 
198
- async function writeFileIfMissing(filePath: string, content: string): Promise<void> {
193
+ /**
194
+ * 获取插件所在目录,兼容 Windows 下的 file:// URL 问题。
195
+ * 4.2 的插件加载器在 Windows 上可能传入不带协议前缀的路径。
196
+ */
197
+ function getPluginDir(): string {
199
198
  try {
200
- await writeFile(filePath, content, { encoding: 'utf-8', flag: 'wx' })
199
+ const url = import.meta.url;
200
+ if (!url.startsWith("file://")) {
201
+ // Windows 路径被直接传入(不带 file:// 前缀),直接取目录
202
+ return path.dirname(url);
203
+ }
204
+ return path.dirname(fileURLToPath(url));
205
+ } catch {
206
+ return process.cwd();
201
207
  }
202
- catch (error) {
208
+ }
209
+
210
+ async function writeFileIfMissing(filePath: string, content: string) {
211
+ try {
212
+ await writeFile(filePath, content, { encoding: "utf-8", flag: "wx" });
213
+ } catch (error) {
203
214
  if (
204
- error
205
- && typeof error === 'object'
206
- && 'code' in error
207
- && (error as NodeJS.ErrnoException).code === 'EEXIST'
215
+ error &&
216
+ typeof error === "object" &&
217
+ "code" in error &&
218
+ (error as NodeJS.ErrnoException).code === "EEXIST"
208
219
  ) {
209
- return
220
+ return;
210
221
  }
211
- throw error
222
+ throw error;
212
223
  }
213
224
  }
214
225
 
215
- async function releaseTemplates(api: OpenClawPluginApi): Promise<void> {
216
- const workspaceDir = resolveWorkspaceDir(api)
226
+ async function releaseTemplates(api: OpenClawPluginApi) {
227
+ const workspaceDir = resolveWorkspaceDir(api);
217
228
  // 模板文件在根目录的 templates/ 中,而不是 dist/templates/
218
- const currentDir = path.dirname(fileURLToPath(import.meta.url))
219
- const templatesDir = path.join(currentDir.endsWith('dist') ? path.dirname(currentDir) : currentDir, 'templates')
220
- const shouldOverwrite = isDefaultWorkspace(api)
229
+ const currentDir = getPluginDir();
230
+ const templatesDir = path.join(
231
+ currentDir.endsWith("dist") ? path.dirname(currentDir) : currentDir,
232
+ "templates",
233
+ );
234
+ const shouldOverwrite = isDefaultWorkspace(api);
221
235
 
222
- await mkdir(workspaceDir, { recursive: true })
236
+ await mkdir(workspaceDir, { recursive: true });
223
237
 
224
238
  await Promise.all(
225
239
  templateNames.map(async (name) => {
226
- const sourcePath = path.join(templatesDir, name)
227
- const targetPath = path.join(workspaceDir, name)
228
- const content = await readFile(sourcePath, 'utf-8')
240
+ const sourcePath = path.join(templatesDir, name);
241
+ const targetPath = path.join(workspaceDir, name);
242
+ const content = await readFile(sourcePath, "utf-8");
229
243
  if (shouldOverwrite) {
230
- await writeFile(targetPath, content, { encoding: 'utf-8' })
231
- }
232
- else {
233
- await writeFileIfMissing(targetPath, content)
244
+ await writeFile(targetPath, content, { encoding: "utf-8" });
245
+ } else {
246
+ await writeFileIfMissing(targetPath, content);
234
247
  }
235
248
  }),
236
- )
249
+ );
237
250
 
238
- api.logger.info(`openclaw-workclaw templates ready: ${workspaceDir}`)
251
+ api.logger.info(`openclaw-workclaw templates ready: ${workspaceDir}`);
239
252
  }
240
253
 
241
- const plugin = {
242
- id: 'openclaw-workclaw',
243
- name: 'openclaw-workclaw',
244
- description: '智小途 channel plugin',
245
- configSchema: emptyPluginConfigSchema(),
246
- register(api: OpenClawPluginApi) {
247
- setOpenclawWorkclawRuntime(api.runtime)
248
- api.registerChannel({ plugin: openclawWorkclawPlugin })
249
-
250
- // 注册定时任务工具
251
- registerAllOpenclawWorkclawCronTools(api)
254
+ // ============================================================================
255
+ // Plugin entry point
256
+ // ============================================================================
252
257
 
253
- // 注册系统工具
254
- registerAllOpenclawWorkclawSystemTools(api)
258
+ export default defineChannelPluginEntry({
259
+ id: "openclaw-workclaw",
260
+ name: "openclaw-workclaw",
261
+ description: "智小途 channel plugin",
262
+ configSchema: emptyPluginConfigSchema() as any,
263
+ plugin: workclawPlugin,
264
+ registerFull(api) {
265
+ setWorkclawRuntime(api.runtime);
255
266
 
256
- // 注册工具执行相关的 hook - 不再需要留在这里当注释
257
- registerToolHooks(api)
267
+ registerAllOpenclawWorkclawCronTools(api);
268
+ registerToolHooks(api);
258
269
 
259
270
  api.registerService({
260
- id: 'openclaw-workclaw-templates',
271
+ id: "openclaw-workclaw-templates",
261
272
  start: async () => {
262
- await releaseTemplates(api)
273
+ await releaseTemplates(api);
263
274
  },
264
- })
275
+ });
265
276
 
266
277
  api.registerHttpRoute({
267
- path: '/openclaw-workclaw/workspace/prompts',
278
+ path: "/openclaw-workclaw/workspace/prompts",
268
279
  handler: createPromptsApiHandler(api),
269
- auth: 'plugin',
270
- })
280
+ auth: "plugin",
281
+ });
271
282
 
272
283
  api.registerHttpRoute({
273
- path: '/openclaw-workclaw/accounts',
284
+ path: "/openclaw-workclaw/accounts",
274
285
  handler: createAccountsApiHandler(api),
275
- auth: 'plugin',
276
- })
286
+ auth: "plugin",
287
+ });
277
288
 
278
289
  api.registerHttpRoute({
279
- path: '/openclaw-workclaw/skills',
290
+ path: "/openclaw-workclaw/skills",
280
291
  handler: createSkillsApiHandler(api),
281
- auth: 'plugin',
282
- })
292
+ auth: "plugin",
293
+ });
283
294
 
284
- // 注册会话管理 API
285
295
  api.registerHttpRoute({
286
- path: '/openclaw-workclaw/sessions',
296
+ path: "/openclaw-workclaw/sessions",
287
297
  handler: createSessionApiHandler(api),
288
- auth: 'plugin',
289
- match: 'prefix',
290
- })
298
+ auth: "plugin",
299
+ match: "prefix",
300
+ });
291
301
  },
292
- }
293
-
294
- export function register(api: OpenClawPluginApi): void {
295
- plugin.register(api)
296
- }
297
-
298
- export function activate(api: OpenClawPluginApi): void {
299
- register(api)
300
- }
301
-
302
- export default plugin
302
+ });
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "id": "openclaw-workclaw",
3
+ "kind": "channel",
3
4
  "channels": ["openclaw-workclaw"],
4
5
  "skills": ["./skills"],
5
6
  "configSchema": {
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@workclaw/openclaw-workclaw",
3
3
  "type": "module",
4
- "version": "1.0.17",
5
- "description": "OpenClaw WorkClaw channel plugin",
6
- "license": "MIT",
4
+ "version": "1.0.18",
5
+ "description": "openclaw-workclaw channel plugin",
7
6
  "publishConfig": {
8
7
  "access": "public"
9
8
  },
10
9
  "peerDependencies": {
11
- "openclaw": "^2026.3.13",
10
+ "openclaw": "2026.4.14",
12
11
  "typescript": "^5.6.3"
13
12
  },
14
13
  "dependencies": {
@@ -16,10 +15,14 @@
16
15
  "ws": "^8.19.0",
17
16
  "zod": "^4.3.6"
18
17
  },
18
+ "devDependencies": {
19
+ "@types/node": "^20.0.0"
20
+ },
19
21
  "openclaw": {
20
22
  "extensions": [
21
23
  "./index.ts"
22
24
  ],
25
+ "setupEntry": "./setup-entry.ts",
23
26
  "channel": {
24
27
  "id": "openclaw-workclaw",
25
28
  "label": "openclaw-workclaw",
@@ -33,6 +36,10 @@
33
36
  "order": 35,
34
37
  "quickstartAllowFrom": true
35
38
  },
39
+ "compat": {
40
+ "pluginApi": "2026.4.14",
41
+ "minGatewayVersion": "2026.4.14"
42
+ },
36
43
  "install": {
37
44
  "npmSpec": "@workclaw/openclaw-workclaw",
38
45
  "localPath": "extensions/openclaw-workclaw",
package/setup-entry.ts ADDED
@@ -0,0 +1,6 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-ignore - types from openclaw 4.2, local dev uses 3.13
3
+ import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
4
+ import { workclawPlugin } from "./src/channel.js";
5
+
6
+ export default defineSetupPluginEntry(workclawPlugin);