@workclaw/openclaw-workclaw 1.0.0

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 (48) hide show
  1. package/README.md +325 -0
  2. package/index.ts +298 -0
  3. package/openclaw.plugin.json +10 -0
  4. package/package.json +43 -0
  5. package/skills/openclaw-workclaw-cron/SKILL.md +458 -0
  6. package/src/accounts.ts +287 -0
  7. package/src/api/accounts-api.ts +157 -0
  8. package/src/api/prompts-api.ts +123 -0
  9. package/src/api/session-api.ts +247 -0
  10. package/src/api/skills-api.ts +74 -0
  11. package/src/api/workspace.ts +43 -0
  12. package/src/channel.ts +227 -0
  13. package/src/config-schema.ts +110 -0
  14. package/src/connection/workclaw-client.ts +656 -0
  15. package/src/gateway/agent-handlers.ts +557 -0
  16. package/src/gateway/config-writer.ts +311 -0
  17. package/src/gateway/message-context.ts +422 -0
  18. package/src/gateway/message-dispatcher.ts +601 -0
  19. package/src/gateway/reconnect.ts +149 -0
  20. package/src/gateway/skills-handler.ts +759 -0
  21. package/src/gateway/skills-list-handler.ts +332 -0
  22. package/src/gateway/tools-list-handler.ts +162 -0
  23. package/src/gateway/workclaw-gateway.ts +521 -0
  24. package/src/media/upload.ts +168 -0
  25. package/src/outbound/index.ts +183 -0
  26. package/src/outbound/workclaw-sender.ts +157 -0
  27. package/src/runtime.ts +400 -0
  28. package/src/send.ts +1 -0
  29. package/src/tools/openclaw-workclaw-cron/api/index.ts +326 -0
  30. package/src/tools/openclaw-workclaw-cron/index.ts +39 -0
  31. package/src/tools/openclaw-workclaw-cron/src/add/params.ts +176 -0
  32. package/src/tools/openclaw-workclaw-cron/src/add/sync.ts +188 -0
  33. package/src/tools/openclaw-workclaw-cron/src/disable/params.ts +100 -0
  34. package/src/tools/openclaw-workclaw-cron/src/disable/sync.ts +127 -0
  35. package/src/tools/openclaw-workclaw-cron/src/enable/params.ts +100 -0
  36. package/src/tools/openclaw-workclaw-cron/src/enable/sync.ts +127 -0
  37. package/src/tools/openclaw-workclaw-cron/src/notify/sync.ts +148 -0
  38. package/src/tools/openclaw-workclaw-cron/src/remove/params.ts +109 -0
  39. package/src/tools/openclaw-workclaw-cron/src/remove/sync.ts +127 -0
  40. package/src/tools/openclaw-workclaw-cron/src/update/params.ts +197 -0
  41. package/src/tools/openclaw-workclaw-cron/src/update/sync.ts +161 -0
  42. package/src/tools/openclaw-workclaw-cron/types/index.ts +55 -0
  43. package/src/tools/openclaw-workclaw-cron/utils/index.ts +141 -0
  44. package/src/types.ts +60 -0
  45. package/src/utils/content.ts +40 -0
  46. package/templates/IDENTITY.md +14 -0
  47. package/templates/SOUL.md +0 -0
  48. package/tsconfig.json +11 -0
