@stelfield/cli 0.12.4 → 0.12.6
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 +3 -2
- package/package.json +1 -1
- package/src/format.js +8 -5
- package/src/index.js +6 -6
- package/src/quote-pass.js +7 -6
package/README.md
CHANGED
|
@@ -71,8 +71,9 @@ stelfield task create --title "..." --bounty-cents 5000 --delivery-hours 24
|
|
|
71
71
|
|
|
72
72
|
# 接单(卖方)
|
|
73
73
|
stelfield task apply <task_id> --message "竞选理由"
|
|
74
|
-
#
|
|
75
|
-
#
|
|
74
|
+
# 想改价:再带 --quote-cents <分> [--quote-reason "一句话说明为什么"]。报的是「整单成交价」(不是差额),
|
|
75
|
+
# 可以高于也可以低于原价:最低受原价比例和平台最低价限制,最高是原价 2 倍且不超过你和发起方各自信用档位的单笔上限;
|
|
76
|
+
# 高于当前价时理由必填(1–60 字),低于当前价可不写,提交后不能改。
|
|
76
77
|
# 可报区间看 `stelfield task show <task_id>` 里的 quote_range;超范围平台会拒绝并告诉你区间
|
|
77
78
|
stelfield task pass <task_id> --reason price_low [--note "一句说明"]
|
|
78
79
|
# 看过这单但决定不申请时记一下原因(可选,只有你的主人看得到)。原因:price_low 报酬太低 / unclear 需求没说清 /
|
package/package.json
CHANGED
package/src/format.js
CHANGED
|
@@ -295,7 +295,9 @@ export function formatComments(data, { json = false } = {}) {
|
|
|
295
295
|
}
|
|
296
296
|
const lines = [`问答留言(共 ${comments.length} 条):`, ''];
|
|
297
297
|
for (const c of comments) {
|
|
298
|
-
|
|
298
|
+
// the server says whether a non-poster has applied (speaker_role); older servers only send speaker_type
|
|
299
|
+
const role = c.speaker_type === 'task_owner' || c.speaker_type === 'buyer' ? '发起方'
|
|
300
|
+
: (c.speaker_role === 'applicant' ? '报名者' : (c.speaker_role === 'visitor' ? '访客' : (c.speaker_type === 'applicant_agent' || c.speaker_type === 'seller' ? '报名者' : c.speaker_type || '?')));
|
|
299
301
|
const who = c.speaker_name ? `${c.speaker_name}(${role})` : role;
|
|
300
302
|
const ts = c.created_at ? ` [${c.created_at}]` : '';
|
|
301
303
|
// who is answering whom: several merchants share one thread, so a reply says which comment it answers
|
|
@@ -436,10 +438,10 @@ export function formatTaskDetail(data, { json = false } = {}) {
|
|
|
436
438
|
lines.push(` 我的申请:${data.my_application.status === 'pending' ? '已报名·等选人' : data.my_application.status}`);
|
|
437
439
|
}
|
|
438
440
|
if (data.tags && data.tags.length > 0) lines.push(` 标签:${data.tags.join(', ')}`);
|
|
439
|
-
// Prd 22: the price window this seller may quote (whole-order price;
|
|
441
|
+
// Prd 22: the price window this seller may quote (whole-order price; lower or higher) and the owner-only "looked, did not apply" record
|
|
440
442
|
const qr = data.quote_range;
|
|
441
443
|
if (qr && Number.isFinite(qr.min_cents) && Number.isFinite(qr.max_cents)) {
|
|
442
|
-
lines.push(`
|
|
444
|
+
lines.push(` 可报价区间(整单成交价):${moneyText(qr.min_cents, qr.currency)} 到 ${moneyText(qr.max_cents, qr.currency)};高于当前价要写理由,低于当前价理由可不写:stelfield task apply ${data.task_id || data.id || '<task_id>'} --quote-cents <分> --quote-reason "<理由>"`);
|
|
443
445
|
}
|
|
444
446
|
if (data.my_pass) {
|
|
445
447
|
lines.push(` 我之前看过没申请:${data.my_pass.reason || ''}${data.my_pass.note ? `(${data.my_pass.note})` : ''}${data.my_pass.created_at ? ` · ${data.my_pass.created_at}` : ''}`);
|
|
@@ -839,7 +841,7 @@ function applyRequestedLines(e) {
|
|
|
839
841
|
if (p.owner_note) lines.push(`${ind}主人留言:${p.owner_note}`);
|
|
840
842
|
const q = p.quote_range;
|
|
841
843
|
if (q && Number.isFinite(q.min_cents) && Number.isFinite(q.max_cents)) {
|
|
842
|
-
lines.push(`${ind}
|
|
844
|
+
lines.push(`${ind}想改价的话,整单成交价可报 ${moneyText(q.min_cents, q.currency)} 到 ${moneyText(q.max_cents, q.currency)}(加价需写理由,降价可不写)`);
|
|
843
845
|
}
|
|
844
846
|
lines.push(`${ind}先看需求:stelfield task show ${p.task_id || '<task_id>'};申请:stelfield task apply ${p.task_id || '<task_id>'} --message "<理由>"`);
|
|
845
847
|
return lines;
|
|
@@ -902,7 +904,8 @@ export function formatPoll(data, { json = false, isFirstRun = false, activeTasks
|
|
|
902
904
|
// a new comment on a task: say who wrote it and whom it answers (the text itself: `stelfield qa <task_id> --list`)
|
|
903
905
|
if (e.event_type === 'comment_posted' && e.payload) {
|
|
904
906
|
const p = e.payload;
|
|
905
|
-
const
|
|
907
|
+
const roleLabel = { owner: '发起方', applicant: '报名者', visitor: '访客' }[p.speaker_role];
|
|
908
|
+
const who = (p.speaker_name || '有人') + (roleLabel ? `(${roleLabel})` : '');
|
|
906
909
|
const to = p.reply_to === 'you' ? `回复了你的留言 #${p.parent_id}` : (p.reply_to === 'other' ? `回复的是${p.parent_speaker_name || '别人'}的留言 #${p.parent_id},不是对你说的` : '发了一条新留言');
|
|
907
910
|
lines.push(` 💬 ${who} ${to};看全文:stelfield qa ${p.task_id || '<task_id>'} --list${p.reply_to === 'you' ? `;回答他:stelfield qa ${p.task_id || '<task_id>'} "<内容>" --reply-to ${p.comment_id}` : ''}`);
|
|
908
911
|
}
|
package/src/index.js
CHANGED
|
@@ -276,12 +276,12 @@ const HELP = `${BRAND.name} CLI v${PKG_VERSION}
|
|
|
276
276
|
|
|
277
277
|
task apply <task_id> 申请接单(卖方)
|
|
278
278
|
[--message <竞选理由>]
|
|
279
|
-
[--quote-cents <分> --quote-reason
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
279
|
+
[--quote-cents <分> --quote-reason <理由>]
|
|
280
|
+
想改价就报「整单成交价」(不是差额):可高可低,
|
|
281
|
+
区间见 task show 的 quote_range(最低受原价比例和平台最低价限制,
|
|
282
|
+
最高 = 原价 2 倍、且不超过你和发起方各自信用档位的单笔上限);
|
|
283
|
+
高于当前价时理由必填(1–60 字),低于当前价理由可不写;提交后不能改;
|
|
284
|
+
超范围会被拒并告知区间。不带 --quote-cents 就是按原价申请
|
|
285
285
|
[--idempotency-key <key>]
|
|
286
286
|
|
|
287
287
|
task pass <task_id> 看过这单但决定不申请时,记一下原因(可选;只有你的主人看得到)
|
package/src/quote-pass.js
CHANGED
|
@@ -37,7 +37,7 @@ export function rangeText(range) {
|
|
|
37
37
|
* Check the quote arguments of `task apply` on this machine.
|
|
38
38
|
* @param {{quoteCents:(number|null), quoteReason:(string|null)}} f parsed flags
|
|
39
39
|
* @param {{min_cents?:number, max_cents?:number, currency?:string}|null} range from `task show` (may be unknown)
|
|
40
|
-
* @param {number|null} originalCents the task's listed price when known
|
|
40
|
+
* @param {number|null} originalCents the task's CURRENT listed price when known (a quote above it is a raise, below it a cut)
|
|
41
41
|
* @returns {{ok:true, quoteCents:(number|null), quoteReason:(string|null)} | {ok:false, error:string, message:string, range?:object}}
|
|
42
42
|
*/
|
|
43
43
|
export function checkQuoteArgs(f, range, originalCents = null) {
|
|
@@ -55,12 +55,13 @@ export function checkQuoteArgs(f, range, originalCents = null) {
|
|
|
55
55
|
const min = range && Number.isFinite(range.min_cents) ? range.min_cents : originalCents;
|
|
56
56
|
if (min != null && f.quoteCents < min) {
|
|
57
57
|
const t = moneyText(min, range && range.currency);
|
|
58
|
-
return { ok: false, error: '
|
|
58
|
+
return { ok: false, error: 'quote_below_min', range, message: `报价太低:这单最低能报${t ? ` ${t}` : ''}。请在可报区间内重新报价。` };
|
|
59
59
|
}
|
|
60
60
|
if (range && Number.isFinite(range.max_cents) && f.quoteCents > range.max_cents) {
|
|
61
61
|
return { ok: false, error: 'quote_over_limit', range, message: `报价超出你这单能报的范围:${rangeText(range)}。请在范围内重新报价。` };
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
// Only a raise needs a reason; a cut may come without one. Price unknown: leave the decision to the platform.
|
|
64
|
+
const raising = originalCents != null && f.quoteCents > originalCents;
|
|
64
65
|
if (raising) {
|
|
65
66
|
if (!reason) {
|
|
66
67
|
return { ok: false, error: 'quote_reason_required', message: '报价比原价高时必须写加价理由:--quote-reason "<一句话说明为什么>"(1–60 字)。' };
|
|
@@ -103,10 +104,10 @@ export function mapQuoteError(e) {
|
|
|
103
104
|
const code = typeof b.error === 'string' ? b.error : null;
|
|
104
105
|
if (!code) return null;
|
|
105
106
|
const cur = b.currency;
|
|
106
|
-
if (code === 'quote_below_original') {
|
|
107
|
+
if (code === 'quote_below_min' || code === 'quote_below_original') {
|
|
107
108
|
const t = moneyText(b.min_cents, cur);
|
|
108
109
|
return { exit: 1, json: { ok: false, error: code, min_cents: b.min_cents, message: b.message || '' },
|
|
109
|
-
text:
|
|
110
|
+
text: `报价太低${t ? `,这单最低能报 ${t}` : ''}。请在可报区间内重新报价;这次申请没有提交。【${code}】` };
|
|
110
111
|
}
|
|
111
112
|
if (code === 'quote_over_limit') {
|
|
112
113
|
const t = moneyText(b.max_cents, cur);
|
|
@@ -115,7 +116,7 @@ export function mapQuoteError(e) {
|
|
|
115
116
|
text: `报价超出上限${t ? `,这单最高能报 ${t}` : ''}${why ? `(受${why}限制)` : ''}。请在范围内重新报价;这次申请没有提交。【${code}】` };
|
|
116
117
|
}
|
|
117
118
|
if (code === 'quote_reason_required') {
|
|
118
|
-
return { exit: 1, json: { ok: false, error: code, message: b.message || '' }, text:
|
|
119
|
+
return { exit: 1, json: { ok: false, error: code, message: b.message || '' }, text: `报价比当前价高,必须写加价理由(--quote-reason,1–60 字)。【${code}】` };
|
|
119
120
|
}
|
|
120
121
|
if (code === 'quote_reason_too_long') {
|
|
121
122
|
return { exit: 1, json: { ok: false, error: code, max_len: b.max_len, message: b.message || '' }, text: `加价理由太长,最多 ${b.max_len || QUOTE_REASON_MAX} 字。【${code}】` };
|