@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
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { readFileSync } from 'node:fs'
|
|
6
6
|
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
11
|
+
|
|
7
12
|
export interface SkillsListEvent {
|
|
8
13
|
agentId: string | number
|
|
9
14
|
userId: string | number
|
|
@@ -83,10 +88,8 @@ async function fetchSkillsFromOpenClaw(
|
|
|
83
88
|
|
|
84
89
|
try {
|
|
85
90
|
// 使用 OpenClaw CLI 获取 skills 列表(全局技能)
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
const result = execSync('openclaw skills list', {
|
|
89
|
-
encoding: 'utf-8',
|
|
91
|
+
const result = execSync("openclaw skills list", {
|
|
92
|
+
encoding: "utf-8",
|
|
90
93
|
timeout: 30000, // 30秒超时
|
|
91
94
|
})
|
|
92
95
|
|
|
@@ -123,13 +126,10 @@ async function fetchAgentWorkspaceSkills(
|
|
|
123
126
|
|
|
124
127
|
try {
|
|
125
128
|
// 智能体工作区路径
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
const agentWorkspace = join(homedir(), '.openclaw', 'agents', `openclaw-workclaw-${agentId}`)
|
|
129
|
-
const skillsDir = join(agentWorkspace, 'skills')
|
|
129
|
+
const agentWorkspace = join(homedir(), ".openclaw", "agents", `openclaw-workclaw-${agentId}`);
|
|
130
|
+
const skillsDir = join(agentWorkspace, "skills");
|
|
130
131
|
|
|
131
132
|
// 检查技能目录是否存在
|
|
132
|
-
const { existsSync, readdirSync, statSync } = await import('node:fs')
|
|
133
133
|
if (!existsSync(skillsDir)) {
|
|
134
134
|
log?.info?.(`SkillsList: No skills directory found in agent workspace: ${skillsDir}`)
|
|
135
135
|
return skills
|
|
@@ -4,66 +4,68 @@
|
|
|
4
4
|
* 从 OpenClaw 获取 tools 列表并通过 HTTP 回调返回
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { fetch } from
|
|
7
|
+
import { fetch } from "undici";
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
8
11
|
|
|
9
12
|
export interface ToolsListEvent {
|
|
10
|
-
agentId: string | number
|
|
11
|
-
userId: string | number
|
|
13
|
+
agentId: string | number;
|
|
14
|
+
userId: string | number;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
export interface ToolItem {
|
|
15
|
-
typeStr: string
|
|
16
|
-
name: string
|
|
17
|
-
description: string
|
|
18
|
+
typeStr: string;
|
|
19
|
+
name: string;
|
|
20
|
+
description: string;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export interface ToolsListCallbackPayload {
|
|
21
|
-
userId: number
|
|
22
|
-
agentId: number
|
|
23
|
-
dataList: ToolItem[]
|
|
24
|
+
userId: number;
|
|
25
|
+
agentId: number;
|
|
26
|
+
dataList: ToolItem[];
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* 处理 TOOLS_LIST 事件
|
|
28
|
-
* @param event
|
|
29
|
-
* @param baseUrl
|
|
30
|
-
* @param token
|
|
31
|
-
* @param log
|
|
31
|
+
* @param event 事件数据
|
|
32
|
+
* @param baseUrl 回调基础 URL
|
|
33
|
+
* @param token 鉴权 token
|
|
34
|
+
* @param log 日志函数
|
|
32
35
|
*/
|
|
33
36
|
export async function handleToolsListEvent(
|
|
34
37
|
event: ToolsListEvent,
|
|
35
38
|
baseUrl: string,
|
|
36
39
|
token: string,
|
|
37
40
|
log?: {
|
|
38
|
-
info?: (msg: string) => void
|
|
39
|
-
error?: (msg: string) => void
|
|
41
|
+
info?: (msg: string) => void;
|
|
42
|
+
error?: (msg: string) => void;
|
|
40
43
|
},
|
|
41
44
|
): Promise<void> {
|
|
42
|
-
const agentId = Number(event.agentId)
|
|
43
|
-
const userId = Number(event.userId)
|
|
45
|
+
const agentId = Number(event.agentId);
|
|
46
|
+
const userId = Number(event.userId);
|
|
44
47
|
|
|
45
|
-
log?.info?.(`ToolsList: Handling TOOLS_LIST event for agentId: ${agentId}, userId: ${userId}`)
|
|
48
|
+
log?.info?.(`ToolsList: Handling TOOLS_LIST event for agentId: ${agentId}, userId: ${userId}`);
|
|
46
49
|
|
|
47
50
|
try {
|
|
48
51
|
// 从 OpenClaw 获取 tools 列表
|
|
49
|
-
const tools = await fetchToolsFromOpenClaw(agentId, userId, log)
|
|
52
|
+
const tools = await fetchToolsFromOpenClaw(agentId, userId, log);
|
|
50
53
|
|
|
51
54
|
// 构建回调 payload
|
|
52
55
|
const callbackPayload: ToolsListCallbackPayload = {
|
|
53
56
|
userId,
|
|
54
57
|
agentId,
|
|
55
58
|
dataList: tools,
|
|
56
|
-
}
|
|
59
|
+
};
|
|
57
60
|
|
|
58
61
|
// 发送回调
|
|
59
|
-
const callbackUrl = `${baseUrl.replace(/\/$/,
|
|
60
|
-
await sendToolsListCallback(callbackUrl, callbackPayload, token, log)
|
|
62
|
+
const callbackUrl = `${baseUrl.replace(/\/$/, "")}/open-apis/v1/claw/push/tools`;
|
|
63
|
+
await sendToolsListCallback(callbackUrl, callbackPayload, token, log);
|
|
61
64
|
|
|
62
|
-
log?.info?.(`ToolsList: Successfully processed TOOLS_LIST event, sent ${tools.length} tools`)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
throw error
|
|
65
|
+
log?.info?.(`ToolsList: Successfully processed TOOLS_LIST event, sent ${tools.length} tools`);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
log?.error?.(`ToolsList: Failed to handle TOOLS_LIST event: ${String(error)}`);
|
|
68
|
+
throw error;
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
|
|
@@ -73,60 +75,56 @@ export async function handleToolsListEvent(
|
|
|
73
75
|
*/
|
|
74
76
|
async function fetchToolsFromOpenClaw(
|
|
75
77
|
agentId: number,
|
|
76
|
-
|
|
78
|
+
userId: number,
|
|
77
79
|
log?: {
|
|
78
|
-
info?: (msg: string) => void
|
|
79
|
-
error?: (msg: string) => void
|
|
80
|
+
info?: (msg: string) => void;
|
|
81
|
+
error?: (msg: string) => void;
|
|
80
82
|
},
|
|
81
83
|
): Promise<ToolItem[]> {
|
|
82
|
-
log?.info?.(`ToolsList: Fetching tools from OpenClaw for agentId: ${agentId}`)
|
|
84
|
+
log?.info?.(`ToolsList: Fetching tools from OpenClaw for agentId: ${agentId}`);
|
|
83
85
|
|
|
84
86
|
// OpenClaw 所有内置工具列表
|
|
85
87
|
const allTools: ToolItem[] = [
|
|
86
|
-
{ typeStr:
|
|
87
|
-
{ typeStr:
|
|
88
|
-
{ typeStr:
|
|
89
|
-
{ typeStr:
|
|
90
|
-
{ typeStr:
|
|
91
|
-
{ typeStr:
|
|
92
|
-
{ typeStr:
|
|
93
|
-
{ typeStr:
|
|
94
|
-
]
|
|
88
|
+
{ typeStr: "function", name: "web_search", description: "Search the web for information using Brave Search API." },
|
|
89
|
+
{ typeStr: "function", name: "web_fetch", description: "Fetch and read content from a URL." },
|
|
90
|
+
{ typeStr: "function", name: "sessions_list", description: "List all active agent sessions." },
|
|
91
|
+
{ typeStr: "function", name: "sessions_history", description: "Get the history of a specific session." },
|
|
92
|
+
{ typeStr: "function", name: "memory_search", description: "Search memory for past conversations and information." },
|
|
93
|
+
{ typeStr: "function", name: "memory_get", description: "Get specific memory by ID." },
|
|
94
|
+
{ typeStr: "function", name: "gateway", description: "Gateway control operations." },
|
|
95
|
+
{ typeStr: "function", name: "exec", description: "Execute shell commands." },
|
|
96
|
+
];
|
|
95
97
|
|
|
96
98
|
try {
|
|
97
99
|
// 读取 OpenClaw 配置文件
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
const configPath = path.join(homedir(), '.openclaw', 'openclaw.json')
|
|
103
|
-
const configContent = await readFile(configPath, 'utf-8')
|
|
104
|
-
const config = JSON.parse(configContent)
|
|
100
|
+
const configPath = path.join(homedir(), ".openclaw", "openclaw.json");
|
|
101
|
+
const configContent = await readFile(configPath, "utf-8");
|
|
102
|
+
const config = JSON.parse(configContent);
|
|
105
103
|
|
|
106
104
|
// 查找对应的 agent 配置
|
|
107
|
-
const agentConfig = config.agents?.list?.find((agent: any) =>
|
|
108
|
-
String(agent.id) === String(agentId)
|
|
109
|
-
|
|
110
|
-
)
|
|
105
|
+
const agentConfig = config.agents?.list?.find((agent: any) =>
|
|
106
|
+
String(agent.id) === String(agentId) ||
|
|
107
|
+
agent.id === `openclaw-workclaw-${agentId}`
|
|
108
|
+
);
|
|
111
109
|
|
|
112
110
|
// 获取 tools.allow 配置
|
|
113
|
-
const allowTools = agentConfig?.tools?.allow
|
|
111
|
+
const allowTools = agentConfig?.tools?.allow;
|
|
114
112
|
|
|
115
113
|
// 如果没有 allow 配置,或者 allow 是 ["*"],返回所有工具
|
|
116
|
-
if (!allowTools || allowTools.length === 0 || allowTools.includes(
|
|
117
|
-
log?.info?.(`ToolsList: Agent has no allow restriction or allow=[*], returning all ${allTools.length} tools`)
|
|
118
|
-
return allTools
|
|
114
|
+
if (!allowTools || allowTools.length === 0 || allowTools.includes("*")) {
|
|
115
|
+
log?.info?.(`ToolsList: Agent has no allow restriction or allow=[*], returning all ${allTools.length} tools`);
|
|
116
|
+
return allTools;
|
|
119
117
|
}
|
|
120
118
|
|
|
121
119
|
// 如果有具体的 allow 列表,只返回列表中的工具
|
|
122
|
-
const filteredTools = allTools.filter(tool => allowTools.includes(tool.name))
|
|
123
|
-
log?.info?.(`ToolsList: Agent allow=${JSON.stringify(allowTools)}, returning ${filteredTools.length} tools`)
|
|
124
|
-
return filteredTools
|
|
125
|
-
|
|
126
|
-
catch (error) {
|
|
127
|
-
log?.error?.(`ToolsList: Error reading agent config: ${String(error)}, returning all tools`)
|
|
120
|
+
const filteredTools = allTools.filter(tool => allowTools.includes(tool.name));
|
|
121
|
+
log?.info?.(`ToolsList: Agent allow=${JSON.stringify(allowTools)}, returning ${filteredTools.length} tools`);
|
|
122
|
+
return filteredTools;
|
|
123
|
+
|
|
124
|
+
} catch (error) {
|
|
125
|
+
log?.error?.(`ToolsList: Error reading agent config: ${String(error)}, returning all tools`);
|
|
128
126
|
// 如果读取配置失败,返回所有工具
|
|
129
|
-
return allTools
|
|
127
|
+
return allTools;
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
130
|
|
|
@@ -138,25 +136,25 @@ async function sendToolsListCallback(
|
|
|
138
136
|
payload: ToolsListCallbackPayload,
|
|
139
137
|
token: string,
|
|
140
138
|
log?: {
|
|
141
|
-
info?: (msg: string) => void
|
|
142
|
-
error?: (msg: string) => void
|
|
139
|
+
info?: (msg: string) => void;
|
|
140
|
+
error?: (msg: string) => void;
|
|
143
141
|
},
|
|
144
142
|
): Promise<void> {
|
|
145
|
-
log?.info?.(`ToolsList: Sending callback to ${callbackUrl}`)
|
|
143
|
+
log?.info?.(`ToolsList: Sending callback to ${callbackUrl}`);
|
|
146
144
|
|
|
147
145
|
const response = await fetch(callbackUrl, {
|
|
148
|
-
method:
|
|
146
|
+
method: "POST",
|
|
149
147
|
headers: {
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
"Content-Type": "application/json",
|
|
149
|
+
"Authorization": `Bearer ${token}`,
|
|
152
150
|
},
|
|
153
151
|
body: JSON.stringify(payload),
|
|
154
|
-
})
|
|
152
|
+
});
|
|
155
153
|
|
|
156
154
|
if (!response.ok) {
|
|
157
|
-
const errorText = await response.text()
|
|
158
|
-
throw new Error(`Callback failed: ${response.status} ${response.statusText} - ${errorText}`)
|
|
155
|
+
const errorText = await response.text();
|
|
156
|
+
throw new Error(`Callback failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
159
157
|
}
|
|
160
158
|
|
|
161
|
-
log?.info?.(`ToolsList: Callback sent successfully: ${response.status}`)
|
|
159
|
+
log?.info?.(`ToolsList: Callback sent successfully: ${response.status}`);
|
|
162
160
|
}
|