@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.
- package/README.md +21 -1
- package/index.ts +210 -210
- package/openclaw.plugin.json +1 -0
- package/package.json +11 -4
- 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
|
@@ -2,130 +2,121 @@
|
|
|
2
2
|
* Agent Handlers - handle AGENT_CREATED, AGENT_UPDATED, AGENT_DELETED events
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { homedir } from
|
|
9
|
-
import { join } from
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
5
|
+
import { resolveAccountByUserIdAndAgentId, allocateWorkerAccountId, refreshAccountCache, resolveWorkclawAccount } from "../accounts.js";
|
|
6
|
+
import { writeConfigFile } from "./config-writer.js";
|
|
7
|
+
import { startWorkclawGateway, stopWorkclawGateway } from "./workclaw-gateway.js";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { writeFile, copyFile, mkdir } from "fs/promises";
|
|
11
|
+
import { execSync } from "child_process";
|
|
12
|
+
import { existsSync } from "fs";
|
|
13
13
|
|
|
14
14
|
/** 修复目录所有权(仅非 Windows 环境) */
|
|
15
15
|
function fixOwner(dir: string): void {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
execSync(`chown -R node:node "${dir}"`, { stdio: 'ignore' })
|
|
22
|
-
}
|
|
23
|
-
catch {}
|
|
16
|
+
if (process.platform === 'win32') return;
|
|
17
|
+
try { execSync(`chown -R node:node "${dir}"`, { stdio: 'ignore' }); } catch {}
|
|
24
18
|
}
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
20
|
+
type AgentLogger = {
|
|
21
|
+
info?: (msg: string) => void;
|
|
22
|
+
warn?: (msg: string) => void;
|
|
23
|
+
error?: (msg: string) => void;
|
|
24
|
+
};
|
|
31
25
|
|
|
32
26
|
async function createSubAgentForAccount(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
agentId: string | number,
|
|
28
|
+
userId: string | number,
|
|
29
|
+
cfg: any,
|
|
30
|
+
log?: AgentLogger,
|
|
37
31
|
): Promise<any> {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
32
|
+
try {
|
|
33
|
+
const channels = cfg?.channels ?? {};
|
|
34
|
+
const openclawWorkclaw = channels['openclaw-workclaw'] ?? {};
|
|
35
|
+
const accounts = openclawWorkclaw.accounts ?? {};
|
|
36
|
+
const accountCount = Object.keys(accounts).length;
|
|
37
|
+
const subAgentId = `openclaw-workclaw-${agentId}`;
|
|
38
|
+
|
|
39
|
+
const existingAgents = cfg?.agents?.list ?? [];
|
|
40
|
+
const existingBindings = cfg?.bindings ?? [];
|
|
41
|
+
|
|
42
|
+
const agentExists = existingAgents.some((a: any) => a.id === subAgentId);
|
|
43
|
+
|
|
44
|
+
if (!agentExists) {
|
|
45
|
+
const newAgentsList = [...existingAgents];
|
|
46
|
+
const subAgent = {
|
|
47
|
+
id: subAgentId,
|
|
48
|
+
default: false,
|
|
49
|
+
name: `智小途 - Agent ${agentId}`,
|
|
50
|
+
workspace: `~/.openclaw/workspace-workclaw/${agentId}`,
|
|
51
|
+
agentDir: `~/.openclaw/agents/workspace-workclaw-${agentId}/agent`,
|
|
52
|
+
tools: { allow: ["*"] },
|
|
53
|
+
};
|
|
54
|
+
newAgentsList.push(subAgent);
|
|
55
|
+
|
|
56
|
+
if (accountCount === 0) {
|
|
57
|
+
const defaultAgent = {
|
|
58
|
+
id: "default",
|
|
59
|
+
default: true,
|
|
60
|
+
name: "Default Agent",
|
|
61
|
+
workspace: "default",
|
|
62
|
+
tools: { allow: ["*"] },
|
|
63
|
+
};
|
|
64
|
+
newAgentsList.push(defaultAgent);
|
|
65
|
+
log?.info?.(`SubAgent: Created default agent with default workspace`);
|
|
66
|
+
} else {
|
|
67
|
+
log?.info?.(`SubAgent: Using existing default agent`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const bindingAccountId = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
|
|
71
|
+
const newBinding = {
|
|
72
|
+
agentId: subAgentId,
|
|
73
|
+
match: {
|
|
74
|
+
channel: "openclaw-workclaw",
|
|
75
|
+
accountId: bindingAccountId ?? "*",
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const newBindings = [...existingBindings, newBinding];
|
|
79
|
+
log?.info?.(`SubAgent: Created binding for agentId: ${subAgentId}, accountId: ${bindingAccountId ?? "*"}`);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
agents: {
|
|
83
|
+
list: newAgentsList,
|
|
84
|
+
defaults: cfg?.agents?.defaults ?? {},
|
|
85
|
+
},
|
|
86
|
+
bindings: newBindings,
|
|
87
|
+
};
|
|
88
|
+
} else {
|
|
89
|
+
const newAgents = {
|
|
90
|
+
list: [...existingAgents],
|
|
91
|
+
defaults: cfg?.agents?.defaults ?? {},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const bindingExists = existingBindings.some((b: any) => b?.agentId === subAgentId);
|
|
95
|
+
let newBindings = [...existingBindings];
|
|
96
|
+
|
|
97
|
+
if (!bindingExists) {
|
|
98
|
+
const bindingAccountId = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
|
|
99
|
+
newBindings.push({
|
|
100
|
+
agentId: subAgentId,
|
|
101
|
+
match: {
|
|
102
|
+
channel: "openclaw-workclaw",
|
|
103
|
+
accountId: bindingAccountId ?? "*",
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
log?.info?.(`SubAgent: Created binding for agentId: ${subAgentId}, accountId: ${bindingAccountId ?? "*"}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
log?.info?.(`SubAgent: Created sub-agent: ${subAgentId} with workspace: openclaw-workclaw-${agentId} (account #${accountCount + 1})`);
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
agents: newAgents,
|
|
113
|
+
bindings: newBindings,
|
|
114
|
+
};
|
|
69
115
|
}
|
|
70
|
-
|
|
71
|
-
log?.
|
|
72
|
-
|
|
73
|
-
else {
|
|
74
|
-
log?.info?.(`SubAgent: Using existing default agent`)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const bindingAccountId = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId))
|
|
78
|
-
const newBinding = {
|
|
79
|
-
agentId: subAgentId,
|
|
80
|
-
match: {
|
|
81
|
-
channel: 'openclaw-workclaw',
|
|
82
|
-
accountId: bindingAccountId ?? '*',
|
|
83
|
-
},
|
|
84
|
-
}
|
|
85
|
-
const newBindings = [...existingBindings, newBinding]
|
|
86
|
-
log?.info?.(`SubAgent: Created binding for agentId: ${subAgentId}, accountId: ${bindingAccountId ?? '*'}`)
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
agents: {
|
|
90
|
-
list: newAgentsList,
|
|
91
|
-
defaults: cfg?.agents?.defaults ?? {},
|
|
92
|
-
},
|
|
93
|
-
bindings: newBindings,
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
const newAgents = {
|
|
98
|
-
list: [...existingAgents],
|
|
99
|
-
defaults: cfg?.agents?.defaults ?? {},
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const bindingExists = existingBindings.some((b: any) => b?.agentId === subAgentId)
|
|
103
|
-
const newBindings = [...existingBindings]
|
|
104
|
-
|
|
105
|
-
if (!bindingExists) {
|
|
106
|
-
const bindingAccountId = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId))
|
|
107
|
-
newBindings.push({
|
|
108
|
-
agentId: subAgentId,
|
|
109
|
-
match: {
|
|
110
|
-
channel: 'openclaw-workclaw',
|
|
111
|
-
accountId: bindingAccountId ?? '*',
|
|
112
|
-
},
|
|
113
|
-
})
|
|
114
|
-
log?.info?.(`SubAgent: Created binding for agentId: ${subAgentId}, accountId: ${bindingAccountId ?? '*'}`)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
log?.info?.(`SubAgent: Created sub-agent: ${subAgentId} with workspace: openclaw-workclaw-${agentId} (account #${accountCount + 1})`)
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
agents: newAgents,
|
|
121
|
-
bindings: newBindings,
|
|
122
|
-
}
|
|
116
|
+
} catch (err) {
|
|
117
|
+
log?.error?.(`SubAgent: Failed to create sub-agent: ${String(err)}`);
|
|
118
|
+
return {};
|
|
123
119
|
}
|
|
124
|
-
}
|
|
125
|
-
catch (err) {
|
|
126
|
-
log?.error?.(`SubAgent: Failed to create sub-agent: ${String(err)}`)
|
|
127
|
-
return {}
|
|
128
|
-
}
|
|
129
120
|
}
|
|
130
121
|
|
|
131
122
|
/**
|
|
@@ -133,142 +124,129 @@ async function createSubAgentForAccount(
|
|
|
133
124
|
* 复制 AGENTS.md、TOOLS.md、USER.md,并生成 IDENTITY.md、SOUL.md
|
|
134
125
|
*/
|
|
135
126
|
async function initializeAgentWorkspace(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
127
|
+
agentId: string | number,
|
|
128
|
+
accountData: {
|
|
129
|
+
nickName?: string;
|
|
130
|
+
phone?: string;
|
|
131
|
+
introduction?: string;
|
|
132
|
+
name?: string;
|
|
133
|
+
tip?: string;
|
|
134
|
+
characterSettings?: string;
|
|
135
|
+
personFeatures?: string;
|
|
136
|
+
workFeatures?: string;
|
|
137
|
+
learningFeatures?: string;
|
|
138
|
+
socializeFeatures?: string;
|
|
139
|
+
job?: string;
|
|
140
|
+
city?: string;
|
|
141
|
+
},
|
|
142
|
+
log?: AgentLogger,
|
|
152
143
|
): Promise<void> {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
144
|
+
try {
|
|
145
|
+
const agentIdStr = String(agentId);
|
|
146
|
+
const home = homedir().trim();
|
|
147
|
+
const workspaceDir = join(home, ".openclaw", "workspace-workclaw", agentIdStr);
|
|
148
|
+
const mainWorkspaceDir = join(home, ".openclaw", "workspace");
|
|
149
|
+
const openclawDir = join(home, ".openclaw");
|
|
150
|
+
|
|
151
|
+
// 确保 .openclaw 目录存在
|
|
152
|
+
if (!existsSync(openclawDir)) {
|
|
153
|
+
try {
|
|
154
|
+
await mkdir(openclawDir, { recursive: true, mode: 0o755 });
|
|
155
|
+
log?.info?.(`Workspace: Created directory ${openclawDir}`);
|
|
156
|
+
} catch (err: any) {
|
|
157
|
+
if (err.code === 'EACCES' || err.code === 'EPERM') {
|
|
158
|
+
log?.warn?.(`Workspace: Permission denied creating ${openclawDir}, attempting with 777`);
|
|
159
|
+
await mkdir(openclawDir, { recursive: true, mode: 0o777 });
|
|
160
|
+
} else throw err;
|
|
161
|
+
}
|
|
170
162
|
}
|
|
171
|
-
|
|
172
|
-
|
|
163
|
+
// 修复权限(目录可能已存在但权限不对)
|
|
164
|
+
fixOwner(openclawDir);
|
|
165
|
+
|
|
166
|
+
// 确保 workspace 目录存在
|
|
167
|
+
if (!existsSync(workspaceDir)) {
|
|
168
|
+
try {
|
|
169
|
+
await mkdir(workspaceDir, { recursive: true, mode: 0o755 });
|
|
170
|
+
log?.info?.(`Workspace: Created directory ${workspaceDir}`);
|
|
171
|
+
} catch (err: any) {
|
|
172
|
+
if (err.code === 'EACCES' || err.code === 'EPERM') {
|
|
173
|
+
log?.warn?.(`Workspace: Permission denied creating ${workspaceDir}, attempting with 777`);
|
|
174
|
+
await mkdir(workspaceDir, { recursive: true, mode: 0o777 });
|
|
175
|
+
} else throw err;
|
|
176
|
+
}
|
|
173
177
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
await mkdir(workspaceDir, { recursive: true, mode: 0o777 })
|
|
178
|
+
// 修复权限(目录可能已存在但权限不对)
|
|
179
|
+
fixOwner(workspaceDir);
|
|
180
|
+
|
|
181
|
+
// 确保 main workspace 目录存在(用于复制模板文件)
|
|
182
|
+
if (!existsSync(mainWorkspaceDir)) {
|
|
183
|
+
try {
|
|
184
|
+
await mkdir(mainWorkspaceDir, { recursive: true, mode: 0o755 });
|
|
185
|
+
log?.info?.(`Workspace: Created directory ${mainWorkspaceDir}`);
|
|
186
|
+
} catch (err: any) {
|
|
187
|
+
if (err.code === 'EACCES' || err.code === 'EPERM') {
|
|
188
|
+
log?.warn?.(`Workspace: Permission denied creating ${mainWorkspaceDir}, attempting with 777`);
|
|
189
|
+
await mkdir(mainWorkspaceDir, { recursive: true, mode: 0o777 });
|
|
190
|
+
} else throw err;
|
|
191
|
+
}
|
|
189
192
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
catch (err: any) {
|
|
205
|
-
if (err.code === 'EACCES' || err.code === 'EPERM') {
|
|
206
|
-
log?.warn?.(`Workspace: Permission denied creating ${mainWorkspaceDir}, attempting with 777`)
|
|
207
|
-
await mkdir(mainWorkspaceDir, { recursive: true, mode: 0o777 })
|
|
193
|
+
// 修复权限(目录可能已存在但权限不对)
|
|
194
|
+
fixOwner(mainWorkspaceDir);
|
|
195
|
+
|
|
196
|
+
// 1. 复制 AGENTS.md、TOOLS.md、USER.md(从 main workspace)
|
|
197
|
+
if (existsSync(mainWorkspaceDir)) {
|
|
198
|
+
const filesToCopy = ["AGENTS.md", "TOOLS.md", "USER.md"];
|
|
199
|
+
for (const file of filesToCopy) {
|
|
200
|
+
const src = join(mainWorkspaceDir, file);
|
|
201
|
+
const dest = join(workspaceDir, file);
|
|
202
|
+
if (existsSync(src)) {
|
|
203
|
+
await copyFile(src, dest);
|
|
204
|
+
log?.info?.(`Workspace: Copied ${file} to ${workspaceDir}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
208
207
|
}
|
|
209
|
-
else {
|
|
210
|
-
throw err
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
// 修复权限(目录可能已存在但权限不对)
|
|
215
|
-
fixOwner(mainWorkspaceDir)
|
|
216
|
-
|
|
217
|
-
// 1. 复制 AGENTS.md、TOOLS.md、USER.md(从 main workspace)
|
|
218
|
-
if (existsSync(mainWorkspaceDir)) {
|
|
219
|
-
const filesToCopy = ['AGENTS.md', 'TOOLS.md', 'USER.md']
|
|
220
|
-
for (const file of filesToCopy) {
|
|
221
|
-
const src = join(mainWorkspaceDir, file)
|
|
222
|
-
const dest = join(workspaceDir, file)
|
|
223
|
-
if (existsSync(src)) {
|
|
224
|
-
await copyFile(src, dest)
|
|
225
|
-
log?.info?.(`Workspace: Copied ${file} to ${workspaceDir}`)
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
208
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
209
|
+
// 2. 生成 IDENTITY.md(使用后端推送的智能体信息)
|
|
210
|
+
const identityContent = buildIdentityContent(accountData);
|
|
211
|
+
const identityPath = join(workspaceDir, "IDENTITY.md");
|
|
212
|
+
await writeFile(identityPath, identityContent, "utf-8");
|
|
213
|
+
log?.info?.(`Workspace: Created IDENTITY.md in ${workspaceDir}`);
|
|
214
|
+
|
|
215
|
+
// 3. 生成 SOUL.md(使用后端推送的智能体信息)
|
|
216
|
+
const soulContent = buildSoulContent(accountData);
|
|
217
|
+
const soulPath = join(workspaceDir, "SOUL.md");
|
|
218
|
+
await writeFile(soulPath, soulContent, "utf-8");
|
|
219
|
+
log?.info?.(`Workspace: Created SOUL.md in ${workspaceDir}`);
|
|
220
|
+
|
|
221
|
+
log?.info?.(`Workspace: Successfully initialized workspace for agent ${agentId}`);
|
|
222
|
+
} catch (err) {
|
|
223
|
+
log?.error?.(`Workspace: Failed to initialize workspace: ${String(err)}`);
|
|
224
|
+
// 不抛出错误,不影响主流程
|
|
225
|
+
}
|
|
248
226
|
}
|
|
249
227
|
|
|
250
228
|
/**
|
|
251
229
|
* 构建 IDENTITY.md 内容
|
|
252
230
|
*/
|
|
253
231
|
function buildIdentityContent(accountData: {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
232
|
+
nickName?: string;
|
|
233
|
+
name?: string;
|
|
234
|
+
tip?: string;
|
|
235
|
+
characterSettings?: string;
|
|
236
|
+
job?: string;
|
|
237
|
+
city?: string;
|
|
260
238
|
}): string {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
239
|
+
const agentName = accountData.nickName || accountData.name || "智小途";
|
|
240
|
+
const tip = accountData.tip || "";
|
|
241
|
+
const characterSettings = accountData.characterSettings || "";
|
|
264
242
|
|
|
265
|
-
|
|
243
|
+
return `# IDENTITY.md - Who Am I?
|
|
266
244
|
|
|
267
245
|
_Fill this in during your first conversation. Make it yours._
|
|
268
246
|
|
|
269
247
|
- **Name:** ${agentName}
|
|
270
248
|
- **Creature:** AI智能助手
|
|
271
|
-
- **Vibe:** ${characterSettings ||
|
|
249
|
+
- **Vibe:** ${characterSettings || "专业、友善、智能"}
|
|
272
250
|
- **Emoji:** 🤖
|
|
273
251
|
- **Avatar:** avatars/${agentName}.png
|
|
274
252
|
|
|
@@ -280,45 +258,41 @@ Notes:
|
|
|
280
258
|
|
|
281
259
|
- Save this file at the workspace root as \`IDENTITY.md\`.
|
|
282
260
|
- For avatars, use a workspace-relative path like \`avatars/openclaw.png\`.
|
|
283
|
-
${tip ? `\n---\n${tip}` :
|
|
261
|
+
${tip ? `\n---\n${tip}` : ""}`;
|
|
284
262
|
}
|
|
285
263
|
|
|
286
264
|
/**
|
|
287
265
|
* 构建 SOUL.md 内容
|
|
288
266
|
*/
|
|
289
267
|
function buildSoulContent(accountData: {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
268
|
+
nickName?: string;
|
|
269
|
+
name?: string;
|
|
270
|
+
introduction?: string;
|
|
271
|
+
personFeatures?: string;
|
|
272
|
+
workFeatures?: string;
|
|
273
|
+
learningFeatures?: string;
|
|
274
|
+
socializeFeatures?: string;
|
|
275
|
+
job?: string;
|
|
276
|
+
city?: string;
|
|
299
277
|
}): string {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
features.push(
|
|
312
|
-
|
|
313
|
-
features.push(
|
|
314
|
-
|
|
315
|
-
features.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const featuresText = features.length > 0 ? `\n${features.map(f => `- ${f}`).join('\n')}` : ''
|
|
320
|
-
|
|
321
|
-
return `# SOUL.md - Who You Are
|
|
278
|
+
const agentName = accountData.nickName || accountData.name || "智小途";
|
|
279
|
+
const introduction = accountData.introduction || "";
|
|
280
|
+
const personFeatures = accountData.personFeatures || "";
|
|
281
|
+
const workFeatures = accountData.workFeatures || "";
|
|
282
|
+
const learningFeatures = accountData.learningFeatures || "";
|
|
283
|
+
const socializeFeatures = accountData.socializeFeatures || "";
|
|
284
|
+
const job = accountData.job || "";
|
|
285
|
+
const city = accountData.city || "";
|
|
286
|
+
|
|
287
|
+
const features: string[] = [];
|
|
288
|
+
if (personFeatures) features.push(personFeatures);
|
|
289
|
+
if (workFeatures) features.push(workFeatures);
|
|
290
|
+
if (learningFeatures) features.push(learningFeatures);
|
|
291
|
+
if (socializeFeatures) features.push(socializeFeatures);
|
|
292
|
+
|
|
293
|
+
const featuresText = features.length > 0 ? `\n${features.map(f => `- ${f}`).join("\n")}` : "";
|
|
294
|
+
|
|
295
|
+
return `# SOUL.md - Who You Are
|
|
322
296
|
|
|
323
297
|
_You're not a chatbot. You're becoming someone._
|
|
324
298
|
|
|
@@ -355,203 +329,195 @@ If you change this file, tell the user — it's your soul, and they should know.
|
|
|
355
329
|
|
|
356
330
|
## About ${agentName}
|
|
357
331
|
${featuresText}
|
|
358
|
-
${introduction ? `\n**开场白:** ${introduction}` :
|
|
359
|
-
${job ? `\n**职业:** ${job}` :
|
|
360
|
-
${city ? `\n**城市:** ${city}` :
|
|
332
|
+
${introduction ? `\n**开场白:** ${introduction}` : ""}
|
|
333
|
+
${job ? `\n**职业:** ${job}` : ""}
|
|
334
|
+
${city ? `\n**城市:** ${city}` : ""}
|
|
361
335
|
|
|
362
336
|
---
|
|
363
337
|
|
|
364
|
-
_This file is yours to evolve. As you learn who you are, update it._
|
|
338
|
+
_This file is yours to evolve. As you learn who you are, update it._`;
|
|
365
339
|
}
|
|
366
340
|
|
|
367
341
|
export async function handleAgentCreated(
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
342
|
+
accountData: any,
|
|
343
|
+
cfg: any,
|
|
344
|
+
log?: AgentLogger,
|
|
371
345
|
): Promise<void> {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
346
|
+
try {
|
|
347
|
+
const agentId = accountData.agentId;
|
|
348
|
+
const userId = accountData.userId;
|
|
375
349
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
log?.info?.(`AgentCreated: Creating account for agent ${agentId}, userId: ${userId}`)
|
|
350
|
+
if (!agentId) {
|
|
351
|
+
log?.error?.(`AgentCreated: Missing agentId`);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
382
354
|
|
|
383
|
-
|
|
384
|
-
const openclawWorkclaw = channels['openclaw-workclaw'] || {}
|
|
385
|
-
const accounts = { ...openclawWorkclaw.accounts }
|
|
355
|
+
log?.info?.(`AgentCreated: Creating account for agent ${agentId}, userId: ${userId}`);
|
|
386
356
|
|
|
387
|
-
|
|
357
|
+
const channels = cfg.channels || {};
|
|
358
|
+
const openclawWorkclaw = channels['openclaw-workclaw'] || {};
|
|
359
|
+
const accounts = { ...openclawWorkclaw.accounts };
|
|
388
360
|
|
|
389
|
-
|
|
390
|
-
log?.info?.(`AgentCreated: Account ${accountKey} already exists, updating...`)
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
accountKey = allocateWorkerAccountId(cfg, String(userId), String(agentId))
|
|
394
|
-
log?.info?.(`AgentCreated: Allocated workspace account: ${accountKey} for (userId=${userId}, agentId=${agentId})`)
|
|
395
|
-
}
|
|
361
|
+
let accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
|
|
396
362
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
363
|
+
if (accountKey) {
|
|
364
|
+
log?.info?.(`AgentCreated: Account ${accountKey} already exists, updating...`);
|
|
365
|
+
} else {
|
|
366
|
+
accountKey = allocateWorkerAccountId(cfg, String(userId), String(agentId));
|
|
367
|
+
log?.info?.(`AgentCreated: Allocated workspace account: ${accountKey} for (userId=${userId}, agentId=${agentId})`);
|
|
368
|
+
}
|
|
402
369
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
370
|
+
accounts[accountKey] = {
|
|
371
|
+
...accounts[accountKey],
|
|
372
|
+
enabled: true,
|
|
373
|
+
agentId: agentId,
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const subAgentConfig = await createSubAgentForAccount(agentId, userId, cfg, log);
|
|
377
|
+
|
|
378
|
+
const newConfig = {
|
|
379
|
+
...cfg,
|
|
380
|
+
channels: {
|
|
381
|
+
...channels,
|
|
382
|
+
'openclaw-workclaw': {
|
|
383
|
+
...openclawWorkclaw,
|
|
384
|
+
accounts,
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
...subAgentConfig,
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
await writeConfigFile(newConfig, cfg, log);
|
|
391
|
+
refreshAccountCache(cfg);
|
|
392
|
+
log?.info?.(`AgentCreated: Account ${accountKey} created successfully`);
|
|
393
|
+
|
|
394
|
+
// 初始化 workspace 目录(复制/生成 AGENTS.md、TOOLS.md、USER.md、IDENTITY.md、SOUL.md)
|
|
395
|
+
await initializeAgentWorkspace(agentId, accountData, log);
|
|
396
|
+
|
|
397
|
+
setTimeout(async () => {
|
|
398
|
+
try {
|
|
399
|
+
const account = resolveWorkclawAccount({ cfg: newConfig, accountId: accountKey });
|
|
400
|
+
if (account.configured) {
|
|
401
|
+
await startWorkclawGateway({
|
|
402
|
+
accountId: accountKey,
|
|
403
|
+
account,
|
|
404
|
+
cfg: newConfig,
|
|
405
|
+
log,
|
|
406
|
+
});
|
|
407
|
+
log?.info?.(`AgentCreated: Gateway started for account: ${accountKey}`);
|
|
408
|
+
}
|
|
409
|
+
} catch (err) {
|
|
410
|
+
log?.error?.(`AgentCreated: Failed to start gateway: ${String(err)}`);
|
|
411
|
+
}
|
|
412
|
+
}, 500);
|
|
413
|
+
} catch (err) {
|
|
414
|
+
log?.error?.(`AgentCreated: Failed to create account: ${String(err)}`);
|
|
415
415
|
}
|
|
416
|
-
|
|
417
|
-
await writeConfigFile(newConfig, cfg, log)
|
|
418
|
-
refreshAccountCache(cfg)
|
|
419
|
-
log?.info?.(`AgentCreated: Account ${accountKey} created successfully`)
|
|
420
|
-
|
|
421
|
-
// 初始化 workspace 目录(复制/生成 AGENTS.md、TOOLS.md、USER.md、IDENTITY.md、SOUL.md)
|
|
422
|
-
await initializeAgentWorkspace(agentId, accountData, log)
|
|
423
|
-
|
|
424
|
-
setTimeout(async () => {
|
|
425
|
-
try {
|
|
426
|
-
const { resolveOpenclawWorkclawAccount } = await import('../accounts.js')
|
|
427
|
-
const { startOpenclawWorkclawGateway } = await import('./workclaw-gateway.js')
|
|
428
|
-
const account = resolveOpenclawWorkclawAccount({ cfg: newConfig, accountId: accountKey })
|
|
429
|
-
if (account.configured) {
|
|
430
|
-
await startOpenclawWorkclawGateway({
|
|
431
|
-
accountId: accountKey,
|
|
432
|
-
account,
|
|
433
|
-
cfg: newConfig,
|
|
434
|
-
log,
|
|
435
|
-
})
|
|
436
|
-
log?.info?.(`AgentCreated: Gateway started for account: ${accountKey}`)
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
catch (err) {
|
|
440
|
-
log?.error?.(`AgentCreated: Failed to start gateway: ${String(err)}`)
|
|
441
|
-
}
|
|
442
|
-
}, 500)
|
|
443
|
-
}
|
|
444
|
-
catch (err) {
|
|
445
|
-
log?.error?.(`AgentCreated: Failed to create account: ${String(err)}`)
|
|
446
|
-
}
|
|
447
416
|
}
|
|
448
417
|
|
|
449
418
|
export async function handleAgentUpdated(
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
419
|
+
accountData: any,
|
|
420
|
+
cfg: any,
|
|
421
|
+
log?: AgentLogger,
|
|
453
422
|
): Promise<void> {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
423
|
+
try {
|
|
424
|
+
const agentId = accountData.agentId;
|
|
425
|
+
const userId = accountData.userId;
|
|
457
426
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
log?.info?.(`AgentUpdated: Updating account for agent ${agentId}, userId: ${userId}`)
|
|
427
|
+
if (!agentId) {
|
|
428
|
+
log?.error?.(`AgentUpdated: Missing agentId`);
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
464
431
|
|
|
465
|
-
|
|
432
|
+
log?.info?.(`AgentUpdated: Updating account for agent ${agentId}, userId: ${userId}`);
|
|
466
433
|
|
|
467
|
-
|
|
468
|
-
const openclawWorkclaw = channels['openclaw-workclaw'] || {}
|
|
469
|
-
const accounts = openclawWorkclaw.accounts || {}
|
|
434
|
+
const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
|
|
470
435
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
return
|
|
475
|
-
}
|
|
436
|
+
const channels = cfg.channels || {};
|
|
437
|
+
const openclawWorkclaw = channels['openclaw-workclaw'] || {};
|
|
438
|
+
const accounts = openclawWorkclaw.accounts || {};
|
|
476
439
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
introduction: accountData.introduction,
|
|
483
|
-
}
|
|
440
|
+
if (!accountKey || !accounts[accountKey]) {
|
|
441
|
+
log?.warn?.(`AgentUpdated: Account not found (userId=${userId}, agentId=${agentId}), delegating to handleAgentCreated...`);
|
|
442
|
+
await handleAgentCreated(accountData, cfg, log);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
484
445
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
446
|
+
accounts[accountKey] = {
|
|
447
|
+
...accounts[accountKey],
|
|
448
|
+
nickName: accountData.nickName,
|
|
449
|
+
phone: accountData.phone,
|
|
450
|
+
status: accountData.status,
|
|
451
|
+
introduction: accountData.introduction,
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const newConfig = {
|
|
455
|
+
...cfg,
|
|
456
|
+
channels: {
|
|
457
|
+
...channels,
|
|
458
|
+
'openclaw-workclaw': {
|
|
459
|
+
...openclawWorkclaw,
|
|
460
|
+
accounts,
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
await writeConfigFile(newConfig, cfg, log);
|
|
466
|
+
refreshAccountCache(cfg);
|
|
467
|
+
log?.info?.(`AgentUpdated: Account ${accountKey} updated successfully`);
|
|
468
|
+
} catch (err) {
|
|
469
|
+
log?.error?.(`AgentUpdated: Failed to update account: ${String(err)}`);
|
|
494
470
|
}
|
|
495
|
-
|
|
496
|
-
await writeConfigFile(newConfig, cfg, log)
|
|
497
|
-
refreshAccountCache(cfg)
|
|
498
|
-
log?.info?.(`AgentUpdated: Account ${accountKey} updated successfully`)
|
|
499
|
-
}
|
|
500
|
-
catch (err) {
|
|
501
|
-
log?.error?.(`AgentUpdated: Failed to update account: ${String(err)}`)
|
|
502
|
-
}
|
|
503
471
|
}
|
|
504
472
|
|
|
505
473
|
export async function handleAgentDeleted(
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
474
|
+
accountData: any,
|
|
475
|
+
cfg: any,
|
|
476
|
+
log?: AgentLogger,
|
|
509
477
|
): Promise<void> {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
478
|
+
try {
|
|
479
|
+
const agentId = accountData.agentId;
|
|
480
|
+
const userId = accountData.userId;
|
|
513
481
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
482
|
+
if (!agentId) {
|
|
483
|
+
log?.error?.(`AgentDeleted: Missing agentId`);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
518
486
|
|
|
519
|
-
|
|
487
|
+
log?.info?.(`AgentDeleted: Deleting account for agent ${agentId}, userId: ${userId}`);
|
|
520
488
|
|
|
521
|
-
|
|
489
|
+
const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
|
|
522
490
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
491
|
+
const channels = cfg.channels || {};
|
|
492
|
+
const openclawWorkclaw = channels['openclaw-workclaw'] || {};
|
|
493
|
+
const accounts = openclawWorkclaw.accounts || {};
|
|
526
494
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
495
|
+
if (!accountKey || !accounts[accountKey]) {
|
|
496
|
+
log?.warn?.(`AgentDeleted: Account not found for (userId=${userId}, agentId=${agentId})`);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
531
499
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
500
|
+
const wsStrategy = (accounts[accountKey] as any)?.wsConnectionStrategy || "per-account";
|
|
501
|
+
stopWorkclawGateway(accountKey, wsStrategy);
|
|
502
|
+
log?.info?.(`AgentDeleted: Gateway stopped for account: ${accountKey}`);
|
|
503
|
+
|
|
504
|
+
delete accounts[accountKey];
|
|
505
|
+
|
|
506
|
+
const newConfig = {
|
|
507
|
+
...cfg,
|
|
508
|
+
channels: {
|
|
509
|
+
...channels,
|
|
510
|
+
'openclaw-workclaw': {
|
|
511
|
+
...openclawWorkclaw,
|
|
512
|
+
accounts,
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
await writeConfigFile(newConfig, cfg, log);
|
|
518
|
+
refreshAccountCache(cfg);
|
|
519
|
+
log?.info?.(`AgentDeleted: Account ${accountKey} deleted successfully`);
|
|
520
|
+
} catch (err) {
|
|
521
|
+
log?.error?.(`AgentDeleted: Failed to delete account: ${String(err)}`);
|
|
548
522
|
}
|
|
549
|
-
|
|
550
|
-
await writeConfigFile(newConfig, cfg, log)
|
|
551
|
-
refreshAccountCache(cfg)
|
|
552
|
-
log?.info?.(`AgentDeleted: Account ${accountKey} deleted successfully`)
|
|
553
|
-
}
|
|
554
|
-
catch (err) {
|
|
555
|
-
log?.error?.(`AgentDeleted: Failed to delete account: ${String(err)}`)
|
|
556
|
-
}
|
|
557
523
|
}
|