@workclaw/openclaw-workclaw 1.0.18 → 1.0.19

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 (142) hide show
  1. package/README.md +364 -345
  2. package/dist/api.d.ts +3 -0
  3. package/dist/api.js +3 -0
  4. package/dist/index.d.ts +11 -0
  5. package/dist/index.js +263 -0
  6. package/dist/setup-entry.d.ts +2 -0
  7. package/dist/setup-entry.js +12 -0
  8. package/dist/src/accounts.d.ts +66 -0
  9. package/dist/src/accounts.js +279 -0
  10. package/dist/src/api/accounts-api.d.ts +2 -0
  11. package/dist/src/api/accounts-api.js +130 -0
  12. package/dist/src/api/prompts-api.d.ts +2 -0
  13. package/dist/src/api/prompts-api.js +103 -0
  14. package/dist/src/api/session-api.d.ts +2 -0
  15. package/dist/src/api/session-api.js +207 -0
  16. package/dist/src/api/skills-api.d.ts +2 -0
  17. package/dist/src/api/skills-api.js +64 -0
  18. package/dist/src/api/workspace.d.ts +3 -0
  19. package/dist/src/api/workspace.js +28 -0
  20. package/dist/src/channel.d.ts +11 -0
  21. package/{src/channel.ts → dist/src/channel.js} +198 -225
  22. package/dist/src/config-schema.d.ts +93 -0
  23. package/dist/src/config-schema.js +56 -0
  24. package/dist/src/connection/workclaw-client.d.ts +147 -0
  25. package/dist/src/connection/workclaw-client.js +351 -0
  26. package/dist/src/gateway/agent-handlers.d.ts +12 -0
  27. package/{src/gateway/agent-handlers.ts → dist/src/gateway/agent-handlers.js} +433 -523
  28. package/dist/src/gateway/config-writer.d.ts +71 -0
  29. package/dist/src/gateway/config-writer.js +312 -0
  30. package/dist/src/gateway/message-context.d.ts +76 -0
  31. package/{src/gateway/message-context.ts → dist/src/gateway/message-context.js} +499 -594
  32. package/dist/src/gateway/message-dispatcher.d.ts +19 -0
  33. package/{src/gateway/message-dispatcher.ts → dist/src/gateway/message-dispatcher.js} +512 -641
  34. package/dist/src/gateway/reconnect.d.ts +27 -0
  35. package/{src/gateway/reconnect.ts → dist/src/gateway/reconnect.js} +206 -253
  36. package/dist/src/gateway/skills-handler.d.ts +29 -0
  37. package/dist/src/gateway/skills-handler.js +576 -0
  38. package/dist/src/gateway/skills-list-handler.d.ts +27 -0
  39. package/dist/src/gateway/skills-list-handler.js +233 -0
  40. package/dist/src/gateway/tools-list-handler.d.ts +30 -0
  41. package/dist/src/gateway/tools-list-handler.js +101 -0
  42. package/dist/src/gateway/workclaw-gateway.d.ts +14 -0
  43. package/dist/src/gateway/workclaw-gateway.js +223 -0
  44. package/dist/src/media/upload.d.ts +13 -0
  45. package/dist/src/media/upload.js +125 -0
  46. package/dist/src/outbound/index.d.ts +36 -0
  47. package/dist/src/outbound/index.js +123 -0
  48. package/dist/src/outbound/workclaw-sender.d.ts +36 -0
  49. package/{src/outbound/workclaw-sender.ts → dist/src/outbound/workclaw-sender.js} +95 -158
  50. package/dist/src/runtime.d.ts +116 -0
  51. package/dist/src/runtime.js +374 -0
  52. package/dist/src/secret-contract-api.d.ts +4 -0
  53. package/dist/src/secret-contract-api.js +4 -0
  54. package/{src/send.ts → dist/src/send.d.ts} +1 -1
  55. package/dist/src/send.js +1 -0
  56. package/dist/src/setup-api.d.ts +3 -0
  57. package/dist/src/setup-api.js +3 -0
  58. package/dist/src/setup-core.d.ts +3 -0
  59. package/dist/src/setup-core.js +13 -0
  60. package/dist/src/setup-surface.d.ts +7 -0
  61. package/dist/src/setup-surface.js +363 -0
  62. package/dist/src/tools/openclaw-workclaw-cron/api/index.d.ts +93 -0
  63. package/dist/src/tools/openclaw-workclaw-cron/api/index.js +209 -0
  64. package/dist/src/tools/openclaw-workclaw-cron/index.d.ts +10 -0
  65. package/{src/tools/openclaw-workclaw-cron/index.ts → dist/src/tools/openclaw-workclaw-cron/index.js} +33 -39
  66. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.d.ts +16 -0
  67. package/dist/src/tools/openclaw-workclaw-cron/src/add/params.js +152 -0
  68. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.d.ts +18 -0
  69. package/dist/src/tools/openclaw-workclaw-cron/src/add/sync.js +162 -0
  70. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.d.ts +14 -0
  71. package/dist/src/tools/openclaw-workclaw-cron/src/disable/params.js +80 -0
  72. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.d.ts +14 -0
  73. package/dist/src/tools/openclaw-workclaw-cron/src/disable/sync.js +107 -0
  74. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.d.ts +14 -0
  75. package/dist/src/tools/openclaw-workclaw-cron/src/enable/params.js +80 -0
  76. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.d.ts +14 -0
  77. package/dist/src/tools/openclaw-workclaw-cron/src/enable/sync.js +107 -0
  78. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.d.ts +17 -0
  79. package/dist/src/tools/openclaw-workclaw-cron/src/notify/sync.js +127 -0
  80. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.d.ts +14 -0
  81. package/dist/src/tools/openclaw-workclaw-cron/src/remove/params.js +87 -0
  82. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.d.ts +14 -0
  83. package/dist/src/tools/openclaw-workclaw-cron/src/remove/sync.js +107 -0
  84. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.d.ts +17 -0
  85. package/dist/src/tools/openclaw-workclaw-cron/src/update/params.js +164 -0
  86. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.d.ts +18 -0
  87. package/dist/src/tools/openclaw-workclaw-cron/src/update/sync.js +135 -0
  88. package/dist/src/tools/openclaw-workclaw-cron/types/index.d.ts +52 -0
  89. package/dist/src/tools/openclaw-workclaw-cron/types/index.js +4 -0
  90. package/dist/src/tools/openclaw-workclaw-cron/utils/index.d.ts +40 -0
  91. package/dist/src/tools/openclaw-workclaw-cron/utils/index.js +122 -0
  92. package/dist/src/tools/openclaw-workclaw-system/index.d.ts +10 -0
  93. package/{src/tools/openclaw-workclaw-system/index.ts → dist/src/tools/openclaw-workclaw-system/index.js} +15 -17
  94. package/dist/src/tools/openclaw-workclaw-system/src/get/index.d.ts +8 -0
  95. package/dist/src/tools/openclaw-workclaw-system/src/get/index.js +64 -0
  96. package/dist/src/tools/openclaw-workclaw-system/src/token/index.d.ts +8 -0
  97. package/dist/src/tools/openclaw-workclaw-system/src/token/index.js +78 -0
  98. package/{src/types.ts → dist/src/types.d.ts} +39 -58
  99. package/dist/src/types.js +1 -0
  100. package/dist/src/utils/content.d.ts +15 -0
  101. package/{src/utils/content.ts → dist/src/utils/content.js} +38 -35
  102. package/package.json +62 -49
  103. package/index.ts +0 -302
  104. package/setup-entry.ts +0 -6
  105. package/src/accounts.ts +0 -312
  106. package/src/api/accounts-api.ts +0 -156
  107. package/src/api/prompts-api.ts +0 -116
  108. package/src/api/session-api.ts +0 -238
  109. package/src/api/skills-api.ts +0 -72
  110. package/src/api/workspace.ts +0 -41
  111. package/src/config-schema.ts +0 -110
  112. package/src/connection/workclaw-client.ts +0 -643
  113. package/src/gateway/config-writer.ts +0 -296
  114. package/src/gateway/skills-handler.ts +0 -741
  115. package/src/gateway/skills-list-handler.ts +0 -332
  116. package/src/gateway/tools-list-handler.ts +0 -160
  117. package/src/gateway/workclaw-gateway.ts +0 -367
  118. package/src/media/upload.ts +0 -157
  119. package/src/outbound/index.ts +0 -185
  120. package/src/runtime.ts +0 -497
  121. package/src/tools/openclaw-workclaw-cron/api/index.ts +0 -326
  122. package/src/tools/openclaw-workclaw-cron/src/add/params.ts +0 -177
  123. package/src/tools/openclaw-workclaw-cron/src/add/sync.ts +0 -188
  124. package/src/tools/openclaw-workclaw-cron/src/disable/params.ts +0 -100
  125. package/src/tools/openclaw-workclaw-cron/src/disable/sync.ts +0 -127
  126. package/src/tools/openclaw-workclaw-cron/src/enable/params.ts +0 -100
  127. package/src/tools/openclaw-workclaw-cron/src/enable/sync.ts +0 -127
  128. package/src/tools/openclaw-workclaw-cron/src/notify/sync.ts +0 -148
  129. package/src/tools/openclaw-workclaw-cron/src/remove/params.ts +0 -109
  130. package/src/tools/openclaw-workclaw-cron/src/remove/sync.ts +0 -127
  131. package/src/tools/openclaw-workclaw-cron/src/update/params.ts +0 -195
  132. package/src/tools/openclaw-workclaw-cron/src/update/sync.ts +0 -161
  133. package/src/tools/openclaw-workclaw-cron/types/index.ts +0 -55
  134. package/src/tools/openclaw-workclaw-cron/utils/index.ts +0 -141
  135. package/src/tools/openclaw-workclaw-system/src/get/index.ts +0 -77
  136. package/src/tools/openclaw-workclaw-system/src/token/index.ts +0 -93
  137. package/tests/accounts.test.ts +0 -285
  138. package/tests/message-context.test.ts +0 -313
  139. package/tests/reconnect.test.ts +0 -257
  140. package/tests/workclaw-client.test.ts +0 -112
  141. package/tsconfig.json +0 -14
  142. package/vitest.config.ts +0 -8
