@workclaw/openclaw-workclaw 1.0.19 → 1.0.201

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/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
6
6
  import path from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { workclawPlugin } from "./src/channel.js";
9
- import { setWorkclawRuntime, getWorkclawRuntime, getToolResultHint, } from "./src/runtime.js";
9
+ import { setWorkclawRuntime, getWorkclawRuntime, getToolResultHint, getWorkclawLogger, } from "./src/runtime.js";
10
10
  import { resolveWorkspaceDir, isDefaultWorkspace } from "./src/api/workspace.js";
11
11
  import { createPromptsApiHandler } from "./src/api/prompts-api.js";
12
12
  import { createAccountsApiHandler } from "./src/api/accounts-api.js";
@@ -201,6 +201,7 @@ async function writeFileIfMissing(filePath, content) {
201
201
  }
202
202
  async function releaseTemplates(api) {
203
203
  const workspaceDir = resolveWorkspaceDir(api);
204
+ getWorkclawLogger().info(`workspaceDir: ${workspaceDir}`);
204
205
  // 模板文件在根目录的 templates/ 中,而不是 dist/templates/
205
206
  const currentDir = getPluginDir();
206
207
  const templatesDir = path.join(currentDir.endsWith("dist") ? path.dirname(currentDir) : currentDir, "templates");
@@ -1,5 +1,6 @@
1
1
  import os from "node:os";
2
2
  import path from "node:path";
3
+ import { getWorkclawLogger } from "../runtime.js";
3
4
  export function resolveWorkspaceDir(api) {
4
5
  const agents = api.config.agents?.list ?? [];
5
6
  const defaultAgent = agents.find((agent) => agent?.default === true) ??
@@ -7,6 +8,7 @@ export function resolveWorkspaceDir(api) {
7
8
  const workspaceRaw = (typeof defaultAgent?.workspace === "string" && defaultAgent.workspace.trim()) ||
8
9
  (typeof api.config.agents?.defaults?.workspace === "string" &&
9
10
  api.config.agents.defaults.workspace.trim());
11
+ getWorkclawLogger().info(`workspaceRaw: ${workspaceRaw}`);
10
12
  if (workspaceRaw) {
11
13
  return api.resolvePath(workspaceRaw);
12
14
  }
@@ -79,6 +79,7 @@ export const workclawPlugin = {
79
79
  cfg: cfg,
80
80
  to,
81
81
  text,
82
+ ...(replyToId ? { replyToMessageId: replyToId } : {}),
82
83
  });
83
84
  return {
84
85
  channel: "openclaw-workclaw",
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Agent Handlers - handle AGENT_CREATED, AGENT_UPDATED, AGENT_DELETED events
3
3
  */
4
- import { resolveAccountByUserIdAndAgentId, allocateWorkerAccountId, refreshAccountCache, resolveWorkclawAccount } from "../accounts.js";
4
+ import { resolveAccountByUserIdAndAgentId, allocateWorkerAccountId, refreshAccountCache } from "../accounts.js";
5
5
  import { writeConfigFile } from "./config-writer.js";
6
- import { startWorkclawGateway, stopWorkclawGateway } from "./workclaw-gateway.js";
6
+ import { stopWorkclawGateway } from "./workclaw-gateway.js";
7
7
  import { homedir } from "os";
8
8
  import { join } from "path";
9
9
  import { writeFile, copyFile, mkdir } from "fs/promises";
@@ -18,13 +18,13 @@ function fixOwner(dir) {
18
18
  }
19
19
  catch { }
20
20
  }
21
- async function createSubAgentForAccount(agentId, userId, cfg, log) {
21
+ async function createSubAgentForAccount(agentId, userId, accountData, cfg, log) {
22
22
  try {
23
23
  const channels = cfg?.channels ?? {};
24
24
  const openclawWorkclaw = channels['openclaw-workclaw'] ?? {};
25
25
  const accounts = openclawWorkclaw.accounts ?? {};
26
26
  const accountCount = Object.keys(accounts).length;
27
- const subAgentId = `openclaw-workclaw-${agentId}`;
27
+ const subAgentId = `workclaw-${agentId}`;
28
28
  const existingAgents = cfg?.agents?.list ?? [];
29
29
  const existingBindings = cfg?.bindings ?? [];
30
30
  const agentExists = existingAgents.some((a) => a.id === subAgentId);
@@ -33,9 +33,9 @@ async function createSubAgentForAccount(agentId, userId, cfg, log) {
33
33
  const subAgent = {
34
34
  id: subAgentId,
35
35
  default: false,
36
- name: `智小途 - Agent ${agentId}`,
36
+ name: accountData.nickName || `智小途 - Agent ${agentId}`,
37
37
  workspace: `~/.openclaw/workspace-workclaw/${agentId}`,
38
- agentDir: `~/.openclaw/agents/workspace-workclaw-${agentId}/agent`,
38
+ agentDir: `~/.openclaw/agents/workclaw-${agentId}/agent`,
39
39
  tools: { allow: ["*"] },
40
40
  };
41
41
  newAgentsList.push(subAgent);
@@ -89,7 +89,7 @@ async function createSubAgentForAccount(agentId, userId, cfg, log) {
89
89
  });
90
90
  log?.info?.(`SubAgent: Created binding for agentId: ${subAgentId}, accountId: ${bindingAccountId ?? "*"}`);
91
91
  }
92
- log?.info?.(`SubAgent: Created sub-agent: ${subAgentId} with workspace: openclaw-workclaw-${agentId} (account #${accountCount + 1})`);
92
+ log?.info?.(`SubAgent: Created sub-agent: ${subAgentId} with workspace: workclaw-${agentId} (account #${accountCount + 1})`);
93
93
  return {
94
94
  agents: newAgents,
95
95
  bindings: newBindings,
@@ -199,24 +199,24 @@ function buildIdentityContent(accountData) {
199
199
  const agentName = accountData.nickName || accountData.name || "智小途";
200
200
  const tip = accountData.tip || "";
201
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\`.
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
220
  ${tip ? `\n---\n${tip}` : ""}`;
221
221
  }
222
222
  /**
@@ -241,49 +241,49 @@ function buildSoulContent(accountData) {
241
241
  if (socializeFeatures)
242
242
  features.push(socializeFeatures);
243
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
-
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
287
  _This file is yours to evolve. As you learn who you are, update it._`;
288
288
  }
289
289
  export async function handleAgentCreated(accountData, cfg, log) {
@@ -311,7 +311,7 @@ export async function handleAgentCreated(accountData, cfg, log) {
311
311
  enabled: true,
312
312
  agentId: agentId,
313
313
  };
314
- const subAgentConfig = await createSubAgentForAccount(agentId, userId, cfg, log);
314
+ const subAgentConfig = await createSubAgentForAccount(agentId, userId, accountData, cfg, log);
315
315
  const newConfig = {
316
316
  ...cfg,
317
317
  channels: {
@@ -328,23 +328,6 @@ export async function handleAgentCreated(accountData, cfg, log) {
328
328
  log?.info?.(`AgentCreated: Account ${accountKey} created successfully`);
329
329
  // 初始化 workspace 目录(复制/生成 AGENTS.md、TOOLS.md、USER.md、IDENTITY.md、SOUL.md)
330
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
331
  }
349
332
  catch (err) {
350
333
  log?.error?.(`AgentCreated: Failed to create account: ${String(err)}`);
@@ -385,7 +368,24 @@ export async function handleAgentUpdated(accountData, cfg, log) {
385
368
  },
386
369
  },
387
370
  };
388
- await writeConfigFile(newConfig, cfg, log);
371
+ // 更新 agents.list 中对应 agent 的 name
372
+ const subAgentId = `workclaw-${agentId}`;
373
+ const agentsList = cfg?.agents?.list ?? [];
374
+ const agentIndex = agentsList.findIndex((a) => a.id === subAgentId);
375
+ if (agentIndex >= 0 && accountData.nickName) {
376
+ agentsList[agentIndex] = {
377
+ ...agentsList[agentIndex],
378
+ name: accountData.nickName,
379
+ };
380
+ }
381
+ const finalConfig = {
382
+ ...newConfig,
383
+ agents: {
384
+ ...cfg?.agents,
385
+ list: agentsList,
386
+ },
387
+ };
388
+ await writeConfigFile(finalConfig, cfg, log);
389
389
  refreshAccountCache(cfg);
390
390
  log?.info?.(`AgentUpdated: Account ${accountKey} updated successfully`);
391
391
  }
@@ -21,7 +21,7 @@ function fixOwner(dir) {
21
21
  }
22
22
  catch { }
23
23
  }
24
- const STATE_DIR = "workclaw-state";
24
+ const STATE_DIR = join("workclaw", "data");
25
25
  // 写入节流时间(避免频繁写入)
26
26
  const SAVE_THROTTLE_MS = 1000;
27
27
  // 每个账户的上次写入时间,key 格式: accountId:userId
@@ -43,11 +43,11 @@ function findConfigPath() {
43
43
  }
44
44
  /**
45
45
  * Get the state directory for workclaw runtime data.
46
+ * Uses ~/.openclaw as the canonical base, consistent with openclaw's recommended layout.
46
47
  */
47
48
  function findStateDir() {
48
- const configPath = findConfigPath();
49
- const openclawDir = join(configPath, "..", "..");
50
- return join(openclawDir, STATE_DIR);
49
+ const stateDir = join(homedir(), ".openclaw");
50
+ return join(stateDir, STATE_DIR);
51
51
  }
52
52
  function getStateFilePath() {
53
53
  return join(findStateDir(), "conversations.json");
@@ -196,16 +196,6 @@ export async function initWorkclawAgent(params, cfg, log) {
196
196
  log?.info?.(`initWorkclawAgent: apiKey set (plain value)`);
197
197
  }
198
198
  }
199
- if (params.agentId) {
200
- if (!cfg.models)
201
- cfg.models = {};
202
- if (!cfg.models.providers)
203
- cfg.models.providers = {};
204
- if (!cfg.models.providers["sophnet-minimax"])
205
- cfg.models.providers["sophnet-minimax"] = {};
206
- cfg.models.providers["sophnet-minimax"].apiKey = params.apiKey;
207
- log?.info?.(`initWorkclawAgent: apiKey set`);
208
- }
209
199
  if (params.agentId) {
210
200
  const accountId = params.accountId || "default";
211
201
  const accounts = workclawCfg.accounts ?? {};
@@ -0,0 +1,19 @@
1
+ export interface ParsedTaskData {
2
+ clawJobId?: string | null;
3
+ agentIds: string[];
4
+ dotype: 'add' | 'remove' | 'edit' | 'search';
5
+ name: string;
6
+ expr: string;
7
+ mainId: string;
8
+ userId: string;
9
+ }
10
+ interface Logger {
11
+ info?: (msg: string) => void;
12
+ warn?: (msg: string) => void;
13
+ error?: (msg: string) => void;
14
+ }
15
+ export declare function handleCronTaskEvent(backendEvent: {
16
+ dotype: string;
17
+ data: any;
18
+ }, token: string, baseUrl: string, appKey: string, log?: Logger): Promise<void>;
19
+ export {};
@@ -0,0 +1,188 @@
1
+ import { exec } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ const execAsync = promisify(exec);
4
+ // --- 核心公共工具函数 ---
5
+ /**
6
+ * 1. 统一命令执行器
7
+ * @param ignoreError 如果为 true,即便命令执行失败也不会抛出异常(用于 disable/enable 等非关键步骤)
8
+ */
9
+ async function runCli(command, log, ignoreError = false) {
10
+ log?.info?.(`[CLI] Executing: ${command}`);
11
+ try {
12
+ const { stdout, stderr } = await execAsync(command, {
13
+ timeout: 60000,
14
+ maxBuffer: 10 * 1024 * 1024,
15
+ });
16
+ if (stderr)
17
+ log?.info?.(`[CLI] stderr: ${stderr}`);
18
+ return { stdout: stdout.trim(), success: true };
19
+ }
20
+ catch (error) {
21
+ const errorMsg = error?.stderr || error?.message || 'unknown error';
22
+ log?.error?.(`[CLI] Command failed: ${command} -> ${errorMsg}`);
23
+ if (ignoreError)
24
+ return { stdout: errorMsg, success: false };
25
+ throw new Error(errorMsg);
26
+ }
27
+ }
28
+ /**
29
+ * 2. 统一回调发送器
30
+ * 封装了复杂的后端数据结构,减少各处的重复代码
31
+ */
32
+ async function sendTaskResult(url, token, taskData, status, log) {
33
+ const { agentIds, dotype, userId, mainId, clawJobId, name, expr } = taskData;
34
+ const payload = {
35
+ appKey: '',
36
+ userId,
37
+ agentId: mainId,
38
+ agentIds,
39
+ dotype,
40
+ doStatus: status.success,
41
+ doErrorMsg: status.success ? '' : status.message,
42
+ dataList: status.dataListOverride || [{
43
+ messageId: 0,
44
+ clawJobId,
45
+ name,
46
+ kind: '',
47
+ expr,
48
+ message: status.success ? status.message : '',
49
+ agentId: mainId,
50
+ nextRunAtMs: 0,
51
+ }],
52
+ };
53
+ try {
54
+ log?.info?.(`[Callback] Sending status to ${url}`);
55
+ const response = await fetch(url, {
56
+ method: 'POST',
57
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
58
+ body: JSON.stringify(payload),
59
+ });
60
+ if (!response.ok)
61
+ log?.error?.(`[Callback] Http error: ${response.status}`);
62
+ }
63
+ catch (err) {
64
+ log?.error?.(`[Callback] Network error: ${String(err)}`);
65
+ }
66
+ }
67
+ /**
68
+ * 3. 参数安全拼接工具
69
+ */
70
+ const buildCmd = (args) => args.map(a => (a && a.includes(' ') ? `"${a}"` : a)).join(' ');
71
+ // --- 业务处理器 ---
72
+ /**
73
+ * 创建任务
74
+ */
75
+ async function handleTaskCreate(taskData, token, url, log) {
76
+ try {
77
+ const cmd = buildCmd(['openclaw', 'cron', 'add', '--name', taskData.name, '--cron', taskData.expr, '--command', 'echo "Task executed"']);
78
+ const { stdout } = await runCli(cmd, log);
79
+ await sendTaskResult(url, token, taskData, { success: true, message: '创建成功' }, log);
80
+ }
81
+ catch (e) {
82
+ await sendTaskResult(url, token, taskData, { success: false, message: `创建失败: ${e.message}` }, log);
83
+ throw e;
84
+ }
85
+ }
86
+ /**
87
+ * 修改任务 (禁用 -> 修改 -> 启用)
88
+ */
89
+ async function handleTaskUpdate(taskData, token, url, log) {
90
+ const taskId = taskData.clawJobId;
91
+ if (!taskId)
92
+ throw new Error('clawJobId为空无法修改');
93
+ try {
94
+ // 1. 尝试禁用 (忽略失败)
95
+ await runCli(`openclaw cron disable ${taskId}`, log, true);
96
+ await new Promise(r => setTimeout(r, 500));
97
+ // 2. 执行更新 (核心逻辑)
98
+ const args = ['openclaw', 'cron', 'edit', taskId];
99
+ if (taskData.name)
100
+ args.push('--name', taskData.name);
101
+ if (taskData.expr)
102
+ args.push('--cron', taskData.expr);
103
+ await runCli(buildCmd(args), log);
104
+ // 3. 尝试启用 (忽略失败)
105
+ await runCli(`openclaw cron enable ${taskId}`, log, true);
106
+ await sendTaskResult(url, token, taskData, { success: true, message: '更新成功' }, log);
107
+ }
108
+ catch (e) {
109
+ await sendTaskResult(url, token, taskData, { success: false, message: `更新失败: ${e.message}` }, log);
110
+ throw e;
111
+ }
112
+ }
113
+ /**
114
+ * 删除任务
115
+ */
116
+ async function handleTaskDelete(taskData, token, url, log) {
117
+ const taskId = taskData.clawJobId;
118
+ if (!taskId)
119
+ throw new Error('clawJobId为空无法删除');
120
+ try {
121
+ await runCli(`openclaw cron disable ${taskId}`, log, true);
122
+ await runCli(`openclaw cron rm ${taskId}`, log);
123
+ await sendTaskResult(url, token, taskData, { success: true, message: '删除成功' }, log);
124
+ }
125
+ catch (e) {
126
+ // 特殊处理:如果本身就不存在,也视为成功
127
+ const msg = e.message;
128
+ const isAlreadyGone = msg.includes('not found') || msg.includes('gateway timeout');
129
+ await sendTaskResult(url, token, taskData, {
130
+ success: isAlreadyGone,
131
+ message: isAlreadyGone ? '任务已不存在' : `删除失败: ${msg}`,
132
+ }, log);
133
+ }
134
+ }
135
+ /**
136
+ * 查询任务 (增加了回调处理)
137
+ */
138
+ async function handleTaskSearch(taskData, token, url, log) {
139
+ try {
140
+ const { stdout } = await runCli('openclaw cron list', log);
141
+ let tasks = [];
142
+ try {
143
+ tasks = JSON.parse(stdout);
144
+ }
145
+ catch { /* 容错非JSON输出 */ }
146
+ // 处理 search 回调:将查询到的列表放入 dataList
147
+ const dataList = Array.isArray(tasks)
148
+ ? tasks.map(t => ({
149
+ clawJobId: t.id || t.clawJobId,
150
+ name: t.name,
151
+ expr: t.cron || t.expr,
152
+ agentId: t.agentId || taskData.mainId,
153
+ }))
154
+ : [];
155
+ await sendTaskResult(url, token, taskData, {
156
+ success: true,
157
+ message: '查询成功',
158
+ dataListOverride: dataList,
159
+ }, log);
160
+ return tasks;
161
+ }
162
+ catch (e) {
163
+ await sendTaskResult(url, token, taskData, { success: false, message: `查询失败: ${e.message}` }, log);
164
+ throw e;
165
+ }
166
+ }
167
+ // --- 主入口 ---
168
+ export async function handleCronTaskEvent(backendEvent, token, baseUrl, appKey, log) {
169
+ const taskData = backendEvent.data;
170
+ const callbackUrl = `${baseUrl}/open-apis/v1/claw/do/cron/result`;
171
+ log?.info?.(`[Event] Processing ${taskData.dotype}`);
172
+ switch (taskData.dotype) {
173
+ case 'add':
174
+ await handleTaskCreate(taskData, token, callbackUrl, log);
175
+ break;
176
+ case 'remove':
177
+ await handleTaskDelete(taskData, token, callbackUrl, log);
178
+ break;
179
+ case 'edit':
180
+ await handleTaskUpdate(taskData, token, callbackUrl, log);
181
+ break;
182
+ case 'search':
183
+ await handleTaskSearch(taskData, token, callbackUrl, log);
184
+ break;
185
+ default:
186
+ log?.warn?.(`Unknown dotype: ${taskData.dotype}`);
187
+ }
188
+ }
@@ -1,4 +1,4 @@
1
- import type { PluginRuntime } from "openclaw/plugin-sdk";
1
+ import type { PluginRuntime } from 'openclaw/plugin-sdk';
2
2
  export interface Attachment {
3
3
  url: string;
4
4
  fileId: string;
@@ -11,7 +11,7 @@ export interface InboundMessage {
11
11
  text: string;
12
12
  to: string;
13
13
  from: string;
14
- chatType: "dm" | "group";
14
+ chatType: 'dm' | 'group';
15
15
  messageId: string;
16
16
  rawPayload: string;
17
17
  timestamp: number;
@@ -24,7 +24,7 @@ export interface InboundContextPayload {
24
24
  RawBody: string;
25
25
  From: string;
26
26
  To: string;
27
- ChatType: "dm" | "group";
27
+ ChatType: 'dm' | 'group';
28
28
  Provider: string;
29
29
  Surface: string;
30
30
  Timestamp: number;
@@ -34,7 +34,7 @@ export interface InboundContextPayload {
34
34
  OriginatingChannel: string;
35
35
  OriginatingTo: string;
36
36
  }
37
- export type WorkClawMessageType = "agent_message" | "ping" | "disconnect" | "agent_created" | "agent_updated" | "agent_deleted" | "tools_list" | "skills_list" | "skills_event" | "init_agent" | "ignored";
37
+ export type WorkClawMessageType = 'agent_message' | 'ping' | 'disconnect' | 'agent_created' | 'agent_updated' | 'agent_deleted' | 'tools_list' | 'skills_list' | 'skills_event' | 'init_agent' | 'ignored' | 'cron_task_event';
38
38
  export interface ParseWorkClawResult {
39
39
  type: WorkClawMessageType;
40
40
  message?: {
@@ -47,6 +47,10 @@ export interface ParseWorkClawResult {
47
47
  };
48
48
  pongData?: any;
49
49
  eventData?: any;
50
+ cronTaskEvent?: {
51
+ dotype: string;
52
+ data: any;
53
+ };
50
54
  }
51
55
  export declare function parseInboundMessage(data: string, allowRawJsonPayload?: boolean): Partial<InboundMessage>;
52
56
  export declare function parseWorkClawMessage(data: string, log?: {