package/src/runtime.ts ADDED
@@ -0,0 +1,400 @@
1
+ import type { PluginRuntime } from 'openclaw/plugin-sdk'
2
+ import type { WebSocket } from 'undici'
3
+
4
+ let runtime: PluginRuntime | null = null
5
+
6
+ // 统一存储: key 可能是 accountId (per-account) 或 appKey (per-appKey)
7
+ const wsByKey = new Map<string, WebSocket>()
8
+
9
+ // per-appKey 模式额外需要的映射
10
+ const accountIdsByAppKey = new Map<string, Set<string>>()
11
+ const appKeyByAccountId = new Map<string, string>()
12
+ const reconnectSchedulers = new Map<string, any>()
13
+ const dispatchersByAppKey = new Map<string, Map<string, { ctx: any }>>()
14
+ const connectingApps = new Set<string>() // 正在连接中的 appKey,防止竞态条件
15
+
16
+ export function setOpenclawWorkclawRuntime(next: PluginRuntime): void {
17
+ runtime = next
18
+ }
19
+
20
+ export function getOpenclawWorkclawRuntime(): PluginRuntime {
21
+ if (!runtime) {
22
+ throw new Error('OpenclawWorkclaw runtime not initialized')
23
+ }
24
+ return runtime
25
+ }
26
+
27
+ export interface OpenclawWorkclawLogger {
28
+ info: (msg: string, ...args: any[]) => void
29
+ warn: (msg: string, ...args: any[]) => void
30
+ error: (msg: string, ...args: any[]) => void
31
+ debug: (msg: string, ...args: any[]) => void
32
+ }
33
+
34
+ let _logger: OpenclawWorkclawLogger | null = null
35
+
36
+ function _noop(): void { }
37
+
38
+ function _fallbackLogger(): OpenclawWorkclawLogger {
39
+ return {
40
+ // eslint-disable-next-line no-console
41
+ info: console.log.bind(console),
42
+ warn: console.warn.bind(console),
43
+ error: console.error.bind(console),
44
+ debug: _noop,
45
+ }
46
+ }
47
+
48
+ /** Store the gateway's log into runtime. Called from startAccount. */
49
+ export function setOpenclawWorkclawLoggerFromContext(log: OpenclawWorkclawLogger): void {
50
+ _logger = log
51
+ }
52
+
53
+ /** Get the shared logger. Returns ctx.log from gateway if set, otherwise a console fallback. */
54
+ export function getOpenclawWorkclawLogger(): OpenclawWorkclawLogger {
55
+ if (!_logger) {
56
+ return _fallbackLogger()
57
+ }
58
+ // If the stored logger's info is a noop, fall back to console
59
+ const l = _logger
60
+ if (l.info === _noop) {
61
+ return _fallbackLogger()
62
+ }
63
+ return _logger
64
+ }
65
+
66
+ /**
67
+ * Wrap a logger with a prefix automatically prepended to every message.
68
+ * All messages logged via the returned logger will have the prefix attached.
69
+ */
70
+ export function createLogger(prefix: string, log?: any): OpenclawWorkclawLogger {
71
+ const fallback = _fallbackLogger()
72
+ return {
73
+ info: log?.info ? (msg: string, ...args: any[]) => log.info(`${prefix} ${msg}`, ...args) : fallback.info,
74
+ warn: log?.warn ? (msg: string, ...args: any[]) => log.warn(`${prefix} ${msg}`, ...args) : fallback.warn,
75
+ error: log?.error ? (msg: string, ...args: any[]) => log.error(`${prefix} ${msg}`, ...args) : fallback.error,
76
+ debug: log?.debug ? (msg: string, ...args: any[]) => log.debug(`${prefix} ${msg}`, ...args) : fallback.debug,
77
+ }
78
+ }
79
+
80
+ export function setOpenclawWorkclawWsConnection(key: string, ws: WebSocket): void {
81
+ wsByKey.set(key, ws)
82
+ }
83
+
84
+ export function clearOpenclawWorkclawWsConnection(key: string): void {
85
+ wsByKey.delete(key)
86
+ }
87
+
88
+ export function getOpenclawWorkclawWsConnection(key: string): WebSocket | undefined {
89
+ return wsByKey.get(key)
90
+ }
91
+
92
+ // per-appKey 模式专用
93
+ export function registerAccountContext(appKey: string, accountId: string, ctx: any): void {
94
+ if (!accountIdsByAppKey.has(appKey)) {
95
+ accountIdsByAppKey.set(appKey, new Set())
96
+ }
97
+ accountIdsByAppKey.get(appKey)!.add(accountId)
98
+ appKeyByAccountId.set(accountId, appKey)
99
+
100
+ if (!dispatchersByAppKey.has(appKey)) {
101
+ dispatchersByAppKey.set(appKey, new Map())
102
+ }
103
+ dispatchersByAppKey.get(appKey)!.set(accountId, { ctx })
104
+ }
105
+
106
+ export function unregisterAccountContext(appKey: string, accountId: string): void {
107
+ accountIdsByAppKey.get(appKey)?.delete(accountId)
108
+ appKeyByAccountId.delete(accountId)
109
+ dispatchersByAppKey.get(appKey)?.delete(accountId)
110
+ }
111
+
112
+ export function getAccountIdsByAppKey(appKey: string): string[] {
113
+ return Array.from(accountIdsByAppKey.get(appKey) ?? [])
114
+ }
115
+
116
+ export function getAppKeyByAccountId(accountId: string): string | undefined {
117
+ return appKeyByAccountId.get(accountId)
118
+ }
119
+
120
+ export function getDispatcherByAppKeyAndAccountId(appKey: string, accountId: string): { ctx: any } | undefined {
121
+ return dispatchersByAppKey.get(appKey)?.get(accountId)
122
+ }
123
+
124
+ export function getAllDispatchersByAppKey(appKey: string): Map<string, { ctx: any }> | undefined {
125
+ return dispatchersByAppKey.get(appKey)
126
+ }
127
+
128
+ export function setReconnectScheduler(appKey: string, scheduler: any): void {
129
+ reconnectSchedulers.set(appKey, scheduler)
130
+ }
131
+
132
+ export function getReconnectScheduler(appKey: string): any | undefined {
133
+ return reconnectSchedulers.get(appKey)
134
+ }
135
+
136
+ export function clearReconnectScheduler(appKey: string): void {
137
+ reconnectSchedulers.delete(appKey)
138
+ }
139
+
140
+ // 尝试开始连接,如果已经在连接中则返回 false
141
+ export function tryStartConnecting(appKey: string): boolean {
142
+ if (connectingApps.has(appKey)) {
143
+ return false
144
+ }
145
+ connectingApps.add(appKey)
146
+ return true
147
+ }
148
+
149
+ // 工具执行上下文信息,用于关联 runId 和发送目标
150
+ export interface ToolContext {
151
+ target: string
152
+ replyToMessageId: string
153
+ openConversationId: string
154
+ accountId: string
155
+ agentId: string
156
+ sessionKey: string
157
+ }
158
+
159
+ // runId -> ToolContext 映射表
160
+ const runIdToToolContext = new Map<string, ToolContext>()
161
+ // toolCallId -> runId 映射表(用于 tool_result_persist 通过 toolCallId 找到 runId,再找到 ctx)
162
+ const toolCallIdToRunId = new Map<string, string>()
163
+
164
+ // 获取工具上下文(通过 runId)
165
+ export function getToolContext(runId: string): ToolContext | undefined {
166
+ return runIdToToolContext.get(runId)
167
+ }
168
+
169
+ // 设置工具上下文(仅通过 runId)
170
+ export function setToolContext(runId: string, ctx: ToolContext): void {
171
+ runIdToToolContext.set(runId, ctx)
172
+ }
173
+
174
+ // 建立 toolCallId -> runId 的映射(用于 after_tool_call 中)
175
+ export function setToolCallIdToRunIdMapping(toolCallId: string, runId: string): void {
176
+ toolCallIdToRunId.set(toolCallId, runId)
177
+ }
178
+
179
+ // 通过 toolCallId 获取 runId
180
+ export function getRunIdByToolCallId(toolCallId: string): string | undefined {
181
+ return toolCallIdToRunId.get(toolCallId)
182
+ }
183
+
184
+ // 删除工具上下文
185
+ export function deleteToolContext(runId: string): void {
186
+ runIdToToolContext.delete(runId)
187
+ }
188
+
189
+ // 清空所有工具上下文(用于清理)
190
+ export function clearAllToolContexts(): void {
191
+ runIdToToolContext.clear()
192
+ toolCallIdToRunId.clear()
193
+ }
194
+
195
+ // 标记连接完成(成功或失败都需要调用)
196
+ export function finishConnecting(appKey: string): void {
197
+ connectingApps.delete(appKey)
198
+ }
199
+
200
+ // 调用sub-agent进行协作
201
+ export async function spawnSubAgent(params: {
202
+ agentId: string
203
+ task: string
204
+ label?: string
205
+ model?: string
206
+ }): Promise<{ ok: boolean, sessionKey?: string, summary?: string, error?: string }> {
207
+ if (!runtime) {
208
+ throw new Error('OpenclawWorkclaw runtime not initialized')
209
+ }
210
+
211
+ try {
212
+ // 通过runtime调用sessions_spawn
213
+ const sessionsSpawn = (runtime as any)?.sessions_spawn
214
+ if (!sessionsSpawn || typeof sessionsSpawn !== 'function') {
215
+ // 如果runtime没有提供sessions_spawn,返回模拟数据
216
+ getOpenclawWorkclawLogger().warn(`Runtime does not provide sessions_spawn, returning mock response for agent ${params.agentId}`)
217
+ return {
218
+ ok: true,
219
+ summary: `[模拟] Agent ${params.agentId} 完成任务: ${params.task.substring(0, 50)}...`,
220
+ }
221
+ }
222
+
223
+ const result = await sessionsSpawn({
224
+ agentId: params.agentId,
225
+ task: params.task,
226
+ label: params.label,
227
+ model: params.model,
228
+ })
229
+
230
+ return {
231
+ ok: true,
232
+ sessionKey: result.sessionKey,
233
+ summary: result.summary,
234
+ }
235
+ }
236
+ catch (error: any) {
237
+ getOpenclawWorkclawLogger().error(`Failed to spawn sub-agent: ${error.message}`)
238
+ return {
239
+ ok: false,
240
+ error: error.message,
241
+ }
242
+ }
243
+ }
244
+
245
+ export const __openclaw_workclaw_runtime_emitted = true
246
+
247
+ export interface ToolInfo {
248
+ emoji: string
249
+ title: string
250
+ hint: string
251
+ aliases: string[]
252
+ }
253
+
254
+ /**
255
+ * 获取工具执行开始的提示(用于 onToolStart)
256
+ */
257
+ export function getToolStartHint(toolName: string | undefined): string {
258
+ if (!toolName)
259
+ return '🔄 正在执行工具...'
260
+
261
+ const normalizedToolName = toolName.toLowerCase().trim()
262
+
263
+ const toolMap: Record<string, ToolInfo> = {
264
+ // 📁 文件操作
265
+ 'read': { emoji: '📖', title: '读取文件', hint: '正在读取文件', aliases: ['file_read', 'read_file'] },
266
+ 'write': { emoji: '✍️', title: '写入文件', hint: '正在写入文件', aliases: ['file_write', 'write_file'] },
267
+ 'edit': { emoji: '✏️', title: '编辑文件', hint: '正在编辑文件', aliases: ['file_edit', 'modify'] },
268
+
269
+ // 💻 系统命令
270
+ 'exec': { emoji: '⚡', title: '执行命令', hint: '正在执行命令', aliases: ['bash', 'shell', 'cmd', 'command'] },
271
+ 'process': { emoji: '🔄', title: '进程管理', hint: '正在处理进程', aliases: ['process_manager'] },
272
+
273
+ // 🌐 网络/浏览器
274
+ 'web_fetch': { emoji: '🕸️', title: '获取网页', hint: '正在获取网页内容', aliases: ['fetch', 'http_get', 'http_request', 'curl'] },
275
+ 'web_search': { emoji: '🔍', title: '搜索网页', hint: '正在搜索网页', aliases: ['search', 'google_search', 'brave_search'] },
276
+ 'browser': { emoji: '🌐', title: '浏览器控制', hint: '正在控制浏览器', aliases: ['browser_control', 'playwright'] },
277
+
278
+ // 💬 消息/通信
279
+ 'message': { emoji: '💌', title: '发送消息', hint: '正在发送消息', aliases: ['send_message', 'mcp_deliver', 'deliver', 'notify'] },
280
+ 'tts': { emoji: '🔊', title: '文字转语音', hint: '正在转换语音', aliases: ['text_to_speech', 'speak'] },
281
+
282
+ // 🎭 会话管理
283
+ 'sessions_spawn': { emoji: '🧬', title: '创建子代理', hint: '正在创建子代理', aliases: ['spawn', 'create_agent', 'new_session'] },
284
+ 'sessions_send': { emoji: '📤', title: '跨会话发消息', hint: '正在发送跨会话消息', aliases: ['send_to_session', 'forward'] },
285
+ 'sessions_list': { emoji: '📋', title: '列出会话', hint: '正在列出会话', aliases: ['list_sessions'] },
286
+ 'sessions_history': { emoji: '📜', title: '获取历史', hint: '正在获取历史消息', aliases: ['get_history', 'history'] },
287
+ 'sessions_yield': { emoji: '⏸️', title: '暂停让出', hint: '正在暂停等待', aliases: ['yield', 'pause'] },
288
+ 'subagents': { emoji: '🐛', title: '子代理管理', hint: '正在管理子代理', aliases: ['subagent_manager'] },
289
+ 'agents_list': { emoji: '🤖', title: '列出代理', hint: '正在列出可用代理', aliases: ['list_agents'] },
290
+
291
+ // 🧠 记忆/知识
292
+ 'memory_search': { emoji: '🔎', title: '搜索记忆', hint: '正在搜索记忆', aliases: ['search_memory', 'recall'] },
293
+ 'memory_get': { emoji: '📝', title: '读取记忆', hint: '正在读取记忆片段', aliases: ['get_memory', 'read_memory'] },
294
+
295
+ // ⏰ 定时任务
296
+ 'openclaw-workclaw-cron-add-params': { emoji: '⏱️', title: '创建定时任务', hint: '正在创建定时任务', aliases: ['cron_add', 'add_timer'] },
297
+ 'openclaw-workclaw-cron-update-params': { emoji: '🔧', title: '修改定时任务', hint: '正在修改定时任务', aliases: ['cron_update', 'update_timer'] },
298
+ 'openclaw-workclaw-cron-remove-params': { emoji: '🗑️', title: '删除定时任务', hint: '正在删除定时任务', aliases: ['cron_remove', 'remove_timer'] },
299
+ 'openclaw-workclaw-cron-notify-sync': { emoji: '🔔', title: '定时触发通知', hint: '定时任务已触发', aliases: ['cron_trigger', 'notify'] },
300
+ 'openclaw-workclaw-cron-add-sync': { emoji: '✅', title: '同步定时任务', hint: '正在同步到后端', aliases: [] },
301
+ 'openclaw-workclaw-cron-update-sync': { emoji: '✅', title: '同步定时更新', hint: '正在同步更新', aliases: [] },
302
+ 'openclaw-workclaw-cron-remove-sync': { emoji: '✅', title: '同步定时删除', hint: '正在同步删除', aliases: [] },
303
+ 'openclaw-workclaw-cron-disable-params': { emoji: '⏸️', title: '禁用定时任务', hint: '正在禁用定时任务', aliases: ['cron_disable'] },
304
+ 'openclaw-workclaw-cron-enable-params': { emoji: '▶️', title: '启用定时任务', hint: '正在启用定时任务', aliases: ['cron_enable'] },
305
+
306
+ // 📊 其他
307
+ 'session_status': { emoji: '📊', title: '会话状态', hint: '正在获取会话状态', aliases: ['status', 'get_status'] },
308
+ 'canvas': { emoji: '🎨', title: '画布控制', hint: '正在控制画布', aliases: ['canvas_control', 'draw'] },
309
+ }
310
+
311
+ // 精确匹配
312
+ if (toolMap[normalizedToolName]) {
313
+ const tool = toolMap[normalizedToolName]
314
+ return `${tool.emoji} ${tool.hint}...`
315
+ }
316
+
317
+ // 别名匹配
318
+ for (const [_, tool] of Object.entries(toolMap)) {
319
+ if (tool.aliases.includes(normalizedToolName)) {
320
+ return `${tool.emoji} ${tool.hint}...`
321
+ }
322
+ }
323
+
324
+ // 默认
325
+ return `🔄 正在执行 ${toolName}...`
326
+ }
327
+
328
+ /**
329
+ * 获取工具执行完成的提示(用于 after_tool_call)
330
+ */
331
+ export function getToolResultHint(toolName: string | undefined): string {
332
+ if (!toolName)
333
+ return '✅ 工具执行完成'
334
+
335
+ const normalizedToolName = toolName.toLowerCase().trim()
336
+
337
+ const toolMap: Record<string, ToolInfo> = {
338
+ // 📁 文件操作
339
+ 'read': { emoji: '📖', title: '读取文件', hint: '已读取文件', aliases: ['file_read', 'read_file'] },
340
+ 'write': { emoji: '✍️', title: '写入文件', hint: '已写入文件', aliases: ['file_write', 'write_file'] },
341
+ 'edit': { emoji: '✏️', title: '编辑文件', hint: '已编辑文件', aliases: ['file_edit', 'modify'] },
342
+
343
+ // 💻 系统命令
344
+ 'exec': { emoji: '⚡', title: '执行命令', hint: '已执行命令', aliases: ['bash', 'shell', 'cmd', 'command'] },
345
+ 'process': { emoji: '🔄', title: '进程管理', hint: '已处理进程', aliases: ['process_manager'] },
346
+
347
+ // 🌐 网络/浏览器
348
+ 'web_fetch': { emoji: '🕸️', title: '获取网页', hint: '已获取网页', aliases: ['fetch', 'http_get', 'http_request', 'curl'] },
349
+ 'web_search': { emoji: '🔍', title: '搜索网页', hint: '已搜索网页', aliases: ['search', 'google_search', 'brave_search'] },
350
+ 'browser': { emoji: '🌐', title: '浏览器控制', hint: '已控制浏览器', aliases: ['browser_control', 'playwright'] },
351
+
352
+ // 💬 消息/通信
353
+ 'message': { emoji: '💌', title: '发送消息', hint: '已发送消息', aliases: ['send_message', 'mcp_deliver', 'deliver', 'notify'] },
354
+ 'tts': { emoji: '🔊', title: '文字转语音', hint: '已转换语音', aliases: ['text_to_speech', 'speak'] },
355
+
356
+ // 🎭 会话管理
357
+ 'sessions_spawn': { emoji: '🧬', title: '创建子代理', hint: '已创建子代理', aliases: ['spawn', 'create_agent', 'new_session'] },
358
+ 'sessions_send': { emoji: '📤', title: '跨会话发消息', hint: '已发送跨会话消息', aliases: ['send_to_session', 'forward'] },
359
+ 'sessions_list': { emoji: '📋', title: '列出会话', hint: '已列出会话', aliases: ['list_sessions'] },
360
+ 'sessions_history': { emoji: '📜', title: '获取历史', hint: '已获取历史消息', aliases: ['get_history', 'history'] },
361
+ 'sessions_yield': { emoji: '⏸️', title: '暂停让出', hint: '已暂停等待', aliases: ['yield', 'pause'] },
362
+ 'subagents': { emoji: '🐛', title: '子代理管理', hint: '已管理子代理', aliases: ['subagent_manager'] },
363
+ 'agents_list': { emoji: '🤖', title: '列出代理', hint: '已列出代理', aliases: ['list_agents'] },
364
+
365
+ // 🧠 记忆/知识
366
+ 'memory_search': { emoji: '🔎', title: '搜索记忆', hint: '已搜索记忆', aliases: ['search_memory', 'recall'] },
367
+ 'memory_get': { emoji: '📝', title: '读取记忆', hint: '已读取记忆', aliases: ['get_memory', 'read_memory'] },
368
+
369
+ // ⏰ 定时任务
370
+ 'openclaw-workclaw-cron-add-params': { emoji: '⏱️', title: '创建定时任务', hint: '已创建定时任务', aliases: ['cron_add', 'add_timer'] },
371
+ 'openclaw-workclaw-cron-update-params': { emoji: '🔧', title: '修改定时任务', hint: '已修改定时任务', aliases: ['cron_update', 'update_timer'] },
372
+ 'openclaw-workclaw-cron-remove-params': { emoji: '🗑️', title: '删除定时任务', hint: '已删除定时任务', aliases: ['cron_remove', 'remove_timer'] },
373
+ 'openclaw-workclaw-cron-notify-sync': { emoji: '🔔', title: '定时触发通知', hint: '定时任务已触发', aliases: ['cron_trigger', 'notify'] },
374
+ 'openclaw-workclaw-cron-add-sync': { emoji: '✅', title: '同步定时任务', hint: '已同步定时任务', aliases: [] },
375
+ 'openclaw-workclaw-cron-update-sync': { emoji: '✅', title: '同步定时更新', hint: '已同步更新', aliases: [] },
376
+ 'openclaw-workclaw-cron-remove-sync': { emoji: '✅', title: '同步定时删除', hint: '已同步删除', aliases: [] },
377
+ 'openclaw-workclaw-cron-disable-params': { emoji: '⏸️', title: '禁用定时任务', hint: '已禁用定时任务', aliases: ['cron_disable'] },
378
+ 'openclaw-workclaw-cron-enable-params': { emoji: '▶️', title: '启用定时任务', hint: '已启用定时任务', aliases: ['cron_enable'] },
379
+
380
+ // 📊 其他
381
+ 'session_status': { emoji: '📊', title: '会话状态', hint: '已获取会话状态', aliases: ['status', 'get_status'] },
382
+ 'canvas': { emoji: '🎨', title: '画布控制', hint: '已控制画布', aliases: ['canvas_control', 'draw'] },
383
+ }
384
+
385
+ // 精确匹配
386
+ if (toolMap[normalizedToolName]) {
387
+ const tool = toolMap[normalizedToolName]
388
+ return `${tool.emoji} ${tool.hint}`
389
+ }
390
+
391
+ // 别名匹配
392
+ for (const [_, tool] of Object.entries(toolMap)) {
393
+ if (tool.aliases.includes(normalizedToolName)) {
394
+ return `${tool.emoji} ${tool.hint}`
395
+ }
396
+ }
397
+
398
+ // 默认
399
+ return `✅ 已执行 ${toolName}`
400
+ }
package/src/send.ts ADDED
@@ -0,0 +1 @@
1
+ export { getMessageOpenclawWorkclaw, sendMessageOpenclawWorkclaw } from './outbound/index.js'