@manturhub/cli 0.9.2 → 0.9.4

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/lib/archive.js +52 -28
  2. package/package.json +1 -1
package/lib/archive.js CHANGED
@@ -10,8 +10,9 @@ import {
10
10
  import { basename, join, posix } from "node:path";
11
11
  import { tmpdir } from "node:os";
12
12
 
13
- const MAX_FILES = 2000;
13
+ const MAX_FILES = 20000;
14
14
  const MAX_UNPACKED_BYTES = 500 * 1024 * 1024;
15
+ const ARCHIVE_LIST_MAX_BUFFER = 32 * 1024 * 1024;
15
16
 
16
17
  export function validateSlug(slug, label = "ID") {
17
18
  if (!slug || !/^[a-z0-9][a-z0-9._-]*$/i.test(slug)) {
@@ -20,8 +21,8 @@ export function validateSlug(slug, label = "ID") {
20
21
  return slug;
21
22
  }
22
23
 
23
- function listArchive(zipPath) {
24
- const commands = [
24
+ function archiveTools(zipPath) {
25
+ return [
25
26
  {
26
27
  command: "unzip",
27
28
  list: ["-Z1", zipPath],
@@ -36,19 +37,16 @@ function listArchive(zipPath) {
36
37
  extract: (dest) => ["-xf", zipPath, "-C", dest],
37
38
  },
38
39
  ];
39
- for (const tool of commands) {
40
- try {
41
- const output = execFileSync(tool.command, tool.list, { encoding: "utf8" });
42
- const verbose = execFileSync(tool.command, tool.verbose, { encoding: "utf8" });
43
- const summary = tool.summary
44
- ? execFileSync(tool.command, tool.summary, { encoding: "utf8" })
45
- : "";
46
- return { tool, entries: output.split(/\r?\n/).filter(Boolean), verbose, summary };
47
- } catch {
48
- // Try the next installed extractor.
49
- }
50
- }
51
- throw new Error("解压失败(需系统 tar 或 unzip 命令)");
40
+ }
41
+
42
+ function inspectArchive(tool) {
43
+ const execOptions = { encoding: "utf8", maxBuffer: ARCHIVE_LIST_MAX_BUFFER };
44
+ const output = execFileSync(tool.command, tool.list, execOptions);
45
+ const verbose = execFileSync(tool.command, tool.verbose, execOptions);
46
+ const summary = tool.summary
47
+ ? execFileSync(tool.command, tool.summary, execOptions)
48
+ : "";
49
+ return { entries: output.split(/\r?\n/).filter(Boolean), verbose, summary };
52
50
  }
53
51
 
54
52
  function declaredUnpackedBytes(tool, verbose, summary) {
@@ -129,18 +127,44 @@ function rejectDestinationLinks(dir) {
129
127
  }
130
128
 
131
129
  export function extractZipSafely(zipPath, dest) {
132
- const { tool, entries, verbose, summary } = listArchive(zipPath);
133
- validateArchiveEntries(entries, verbose, declaredUnpackedBytes(tool, verbose, summary));
134
- const staging = mkdtempSync(join(tmpdir(), "manturhub-extract-"));
135
- try {
136
- execFileSync(tool.command, tool.extract(staging), { stdio: ["ignore", "ignore", "ignore"] });
137
- inspectExtracted(staging);
138
- mkdirSync(dest, { recursive: true });
139
- rejectDestinationLinks(dest);
140
- for (const entry of readdirSync(staging)) {
141
- cpSync(join(staging, entry), join(dest, basename(entry)), { recursive: true, force: true });
130
+ let foundExtractor = false;
131
+ for (const tool of archiveTools(zipPath)) {
132
+ let archive;
133
+ try {
134
+ archive = inspectArchive(tool);
135
+ foundExtractor = true;
136
+ } catch (error) {
137
+ if (error?.code !== "ENOENT") foundExtractor = true;
138
+ continue;
142
139
  }
143
- } finally {
144
- rmSync(staging, { recursive: true, force: true });
140
+ validateArchiveEntries(
141
+ archive.entries,
142
+ archive.verbose,
143
+ declaredUnpackedBytes(tool, archive.verbose, archive.summary)
144
+ );
145
+
146
+ const staging = mkdtempSync(join(tmpdir(), "manturhub-extract-"));
147
+ try {
148
+ try {
149
+ execFileSync(tool.command, tool.extract(staging), { stdio: ["ignore", "ignore", "ignore"] });
150
+ } catch {
151
+ // Some macOS unzip builds can list legacy-encoded Chinese filenames but fail
152
+ // while creating them. Let the next validated extractor (bsdtar via `tar`) try.
153
+ continue;
154
+ }
155
+ inspectExtracted(staging);
156
+ mkdirSync(dest, { recursive: true });
157
+ rejectDestinationLinks(dest);
158
+ for (const entry of readdirSync(staging)) {
159
+ cpSync(join(staging, entry), join(dest, basename(entry)), { recursive: true, force: true });
160
+ }
161
+ return;
162
+ } finally {
163
+ rmSync(staging, { recursive: true, force: true });
164
+ }
165
+ }
166
+ if (foundExtractor) {
167
+ throw new Error("解压失败:安装包格式无效、已损坏或文件清单过大");
145
168
  }
169
+ throw new Error("解压失败(需系统 tar 或 unzip 命令)");
146
170
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manturhub/cli",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "ManturHub 算子广场 CLI:通过 REST 发现和调用 AI 算子、浏览配方,并安装 Skill 与 Agent 套件",
5
5
  "type": "module",
6
6
  "bin": {