@workclaw/openclaw-workclaw 1.0.16 → 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.
- package/README.md +21 -1
- package/index.ts +210 -210
- package/openclaw.plugin.json +1 -0
- package/package.json +12 -5
- package/setup-entry.ts +6 -0
- package/skills/openclaw-workclaw-cron/SKILL.md +45 -28
- package/src/accounts.ts +62 -37
- package/src/api/accounts-api.ts +88 -89
- package/src/api/prompts-api.ts +70 -77
- package/src/api/session-api.ts +99 -108
- package/src/api/skills-api.ts +35 -37
- package/src/api/workspace.ts +27 -29
- package/src/channel.ts +200 -202
- package/src/config-schema.ts +9 -9
- package/src/connection/workclaw-client.ts +554 -567
- package/src/gateway/agent-handlers.ts +392 -426
- package/src/gateway/config-writer.ts +228 -243
- package/src/gateway/message-context.ts +534 -362
- package/src/gateway/message-dispatcher.ts +529 -489
- package/src/gateway/reconnect.ts +217 -113
- package/src/gateway/skills-handler.ts +408 -472
- package/src/gateway/skills-list-handler.ts +9 -9
- package/src/gateway/tools-list-handler.ts +70 -72
- package/src/gateway/workclaw-gateway.ts +328 -486
- package/src/media/upload.ts +83 -94
- package/src/outbound/index.ts +57 -55
- package/src/outbound/workclaw-sender.ts +134 -133
- package/src/runtime.ts +291 -194
- package/src/send.ts +1 -1
- package/src/tools/openclaw-workclaw-cron/api/index.ts +6 -6
- package/src/tools/openclaw-workclaw-cron/src/add/params.ts +20 -19
- package/src/tools/openclaw-workclaw-cron/src/add/sync.ts +2 -2
- package/src/tools/openclaw-workclaw-cron/src/disable/params.ts +1 -1
- package/src/tools/openclaw-workclaw-cron/src/disable/sync.ts +3 -3
- package/src/tools/openclaw-workclaw-cron/src/enable/params.ts +1 -1
- package/src/tools/openclaw-workclaw-cron/src/enable/sync.ts +3 -3
- package/src/tools/openclaw-workclaw-cron/src/notify/sync.ts +2 -2
- package/src/tools/openclaw-workclaw-cron/src/remove/params.ts +1 -1
- package/src/tools/openclaw-workclaw-cron/src/remove/sync.ts +3 -3
- package/src/tools/openclaw-workclaw-cron/src/update/params.ts +195 -197
- package/src/tools/openclaw-workclaw-cron/src/update/sync.ts +4 -4
- package/src/tools/openclaw-workclaw-system/src/get/index.ts +2 -2
- package/src/tools/openclaw-workclaw-system/src/token/index.ts +4 -4
- package/src/types.ts +38 -40
- package/src/utils/content.ts +16 -21
- package/tests/accounts.test.ts +285 -0
- package/tests/message-context.test.ts +313 -0
- package/tests/reconnect.test.ts +257 -0
- package/tests/workclaw-client.test.ts +112 -0
- package/tsconfig.json +8 -5
- 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
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
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 !==
|
|
24
|
-
return
|
|
34
|
+
if (!params || typeof params !== "object") {
|
|
35
|
+
return "";
|
|
25
36
|
}
|
|
26
37
|
|
|
27
|
-
const normalizedToolName = (toolName ||
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
normalizedToolName ===
|
|
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
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
normalizedToolName ===
|
|
73
|
-
|
|
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
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
normalizedToolName ===
|
|
82
|
-
|
|
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
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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 ===
|
|
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(
|
|
106
|
+
.join("\n");
|
|
104
107
|
}
|
|
105
108
|
}
|
|
106
|
-
}
|
|
107
|
-
|
|
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
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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(
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
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 =
|
|
147
|
+
const cfg = getWorkclawRuntime().config.loadConfig();
|
|
148
148
|
const formattedToolOutput = {
|
|
149
149
|
name: getToolResultHint(toolName),
|
|
150
|
-
toolName,
|
|
151
|
-
content,
|
|
152
|
-
state:
|
|
153
|
-
}
|
|
154
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
sendMessageOpenclawWorkclaw,
|
|
194
|
-
} from './src/outbound/index.js'
|
|
187
|
+
// ============================================================================
|
|
188
|
+
// Templates
|
|
189
|
+
// ============================================================================
|
|
195
190
|
|
|
196
|
-
const templateNames = [
|
|
191
|
+
const templateNames = ["SOUL.md", "IDENTITY.md"] as const;
|
|
197
192
|
|
|
198
|
-
|
|
193
|
+
/**
|
|
194
|
+
* 获取插件所在目录,兼容 Windows 下的 file:// URL 问题。
|
|
195
|
+
* 4.2 的插件加载器在 Windows 上可能传入不带协议前缀的路径。
|
|
196
|
+
*/
|
|
197
|
+
function getPluginDir(): string {
|
|
199
198
|
try {
|
|
200
|
-
|
|
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
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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)
|
|
216
|
-
const workspaceDir = resolveWorkspaceDir(api)
|
|
226
|
+
async function releaseTemplates(api: OpenClawPluginApi) {
|
|
227
|
+
const workspaceDir = resolveWorkspaceDir(api);
|
|
217
228
|
// 模板文件在根目录的 templates/ 中,而不是 dist/templates/
|
|
218
|
-
const currentDir =
|
|
219
|
-
const templatesDir = path.join(
|
|
220
|
-
|
|
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,
|
|
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:
|
|
231
|
-
}
|
|
232
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
257
|
-
registerToolHooks(api)
|
|
267
|
+
registerAllOpenclawWorkclawCronTools(api);
|
|
268
|
+
registerToolHooks(api);
|
|
258
269
|
|
|
259
270
|
api.registerService({
|
|
260
|
-
id:
|
|
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:
|
|
278
|
+
path: "/openclaw-workclaw/workspace/prompts",
|
|
268
279
|
handler: createPromptsApiHandler(api),
|
|
269
|
-
auth:
|
|
270
|
-
})
|
|
280
|
+
auth: "plugin",
|
|
281
|
+
});
|
|
271
282
|
|
|
272
283
|
api.registerHttpRoute({
|
|
273
|
-
path:
|
|
284
|
+
path: "/openclaw-workclaw/accounts",
|
|
274
285
|
handler: createAccountsApiHandler(api),
|
|
275
|
-
auth:
|
|
276
|
-
})
|
|
286
|
+
auth: "plugin",
|
|
287
|
+
});
|
|
277
288
|
|
|
278
289
|
api.registerHttpRoute({
|
|
279
|
-
path:
|
|
290
|
+
path: "/openclaw-workclaw/skills",
|
|
280
291
|
handler: createSkillsApiHandler(api),
|
|
281
|
-
auth:
|
|
282
|
-
})
|
|
292
|
+
auth: "plugin",
|
|
293
|
+
});
|
|
283
294
|
|
|
284
|
-
// 注册会话管理 API
|
|
285
295
|
api.registerHttpRoute({
|
|
286
|
-
path:
|
|
296
|
+
path: "/openclaw-workclaw/sessions",
|
|
287
297
|
handler: createSessionApiHandler(api),
|
|
288
|
-
auth:
|
|
289
|
-
match:
|
|
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
|
+
});
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workclaw/openclaw-workclaw",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
5
|
-
"description": "
|
|
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": "
|
|
10
|
+
"openclaw": "2026.4.14",
|
|
12
11
|
"typescript": "^5.6.3"
|
|
13
12
|
},
|
|
14
13
|
"dependencies": {
|
|
15
14
|
"undici": "^7.16.0",
|
|
16
15
|
"ws": "^8.19.0",
|
|
17
|
-
"zod": "^3.
|
|
16
|
+
"zod": "^4.3.6"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^20.0.0"
|
|
18
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);
|