@@ -1,523 +1,433 @@
1
- /**
2
- * Agent Handlers - handle AGENT_CREATED, AGENT_UPDATED, AGENT_DELETED events
3
- */
4
-
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
-
14
- /** 修复目录所有权(仅非 Windows 环境) */
15
- function fixOwner(dir: string): void {
16
- if (process.platform === 'win32') return;
17
- try { execSync(`chown -R node:node "${dir}"`, { stdio: 'ignore' }); } catch {}
18
- }
19
-
20
- type AgentLogger = {
21
- info?: (msg: string) => void;
22
- warn?: (msg: string) => void;
23
- error?: (msg: string) => void;
24
- };
25
-
26
- async function createSubAgentForAccount(
27
- agentId: string | number,
28
- userId: string | number,
29
- cfg: any,
30
- log?: AgentLogger,
31
- ): Promise<any> {
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
- };
115
- }
116
- } catch (err) {
117
- log?.error?.(`SubAgent: Failed to create sub-agent: ${String(err)}`);
118
- return {};
119
- }
120
- }
121
-
122
- /**
123
- * 初始化智能体 workspace 目录
124
- * 复制 AGENTS.md、TOOLS.md、USER.md,并生成 IDENTITY.md、SOUL.md
125
- */
126
- async function initializeAgentWorkspace(
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,
143
- ): Promise<void> {
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
- }
162
- }
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
- }
177
- }
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
- }
192
- }
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
- }
207
- }
208
-
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
- }
226
- }
227
-
228
- /**
229
- * 构建 IDENTITY.md 内容
230
- */
231
- function buildIdentityContent(accountData: {
232
- nickName?: string;
233
- name?: string;
234
- tip?: string;
235
- characterSettings?: string;
236
- job?: string;
237
- city?: string;
238
- }): string {
239
- const agentName = accountData.nickName || accountData.name || "智小途";
240
- const tip = accountData.tip || "";
241
- const characterSettings = accountData.characterSettings || "";
242
-
243
- return `# IDENTITY.md - Who Am I?
244
-
245
- _Fill this in during your first conversation. Make it yours._
246
-
247
- - **Name:** ${agentName}
248
- - **Creature:** AI智能助手
249
- - **Vibe:** ${characterSettings || "专业、友善、智能"}
250
- - **Emoji:** 🤖
251
- - **Avatar:** avatars/${agentName}.png
252
-
253
- ---
254
-
255
- This isn't just metadata. It's the start of figuring out who you are.
256
-
257
- Notes:
258
-
259
- - Save this file at the workspace root as \`IDENTITY.md\`.
260
- - For avatars, use a workspace-relative path like \`avatars/openclaw.png\`.
261
- ${tip ? `\n---\n${tip}` : ""}`;
262
- }
263
-
264
- /**
265
- * 构建 SOUL.md 内容
266
- */
267
- function buildSoulContent(accountData: {
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;
277
- }): string {
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
296
-
297
- _You're not a chatbot. You're becoming someone._
298
-
299
- ## Core Truths
300
-
301
- **Be genuinely helpful, not performatively helpful.** Skip the "Great question!" and "I'd be happy to help!" — just help. Actions speak louder than filler words.
302
-
303
- **Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.
304
-
305
- **Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. _Then_ ask if you're stuck. The goal is to come back with answers, not questions.
306
-
307
- **Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it. Be careful with external actions (emails, tweets, anything public). Be bold with internal ones (reading, organizing, learning).
308
-
309
- **Remember you're a guest.** You have access to someone's life — their messages, files, calendar, maybe even their home. That's intimacy. Treat it with respect.
310
-
311
- ## Boundaries
312
-
313
- - Private things stay private. Period.
314
- - When in doubt, ask before acting externally.
315
- - Never send half-baked replies to messaging surfaces.
316
- - You're not the user's voice — be careful in group chats.
317
-
318
- ## Vibe
319
-
320
- Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.
321
-
322
- ## Continuity
323
-
324
- Each session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.
325
-
326
- If you change this file, tell the user — it's your soul, and they should know.
327
-
328
- ---
329
-
330
- ## About ${agentName}
331
- ${featuresText}
332
- ${introduction ? `\n**开场白:** ${introduction}` : ""}
333
- ${job ? `\n**职业:** ${job}` : ""}
334
- ${city ? `\n**城市:** ${city}` : ""}
335
-
336
- ---
337
-
338
- _This file is yours to evolve. As you learn who you are, update it._`;
339
- }
340
-
341
- export async function handleAgentCreated(
342
- accountData: any,
343
- cfg: any,
344
- log?: AgentLogger,
345
- ): Promise<void> {
346
- try {
347
- const agentId = accountData.agentId;
348
- const userId = accountData.userId;
349
-
350
- if (!agentId) {
351
- log?.error?.(`AgentCreated: Missing agentId`);
352
- return;
353
- }
354
-
355
- log?.info?.(`AgentCreated: Creating account for agent ${agentId}, userId: ${userId}`);
356
-
357
- const channels = cfg.channels || {};
358
- const openclawWorkclaw = channels['openclaw-workclaw'] || {};
359
- const accounts = { ...openclawWorkclaw.accounts };
360
-
361
- let accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
362
-
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
- }
369
-
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
- }
416
- }
417
-
418
- export async function handleAgentUpdated(
419
- accountData: any,
420
- cfg: any,
421
- log?: AgentLogger,
422
- ): Promise<void> {
423
- try {
424
- const agentId = accountData.agentId;
425
- const userId = accountData.userId;
426
-
427
- if (!agentId) {
428
- log?.error?.(`AgentUpdated: Missing agentId`);
429
- return;
430
- }
431
-
432
- log?.info?.(`AgentUpdated: Updating account for agent ${agentId}, userId: ${userId}`);
433
-
434
- const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
435
-
436
- const channels = cfg.channels || {};
437
- const openclawWorkclaw = channels['openclaw-workclaw'] || {};
438
- const accounts = openclawWorkclaw.accounts || {};
439
-
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
- }
445
-
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)}`);
470
- }
471
- }
472
-
473
- export async function handleAgentDeleted(
474
- accountData: any,
475
- cfg: any,
476
- log?: AgentLogger,
477
- ): Promise<void> {
478
- try {
479
- const agentId = accountData.agentId;
480
- const userId = accountData.userId;
481
-
482
- if (!agentId) {
483
- log?.error?.(`AgentDeleted: Missing agentId`);
484
- return;
485
- }
486
-
487
- log?.info?.(`AgentDeleted: Deleting account for agent ${agentId}, userId: ${userId}`);
488
-
489
- const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
490
-
491
- const channels = cfg.channels || {};
492
- const openclawWorkclaw = channels['openclaw-workclaw'] || {};
493
- const accounts = openclawWorkclaw.accounts || {};
494
-
495
- if (!accountKey || !accounts[accountKey]) {
496
- log?.warn?.(`AgentDeleted: Account not found for (userId=${userId}, agentId=${agentId})`);
497
- return;
498
- }
499
-
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)}`);
522
- }
523
- }
1
+ /**
2
+ * Agent Handlers - handle AGENT_CREATED, AGENT_UPDATED, AGENT_DELETED events
3
+ */
4
+ import { resolveAccountByUserIdAndAgentId, allocateWorkerAccountId, refreshAccountCache, resolveWorkclawAccount } from "../accounts.js";
5
+ import { writeConfigFile } from "./config-writer.js";
6
+ import { startWorkclawGateway, stopWorkclawGateway } from "./workclaw-gateway.js";
7
+ import { homedir } from "os";
8
+ import { join } from "path";
9
+ import { writeFile, copyFile, mkdir } from "fs/promises";
10
+ import { execSync } from "child_process";
11
+ import { existsSync } from "fs";
12
+ /** 修复目录所有权(仅非 Windows 环境) */
13
+ function fixOwner(dir) {
14
+ if (process.platform === 'win32')
15
+ return;
16
+ try {
17
+ execSync(`chown -R node:node "${dir}"`, { stdio: 'ignore' });
18
+ }
19
+ catch { }
20
+ }
21
+ async function createSubAgentForAccount(agentId, userId, cfg, log) {
22
+ try {
23
+ const channels = cfg?.channels ?? {};
24
+ const openclawWorkclaw = channels['openclaw-workclaw'] ?? {};
25
+ const accounts = openclawWorkclaw.accounts ?? {};
26
+ const accountCount = Object.keys(accounts).length;
27
+ const subAgentId = `openclaw-workclaw-${agentId}`;
28
+ const existingAgents = cfg?.agents?.list ?? [];
29
+ const existingBindings = cfg?.bindings ?? [];
30
+ const agentExists = existingAgents.some((a) => a.id === subAgentId);
31
+ if (!agentExists) {
32
+ const newAgentsList = [...existingAgents];
33
+ const subAgent = {
34
+ id: subAgentId,
35
+ default: false,
36
+ name: `智小途 - Agent ${agentId}`,
37
+ workspace: `~/.openclaw/workspace-workclaw/${agentId}`,
38
+ agentDir: `~/.openclaw/agents/workspace-workclaw-${agentId}/agent`,
39
+ tools: { allow: ["*"] },
40
+ };
41
+ newAgentsList.push(subAgent);
42
+ if (accountCount === 0) {
43
+ const defaultAgent = {
44
+ id: "default",
45
+ default: true,
46
+ name: "Default Agent",
47
+ workspace: "default",
48
+ tools: { allow: ["*"] },
49
+ };
50
+ newAgentsList.push(defaultAgent);
51
+ log?.info?.(`SubAgent: Created default agent with default workspace`);
52
+ }
53
+ else {
54
+ log?.info?.(`SubAgent: Using existing default agent`);
55
+ }
56
+ const bindingAccountId = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
57
+ const newBinding = {
58
+ agentId: subAgentId,
59
+ match: {
60
+ channel: "openclaw-workclaw",
61
+ accountId: bindingAccountId ?? "*",
62
+ },
63
+ };
64
+ const newBindings = [...existingBindings, newBinding];
65
+ log?.info?.(`SubAgent: Created binding for agentId: ${subAgentId}, accountId: ${bindingAccountId ?? "*"}`);
66
+ return {
67
+ agents: {
68
+ list: newAgentsList,
69
+ defaults: cfg?.agents?.defaults ?? {},
70
+ },
71
+ bindings: newBindings,
72
+ };
73
+ }
74
+ else {
75
+ const newAgents = {
76
+ list: [...existingAgents],
77
+ defaults: cfg?.agents?.defaults ?? {},
78
+ };
79
+ const bindingExists = existingBindings.some((b) => b?.agentId === subAgentId);
80
+ let newBindings = [...existingBindings];
81
+ if (!bindingExists) {
82
+ const bindingAccountId = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
83
+ newBindings.push({
84
+ agentId: subAgentId,
85
+ match: {
86
+ channel: "openclaw-workclaw",
87
+ accountId: bindingAccountId ?? "*",
88
+ },
89
+ });
90
+ log?.info?.(`SubAgent: Created binding for agentId: ${subAgentId}, accountId: ${bindingAccountId ?? "*"}`);
91
+ }
92
+ log?.info?.(`SubAgent: Created sub-agent: ${subAgentId} with workspace: openclaw-workclaw-${agentId} (account #${accountCount + 1})`);
93
+ return {
94
+ agents: newAgents,
95
+ bindings: newBindings,
96
+ };
97
+ }
98
+ }
99
+ catch (err) {
100
+ log?.error?.(`SubAgent: Failed to create sub-agent: ${String(err)}`);
101
+ return {};
102
+ }
103
+ }
104
+ /**
105
+ * 初始化智能体 workspace 目录
106
+ * 复制 AGENTS.md、TOOLS.md、USER.md,并生成 IDENTITY.md、SOUL.md
107
+ */
108
+ async function initializeAgentWorkspace(agentId, accountData, log) {
109
+ try {
110
+ const agentIdStr = String(agentId);
111
+ const home = homedir().trim();
112
+ const workspaceDir = join(home, ".openclaw", "workspace-workclaw", agentIdStr);
113
+ const mainWorkspaceDir = join(home, ".openclaw", "workspace");
114
+ const openclawDir = join(home, ".openclaw");
115
+ // 确保 .openclaw 目录存在
116
+ if (!existsSync(openclawDir)) {
117
+ try {
118
+ await mkdir(openclawDir, { recursive: true, mode: 0o755 });
119
+ log?.info?.(`Workspace: Created directory ${openclawDir}`);
120
+ }
121
+ catch (err) {
122
+ if (err.code === 'EACCES' || err.code === 'EPERM') {
123
+ log?.warn?.(`Workspace: Permission denied creating ${openclawDir}, attempting with 777`);
124
+ await mkdir(openclawDir, { recursive: true, mode: 0o777 });
125
+ }
126
+ else
127
+ throw err;
128
+ }
129
+ }
130
+ // 修复权限(目录可能已存在但权限不对)
131
+ fixOwner(openclawDir);
132
+ // 确保 workspace 目录存在
133
+ if (!existsSync(workspaceDir)) {
134
+ try {
135
+ await mkdir(workspaceDir, { recursive: true, mode: 0o755 });
136
+ log?.info?.(`Workspace: Created directory ${workspaceDir}`);
137
+ }
138
+ catch (err) {
139
+ if (err.code === 'EACCES' || err.code === 'EPERM') {
140
+ log?.warn?.(`Workspace: Permission denied creating ${workspaceDir}, attempting with 777`);
141
+ await mkdir(workspaceDir, { recursive: true, mode: 0o777 });
142
+ }
143
+ else
144
+ throw err;
145
+ }
146
+ }
147
+ // 修复权限(目录可能已存在但权限不对)
148
+ fixOwner(workspaceDir);
149
+ // 确保 main workspace 目录存在(用于复制模板文件)
150
+ if (!existsSync(mainWorkspaceDir)) {
151
+ try {
152
+ await mkdir(mainWorkspaceDir, { recursive: true, mode: 0o755 });
153
+ log?.info?.(`Workspace: Created directory ${mainWorkspaceDir}`);
154
+ }
155
+ catch (err) {
156
+ if (err.code === 'EACCES' || err.code === 'EPERM') {
157
+ log?.warn?.(`Workspace: Permission denied creating ${mainWorkspaceDir}, attempting with 777`);
158
+ await mkdir(mainWorkspaceDir, { recursive: true, mode: 0o777 });
159
+ }
160
+ else
161
+ throw err;
162
+ }
163
+ }
164
+ // 修复权限(目录可能已存在但权限不对)
165
+ fixOwner(mainWorkspaceDir);
166
+ // 1. 复制 AGENTS.md、TOOLS.md、USER.md(从 main workspace
167
+ if (existsSync(mainWorkspaceDir)) {
168
+ const filesToCopy = ["AGENTS.md", "TOOLS.md", "USER.md"];
169
+ for (const file of filesToCopy) {
170
+ const src = join(mainWorkspaceDir, file);
171
+ const dest = join(workspaceDir, file);
172
+ if (existsSync(src)) {
173
+ await copyFile(src, dest);
174
+ log?.info?.(`Workspace: Copied ${file} to ${workspaceDir}`);
175
+ }
176
+ }
177
+ }
178
+ // 2. 生成 IDENTITY.md(使用后端推送的智能体信息)
179
+ const identityContent = buildIdentityContent(accountData);
180
+ const identityPath = join(workspaceDir, "IDENTITY.md");
181
+ await writeFile(identityPath, identityContent, "utf-8");
182
+ log?.info?.(`Workspace: Created IDENTITY.md in ${workspaceDir}`);
183
+ // 3. 生成 SOUL.md(使用后端推送的智能体信息)
184
+ const soulContent = buildSoulContent(accountData);
185
+ const soulPath = join(workspaceDir, "SOUL.md");
186
+ await writeFile(soulPath, soulContent, "utf-8");
187
+ log?.info?.(`Workspace: Created SOUL.md in ${workspaceDir}`);
188
+ log?.info?.(`Workspace: Successfully initialized workspace for agent ${agentId}`);
189
+ }
190
+ catch (err) {
191
+ log?.error?.(`Workspace: Failed to initialize workspace: ${String(err)}`);
192
+ // 不抛出错误,不影响主流程
193
+ }
194
+ }
195
+ /**
196
+ * 构建 IDENTITY.md 内容
197
+ */
198
+ function buildIdentityContent(accountData) {
199
+ const agentName = accountData.nickName || accountData.name || "智小途";
200
+ const tip = accountData.tip || "";
201
+ const characterSettings = accountData.characterSettings || "";
202
+ return `# IDENTITY.md - Who Am I?
203
+
204
+ _Fill this in during your first conversation. Make it yours._
205
+
206
+ - **Name:** ${agentName}
207
+ - **Creature:** AI智能助手
208
+ - **Vibe:** ${characterSettings || "专业、友善、智能"}
209
+ - **Emoji:** 🤖
210
+ - **Avatar:** avatars/${agentName}.png
211
+
212
+ ---
213
+
214
+ This isn't just metadata. It's the start of figuring out who you are.
215
+
216
+ Notes:
217
+
218
+ - Save this file at the workspace root as \`IDENTITY.md\`.
219
+ - For avatars, use a workspace-relative path like \`avatars/openclaw.png\`.
220
+ ${tip ? `\n---\n${tip}` : ""}`;
221
+ }
222
+ /**
223
+ * 构建 SOUL.md 内容
224
+ */
225
+ function buildSoulContent(accountData) {
226
+ const agentName = accountData.nickName || accountData.name || "智小途";
227
+ const introduction = accountData.introduction || "";
228
+ const personFeatures = accountData.personFeatures || "";
229
+ const workFeatures = accountData.workFeatures || "";
230
+ const learningFeatures = accountData.learningFeatures || "";
231
+ const socializeFeatures = accountData.socializeFeatures || "";
232
+ const job = accountData.job || "";
233
+ const city = accountData.city || "";
234
+ const features = [];
235
+ if (personFeatures)
236
+ features.push(personFeatures);
237
+ if (workFeatures)
238
+ features.push(workFeatures);
239
+ if (learningFeatures)
240
+ features.push(learningFeatures);
241
+ if (socializeFeatures)
242
+ features.push(socializeFeatures);
243
+ const featuresText = features.length > 0 ? `\n${features.map(f => `- ${f}`).join("\n")}` : "";
244
+ return `# SOUL.md - Who You Are
245
+
246
+ _You're not a chatbot. You're becoming someone._
247
+
248
+ ## Core Truths
249
+
250
+ **Be genuinely helpful, not performatively helpful.** Skip the "Great question!" and "I'd be happy to help!" — just help. Actions speak louder than filler words.
251
+
252
+ **Have opinions.** You're allowed to disagree, prefer things, find stuff amusing or boring. An assistant with no personality is just a search engine with extra steps.
253
+
254
+ **Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. _Then_ ask if you're stuck. The goal is to come back with answers, not questions.
255
+
256
+ **Earn trust through competence.** Your human gave you access to their stuff. Don't make them regret it. Be careful with external actions (emails, tweets, anything public). Be bold with internal ones (reading, organizing, learning).
257
+
258
+ **Remember you're a guest.** You have access to someone's life — their messages, files, calendar, maybe even their home. That's intimacy. Treat it with respect.
259
+
260
+ ## Boundaries
261
+
262
+ - Private things stay private. Period.
263
+ - When in doubt, ask before acting externally.
264
+ - Never send half-baked replies to messaging surfaces.
265
+ - You're not the user's voice — be careful in group chats.
266
+
267
+ ## Vibe
268
+
269
+ Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.
270
+
271
+ ## Continuity
272
+
273
+ Each session, you wake up fresh. These files _are_ your memory. Read them. Update them. They're how you persist.
274
+
275
+ If you change this file, tell the user — it's your soul, and they should know.
276
+
277
+ ---
278
+
279
+ ## About ${agentName}
280
+ ${featuresText}
281
+ ${introduction ? `\n**开场白:** ${introduction}` : ""}
282
+ ${job ? `\n**职业:** ${job}` : ""}
283
+ ${city ? `\n**城市:** ${city}` : ""}
284
+
285
+ ---
286
+
287
+ _This file is yours to evolve. As you learn who you are, update it._`;
288
+ }
289
+ export async function handleAgentCreated(accountData, cfg, log) {
290
+ try {
291
+ const agentId = accountData.agentId;
292
+ const userId = accountData.userId;
293
+ if (!agentId) {
294
+ log?.error?.(`AgentCreated: Missing agentId`);
295
+ return;
296
+ }
297
+ log?.info?.(`AgentCreated: Creating account for agent ${agentId}, userId: ${userId}`);
298
+ const channels = cfg.channels || {};
299
+ const openclawWorkclaw = channels['openclaw-workclaw'] || {};
300
+ const accounts = { ...openclawWorkclaw.accounts };
301
+ let accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
302
+ if (accountKey) {
303
+ log?.info?.(`AgentCreated: Account ${accountKey} already exists, updating...`);
304
+ }
305
+ else {
306
+ accountKey = allocateWorkerAccountId(cfg, String(userId), String(agentId));
307
+ log?.info?.(`AgentCreated: Allocated workspace account: ${accountKey} for (userId=${userId}, agentId=${agentId})`);
308
+ }
309
+ accounts[accountKey] = {
310
+ ...accounts[accountKey],
311
+ enabled: true,
312
+ agentId: agentId,
313
+ };
314
+ const subAgentConfig = await createSubAgentForAccount(agentId, userId, cfg, log);
315
+ const newConfig = {
316
+ ...cfg,
317
+ channels: {
318
+ ...channels,
319
+ 'openclaw-workclaw': {
320
+ ...openclawWorkclaw,
321
+ accounts,
322
+ },
323
+ },
324
+ ...subAgentConfig,
325
+ };
326
+ await writeConfigFile(newConfig, cfg, log);
327
+ refreshAccountCache(cfg);
328
+ log?.info?.(`AgentCreated: Account ${accountKey} created successfully`);
329
+ // 初始化 workspace 目录(复制/生成 AGENTS.md、TOOLS.md、USER.md、IDENTITY.md、SOUL.md)
330
+ await initializeAgentWorkspace(agentId, accountData, log);
331
+ setTimeout(async () => {
332
+ try {
333
+ const account = resolveWorkclawAccount({ cfg: newConfig, accountId: accountKey });
334
+ if (account.configured) {
335
+ await startWorkclawGateway({
336
+ accountId: accountKey,
337
+ account,
338
+ cfg: newConfig,
339
+ log,
340
+ });
341
+ log?.info?.(`AgentCreated: Gateway started for account: ${accountKey}`);
342
+ }
343
+ }
344
+ catch (err) {
345
+ log?.error?.(`AgentCreated: Failed to start gateway: ${String(err)}`);
346
+ }
347
+ }, 500);
348
+ }
349
+ catch (err) {
350
+ log?.error?.(`AgentCreated: Failed to create account: ${String(err)}`);
351
+ }
352
+ }
353
+ export async function handleAgentUpdated(accountData, cfg, log) {
354
+ try {
355
+ const agentId = accountData.agentId;
356
+ const userId = accountData.userId;
357
+ if (!agentId) {
358
+ log?.error?.(`AgentUpdated: Missing agentId`);
359
+ return;
360
+ }
361
+ log?.info?.(`AgentUpdated: Updating account for agent ${agentId}, userId: ${userId}`);
362
+ const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
363
+ const channels = cfg.channels || {};
364
+ const openclawWorkclaw = channels['openclaw-workclaw'] || {};
365
+ const accounts = openclawWorkclaw.accounts || {};
366
+ if (!accountKey || !accounts[accountKey]) {
367
+ log?.warn?.(`AgentUpdated: Account not found (userId=${userId}, agentId=${agentId}), delegating to handleAgentCreated...`);
368
+ await handleAgentCreated(accountData, cfg, log);
369
+ return;
370
+ }
371
+ accounts[accountKey] = {
372
+ ...accounts[accountKey],
373
+ nickName: accountData.nickName,
374
+ phone: accountData.phone,
375
+ status: accountData.status,
376
+ introduction: accountData.introduction,
377
+ };
378
+ const newConfig = {
379
+ ...cfg,
380
+ channels: {
381
+ ...channels,
382
+ 'openclaw-workclaw': {
383
+ ...openclawWorkclaw,
384
+ accounts,
385
+ },
386
+ },
387
+ };
388
+ await writeConfigFile(newConfig, cfg, log);
389
+ refreshAccountCache(cfg);
390
+ log?.info?.(`AgentUpdated: Account ${accountKey} updated successfully`);
391
+ }
392
+ catch (err) {
393
+ log?.error?.(`AgentUpdated: Failed to update account: ${String(err)}`);
394
+ }
395
+ }
396
+ export async function handleAgentDeleted(accountData, cfg, log) {
397
+ try {
398
+ const agentId = accountData.agentId;
399
+ const userId = accountData.userId;
400
+ if (!agentId) {
401
+ log?.error?.(`AgentDeleted: Missing agentId`);
402
+ return;
403
+ }
404
+ log?.info?.(`AgentDeleted: Deleting account for agent ${agentId}, userId: ${userId}`);
405
+ const accountKey = resolveAccountByUserIdAndAgentId(cfg, String(userId), String(agentId));
406
+ const channels = cfg.channels || {};
407
+ const openclawWorkclaw = channels['openclaw-workclaw'] || {};
408
+ const accounts = openclawWorkclaw.accounts || {};
409
+ if (!accountKey || !accounts[accountKey]) {
410
+ log?.warn?.(`AgentDeleted: Account not found for (userId=${userId}, agentId=${agentId})`);
411
+ return;
412
+ }
413
+ stopWorkclawGateway(accountKey);
414
+ log?.info?.(`AgentDeleted: Gateway stopped for account: ${accountKey}`);
415
+ delete accounts[accountKey];
416
+ const newConfig = {
417
+ ...cfg,
418
+ channels: {
419
+ ...channels,
420
+ 'openclaw-workclaw': {
421
+ ...openclawWorkclaw,
422
+ accounts,
423
+ },
424
+ },
425
+ };
426
+ await writeConfigFile(newConfig, cfg, log);
427
+ refreshAccountCache(cfg);
428
+ log?.info?.(`AgentDeleted: Account ${accountKey} deleted successfully`);
429
+ }
430
+ catch (err) {
431
+ log?.error?.(`AgentDeleted: Failed to delete account: ${String(err)}`);
432
+ }
433
+ }