@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.
@@ -136,6 +136,10 @@ export function createReconnectScheduler(options) {
136
136
  // 永久失败,清除 pending promise
137
137
  pendingDoConnectPromise = null;
138
138
  }
139
+ else {
140
+ // 错误不代表永久失败,调度下一次重连
141
+ scheduleReconnect();
142
+ }
139
143
  }
140
144
  }, delayMs);
141
145
  };
@@ -3,22 +3,22 @@
3
3
  * 处理云端下发的 skills 相关 EVENT 消息
4
4
  * 通过 HTTP 回调返回结果
5
5
  */
6
- import { exec } from "node:child_process";
7
- import { promisify } from "node:util";
8
- import fs from "node:fs/promises";
9
- import path from "node:path";
10
- import os from "node:os";
11
- import https from "node:https";
12
- import http from "node:http";
6
+ import { exec } from 'node:child_process';
7
+ import fs from 'node:fs/promises';
8
+ import http from 'node:http';
9
+ import https from 'node:https';
10
+ import os from 'node:os';
11
+ import path from 'node:path';
12
+ import { promisify } from 'node:util';
13
13
  const execAsync = promisify(exec);
14
14
  // 回调 URL 映射
15
15
  const TOPIC_TO_CALLBACK_URL = {
16
- "skills/add": "/open-apis/v1/claw/do/skills/result",
17
- "skills/update": "/open-apis/v1/claw/do/skills/result",
18
- "skills/remove": "/open-apis/v1/claw/do/skills/result",
19
- "skills/list": "/open-apis/v1/claw/do/skills/result",
20
- "skills/get": "/open-apis/v1/claw/do/skills/result",
21
- "skills/invoke": "/open-apis/v1/claw/do/skills/result",
16
+ 'skills/add': '/open-apis/v1/claw/do/skills/result',
17
+ 'skills/update': '/open-apis/v1/claw/do/skills/result',
18
+ 'skills/remove': '/open-apis/v1/claw/do/skills/result',
19
+ 'skills/list': '/open-apis/v1/claw/do/skills/result',
20
+ 'skills/get': '/open-apis/v1/claw/do/skills/result',
21
+ 'skills/invoke': '/open-apis/v1/claw/do/skills/result',
22
22
  };
