@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
@@ -2,130 +2,121 @@
2
2
  * Agent Handlers - handle AGENT_CREATED, AGENT_UPDATED, AGENT_DELETED events
3
3
  */
4
4
 
5
- import { execSync } from 'node:child_process'
6
- import { existsSync } from 'node:fs'
7
- import { copyFile, mkdir, writeFile } from 'node:fs/promises'
8
- import { homedir } from 'node:os'
9
- import { join } from 'node:path'
10
- import process from 'node:process'
11
- import { allocateWorkerAccountId, refreshAccountCache, resolveAccountByUserIdAndAgentId } from '../accounts.js'
12
- import { writeConfigFile } from './config-writer.js'
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
- if (process.platform === 'win32') {
17
- return
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
- interface AgentLogger {
27
- info?: (msg: string) => void
28
- warn?: (msg: string) => void
29
- error?: (msg: string) => void
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
- agentId: string | number,
34
- userId: string | number,
35
- cfg: any,
36
- log?: AgentLogger,
27
+ agentId: string | number,
28
+ userId: string | number,
29
+ cfg: any,
30
+ log?: AgentLogger,
37
31
  ): Promise<any> {
38
- try {
39
- const channels = cfg?.channels ?? {}
40
- const openclawWorkclaw = channels['openclaw-workclaw'] ?? {}
41
- const accounts = openclawWorkclaw.accounts ?? {}
42
- const accountCount = Object.keys(accounts).length
43
- const subAgentId = `openclaw-workclaw-${agentId}`
44
-
45
- const existingAgents = cfg?.agents?.list ?? []
46
- const existingBindings = cfg?.bindings ?? []
47
-
48
- const agentExists = existingAgents.some((a: any) => a.id === subAgentId)
49
-
50
- if (!agentExists) {
51
- const newAgentsList = [...existingAgents]
52
- const subAgent = {
53
- id: subAgentId,
54
- default: false,
55
- name: `智小途 - Agent ${agentId}`,
56
- workspace: `~/.openclaw/workspace-workclaw/${agentId}`,
57
- agentDir: `~/.openclaw/agents/workspace-workclaw-${agentId}/agent`,
58
- tools: { allow: ['*'] },
59
- }
60
- newAgentsList.push(subAgent)
61
-
62
- if (accountCount === 0) {
63
- const defaultAgent = {
64
- id: 'default',
65
- default: true,
66
- name: 'Default Agent',
67
- workspace: 'default',
68
- tools: { allow: ['*'] },
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
- newAgentsList.push(defaultAgent)
71
- log?.info?.(`SubAgent: Created default agent with default workspace`)
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
- agentId: string | number,
137
- accountData: {
138
- nickName?: string
139
- phone?: string
140
- introduction?: string
141
- name?: string
142
- tip?: string
143
- characterSettings?: string
144
- personFeatures?: string
145
- workFeatures?: string
146
- learningFeatures?: string
147
- socializeFeatures?: string
148
- job?: string
149
- city?: string
150
- },
151
- log?: AgentLogger,
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
- try {
154
- const agentIdStr = String(agentId)
155
- const home = homedir().trim()
156
- const workspaceDir = join(home, '.openclaw', 'workspace-workclaw', agentIdStr)
157
- const mainWorkspaceDir = join(home, '.openclaw', 'workspace')
158
- const openclawDir = join(home, '.openclaw')
159
-
160
- // 确保 .openclaw 目录存在
161
- if (!existsSync(openclawDir)) {
162
- try {
163
- await mkdir(openclawDir, { recursive: true, mode: 0o755 })
164
- log?.info?.(`Workspace: Created directory ${openclawDir}`)
165
- }
166
- catch (err: any) {
167
- if (err.code === 'EACCES' || err.code === 'EPERM') {
168
- log?.warn?.(`Workspace: Permission denied creating ${openclawDir}, attempting with 777`)
169
- await mkdir(openclawDir, { recursive: true, mode: 0o777 })
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
- else {
172
- throw err
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
- fixOwner(openclawDir)
178
-
179
- // 确保 workspace 目录存在
180
- if (!existsSync(workspaceDir)) {
181
- try {
182
- await mkdir(workspaceDir, { recursive: true, mode: 0o755 })
183
- log?.info?.(`Workspace: Created directory ${workspaceDir}`)
184
- }
185
- catch (err: any) {
186
- if (err.code === 'EACCES' || err.code === 'EPERM') {
187
- log?.warn?.(`Workspace: Permission denied creating ${workspaceDir}, attempting with 777`)
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
- else {
191
- throw err
192
- }
193
- }
194
- }
195
- // 修复权限(目录可能已存在但权限不对)
196
- fixOwner(workspaceDir)
197
-
198
- // 确保 main workspace 目录存在(用于复制模板文件)
199
- if (!existsSync(mainWorkspaceDir)) {
200
- try {
201
- await mkdir(mainWorkspaceDir, { recursive: true, mode: 0o755 })
202
- log?.info?.(`Workspace: Created directory ${mainWorkspaceDir}`)
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
- // 2. 生成 IDENTITY.md(使用后端推送的智能体信息)
231
- const identityContent = buildIdentityContent(accountData)
232
- const identityPath = join(workspaceDir, 'IDENTITY.md')
233
- await writeFile(identityPath, identityContent, 'utf-8')
234
- log?.info?.(`Workspace: Created IDENTITY.md in ${workspaceDir}`)
235
-
236
- // 3. 生成 SOUL.md(使用后端推送的智能体信息)
237
- const soulContent = buildSoulContent(accountData)
238
- const soulPath = join(workspaceDir, 'SOUL.md')
239
- await writeFile(soulPath, soulContent, 'utf-8')
240
- log?.info?.(`Workspace: Created SOUL.md in ${workspaceDir}`)
241
-
242
- log?.info?.(`Workspace: Successfully initialized workspace for agent ${agentId}`)
243
- }
244
- catch (err) {
245
- log?.error?.(`Workspace: Failed to initialize workspace: ${String(err)}`)
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
- nickName?: string
255
- name?: string
256
- tip?: string
257
- characterSettings?: string
258
- job?: string
259
- city?: string
232
+ nickName?: string;
233
+ name?: string;
234
+ tip?: string;
235
+ characterSettings?: string;
236
+ job?: string;
237
+ city?: string;
260
238
  }): string {
261
- const agentName = accountData.nickName || accountData.name || '智小途'
262
- const tip = accountData.tip || ''
263
- const characterSettings = accountData.characterSettings || ''
239
+ const agentName = accountData.nickName || accountData.name || "智小途";
240
+ const tip = accountData.tip || "";
241
+ const characterSettings = accountData.characterSettings || "";
264
242
 
265
- return `# IDENTITY.md - Who Am I?
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
- nickName?: string
291
- name?: string
292
- introduction?: string
293
- personFeatures?: string
294
- workFeatures?: string
295
- learningFeatures?: string
296
- socializeFeatures?: string
297
- job?: string
298
- city?: string
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
- const agentName = accountData.nickName || accountData.name || '智小途'
301
- const introduction = accountData.introduction || ''
302
- const personFeatures = accountData.personFeatures || ''
303
- const workFeatures = accountData.workFeatures || ''
304
- const learningFeatures = accountData.learningFeatures || ''
305
- const socializeFeatures = accountData.socializeFeatures || ''
306
- const job = accountData.job || ''
307
- const city = accountData.city || ''
308
-
309
- const features: string[] = []
310
- if (personFeatures)
311
- features.push(personFeatures)
312
- if (workFeatures)
313
- features.push(workFeatures)
314
- if (learningFeatures)
315
- features.push(learningFeatures)
316
- if (socializeFeatures)
317
- features.push(socializeFeatures)
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
- accountData: any,
369
- cfg: any,
370
- log?: AgentLogger,
342
+ accountData: any,
343
+ cfg: any,
344
+ log?: AgentLogger,
371
345
  ): Promise<void> {
372
- try {
373
- const agentId = accountData.agentId
374
- const userId = accountData.userId
346
+ try {
347
+ const agentId = accountData.agentId;
348
+ const userId = accountData.userId;
375
349
 
376
- if (!agentId) {
377
- log?.error?.(`AgentCreated: Missing agentId`)
378
- return
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
- const channels = cfg.channels || {}
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
- let accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId))
357
+ const channels = cfg.channels || {};
358
+ const openclawWorkclaw = channels['openclaw-workclaw'] || {};
359
+ const accounts = { ...openclawWorkclaw.accounts };
388
360
 
389
- if (accountKey) {
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
- accounts[accountKey] = {
398
- ...accounts[accountKey],
399
- enabled: true,
400
- agentId,
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
- const subAgentConfig = await createSubAgentForAccount(agentId, userId, cfg, log)
404
-
405
- const newConfig = {
406
- ...cfg,
407
- channels: {
408
- ...channels,
409
- 'openclaw-workclaw': {
410
- ...openclawWorkclaw,
411
- accounts,
412
- },
413
- },
414
- ...subAgentConfig,
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
- accountData: any,
451
- cfg: any,
452
- log?: AgentLogger,
419
+ accountData: any,
420
+ cfg: any,
421
+ log?: AgentLogger,
453
422
  ): Promise<void> {
454
- try {
455
- const agentId = accountData.agentId
456
- const userId = accountData.userId
423
+ try {
424
+ const agentId = accountData.agentId;
425
+ const userId = accountData.userId;
457
426
 
458
- if (!agentId) {
459
- log?.error?.(`AgentUpdated: Missing agentId`)
460
- return
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
- const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId))
432
+ log?.info?.(`AgentUpdated: Updating account for agent ${agentId}, userId: ${userId}`);
466
433
 
467
- const channels = cfg.channels || {}
468
- const openclawWorkclaw = channels['openclaw-workclaw'] || {}
469
- const accounts = openclawWorkclaw.accounts || {}
434
+ const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
470
435
 
471
- if (!accountKey || !accounts[accountKey]) {
472
- log?.warn?.(`AgentUpdated: Account not found (userId=${userId}, agentId=${agentId}), delegating to handleAgentCreated...`)
473
- await handleAgentCreated(accountData, cfg, log)
474
- return
475
- }
436
+ const channels = cfg.channels || {};
437
+ const openclawWorkclaw = channels['openclaw-workclaw'] || {};
438
+ const accounts = openclawWorkclaw.accounts || {};
476
439
 
477
- accounts[accountKey] = {
478
- ...accounts[accountKey],
479
- nickName: accountData.nickName,
480
- phone: accountData.phone,
481
- status: accountData.status,
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
- const newConfig = {
486
- ...cfg,
487
- channels: {
488
- ...channels,
489
- 'openclaw-workclaw': {
490
- ...openclawWorkclaw,
491
- accounts,
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
- accountData: any,
507
- cfg: any,
508
- log?: AgentLogger,
474
+ accountData: any,
475
+ cfg: any,
476
+ log?: AgentLogger,
509
477
  ): Promise<void> {
510
- try {
511
- const agentId = accountData.agentId
512
- const userId = accountData.userId
478
+ try {
479
+ const agentId = accountData.agentId;
480
+ const userId = accountData.userId;
513
481
 
514
- if (!agentId) {
515
- log?.error?.(`AgentDeleted: Missing agentId`)
516
- return
517
- }
482
+ if (!agentId) {
483
+ log?.error?.(`AgentDeleted: Missing agentId`);
484
+ return;
485
+ }
518
486
 
519
- log?.info?.(`AgentDeleted: Deleting account for agent ${agentId}, userId: ${userId}`)
487
+ log?.info?.(`AgentDeleted: Deleting account for agent ${agentId}, userId: ${userId}`);
520
488
 
521
- const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId))
489
+ const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
522
490
 
523
- const channels = cfg.channels || {}
524
- const openclawWorkclaw = channels['openclaw-workclaw'] || {}
525
- const accounts = openclawWorkclaw.accounts || {}
491
+ const channels = cfg.channels || {};
492
+ const openclawWorkclaw = channels['openclaw-workclaw'] || {};
493
+ const accounts = openclawWorkclaw.accounts || {};
526
494
 
527
- if (!accountKey || !accounts[accountKey]) {
528
- log?.warn?.(`AgentDeleted: Account not found for (userId=${userId}, agentId=${agentId})`)
529
- return
530
- }
495
+ if (!accountKey || !accounts[accountKey]) {
496
+ log?.warn?.(`AgentDeleted: Account not found for (userId=${userId}, agentId=${agentId})`);
497
+ return;
498
+ }
531
499
 
532
- const { stopOpenclawWorkclawGateway } = await import('./workclaw-gateway.js')
533
- const wsStrategy = (accounts[accountKey] as any)?.wsConnectionStrategy || 'per-account'
534
- stopOpenclawWorkclawGateway(accountKey, wsStrategy)
535
- log?.info?.(`AgentDeleted: Gateway stopped for account: ${accountKey}`)
536
-
537
- delete accounts[accountKey]
538
-
539
- const newConfig = {
540
- ...cfg,
541
- channels: {
542
- ...channels,
543
- 'openclaw-workclaw': {
544
- ...openclawWorkclaw,
545
- accounts,
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
  }