@manturhub/cli 0.9.3 → 0.9.5

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/lib/archive.js CHANGED
@@ -21,8 +21,8 @@ export function validateSlug(slug, label = "ID") {
21
21
  return slug;
22
22
  }
23
23
 
24
- function listArchive(zipPath) {
25
- const commands = [
24
+ function archiveTools(zipPath) {
25
+ return [
26
26
  {
27
27
  command: "unzip",
28
28
  list: ["-Z1", zipPath],
@@ -37,25 +37,16 @@ function listArchive(zipPath) {
37
37
  extract: (dest) => ["-xf", zipPath, "-C", dest],
38
38
  },
39
39
  ];
40
- let foundExtractor = false;
41
- for (const tool of commands) {
42
- try {
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 { tool, entries: output.split(/\r?\n/).filter(Boolean), verbose, summary };
50
- } catch (error) {
51
- if (error?.code !== "ENOENT") foundExtractor = true;
52
- // Missing tool or an extractor-specific parse failure: try the next one.
53
- }
54
- }
55
- if (foundExtractor) {
56
- throw new Error("解压失败:安装包格式无效、已损坏或文件清单过大");
57
- }
58
- 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 };
59
50
  }
60
51
 
61
52
  function declaredUnpackedBytes(tool, verbose, summary) {
@@ -136,18 +127,44 @@ function rejectDestinationLinks(dir) {
136
127
  }
137
128
 
138
129
  export function extractZipSafely(zipPath, dest) {
139
- const { tool, entries, verbose, summary } = listArchive(zipPath);
140
- validateArchiveEntries(entries, verbose, declaredUnpackedBytes(tool, verbose, summary));
141
- const staging = mkdtempSync(join(tmpdir(), "manturhub-extract-"));
142
- try {
143
- execFileSync(tool.command, tool.extract(staging), { stdio: ["ignore", "ignore", "ignore"] });
144
- inspectExtracted(staging);
145
- mkdirSync(dest, { recursive: true });
146
- rejectDestinationLinks(dest);
147
- for (const entry of readdirSync(staging)) {
148
- 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;
139
+ }
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 });
149
164
  }
150
- } finally {
151
- rmSync(staging, { recursive: true, force: true });
152
165
  }
166
+ if (foundExtractor) {
167
+ throw new Error("解压失败:安装包格式无效、已损坏或文件清单过大");
168
+ }
169
+ throw new Error("解压失败(需系统 tar 或 unzip 命令)");
153
170
  }
package/lib/params.js CHANGED
@@ -4,7 +4,7 @@ function typeName(value) {
4
4
  return typeof value;
5
5
  }
6
6
 
7
- function normalizeType(type) {
7
+ function normalizeTypeToken(type) {
8
8
  const t = String(type || "").toLowerCase();
9
9
  if (t.endsWith("[]") || t.startsWith("array")) return "array";
10
10
  if (t.includes("integer") || t === "int") return "integer";
@@ -15,8 +15,17 @@ function normalizeType(type) {
15
15
  return null;
16
16
  }
17
17
 
18
- function coerceValue(value, expected, name) {
19
- if (typeof value !== "string" || !expected || expected === "string") return value;
18
+ function normalizeTypes(type) {
19
+ const declared = Array.isArray(type)
20
+ ? type
21
+ : String(type || "").split(/\s*\|\s*/);
22
+ return [...new Set(declared.map(normalizeTypeToken).filter(Boolean))];
23
+ }
24
+
25
+ function coerceValue(value, expectedTypes, name) {
26
+ if (typeof value !== "string" || !expectedTypes.length) return value;
27
+ if (expectedTypes.some((expected) => matchesType(value, expected))) return value;
28
+ const expected = expectedTypes[0];
20
29
  if (expected === "boolean") {
21
30
  if (value === "true") return true;
22
31
  if (value === "false") return false;
@@ -67,10 +76,12 @@ export function validateParams(body, schema, { coerceStrings = false } = {}) {
67
76
  const result = { ...body };
68
77
  for (const [name, value] of Object.entries(result)) {
69
78
  const field = byName.get(name);
70
- const expected = normalizeType(field.type);
71
- const checked = coerceStrings ? coerceValue(value, expected, name) : value;
72
- if (!matchesType(checked, expected)) {
73
- throw new Error(`参数 ${name} 类型错误:需要 ${expected},收到 ${typeName(checked)}`);
79
+ const expectedTypes = normalizeTypes(field.type);
80
+ const checked = coerceStrings ? coerceValue(value, expectedTypes, name) : value;
81
+ if (expectedTypes.length && !expectedTypes.some((expected) => matchesType(checked, expected))) {
82
+ throw new Error(
83
+ `参数 ${name} 类型错误:需要 ${expectedTypes.join(" 或 ")},收到 ${typeName(checked)}`
84
+ );
74
85
  }
75
86
  if (Array.isArray(field.enum) && !field.enum.includes(checked)) {
76
87
  throw new Error(`参数 ${name} 只能是: ${field.enum.join(" | ")}`);
package/lib/skill.js CHANGED
@@ -67,7 +67,7 @@ description: 调用 ManturHub 算子广场的 AI 能力与行业数据(文生图
67
67
  - \`manturhub describe <算子ID>\` → 某算子的精确入参字段
68
68
 
69
69
  > 能力大类参考(具体算子 ID 以 \`ls\` 实时为准):图像生成、花字渲染、视频生成 / 合成 / 理解 / 超分 / 擦字幕 / 配字幕、**剧本/分镜/参考图 → 动态提示词 → 无字黑白线稿 → 视频成片**、语音合成 / 克隆 / 音色设计 / 语音转写、电商文案、**Seedance2.0提示词润色(把需求或粗糙提示词+参考图润色成专业视频生成提示词,生成前先用它省重roll的钱)**、短剧拉片 / 改编 / 剧本评估、漫剧·短剧市场洞察(热榜 / 爆量 / 出海投放数据,\`ls --cat data\` 可见)。
70
- > 例:\`manturhub run image2 --json '{"prompt":"a red fox in snow","n":1}'\`(异步,run 自动等到出图)。字段拼不准就 \`manturhub describe <算子ID>\`,别凭记忆猜。
70
+ > 例:\`manturhub run op.image.generate --json '{"prompt":"a red fox in snow","n":1}'\`(异步,run 自动等到出图)。字段拼不准就 \`manturhub describe <算子ID>\`,别凭记忆猜。
71
71
  `;
72
72
 
73
73
  // 默认同时安装 Claude Code 和 Codex 用户级 Skill。
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@manturhub/cli",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "ManturHub 算子广场 CLI:通过 REST 发现和调用 AI 算子、浏览配方,并安装 Skill 与 Agent 套件",
5
5
  "type": "module",
6
6
  "bin": {
7
- "manturhub": "./bin/cli.js"
7
+ "manturhub": "bin/cli.js"
8
8
  },
9
9
  "engines": {
10
10
  "node": ">=18"