@manturhub/cli 0.9.0 → 0.9.1
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/README.md +9 -7
- package/bin/cli.js +66 -24
- package/lib/billing-confirm.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,15 +24,17 @@ CLI 默认连接生产站 `https://hub.mantur.ai`,Key 也必须在该生产站
|
|
|
24
24
|
```bash
|
|
25
25
|
# 发现与查看算子无需登录
|
|
26
26
|
manturhub ls --cat text
|
|
27
|
-
manturhub describe
|
|
27
|
+
manturhub describe 电商文案生成
|
|
28
28
|
|
|
29
29
|
# 先查实时价格,再调用
|
|
30
|
-
manturhub quote
|
|
31
|
-
manturhub run
|
|
30
|
+
manturhub quote 电商文案生成
|
|
31
|
+
manturhub run 电商文案生成 --json '{"product_info":"便携榨汁杯,USB 充电,300ml","scene":"product_title","tone":"lively"}'
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
CLI 会在付费调用前按算子实时 schema 校验未知字段、必填项、类型和枚举,校验失败不会发起 invoke。校验通过后会显示本次预计消耗和计费依据,获得确认才调用;完成后显示实际消耗和退款。批量参数也按整批请求试算。
|
|
35
35
|
|
|
36
|
+
面向用户的列表、详情、报价、确认和结算统一显示中文算子名,也可直接用中文名调用。英文算子 ID 仅保留在 `--json` 机器输出和内部 API 请求中,供 Agent 与脚本稳定使用。
|
|
37
|
+
|
|
36
38
|
在 Agent 或脚本等非交互环境中,第一次运行只返回报价和 `quote_id`,不会调用或扣费;Agent 向用户确认后,使用提示中的 `--confirm <quote_id>` 执行。报价 5 分钟内有效且只能使用一次。
|
|
37
39
|
|
|
38
40
|
## 主要命令
|
|
@@ -40,10 +42,10 @@ CLI 会在付费调用前按算子实时 schema 校验未知字段、必填项
|
|
|
40
42
|
| 命令 | 说明 |
|
|
41
43
|
|---|---|
|
|
42
44
|
| `manturhub ls [--cat image\|video\|audio\|text\|data] [--json]` | 实时列出上线算子 |
|
|
43
|
-
| `manturhub describe
|
|
44
|
-
| `manturhub quote
|
|
45
|
-
| `manturhub run
|
|
46
|
-
| `manturhub run
|
|
45
|
+
| `manturhub describe <中文算子名> [--json]` | 查看精确入参与异步属性 |
|
|
46
|
+
| `manturhub quote <中文算子名> [--json]` | 查询 Java API 返回的实时计费公式 |
|
|
47
|
+
| `manturhub run <中文算子名> --json '{}'` | 调用算子;异步任务默认轮询到终态 |
|
|
48
|
+
| `manturhub run <中文算子名> --json-file params.json` | 从文件读取参数,适合长提示词和自动化 |
|
|
47
49
|
| `manturhub upload <本地文件>` | 流式上传图片、音频或视频并输出公网 URL |
|
|
48
50
|
| `manturhub status <poll_url>` | 查询 `run --no-wait` 返回的异步任务 |
|
|
49
51
|
| `manturhub balance [--json]` | 查询余额及美元等值(1 馒头 = $0.01 USD) |
|
package/bin/cli.js
CHANGED
|
@@ -84,6 +84,37 @@ function usdFor(dumplings) {
|
|
|
84
84
|
return Number.isFinite(value) ? `$${(value * 0.01).toFixed(2)} USD` : "-";
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
const CAT_LABELS = {
|
|
88
|
+
text: "文本",
|
|
89
|
+
image: "图片",
|
|
90
|
+
video: "视频",
|
|
91
|
+
audio: "音频",
|
|
92
|
+
data: "数据",
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function catLabel(cat) {
|
|
96
|
+
return CAT_LABELS[cat] || cat || "其他";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Human commands accept either the stable operator ID or its Chinese display name.
|
|
100
|
+
// Machine-facing --json responses keep IDs unchanged for automation compatibility.
|
|
101
|
+
async function resolveOperator(ref) {
|
|
102
|
+
const direct = await apiFetch(`/api/v1/operators/${encodeURIComponent(ref)}`, { auth: "optional" });
|
|
103
|
+
if (direct.ok) return direct.json.operator || direct.json;
|
|
104
|
+
if (direct.status !== 404) throw new Error(`算子信息获取失败(HTTP ${direct.status})`);
|
|
105
|
+
|
|
106
|
+
const listed = await apiFetch("/api/v1/operators?status=online", { auth: "optional" });
|
|
107
|
+
if (!listed.ok) throw new Error(`算子列表获取失败(HTTP ${listed.status})`);
|
|
108
|
+
const operators = listed.json.operators || listed.json || [];
|
|
109
|
+
const matches = operators.filter((item) => item?.name === ref);
|
|
110
|
+
if (matches.length === 0) throw new Error(`未找到算子“${ref}”`);
|
|
111
|
+
if (matches.length > 1) throw new Error(`算子名称“${ref}”不唯一,请联系平台处理`);
|
|
112
|
+
|
|
113
|
+
const detail = await apiFetch(`/api/v1/operators/${encodeURIComponent(matches[0].id)}`, { auth: "optional" });
|
|
114
|
+
if (!detail.ok) throw new Error(`算子信息获取失败(HTTP ${detail.status})`);
|
|
115
|
+
return detail.json.operator || detail.json;
|
|
116
|
+
}
|
|
117
|
+
|
|
87
118
|
const HELP = `manturhub — ManturHub 算子广场 CLI v${VERSION}
|
|
88
119
|
|
|
89
120
|
用法:
|
|
@@ -91,10 +122,10 @@ const HELP = `manturhub — ManturHub 算子广场 CLI v${VERSION}
|
|
|
91
122
|
manturhub login --key sk-xxx 手动配置 API Key(存 ~/.manturhub/config.json)
|
|
92
123
|
manturhub login --key-stdin 从 stdin 安全读取 API Key
|
|
93
124
|
manturhub ls [--cat <分类>] [--json] 列出上线算子(无需登录)
|
|
94
|
-
manturhub describe
|
|
95
|
-
manturhub quote
|
|
96
|
-
manturhub run
|
|
97
|
-
manturhub run
|
|
125
|
+
manturhub describe <中文算子名> [--json] 查看算子入参字段(无需登录)
|
|
126
|
+
manturhub quote <中文算子名> 查询实时计费公式(不要使用 Skill 内的历史价格)
|
|
127
|
+
manturhub run <中文算子名> --json '{}' 试算并确认费用后调用(异步算子自动轮询到出结果)
|
|
128
|
+
manturhub run <中文算子名> --json-file x.json 从文件读参数(prompt 来自配方/用户时更安全)
|
|
98
129
|
manturhub upload <本地文件> 上传图片/音频/视频 → 公网 URL(喂算子前先转换本地文件)
|
|
99
130
|
manturhub status <poll_url> 查异步任务状态(配合 run --no-wait)
|
|
100
131
|
manturhub balance 查询馒头余额
|
|
@@ -206,9 +237,9 @@ async function main() {
|
|
|
206
237
|
}
|
|
207
238
|
console.log(`ManturHub 上线算子(${ops.length} 个):\n`);
|
|
208
239
|
for (const o of ops) {
|
|
209
|
-
console.log(` ${o.
|
|
240
|
+
console.log(` ${o.name} [${catLabel(o.cat)}]`);
|
|
210
241
|
}
|
|
211
|
-
console.log(`\n用 \`manturhub describe
|
|
242
|
+
console.log(`\n用 \`manturhub describe <中文算子名>\` 查入参,\`manturhub run <中文算子名> --json '{...}'\` 调用`);
|
|
212
243
|
break;
|
|
213
244
|
}
|
|
214
245
|
|
|
@@ -225,17 +256,18 @@ async function main() {
|
|
|
225
256
|
console.error(error.message);
|
|
226
257
|
process.exit(1);
|
|
227
258
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
259
|
+
let o;
|
|
260
|
+
try {
|
|
261
|
+
o = await resolveOperator(op);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
console.error(error.message);
|
|
231
264
|
process.exit(1);
|
|
232
265
|
}
|
|
233
|
-
const o = r.json.operator || r.json;
|
|
234
266
|
if (hasFlag("json")) {
|
|
235
267
|
console.log(JSON.stringify(o, null, 2));
|
|
236
268
|
break;
|
|
237
269
|
}
|
|
238
|
-
console.log(`\n${o.
|
|
270
|
+
console.log(`\n${o.name || "未命名算子"} [${catLabel(o.cat)}] · ${o.status || "-"}`);
|
|
239
271
|
if (o.description) console.log(o.description);
|
|
240
272
|
const ps = o.params_schema || (o.meta && o.meta.params_schema);
|
|
241
273
|
if (ps && Array.isArray(ps.fields) && ps.fields.length) {
|
|
@@ -247,9 +279,9 @@ async function main() {
|
|
|
247
279
|
}
|
|
248
280
|
if (ps.async) console.log(`\n异步算子:run 默认自动轮询到出结果(--no-wait 只拿 task_id)`);
|
|
249
281
|
} else {
|
|
250
|
-
console.log(`\n(该算子未声明入参 schema
|
|
282
|
+
console.log(`\n(该算子未声明入参 schema,请在 ManturHub 算子广场查看详情)`);
|
|
251
283
|
}
|
|
252
|
-
console.log(`\n调用: manturhub run ${o.
|
|
284
|
+
console.log(`\n调用: manturhub run '${o.name}' --json '{...}'`);
|
|
253
285
|
break;
|
|
254
286
|
}
|
|
255
287
|
|
|
@@ -265,7 +297,14 @@ async function main() {
|
|
|
265
297
|
console.error(error.message);
|
|
266
298
|
process.exit(1);
|
|
267
299
|
}
|
|
268
|
-
|
|
300
|
+
let operator;
|
|
301
|
+
try {
|
|
302
|
+
operator = await resolveOperator(op);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error(error.message);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
const r = await apiFetch(`/api/v1/operators/${encodeURIComponent(operator.id)}/quote`, { auth: "optional" });
|
|
269
308
|
if (!r.ok) {
|
|
270
309
|
console.error(`查询价格失败(HTTP ${r.status}): ${JSON.stringify(r.json)}`);
|
|
271
310
|
process.exit(1);
|
|
@@ -280,7 +319,7 @@ async function main() {
|
|
|
280
319
|
)
|
|
281
320
|
);
|
|
282
321
|
} else {
|
|
283
|
-
console.log(`${
|
|
322
|
+
console.log(`${operator.name}: ${r.json.formula || "详见算子页"}`);
|
|
284
323
|
if (r.json.floor !== undefined) console.log(`最低扣费: ${usdFor(r.json.floor)}(${r.json.floor} 馒头)`);
|
|
285
324
|
}
|
|
286
325
|
break;
|
|
@@ -333,12 +372,14 @@ async function main() {
|
|
|
333
372
|
process.exit(1);
|
|
334
373
|
}
|
|
335
374
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
375
|
+
let operator;
|
|
376
|
+
try {
|
|
377
|
+
operator = await resolveOperator(op);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.error(`${error.message},已停止调用避免误扣费`);
|
|
339
380
|
process.exit(1);
|
|
340
381
|
}
|
|
341
|
-
const
|
|
382
|
+
const operatorId = operator.id;
|
|
342
383
|
const schema = operator.params_schema || operator.meta?.params_schema;
|
|
343
384
|
try {
|
|
344
385
|
body = validateParams(body, schema, { coerceStrings: !jsonArg && !jsonFile });
|
|
@@ -348,7 +389,7 @@ async function main() {
|
|
|
348
389
|
}
|
|
349
390
|
let quoteId = getFlag("confirm");
|
|
350
391
|
if (!quoteId) {
|
|
351
|
-
const quote = await apiFetch(`/api/v1/operators/${encodeURIComponent(
|
|
392
|
+
const quote = await apiFetch(`/api/v1/operators/${encodeURIComponent(operatorId)}/quote`, {
|
|
352
393
|
method: "POST",
|
|
353
394
|
body,
|
|
354
395
|
});
|
|
@@ -360,13 +401,14 @@ async function main() {
|
|
|
360
401
|
quoteId = quote.json?.quote_id;
|
|
361
402
|
if (Number.isFinite(estimated) && estimated > 0) {
|
|
362
403
|
if (process.stdin.isTTY && process.stderr.isTTY) {
|
|
363
|
-
if (!(await confirmCharge(quote.json))) {
|
|
404
|
+
if (!(await confirmCharge({ ...quote.json, operator_name: operator.name }))) {
|
|
364
405
|
console.error("已取消,未调用算子、未扣费。");
|
|
365
406
|
process.exit(2);
|
|
366
407
|
}
|
|
367
408
|
} else {
|
|
368
409
|
console.error(JSON.stringify({
|
|
369
410
|
error: "CONFIRMATION_REQUIRED",
|
|
411
|
+
operator_name: operator.name,
|
|
370
412
|
message: `本次预计消耗 ${formatMantou(estimated)},请先取得用户确认`,
|
|
371
413
|
estimated_dumplings: estimated,
|
|
372
414
|
balance: quote.json?.balance,
|
|
@@ -379,7 +421,7 @@ async function main() {
|
|
|
379
421
|
}
|
|
380
422
|
}
|
|
381
423
|
const r = await apiFetch(
|
|
382
|
-
`/api/v1/operators/${encodeURIComponent(
|
|
424
|
+
`/api/v1/operators/${encodeURIComponent(operatorId)}/invoke`,
|
|
383
425
|
{
|
|
384
426
|
method: "POST",
|
|
385
427
|
body,
|
|
@@ -400,12 +442,12 @@ async function main() {
|
|
|
400
442
|
),
|
|
401
443
|
});
|
|
402
444
|
console.log(JSON.stringify(final, null, 2));
|
|
403
|
-
printBillingResult(final);
|
|
445
|
+
printBillingResult(final, process.stderr, operator.name);
|
|
404
446
|
const st = final && final.status;
|
|
405
447
|
if (st === "failed" || st === "error" || (final && final._timeout)) process.exit(1);
|
|
406
448
|
} else {
|
|
407
449
|
console.log(JSON.stringify(r.json, null, 2));
|
|
408
|
-
printBillingResult(r.json);
|
|
450
|
+
printBillingResult(r.json, process.stderr, operator.name);
|
|
409
451
|
if (!r.ok) process.exit(1);
|
|
410
452
|
}
|
|
411
453
|
break;
|
package/lib/billing-confirm.js
CHANGED
|
@@ -6,6 +6,7 @@ export function formatMantou(value) {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export async function confirmCharge(quote, { input = process.stdin, output = process.stderr } = {}) {
|
|
9
|
+
if (quote.operator_name) output.write(`\n算子:${quote.operator_name}\n`);
|
|
9
10
|
output.write(`\n⚠️ 本次预计消耗:${formatMantou(quote.estimated_dumplings)}\n`);
|
|
10
11
|
if (quote.formula) output.write(`计费依据:${quote.formula}\n`);
|
|
11
12
|
if (Number.isFinite(Number(quote.balance))) output.write(`当前余额:${quote.balance} 馒头\n`);
|
|
@@ -18,13 +19,14 @@ export async function confirmCharge(quote, { input = process.stdin, output = pro
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export function printBillingResult(result, output = process.stderr) {
|
|
22
|
+
export function printBillingResult(result, output = process.stderr, operatorName = null) {
|
|
22
23
|
const billing = result?._billing;
|
|
23
24
|
if (!billing) return;
|
|
24
25
|
const estimated = Number(billing.estimated_dumplings);
|
|
25
26
|
const charged = Number(billing.charged_dumplings);
|
|
26
27
|
const refunded = Number(billing.refunded_dumplings);
|
|
27
28
|
if (billing.final) {
|
|
29
|
+
if (operatorName) output.write(`\n算子:${operatorName}\n`);
|
|
28
30
|
output.write(`\n✓ 本次实际消耗:${formatMantou(charged)}\n`);
|
|
29
31
|
if (Number.isFinite(refunded) && refunded > 0) output.write(` 已退款:${formatMantou(refunded)}\n`);
|
|
30
32
|
if (Number.isFinite(estimated) && estimated !== charged) output.write(` 调用前预计:${formatMantou(estimated)}\n`);
|