@dcrays/dcgchat 0.1.9 → 0.1.10

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/skill.ts +68 -29
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcrays/dcgchat",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "description": "OpenClaw channel plugin for DCG Chat (WebSocket)",
6
6
  "main": "index.ts",
package/src/skill.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  import axios from 'axios';
3
3
  /** @ts-ignore */
4
4
  import unzipper from 'unzipper';
5
+ import { pipeline } from "stream/promises";
5
6
  import fs from 'fs';
6
7
  import path from 'path';
7
8
  import { logDcgchat } from './log.js';
@@ -53,36 +54,74 @@ export async function installSkill(params: ISkillParams, msgContent: Record<stri
53
54
  fs.mkdirSync(skillDir, { recursive: true });
54
55
  // 解压文件到目标目录,跳过顶层文件夹
55
56
  await new Promise((resolve, reject) => {
56
- response.data
57
- .pipe(unzipper.Parse())
58
- .on('entry', (entry: any) => {
59
- const entryPath = entry.path;
60
- // 跳过顶层目录,只处理子文件和文件夹
61
- const pathParts = entryPath.split('/');
62
- if (pathParts.length > 1) {
63
- // 移除第一级目录
64
- const newPath = pathParts.slice(1).join('/');
65
- const targetPath = path.join(skillDir, newPath);
66
-
67
- if (entry.type === 'Directory') {
68
- fs.mkdirSync(targetPath, { recursive: true });
69
- entry.autodrain();
70
- } else {
71
- // 确保父目录存在
72
- const parentDir = path.dirname(targetPath);
73
- if (!fs.existsSync(parentDir)) {
74
- fs.mkdirSync(parentDir, { recursive: true });
75
- }
76
- entry.pipe(fs.createWriteStream(targetPath));
77
- }
78
- } else {
79
- entry.autodrain();
80
- }
81
- })
82
- .on('close', resolve)
83
- .on('error', reject);
57
+ const tasks: Promise<void>[] = [];
58
+ let rootDir: string | null = null;
59
+ let hasError = false;
60
+
61
+ response.data
62
+ .pipe(unzipper.Parse())
63
+ .on("entry", (entry: any) => {
64
+ if (hasError) {
65
+ entry.autodrain();
66
+ return;
67
+ }
68
+
69
+ try {
70
+ const entryPath = entry.path;
71
+ const pathParts = entryPath.split("/");
72
+
73
+ // 检测根目录
74
+ if (!rootDir && pathParts.length > 1) {
75
+ rootDir = pathParts[0];
76
+ }
77
+
78
+ let newPath = entryPath;
79
+
80
+ // 移除顶层文件夹
81
+ if (rootDir && entryPath.startsWith(rootDir + "/")) {
82
+ newPath = entryPath.slice(rootDir.length + 1);
83
+ }
84
+
85
+ if (!newPath) {
86
+ entry.autodrain();
87
+ return;
88
+ }
89
+
90
+ const targetPath = path.join(skillDir, newPath);
91
+
92
+ if (entry.type === "Directory") {
93
+ fs.mkdirSync(targetPath, { recursive: true });
94
+ entry.autodrain();
95
+ } else {
96
+ const parentDir = path.dirname(targetPath);
97
+ fs.mkdirSync(parentDir, { recursive: true });
98
+ const writeStream = fs.createWriteStream(targetPath);
99
+ const task = pipeline(entry, writeStream).catch((err) => {
100
+ hasError = true;
101
+ throw new Error(`解压文件失败 ${entryPath}: ${err.message}`);
102
+ });
103
+ tasks.push(task);
104
+ }
105
+ } catch (err) {
106
+ hasError = true;
107
+ entry.autodrain();
108
+ reject(new Error(`处理entry失败: ${err}`));
109
+ }
110
+ })
111
+ .on("close", async () => {
112
+ try {
113
+ await Promise.all(tasks);
114
+ resolve(null);
115
+ } catch (err) {
116
+ reject(err);
117
+ }
118
+ })
119
+ .on("error", (err) => {
120
+ hasError = true;
121
+ reject(new Error(`解压流错误: ${err.message}`));
84
122
  });
85
- sendEvent({ ...msgContent, status: 'ok' })
123
+ });
124
+ sendEvent({ ...msgContent, status: 'ok' })
86
125
  } catch (error) {
87
126
  // 如果安装失败,清理目录
88
127
  if (fs.existsSync(skillDir)) {