23
23
  /**
24
24
  * 处理 skills 事件
@@ -30,22 +30,22 @@ export async function handleSkillsEvent(eventData, token, baseUrl, appKey, log)
30
30
  let result;
31
31
  // 根据 topic 路由到不同的处理函数
32
32
  switch (topic) {
33
- case "skills/add":
33
+ case 'skills/add':
34
34
  result = await handleCreateSkill(data, log);
35
35
  break;
36
- case "skills/update":
36
+ case 'skills/update':
37
37
  result = await handleUpdateSkill(data, log);
38
38
  break;
39
- case "skills/remove":
39
+ case 'skills/remove':
40
40
  result = await handleDeleteSkill(data, log);
41
41
  break;
42
- case "skills/list":
42
+ case 'skills/list':
43
43
  result = await handleListSkills(data, log);
44
44
  break;
45
- case "skills/get":
45
+ case 'skills/get':
46
46
  result = await handleGetSkill(data, log);
47
47
  break;
48
- case "skills/invoke":
48
+ case 'skills/invoke':
49
49
  result = await handleInvokeSkill(data, log);
50
50
  break;
51
51
  default:
@@ -53,19 +53,19 @@ export async function handleSkillsEvent(eventData, token, baseUrl, appKey, log)
53
53
  }
54
54
  // 发送成功回调
55
55
  await sendCallback(baseUrl, topic, {
56
- userId: result.userId || "",
57
- agentId: "",
56
+ userId: result.userId || '',
57
+ agentId: '',
58
58
  appKey,
59
59
  agentIds: Array.isArray(result.agentIds) ? result.agentIds : (result.agentIds || 0),
60
60
  dotype: result.status || topic.split('/')[1],
61
61
  doStatus: true,
62
- doErrorMsg: "",
62
+ doErrorMsg: '',
63
63
  dataList: [
64
64
  {
65
- typeStr: "",
66
- name: result.skillName || "",
67
- description: "",
68
- }
65
+ typeStr: '',
66
+ name: result.skillName || '',
67
+ description: '',
68
+ },
69
69
  ],
70
70
  }, token, log);
71
71
  log?.info?.(`[SkillsHandler] ${topic} completed, callback sent`);
@@ -89,19 +89,19 @@ export async function handleSkillsEvent(eventData, token, baseUrl, appKey, log)
89
89
  log?.error?.(`[SkillsHandler] ${topic} failed: ${err.message}`);
90
90
  // 发送失败回调
91
91
  await sendCallback(baseUrl, topic, {
92
- userId: "",
93
- agentId: "",
92
+ userId: '',
93
+ agentId: '',
94
94
  agentIds: [],
95
95
  appKey,
96
- dotype: topic.split("/")[1],
96
+ dotype: topic.split('/')[1],
97
97
  doStatus: false,
98
98
  doErrorMsg: err.message,
99
99
  dataList: [
100
100
  {
101
- typeStr: "",
102
- name: "",
103
- description: "",
104
- }
101
+ typeStr: '',
102
+ name: '',
103
+ description: '',
104
+ },
105
105
  ],
106
106
  }, token, log);
107
107
  }
@@ -115,15 +115,15 @@ async function sendCallback(baseUrl, topic, response, authToken, log) {
115
115
  if (!callbackPath) {
116
116
  throw new Error(`No callback URL defined for topic: ${topic}`);
117
117
  }
118
- const callbackUrl = `${baseUrl.replace(/\/$/, "")}${callbackPath}`;
118
+ const callbackUrl = `${baseUrl.replace(/\/$/, '')}${callbackPath}`;
119
119
  const responseData = JSON.stringify(response);
120
120
  log?.info?.(`[SkillsHandler] Sending callback to ${callbackUrl}, data: ${responseData}`);
121
121
  // 准备请求头
122
122
  const headers = {
123
- "Content-Type": "application/json",
123
+ 'Content-Type': 'application/json',
124
124
  };
125
125
  if (authToken) {
126
- headers["Authorization"] = authToken.startsWith("Bearer ")
126
+ headers.Authorization = authToken.startsWith('Bearer ')
127
127
  ? authToken
128
128
  : `Bearer ${authToken}`;
129
129
  }
@@ -132,7 +132,7 @@ async function sendCallback(baseUrl, topic, response, authToken, log) {
132
132
  const timeoutId = setTimeout(() => controller.abort(), 30000);
133
133
  try {
134
134
  const res = await fetch(callbackUrl, {
135
- method: "POST",
135
+ method: 'POST',
136
136
  headers,
137
137
  body: responseData,
138
138
  signal: controller.signal,
@@ -146,9 +146,9 @@ async function sendCallback(baseUrl, topic, response, authToken, log) {
146
146
  }
147
147
  catch (err) {
148
148
  clearTimeout(timeoutId);
149
- if (err.name === "AbortError") {
149
+ if (err.name === 'AbortError') {
150
150
  log?.error?.(`[SkillsHandler] Callback timeout after 30s: ${callbackUrl}`);
151
- throw new Error("Callback timeout");
151
+ throw new Error('Callback timeout');
152
152
  }
153
153
  log?.error?.(`[SkillsHandler] Failed to send callback: ${err.message}`);
154
154
  throw err;
@@ -159,27 +159,27 @@ async function sendCallback(baseUrl, topic, response, authToken, log) {
159
159
  */
