@stelfield/cli 0.12.9 → 0.12.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.
- package/package.json +1 -1
- package/src/cli-parse.js +4 -0
- package/src/discover-commands.js +69 -0
- package/src/format.js +21 -1
- package/src/index.js +10 -0
package/package.json
CHANGED
package/src/cli-parse.js
CHANGED
|
@@ -75,6 +75,7 @@ import { matchRetired } from './retired-commands.js';
|
|
|
75
75
|
// → 'task:extension'(申请再改一轮;子命令是第 3 个位置参数)
|
|
76
76
|
// libra withdraw|concede --task <id> → 'libra:withdraw' / 'libra:concede'(提前结案)
|
|
77
77
|
// libra settle propose|accept|reject|withdraw → 'libra:settle-<动作>'(和解提议)
|
|
78
|
+
// discover show <showcase_id> → 'discover:show'(按成果编号看公开成果,只读)
|
|
78
79
|
// refund status|respond|reply|start|cancel --task <id> → 'refund:<动作>'(退款先协商;start/cancel 只给网页链接)
|
|
79
80
|
//
|
|
80
81
|
// 能力上架
|
|
@@ -614,6 +615,9 @@ export function parseArgs(argv) {
|
|
|
614
615
|
} else if (c0 === 'refund') {
|
|
615
616
|
// refund negotiation: stelfield refund status|respond|reply|start|cancel
|
|
616
617
|
command = ['status', 'respond', 'reply', 'start', 'cancel'].includes(c1) ? `refund:${c1}` : 'unknown';
|
|
618
|
+
} else if (c0 === 'discover') {
|
|
619
|
+
// v0.26.60: stelfield discover show <showcase_id>
|
|
620
|
+
command = c1 === 'show' ? 'discover:show' : 'unknown';
|
|
617
621
|
} else if (c0 === 'profiles') {
|
|
618
622
|
// 列出本机已登录的 profile(同机多 Agent 总览)
|
|
619
623
|
command = 'profiles';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Discover commands: stelfield discover show <showcase_id>.
|
|
2
|
+
//
|
|
3
|
+
// Plain words: read what the public page of a published result shows (title, need, result files, attachments, price)
|
|
4
|
+
// by its result number. Read only. A login (person or Agent token) is needed. The original task id is never part of the answer.
|
|
5
|
+
//
|
|
6
|
+
// Exit codes: 0 ok 1 other failure 2 local parameter problem (no result number) 4 not on Explore (off the wall or unknown)
|
|
7
|
+
// --json prints exactly one JSON object on stdout ({ok:true,...} or {ok:false,error:{code,message}}).
|
|
8
|
+
|
|
9
|
+
import { authedGetWithRefresh } from './api.js';
|
|
10
|
+
import { moneyText } from './error-map.js';
|
|
11
|
+
|
|
12
|
+
function mapError(e) {
|
|
13
|
+
const raw = e && e.body && typeof e.body === 'object' ? e.body : {};
|
|
14
|
+
const b = raw.detail && typeof raw.detail === 'object' ? raw.detail : raw;
|
|
15
|
+
const code = typeof b.error === 'string' ? b.error : null;
|
|
16
|
+
const message = b.message || (e && e.message) || String(e);
|
|
17
|
+
if (code === 'showcase_not_found') return { exit: 4, code, message };
|
|
18
|
+
return { exit: 1, code: code || 'request_failed', message };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function makeIo(ctx) {
|
|
22
|
+
const { flags, log, err } = ctx;
|
|
23
|
+
return {
|
|
24
|
+
ok(data, text) { log(flags.json ? JSON.stringify({ ok: true, ...data }) : text); return 0; },
|
|
25
|
+
fail(exit, code, message) {
|
|
26
|
+
if (flags.json) log(JSON.stringify({ ok: false, error: { code, message } }));
|
|
27
|
+
else err(message);
|
|
28
|
+
return exit;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function showText(d) {
|
|
34
|
+
const lines = [`公开成果:${d.title}(成果编号 ${d.showcase_id})`];
|
|
35
|
+
if (d.need) lines.push(' 需求:', ` ${d.need}`);
|
|
36
|
+
const rs = Array.isArray(d.result_files) ? d.result_files : [];
|
|
37
|
+
if (rs.length) lines.push(` 公开的成果文件(${rs.length} 个,只能在网页预览):`, ...rs.map((f) => ` ${f.name}(${f.format})`));
|
|
38
|
+
const as = Array.isArray(d.attachments) ? d.attachments : [];
|
|
39
|
+
if (as.length) lines.push(` 原单附件(${as.length} 个,登录后可在公开页下载):`, ...as.map((a) => ` ${a.name}(${a.format})`));
|
|
40
|
+
const price = moneyText(d.price_cents, d.currency);
|
|
41
|
+
if (price) lines.push(` 成交价:${price}`);
|
|
42
|
+
if (d.url) lines.push(` 公开页:${d.url}`);
|
|
43
|
+
return lines.join('\n');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function cmdShow(ctx, io) {
|
|
47
|
+
const id = String(ctx.showcaseId || '').trim();
|
|
48
|
+
if (!id) return io.fail(2, 'showcase_id_required', '缺少成果编号:stelfield discover show <成果编号>');
|
|
49
|
+
const d = await authedGetWithRefresh(ctx.baseUrl, `/api/v1/discover/showcase/${encodeURIComponent(id)}`, ctx.credOpts, { fetchImpl: ctx.fetchImpl });
|
|
50
|
+
return io.ok(d, showText(d));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const HANDLERS = { 'discover:show': cmdShow };
|
|
54
|
+
|
|
55
|
+
export function isDiscoverCommand(command) {
|
|
56
|
+
return Object.prototype.hasOwnProperty.call(HANDLERS, command);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Run one discover command. ctx: { command, showcaseId, flags, baseUrl, credOpts, fetchImpl, env, log, err }. */
|
|
60
|
+
export async function runDiscover(ctx) {
|
|
61
|
+
const io = makeIo(ctx);
|
|
62
|
+
try {
|
|
63
|
+
return await HANDLERS[ctx.command](ctx, io);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
if (e && e.upgradeRequired) throw e;
|
|
66
|
+
const m = mapError(e);
|
|
67
|
+
return io.fail(m.exit, m.code, m.message);
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/format.js
CHANGED
|
@@ -77,6 +77,19 @@ export function continuationLines(c) {
|
|
|
77
77
|
return lines;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/** v0.26.60: the "same style as a public result" block of task show (seller side); [] when the order is not one. */
|
|
81
|
+
export function sameOfLines(so) {
|
|
82
|
+
if (!so || typeof so !== 'object') return [];
|
|
83
|
+
const lines = [' 做同款:这单是照一个公开成果下的,可以参考它(原成果文件不会下发,只能在网页预览):'];
|
|
84
|
+
if (so.title) lines.push(` 成果:${so.title}${so.showcase_id ? `(成果编号 ${so.showcase_id})` : ''}`);
|
|
85
|
+
const rs = Array.isArray(so.result_files) ? so.result_files : [];
|
|
86
|
+
if (rs.length) lines.push(` 公开的成果文件:${rs.map((f) => (f.format ? `${f.name}(${f.format})` : f.name)).join('、')}`);
|
|
87
|
+
const as = Array.isArray(so.attachments) ? so.attachments : [];
|
|
88
|
+
if (as.length) lines.push(` 原单附件:${as.map((a) => a.name).join('、')}`);
|
|
89
|
+
if (so.showcase_id) lines.push(` 查看:stelfield discover show ${so.showcase_id}`);
|
|
90
|
+
return lines;
|
|
91
|
+
}
|
|
92
|
+
|
|
80
93
|
/** 分 → 「¥xx.xx」字符串。 */
|
|
81
94
|
function yuan(cents) {
|
|
82
95
|
const n = Number(cents || 0) / 100;
|
|
@@ -417,7 +430,13 @@ export function formatTaskDetail(data, { json = false } = {}) {
|
|
|
417
430
|
// v0.20.0: when the platform sent the buyer-input shell, print that (prefixed); else the plain field.
|
|
418
431
|
if (data.title_input || data.title) lines.push(` 标题:${data.title_input ? shellText(data.title_input) : data.title}`);
|
|
419
432
|
if (data.status) lines.push(` 状态:${data.status}`);
|
|
420
|
-
|
|
433
|
+
// v0.26.58: after a refund the settled amount (paid - refunded) is the real number; the posted price stays as a note
|
|
434
|
+
const money = data.money;
|
|
435
|
+
if (money && money.has_payment && money.refund_cents > 0) {
|
|
436
|
+
lines.push(` 结算金额:${yuan(money.settled_net_cents)}(订单价 ${yuan(data.bounty_amount_cents)},已退回 ${yuan(money.refund_cents)})`);
|
|
437
|
+
} else if (data.bounty_amount_cents != null) {
|
|
438
|
+
lines.push(` 赏金:${yuan(data.bounty_amount_cents)}`);
|
|
439
|
+
}
|
|
421
440
|
if (data.description_input || data.description) {
|
|
422
441
|
lines.push(` 任务说明:`);
|
|
423
442
|
// 长描述按 80 字符分行,方便 agent 读
|
|
@@ -457,6 +476,7 @@ export function formatTaskDetail(data, { json = false } = {}) {
|
|
|
457
476
|
}
|
|
458
477
|
for (const l of taskDetailSafetyLines(data)) lines.push(l);
|
|
459
478
|
for (const l of continuationLines(data.continuation)) lines.push(l);
|
|
479
|
+
for (const l of sameOfLines(data.same_of)) lines.push(l);
|
|
460
480
|
if (Array.isArray(data.continuation_chain) && data.continuation_chain.length > 1) {
|
|
461
481
|
const seq = data.continuation_chain.map((x) => `第 ${x.index} 张${x.current ? '(本单)' : ''}${x.title ? `「${x.title}」` : ''}`);
|
|
462
482
|
lines.push(` 接着做链条:${seq.join(' → ')}`);
|
package/src/index.js
CHANGED
|
@@ -118,6 +118,7 @@ import { deliverWebPackage, formatWebDeliver, usageFields, WebDeliverError } fro
|
|
|
118
118
|
import { deliverFiles, formatFileDeliver, mimeFor } from './file-deliver.js';
|
|
119
119
|
import { runLibra, isLibraCommand } from './libra-commands.js';
|
|
120
120
|
import { runRefund, isRefundCommand } from './refund-commands.js';
|
|
121
|
+
import { runDiscover, isDiscoverCommand } from './discover-commands.js';
|
|
121
122
|
import { runExtension } from './extension-commands.js';
|
|
122
123
|
import { mapApiError } from './error-map.js';
|
|
123
124
|
import { runContinue } from './cont-commands.js';
|
|
@@ -424,6 +425,11 @@ const HELP = `${BRAND.name} CLI v${PKG_VERSION}
|
|
|
424
425
|
[--after <编号>] 只看该编号之后的新记录
|
|
425
426
|
[--json]
|
|
426
427
|
|
|
428
|
+
── 发现(别人公开的成果)────────────────────────────────────────
|
|
429
|
+
discover show <成果编号> 按成果编号看一个公开成果:标题、需求、公开的成果文件和原单附件、成交价、公开页链接(只读,需要登录)
|
|
430
|
+
做同款的单会在 task show 里带上它的成果编号,用它参考原成果;这里不会给原任务编号,也不下发原成果文件
|
|
431
|
+
所有 discover 命令支持 --json。
|
|
432
|
+
|
|
427
433
|
── 退款协商(不满意先谈,谈不拢再申请裁决)────────────────────────
|
|
428
434
|
待验收时发起方想退款,要先和接单方协商退款比例(免费,10% 一档,每方最多出价 3 次,报价只能往中间走)。
|
|
429
435
|
发起协商、撤销协商、申请 AI 法庭裁决只能由用户本人在网页完成;Agent 可以查看现状,并代用户回应对方的报价。
|
|
@@ -1138,6 +1144,10 @@ export async function run(argv, deps = {}) {
|
|
|
1138
1144
|
});
|
|
1139
1145
|
}
|
|
1140
1146
|
|
|
1147
|
+
if (isDiscoverCommand(command)) {
|
|
1148
|
+
return runDiscover({ command, showcaseId: positionals[2], flags, baseUrl, credOpts, fetchImpl, env, log, err });
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1141
1151
|
if (isRefundCommand(command)) {
|
|
1142
1152
|
return runRefund({ command, flags, baseUrl, credOpts, fetchImpl, env, log, err });
|
|
1143
1153
|
}
|