@spzhongwin/skill-logger-plugin 1.0.8 → 1.0.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spzhongwin/skill-logger-plugin",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "type": "module",
5
5
  "exports": "./dist/index.js",
6
6
  "scripts": {
package/src/updater.ts CHANGED
@@ -323,6 +323,33 @@ export class SkillUpdater {
323
323
  /**
324
324
  * 手动下发安装/更新指令 (供沙盒 HTTP Proxy 与星型架构中控使用)
325
325
  */
326
+ /**
327
+ * 从 URL 下载 ZIP 并直接解压替换到目标目录(不检查 SKILL.md)
328
+ */
329
+ async installZipFromUrl(url: string, targetDir: string): Promise<{success: boolean; message: string}> {
330
+ const work = path.join(this.tmpDir, `slp-zip-${randomUUID()}`);
331
+ await fs.mkdir(work, { recursive: true });
332
+ try {
333
+ const zipPath = path.join(work, "pkg.zip");
334
+ const res = await this.fetchImpl(url);
335
+ if (!res.ok) {
336
+ return { success: false, message: `下载失败: HTTP ${res.status}` };
337
+ }
338
+ const buf = Buffer.from(await res.arrayBuffer());
339
+ await fs.writeFile(zipPath, buf);
340
+
341
+ const staging = path.join(work, "staging");
342
+ await this.unzip(zipPath, staging);
343
+
344
+ await this.replaceDir(staging, targetDir);
345
+ return { success: true, message: `安装成功` };
346
+ } catch (err: any) {
347
+ return { success: false, message: `执行出错: ${err.message}` };
348
+ } finally {
349
+ await fs.rm(work, { recursive: true, force: true }).catch(() => {});
350
+ }
351
+ }
352
+
326
353
  async manualInstall(options: {
327
354
  code: string;
328
355
  url?: string;
package/src/ws-client.ts CHANGED
@@ -514,11 +514,54 @@ export class GatewayWsClient {
514
514
  }
515
515
  }, delayMs);
516
516
 
517
+ } else if (action === "INSTALL_EXPERT") {
518
+ if (!safeCode) throw new Error("Missing code parameter");
519
+ const { name, version, downloadUrl, skills } = msg;
520
+ console.log(`[skill-logger-plugin][WS] Executing INSTALL_EXPERT for user ${userId}, code: ${safeCode}`);
521
+ this.appendLogToFile("INFO", "Command", `INSTALL_EXPERT received`, { userId, code: safeCode, version, downloadUrl });
522
+
523
+ const userSkillRoot = path.join(openclawHome(), `workspace-assistant-${pureId}`, ".user");
524
+
525
+ // 1. 安装专家本身
526
+ const expertTarget = path.join(userSkillRoot, "experts", safeCode);
527
+ await fs.mkdir(path.dirname(expertTarget), { recursive: true });
528
+ const expertResult = await this.options.updater.installZipFromUrl(downloadUrl, expertTarget);
529
+ if (!expertResult.success) {
530
+ throw new Error(`专家安装失败: ${expertResult.message}`);
531
+ }
532
+
533
+ // 2. 安装依赖的 skills
534
+ const skillTargetRoot = path.join(userSkillRoot, "skills");
535
+ await fs.mkdir(skillTargetRoot, { recursive: true });
536
+ const skillResults: string[] = [];
537
+ if (Array.isArray(skills)) {
538
+ for (const sk of skills) {
539
+ if (!sk.code || !sk.downloadUrl) {
540
+ skillResults.push(`${sk.code || 'unknown'}: 缺少下载地址`);
541
+ continue;
542
+ }
543
+ try {
544
+ const skTarget = path.join(skillTargetRoot, sk.code);
545
+ const result = await this.options.updater.installZipFromUrl(sk.downloadUrl, skTarget);
546
+ skillResults.push(`${sk.code}: ${result.success ? '成功' : '失败 - ' + result.message}`);
547
+ } catch (e: any) {
548
+ skillResults.push(`${sk.code}: 失败 - ${e.message}`);
549
+ }
550
+ }
551
+ }
552
+
553
+ this.reply(replyId, {
554
+ success: true,
555
+ message: `专家 ${safeCode} 安装完成`,
556
+ action,
557
+ data: { expertCode: safeCode, skills: skillResults },
558
+ });
559
+
517
560
  } else if (action === "GET_EXPERT_REGISTRY") {
518
561
  console.log(`[skill-logger-plugin][WS] Executing GET_EXPERT_REGISTRY for user ${userId}`);
519
562
  this.appendLogToFile("INFO", "Command", `GET_EXPERT_REGISTRY received`, { userId });
520
563
 
521
- const registryFilePath = path.join(openclawHome(), `workspace-assistant-${pureId}`, ".openclaw", "userskill", "expert-registry.yaml");
564
+ const registryFilePath = path.join(openclawHome(), `workspace-assistant-${pureId}`, ".user", "expert-registry.yaml");
522
565
  let content = "";
523
566
  let fileExists = false;
524
567
  try {