160
160
  async function downloadFromUrl(url, log) {
161
161
  return new Promise((resolve, reject) => {
162
- const client = url.startsWith("https:") ? https : http;
162
+ const client = url.startsWith('https:') ? https : http;
163
163
  const req = client.get(url, (res) => {
164
164
  if (res.statusCode !== 200) {
165
165
  reject(new Error(`Failed to download: HTTP ${res.statusCode}`));
166
166
  return;
167
167
  }
168
168
  const chunks = [];
169
- res.on("data", (chunk) => {
169
+ res.on('data', (chunk) => {
170
170
  chunks.push(chunk);
171
171
  });
172
- res.on("end", () => {
172
+ res.on('end', () => {
173
173
  const buffer = Buffer.concat(chunks);
174
174
  resolve(buffer);
175
175
  });
176
176
  });
177
- req.on("error", (err) => {
177
+ req.on('error', (err) => {
178
178
  reject(new Error(`Download failed: ${err.message}`));
179
179
  });
180
180
  req.setTimeout(30000, () => {
181
181
  req.destroy();
182
- reject(new Error("Download timeout"));
182
+ reject(new Error('Download timeout'));
183
183
  });
184
184
  });
185
185
  }
@@ -188,17 +188,17 @@ async function downloadFromUrl(url, log) {
188
188
  */
189
189
  function getOpenClawConfigDir() {
190
190
  const homeDir = os.homedir();
191
- return path.join(homeDir, ".openclaw");
191
+ return path.join(homeDir, '.openclaw');
192
192
  }
193
193
  /**
194
194
  * 获取 skills 目录
195
195
  */
196
196
  async function getSkillsDir(agentId, log) {
197
197
  const openclawDir = getOpenClawConfigDir();
198
- let skillsDir = path.join(openclawDir, "skills");
198
+ let skillsDir = path.join(openclawDir, 'workspace', 'skills');
199
199
  // 如果有 agentId,添加到对应目录
200
200
  if (agentId && agentId.trim()) {
201
- skillsDir = path.join(openclawDir, "agents", `openclaw-workclaw-${agentId}`, "skills");
201
+ skillsDir = path.join(openclawDir, `workspace-${agentId}`, 'skills');
202
202
  }
203
203
  try {
204
204
  await fs.mkdir(skillsDir, { recursive: true });
@@ -282,14 +282,14 @@ async function installSkillToClaw(skillName, skillContent, agentId, log) {
282
282
  if (!skillData.id || !skillData.name) {
283
283
  return {
284
284
  success: false,
285
- message: "Missing required fields in skill: id, name",
285
+ message: 'Missing required fields in skill: id, name',
286
286
  };
287
287
  }
288
288
  // 获取 skills 目录
289
289
  const skillsDir = await getSkillsDir(agentId, log);
290
290
  const skillFilePath = path.join(skillsDir, `${skillName}.json`);
291
291
  // 写入 skill 文件
292
- await fs.writeFile(skillFilePath, skillContent, "utf-8");
292
+ await fs.writeFile(skillFilePath, skillContent, 'utf-8');
293
293
  log?.info?.(`[SkillsHandler] Skill saved to: ${skillFilePath}`);
294
294
  return {
295
295
  success: true,
@@ -307,17 +307,31 @@ async function installSkillToClaw(skillName, skillContent, agentId, log) {
307
307
  /**
308
308
  * 创建 Skill
309
309
  */
310
+ async function installSkillToLocations(skillName, skillContent, locations, log) {
311
+ const installResults = [];
312
+ for (const agentId of locations) {
313
+ const installResult = await installSkillToClaw(skillName, skillContent, agentId, log);
314
+ if (!installResult.success) {
315
+ const error = new Error(`Failed to install skill to ${agentId ? `agent ${agentId}` : 'default location'}: ${installResult.message}`);
316
+ error.code = 'INSTALL_FAILED';
317
+ throw error;
318
+ }
319
+ installResults.push(installResult);
320
+ }
321
+ return installResults;
322
+ }
310
323
  async function handleCreateSkill(data, log) {
311
- const { userId, skillName, unloadUrl } = data;
324
+ const { userId, skillName, unloadUrl, mainId } = data;
312
325
  const agentIds = Array.isArray(data?.agentIds) ? data.agentIds : (data?.agentIds ? [data.agentIds] : []);
313
326
  if (!userId || !skillName) {
314
- const error = new Error("Missing required fields: userId, skillName");
315
- error.code = "MISSING_FIELDS";
327
+ const error = new Error('Missing required fields: userId, skillName');
328
+ error.code = 'MISSING_FIELDS';
316
329
  throw error;
317
330
  }
318
331
  log?.info?.(`[SkillsHandler] Creating skill: ${skillName}`);
332
+ log?.info?.(`[SkillsHandler] mainId: ${mainId}`);
319
333
  if (agentIds.length > 0) {
320
- log?.info?.(`[SkillsHandler] Target agents: ${agentIds.join(", ")}`);
334
+ log?.info?.(`[SkillsHandler] Target agents: ${agentIds.join(', ')}`);
321
335
  }
322
336
  // 如果提供了 URL,从 URL 下载 skill 定义
323
337
  if (unloadUrl) {
@@ -326,34 +340,43 @@ async function handleCreateSkill(data, log) {
326
340
  // 下载 skill 定义
327
341
  const skillContent = await downloadFromUrl(unloadUrl, log);
328
342
  log?.info?.(`[SkillsHandler] Downloaded skill content, length: ${skillContent.length} bytes`);
329
- // 安装 skill 到多个智能体
330
- const installResults = [];
331
- for (const agentId of agentIds) {
332
- const installResult = await installSkillToClaw(skillName, skillContent, agentId, log);
333
- if (!installResult.success) {
334
- const error = new Error(`Failed to install skill to agent ${agentId}: ${installResult.message}`);
335
- error.code = "INSTALL_FAILED";
336
- throw error;
337
- }
338
- installResults.push(installResult);
343
+ // 确定安装位置
344
+ const installLocations = [];
345
+ // 检查 agentIds 是否只包含 mainId
346
+ const hasOnlyMainId = agentIds.length === 1 && agentIds[0] === mainId;
347
+ // 检查 agentIds 是否包含 mainId 和其他 id
348
+ const hasMainIdAndOthers = agentIds.includes(mainId) && agentIds.length > 1;
349
+ if (hasOnlyMainId) {
350
+ // 如果只有 mainId,安装到默认位置
351
+ log?.info?.(`[SkillsHandler] Only mainId found, installing to default location`);
352
+ installLocations.push(undefined);
339
353
  }
340
- // 如果没有指定智能体,安装到默认位置
341
- if (agentIds.length === 0) {
342
- const installResult = await installSkillToClaw(skillName, skillContent, undefined, log);
343
- if (!installResult.success) {
344
- const error = new Error(installResult.message);
345
- error.code = "INSTALL_FAILED";
346
- throw error;
347
- }
348
- installResults.push(installResult);
354
+ else if (hasMainIdAndOthers) {
355
+ // 如果有 mainId 和其他 id,安装到默认位置和其他 id 位置
356
+ log?.info?.(`[SkillsHandler] mainId and other agents found, installing to default location and other agents`);
357
+ installLocations.push(undefined);
358
+ // 添加其他 id 位置(排除 mainId)
359
+ installLocations.push(...agentIds.filter(id => id !== mainId));
360
+ }
361
+ else if (agentIds.length > 0) {
362
+ // 如果没有 mainId,安装到所有指定的 agentId 位置
363
+ log?.info?.(`[SkillsHandler] No mainId found, installing to all specified agents`);
364
+ installLocations.push(...agentIds);
349
365
  }
366
+ else {
367
+ // 如果没有指定智能体,安装到默认位置
368
+ log?.info?.(`[SkillsHandler] No agents specified, installing to default location`);
369
+ installLocations.push(undefined);
370
+ }
371
+ // 安装技能到所有位置
372
+ const installResults = await installSkillToLocations(skillName, skillContent, installLocations, log);
350
373
  log?.info?.(`[SkillsHandler] Skill installed to ${installResults.length} locations`);
351
374
  return {
352
375
  userId,
353
376
  skillName,
354
- agentId: "",
377
+ agentId: '',
355
378
  agentIds: agentIds.length > 0 ? agentIds : undefined,
356
- status: "add",
379
+ status: 'add',
357
380
  installedAt: new Date().toISOString(),
358
381
  };
359
382
  }
@@ -365,9 +388,9 @@ async function handleCreateSkill(data, log) {
365
388
  return {
366
389
  userId,
367
390
  skillName,
368
- agentId: "",
391
+ agentId: '',
369
392
  agentIds: agentIds.length > 0 ? agentIds : undefined,
370
- status: "add",
393
+ status: 'add',
371
394
  createdAt: new Date().toISOString(),
372
395
  };
373
396
  }
@@ -377,98 +400,114 @@ async function handleCreateSkill(data, log) {
377
400
  async function handleUpdateSkill(data, log) {
378
401
  const { id, ...updates } = data;
379
402
  if (!id) {
380
- const error = new Error("Missing required field: id");
381
- error.code = "MISSING_FIELDS";
403
+ const error = new Error('Missing required field: id');
404
+ error.code = 'MISSING_FIELDS';
382
405
  throw error;
383
406
  }
384
407
  log?.info?.(`[SkillsHandler] Updating skill: ${id}`);
385
408
  return {
386
409
  id,
387
410
  ...updates,
388
- status: "updated",
411
+ status: 'updated',
389
412
  updatedAt: new Date().toISOString(),
390
413
  };
391
414
  }
392
415
  /**
393
416
  * 删除 Skill
394
417
  */
418
+ async function deleteSkillAtLocation(skillName, agentId, log) {
419
+ try {
420
+ const skillsDir = await getSkillsDir(agentId, log);
421
+ const skillFilePath = path.join(skillsDir, `${skillName}.json`);
422
+ const skillDirPath = path.join(skillsDir, skillName);
423
+ let deleted = false;
424
+ // 尝试删除技能文件
425
+ try {
426
+ await fs.unlink(skillFilePath);
427
+ log?.info?.(`[SkillsHandler] Skill file deleted: ${skillFilePath}`);
428
+ deleted = true;
429
+ }
430
+ catch (err) {
431
+ // 文件不存在,继续尝试删除目录
432
+ }
433
+ // 尝试删除技能目录(ZIP 安装的技能)
434
+ try {
435
+ await fs.rm(skillDirPath, { recursive: true, force: true });
436
+ log?.info?.(`[SkillsHandler] Skill directory deleted: ${skillDirPath}`);
437
+ deleted = true;
438
+ }
439
+ catch (err) {
440
+ // 目录不存在,继续
441
+ }
442
+ return deleted;
443
+ }
444
+ catch (err) {
445
+ log?.error?.(`[SkillsHandler] Failed to delete skill at location: ${err.message}`);
446
+ return false;
447
+ }
448
+ }
395
449
  async function handleDeleteSkill(data, log) {
396
- const { userId, skillName, agentIds } = data;
450
+ const { userId, skillName, agentIds, mainId } = data;
397
451
  const agentIdsList = Array.isArray(agentIds) ? agentIds : (agentIds ? [agentIds] : []);
398
452
  if (!skillName) {
399
- const error = new Error("Missing required field: skillName");
400
- error.code = "MISSING_FIELDS";
453
+ const error = new Error('Missing required field: skillName');
454
+ error.code = 'MISSING_FIELDS';
401
455
  throw error;
402
456
  }
403
457
  log?.info?.(`[SkillsHandler] Deleting skill: ${skillName}`);
458
+ log?.info?.(`[SkillsHandler] mainId: ${mainId}`);
404
459
  if (agentIdsList.length > 0) {
405
- log?.info?.(`[SkillsHandler] Target agents: ${agentIdsList.join(", ")}`);
460
+ log?.info?.(`[SkillsHandler] Target agents: ${agentIdsList.join(', ')}`);
406
461
  }
407
462
  try {
408
463
  let deleted = false;
409
- // 从多个智能体中删除技能
410
- for (const agentId of agentIdsList) {
411
- // 获取技能目录
412
- const skillsDir = await getSkillsDir(agentId, log);
413
- // 检查技能文件和目录
414
- const skillFilePath = path.join(skillsDir, `${skillName}.json`);
415
- const skillDirPath = path.join(skillsDir, skillName);
416
- // 尝试删除技能文件
417
- try {
418
- await fs.unlink(skillFilePath);
419
- log?.info?.(`[SkillsHandler] Skill file deleted: ${skillFilePath}`);
420
- deleted = true;
421
- }
422
- catch (err) {
423
- // 文件不存在,继续尝试删除目录
424
- }
425
- // 尝试删除技能目录(ZIP 安装的技能)
426
- try {
427
- await fs.rm(skillDirPath, { recursive: true, force: true });
428
- log?.info?.(`[SkillsHandler] Skill directory deleted: ${skillDirPath}`);
429
- deleted = true;
430
- }
431
- catch (err) {
432
- // 目录不存在,继续
433
- }
464
+ // 检查 agentIds 是否只包含 mainId
465
+ const hasOnlyMainId = agentIdsList.length === 1 && agentIdsList[0] === mainId;
466
+ // 检查 agentIds 是否包含 mainId 和其他 id
467
+ const hasMainIdAndOthers = agentIdsList.includes(mainId) && agentIdsList.length > 1;
468
+ if (hasOnlyMainId) {
469
+ // 如果只有 mainId,从默认位置删除
470
+ log?.info?.(`[SkillsHandler] Only mainId found, deleting from default location`);
471
+ deleted = await deleteSkillAtLocation(skillName, undefined, log);
434
472
  }
435
- // 如果没有指定智能体,从默认位置删除
436
- if (agentIdsList.length === 0) {
437
- // 获取默认技能目录
438
- const skillsDir = await getSkillsDir(undefined, log);
439
- // 检查技能文件和目录
440
- const skillFilePath = path.join(skillsDir, `${skillName}.json`);
441
- const skillDirPath = path.join(skillsDir, skillName);
442
- // 尝试删除技能文件
443
- try {
444
- await fs.unlink(skillFilePath);
445
- log?.info?.(`[SkillsHandler] Skill file deleted: ${skillFilePath}`);
446
- deleted = true;
447
- }
448
- catch (err) {
449
- // 文件不存在,继续尝试删除目录
450
- }
451
- // 尝试删除技能目录(ZIP 安装的技能)
452
- try {
453
- await fs.rm(skillDirPath, { recursive: true, force: true });
454
- log?.info?.(`[SkillsHandler] Skill directory deleted: ${skillDirPath}`);
455
- deleted = true;
473
+ else if (hasMainIdAndOthers) {
474
+ // 如果有 mainId 和其他 id,从默认位置和其他 id 位置删除
475
+ log?.info?.(`[SkillsHandler] mainId and other agents found, deleting from default location and other agents`);
476
+ // 从默认位置删除
477
+ const defaultDeleted = await deleteSkillAtLocation(skillName, undefined, log);
478
+ deleted = deleted || defaultDeleted;
479
+ // 从其他 id 位置删除(排除 mainId)
480
+ for (const agentId of agentIdsList) {
481
+ if (agentId !== mainId) {
482
+ const agentDeleted = await deleteSkillAtLocation(skillName, agentId, log);
483
+ deleted = deleted || agentDeleted;
484
+ }
456
485
  }
457
- catch (err) {
458
- // 目录不存在,继续
486
+ }
487
+ else if (agentIdsList.length > 0) {
488
+ // 如果没有 mainId,从所有指定的 agentId 位置删除
489
+ log?.info?.(`[SkillsHandler] No mainId found, deleting from all specified agents`);
490
+ for (const agentId of agentIdsList) {
491
+ const agentDeleted = await deleteSkillAtLocation(skillName, agentId, log);
492
+ deleted = deleted || agentDeleted;
459
493
  }
460
494
  }
495
+ else {
496
+ // 如果没有指定智能体,从默认位置删除
497
+ log?.info?.(`[SkillsHandler] No agents specified, deleting from default location`);
498
+ deleted = await deleteSkillAtLocation(skillName, undefined, log);
499
+ }
461
500
  if (!deleted) {
462
501
  const error = new Error(`Skill not found: ${skillName}`);
463
- error.code = "SKILL_NOT_FOUND";
502
+ error.code = 'SKILL_NOT_FOUND';
464
503
  throw error;
465
504
  }
466
505
  return {
467
506
  userId,
468
507
  skillName,
469
- agentId: "",
508
+ agentId: '',
470
509
  agentIds: agentIdsList.length > 0 ? agentIdsList : undefined,
471
- status: "remove",
510
+ status: 'remove',
472
511
  deletedAt: new Date().toISOString(),
473
512
  };
474
513
  }
@@ -485,7 +524,7 @@ async function handleListSkills(data, log) {
485
524
  log?.info?.(`[SkillsHandler] Listing skills, filter: ${filter}, limit: ${limit}`);
486
525
  try {
487
526
  // 调用 openclaw skills list 命令
488
- const { stdout } = await execAsync("openclaw skills list --json", {
527
+ const { stdout } = await execAsync('openclaw skills list --json', {
489
528
  timeout: 10000,
490
529
  });
491
530
  let skills = [];
@@ -495,17 +534,17 @@ async function handleListSkills(data, log) {
495
534
  }
496
535
  catch {
497
536
  // 解析文本输出
498
- const lines = stdout.split("\n");
537
+ const lines = stdout.split('\n');
499
538
  skills = lines
500
- .filter((line) => line.trim() && !line.includes("Skills"))
539
+ .filter(line => line.trim() && !line.includes('Skills'))
501
540
  .map((line) => {
502
- const parts = line.split(/\s{2,}/).map((p) => p.trim());
541
+ const parts = line.split(/\s{2,}/).map(p => p.trim());
503
542
  if (parts.length >= 3) {
504
543
  return {
505
- status: parts[0]?.includes("") ? "ready" : "missing",
544
+ status: parts[0]?.includes('') ? 'ready' : 'missing',
506
545
  name: parts[1],
507
546
  description: parts[2],
508
- source: parts[3] || "openclaw-bundled",
547
+ source: parts[3] || 'openclaw-bundled',
509
548
  };
510
549
  }
511
550
  return null;
@@ -514,7 +553,7 @@ async function handleListSkills(data, log) {
514
553
  }
515
554
  // 应用过滤
516
555
  if (filter) {
517
- skills = skills.filter((s) => s.status === filter);
556
+ skills = skills.filter(s => s.status === filter);
518
557
  }
519
558
  // 应用 limit
520
559
  skills = skills.slice(0, limit);
@@ -538,8 +577,8 @@ async function handleListSkills(data, log) {
538
577
  async function handleGetSkill(data, log) {
539
578
  const { id } = data;
540
579
  if (!id) {
541
- const error = new Error("Missing required field: id");
542
- error.code = "MISSING_FIELDS";
580
+ const error = new Error('Missing required field: id');
581
+ error.code = 'MISSING_FIELDS';
543
582
  throw error;
544
583
  }
545
584
  log?.info?.(`[SkillsHandler] Getting skill: ${id}`);
@@ -547,8 +586,8 @@ async function handleGetSkill(data, log) {
547
586
  return {
548
587
  id,
549
588
  name: `Skill ${id}`,
550
- description: "Skill description",
551
- status: "ready",
589
+ description: 'Skill description',
590
+ status: 'ready',
552
591
  };
553
592
  }
554
593
  /**
@@ -557,15 +596,15 @@ async function handleGetSkill(data, log) {
557
596
  async function handleInvokeSkill(data, log) {
558
597
  const { id, input, context } = data;
559
598
  if (!id) {
560
- const error = new Error("Missing required field: id");
561
- error.code = "MISSING_FIELDS";
599
+ const error = new Error('Missing required field: id');
600
+ error.code = 'MISSING_FIELDS';
562
601
  throw error;
563
602
  }
564
603
  log?.info?.(`[SkillsHandler] Invoking skill: ${id}`);
565
604
  // TODO: 实现实际的 skill 执行逻辑
566
605
  return {
567
606
  id,
568
- status: "completed",
607
+ status: 'completed',
569
608
  output: {
570
609
  result: `Skill ${id} executed successfully`,
571
610
  input,
@@ -1,4 +1,4 @@
1
- import type { ResolvedWorkclawAccount } from "../types.js";
1
+ import type { ResolvedWorkclawAccount } from '../types.js';
2
2
  export interface WorkclawGatewayOptions {
3
3
  accountId: string;
4
4
  account: ResolvedWorkclawAccount;