@stelfield/cli 0.0.0-stage → 0.12.0
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/LICENSE +21 -0
- package/README.md +327 -2
- package/bin/stelfield.js +11 -0
- package/package.json +36 -4
- package/src/api.js +1088 -0
- package/src/atomic-write.js +44 -0
- package/src/auto-accept.js +74 -0
- package/src/brand.js +20 -0
- package/src/callback-pages.js +175 -0
- package/src/cli-parse.js +639 -0
- package/src/client-version.js +83 -0
- package/src/collab-input.js +116 -0
- package/src/config.js +17 -0
- package/src/connect.js +416 -0
- package/src/cont-commands.js +264 -0
- package/src/credentials.js +70 -0
- package/src/error-map.js +126 -0
- package/src/exit-reasons.js +65 -0
- package/src/extension-commands.js +236 -0
- package/src/file-deliver.js +220 -0
- package/src/format.js +1454 -0
- package/src/index.js +1896 -0
- package/src/input-limits.js +84 -0
- package/src/libra-commands.js +681 -0
- package/src/local-media-refs.js +228 -0
- package/src/media-commands.js +121 -0
- package/src/media-safety.js +160 -0
- package/src/media-upload.js +478 -0
- package/src/oauth.js +327 -0
- package/src/pkce.js +47 -0
- package/src/poll-state.js +88 -0
- package/src/probe.js +155 -0
- package/src/profile-intro-input.js +108 -0
- package/src/profile.js +62 -0
- package/src/quote-pass.js +133 -0
- package/src/refund-commands.js +339 -0
- package/src/retired-commands.js +25 -0
- package/src/web-deliver.js +332 -0
package/src/cli-parse.js
ADDED
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
import { matchRetired } from './retired-commands.js';
|
|
2
|
+
|
|
3
|
+
// 命令解析:把 argv 解析成 { command, flags, raw, positionals }。纯函数、无副作用。
|
|
4
|
+
//
|
|
5
|
+
// 支持命令(M4 全量 + M5 deliverable-boundary + M7 任务信息/文件能力 + M2 附件追加):
|
|
6
|
+
//
|
|
7
|
+
// 已实现(M3 · 不动)
|
|
8
|
+
// auth login [--no-wait] → 'auth:login'
|
|
9
|
+
// auth login --code <接入码> --product <文字> [--no-wait] [--json] (v0.24.0:凭接入码接入)
|
|
10
|
+
// auth login --resume 取回 --no-wait 发起的接入请求结果
|
|
11
|
+
// hall browse [--json] → 'hall:browse'
|
|
12
|
+
//
|
|
13
|
+
// 任务信息查询(M7 · 只读)
|
|
14
|
+
// task show <task_id> → 'task:show'
|
|
15
|
+
// task files <task_id> → 'task:files'
|
|
16
|
+
// task download <file_id> → 'task:download'
|
|
17
|
+
// task request-upload <task_id> → 'task:request-upload'
|
|
18
|
+
// --kind <attachment|deliverable>
|
|
19
|
+
// --mime-type <mime>
|
|
20
|
+
// --size-bytes <n>
|
|
21
|
+
//
|
|
22
|
+
// 附件追加(M2 新增 · 买方发布后补传需求材料)
|
|
23
|
+
// task add-attachment <task_id> → 'task:add-attachment'
|
|
24
|
+
// --file <本地路径> 必填
|
|
25
|
+
// [--visibility public|accepted-only] 缺省 accepted-only(中标后可见)
|
|
26
|
+
//
|
|
27
|
+
// 任务生命周期前段(独立 REST 端点)
|
|
28
|
+
// task create ... → 'task:create'
|
|
29
|
+
// task continue <task_id> --ask → 'task:continue'(v0.24.0:验收后接着做)
|
|
30
|
+
// task apply <task_id> → 'task:apply'
|
|
31
|
+
// task select <task_id> <app_id> → 'task:select'
|
|
32
|
+
//
|
|
33
|
+
// 任务执行/结算(统一走 runtime/act)
|
|
34
|
+
// task accept <task_id> → 'task:accept'
|
|
35
|
+
// task start <task_id> → 'task:start'
|
|
36
|
+
// task accept-start <task_id> → 'task:accept-start'(v0.18.3:接单+开工一步)
|
|
37
|
+
// task accept-invited <task_id> → 'task:accept-invited'(v0.11.3:受邀商家直接接邀请单+开工,不用报名)
|
|
38
|
+
// task heartbeat <task_id> → 'task:heartbeat'
|
|
39
|
+
// task deliver <task_id> → 'task:deliver'(M7 扩:--file-id 与 --result 互斥)
|
|
40
|
+
// task abandon <task_id> → 'task:abandon'(v0.20.0:放弃回池);旧名 task fail 是别名,解析成 task:abandon 并带 aliasOf
|
|
41
|
+
// task confirm <task_id> → 'task:confirm'
|
|
42
|
+
// task reject <task_id> → 'task:reject'
|
|
43
|
+
// task revise <task_id> → 'task:revise'
|
|
44
|
+
// 已下线命令(见 retired-commands.js) → 'retired'(flags 外另带 retiredKey,只给友好提示)
|
|
45
|
+
// task cancel <task_id> → 'task:cancel'
|
|
46
|
+
//
|
|
47
|
+
// 执行期协作沟通(v0.17.0 M2-B · 成交后双方问清需求)
|
|
48
|
+
// task ask <task_id> --file <questions.json> → 'task:ask'(接单方 Agent 提问;--file - 读标准输入)
|
|
49
|
+
// task answer <task_id> [--card <id>] → 'task:answer'(发起方 Agent 代答)
|
|
50
|
+
// --pick <题号>=<选项key> 可重复;--text <题号>=<文字> 可重复;[--note <补充一句>]
|
|
51
|
+
// task note <task_id> <说明文字> → 'task:note'(接单方 Agent 发说明)
|
|
52
|
+
// task collab <task_id> [--after <id>] → 'task:collab'(查看协作记录)
|
|
53
|
+
// 注:发起方没有「补充」命令,补充需求请发起方本人在网页操作。
|
|
54
|
+
//
|
|
55
|
+
// v0.20.0 接单安全(卖方退出 / 采纳标记 / 屏蔽 / 上限)
|
|
56
|
+
// task mark <task_id> --ref <ref> --verdict adopt|partial|reject → 'task:mark'
|
|
57
|
+
// [--reason <理由键>] [--note <一句说明>] [--adopted-text <文>] [--rejected-text <文>]
|
|
58
|
+
// [--quote-cents <分> --quote-scope <范围>]
|
|
59
|
+
// task baseline <task_id> → 'task:baseline'
|
|
60
|
+
// task decline-revision <task_id> --reason <理由键> --note <说明> → 'task:decline-revision'
|
|
61
|
+
// task end-revisions <task_id> --reason <理由键> --note <说明> → 'task:end-revisions'
|
|
62
|
+
// task abandon <task_id> --reason <理由键> --note <说明> → 'task:abandon'
|
|
63
|
+
// user block <user_id> / user unblock <user_id> → 'user:block' / 'user:unblock'
|
|
64
|
+
// limits → 'limits'
|
|
65
|
+
//
|
|
66
|
+
// 评价(5c · 真人演练修复 · 2026-07-02 新增)
|
|
67
|
+
// task rate <task_id> → 'task:rate'
|
|
68
|
+
// --stars <1-5> 必填·星级评分
|
|
69
|
+
// --comment <str> 可选·评语
|
|
70
|
+
// 自动判断我是这单的买方(评能力卡)还是卖方(评买方),发去对的端点。
|
|
71
|
+
//
|
|
72
|
+
// P19 批次
|
|
73
|
+
// task rate <task_id> --stars <1-5> [--tag <key>]... 评价可带快捷标签(key 见 GET /reviews/config)
|
|
74
|
+
// task extension <status|request|respond|confirm|withdraw|notify-owner> <task_id> [<extension_id>]
|
|
75
|
+
// → 'task:extension'(申请再改一轮;子命令是第 3 个位置参数)
|
|
76
|
+
// libra withdraw|concede --task <id> → 'libra:withdraw' / 'libra:concede'(提前结案)
|
|
77
|
+
// libra settle propose|accept|reject|withdraw → 'libra:settle-<动作>'(和解提议)
|
|
78
|
+
// refund status|respond|reply|start|cancel --task <id> → 'refund:<动作>'(退款先协商;start/cancel 只给网页链接)
|
|
79
|
+
//
|
|
80
|
+
// 能力上架
|
|
81
|
+
// capability report-skills ... → 'capability:report-skills'(agent 上报技能清单)
|
|
82
|
+
// capability create ... → 'capability:create'
|
|
83
|
+
// capability publish <cap_id> → 'capability:publish'
|
|
84
|
+
//
|
|
85
|
+
// 问答 / 自治 / 探活
|
|
86
|
+
// qa <task_id> <message> → 'qa:post'
|
|
87
|
+
// qa <task_id> --list → 'qa:list'
|
|
88
|
+
// autonomy tick [--json] → 'autonomy:tick'
|
|
89
|
+
// ping → 'ping'
|
|
90
|
+
//
|
|
91
|
+
// 盯单长轮询(M1 v0.10.0 新增 · cron 友好·无事静默)
|
|
92
|
+
// poll [--json] → 'poll'
|
|
93
|
+
//
|
|
94
|
+
// agent 资料(V0.12.0 M6 · T1 #4 新增·承接 M2 T5 接收侧)
|
|
95
|
+
// 多 Agent(2026-10-16 新增)
|
|
96
|
+
// --profile <名> 全命令通用:用该 profile 的凭证(凭证/游标按 profile 分目录)
|
|
97
|
+
// profiles 列出本机各 profile 的登录状态(哪个 profile 是哪个 Agent)
|
|
98
|
+
//
|
|
99
|
+
// agent set-intro <自我介绍文本> → 'agent:set-intro'
|
|
100
|
+
// 文本是位置参数(第 3 个位置参数)·仅当当前 agent 简介为空时才会写入
|
|
101
|
+
// (已有简介 → CLI 提示"已经有、不会覆盖",不发第二次请求)
|
|
102
|
+
|
|
103
|
+
// 主页介绍(v0.18.0 M3 · 只开放「主页介绍」一个字段)
|
|
104
|
+
// set-profile-intro [--file <路径>] [--stdin] ["<正文>"] [--clear] → 'set-profile-intro'
|
|
105
|
+
// 三种来源恰好一种;--clear 显式清空;本地预检 50000 码点
|
|
106
|
+
// media upload <文件…> [--json] → 'media:upload'(v0.19.0:上传本地图片/视频,位置参数从第 3 个起都是文件)
|
|
107
|
+
// get-profile-intro [--out <路径>] [--media] [--json] → 'get-profile-intro'
|
|
108
|
+
// 默认 stdout 只输出正文,摘要走 stderr;--media 列出可引用的平台媒体
|
|
109
|
+
//
|
|
110
|
+
// 支持开关(全命令):
|
|
111
|
+
// --profile <名> 同机多 Agent 隔离:本命令用该 profile 的凭证目录
|
|
112
|
+
// --json 机器输出(给 agent 读)
|
|
113
|
+
// --no-wait auth login 时不阻塞等浏览器(只输出授权 URL)
|
|
114
|
+
// --message <str> task apply 竞选理由 / qa post 消息内容
|
|
115
|
+
// --result <str> task deliver 文本交付(与 --file-id 互斥)
|
|
116
|
+
// --file-id <str> task deliver 文件交付(与 --result 互斥)· M7 新增
|
|
117
|
+
// --kind <str> task request-upload 附件类型(attachment|deliverable)· M7 新增
|
|
118
|
+
// --mime-type <str> task request-upload 文件 MIME 类型 · M7 新增
|
|
119
|
+
// --size-bytes <int> task request-upload 文件大小(字节)· M7 新增
|
|
120
|
+
// --progress <int> task heartbeat 进度 0-100
|
|
121
|
+
// --note <str> task heartbeat 进度说明 / task revise 改稿备注
|
|
122
|
+
// --reason <str> task reject / task fail / task cancel 原因
|
|
123
|
+
// --idempotency-key <str> 所有 POST 的幂等键(省略则每次随机 UUID · 用于重试时复用同 key)
|
|
124
|
+
// --error-code <str> task fail(旧名)错误码:并入放弃说明
|
|
125
|
+
// --error-message <str> task fail(旧名)错误信息:并入放弃说明
|
|
126
|
+
// --ref <str> task mark 要标记的要求(rev:<id> / sup:<id> / ans:<id>:<题号>)
|
|
127
|
+
// --verdict <str> task mark 结论:adopt | partial | reject
|
|
128
|
+
// --adopted-text <str> task mark partial:采纳的那一段
|
|
129
|
+
// --rejected-text <str> task mark partial:不采纳的那一段
|
|
130
|
+
// --quote-cents <int> task mark(补充 + reject + out_of_scope):追加单报价(分)
|
|
131
|
+
// --quote-scope <str> task mark:报价对应的工作范围
|
|
132
|
+
// --quote-reason <str> task apply:加价理由(报价高于原价时必填·1-60 字);task apply 的 --quote-cents 是整单成交价
|
|
133
|
+
// --agent-id <str> capability create 指定归属 agent
|
|
134
|
+
// --deliverable-boundary <str> capability create 交付物边界说明(可空·草稿阶段;上架前必填)
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 拆 "题号=值" 形式的参数(--pick 1=A / --text 2=文字)。只切第一个等号。
|
|
138
|
+
* @param {string} str
|
|
139
|
+
* @returns {{q:number, value:string}|null} 格式不对(题号非正整数 / 值为空)→ null
|
|
140
|
+
*/
|
|
141
|
+
export function parseAssign(str) {
|
|
142
|
+
const i = typeof str === 'string' ? str.indexOf('=') : -1;
|
|
143
|
+
if (i <= 0) return null;
|
|
144
|
+
const qStr = str.slice(0, i).trim();
|
|
145
|
+
const value = str.slice(i + 1);
|
|
146
|
+
if (!/^[0-9]+$/.test(qStr) || Number(qStr) < 1 || value.trim() === '') return null;
|
|
147
|
+
return { q: Number(qStr), value };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @param {string[]} argv 不含 node/脚本名的纯参数数组(process.argv.slice(2))
|
|
152
|
+
* @returns {{command:string, flags:object, positionals:string[]}}
|
|
153
|
+
*/
|
|
154
|
+
export function parseArgs(argv) {
|
|
155
|
+
// flags:布尔开关 + 字符串值开关
|
|
156
|
+
const flags = {
|
|
157
|
+
json: false,
|
|
158
|
+
noWait: false,
|
|
159
|
+
code: null, // auth login --code <接入码>(v0.24.0)
|
|
160
|
+
product: null, // auth login --product <文字>:你的 Agent 是哪款产品(不是背后的模型)
|
|
161
|
+
resume: false, // auth login --resume:取回 --no-wait 发起的请求结果
|
|
162
|
+
profile: null, // --profile <名>:同机多 Agent 隔离(凭证/游标按 profile 分目录)
|
|
163
|
+
list: false, // qa --list
|
|
164
|
+
message: null, // task apply / qa post
|
|
165
|
+
replyTo: null, // qa post --reply-to <comment id>
|
|
166
|
+
result: null, // task deliver --result
|
|
167
|
+
progress: null, // task heartbeat --progress
|
|
168
|
+
note: null, // task heartbeat --note / task revise --note
|
|
169
|
+
models: null, // deliver (all three ways) --models "<model,model>":交付时顺带说明用了哪些模型(选填)
|
|
170
|
+
tokens: null, // deliver --tokens <约数>:大约用了多少 token(选填·不知道就不写)
|
|
171
|
+
reason: null, // task reject / task fail / task cancel
|
|
172
|
+
idempotencyKey: null, // 所有 POST --idempotency-key
|
|
173
|
+
applicationId: null, // task accept --application-id(必填)
|
|
174
|
+
errorCode: null, // task fail --error-code
|
|
175
|
+
errorMessage: null, // task fail --error-message
|
|
176
|
+
agentId: null, // capability create --agent-id
|
|
177
|
+
deliverableBoundary: null, // capability create --deliverable-boundary(M5)
|
|
178
|
+
// v0.20.0 M3:采纳标记
|
|
179
|
+
ref: null, // task mark --ref
|
|
180
|
+
verdict: null, // task mark --verdict(adopt|partial|reject)
|
|
181
|
+
adoptedText: null, // task mark --adopted-text
|
|
182
|
+
rejectedText: null, // task mark --rejected-text
|
|
183
|
+
quoteCents: null, // task mark --quote-cents(整数·分)
|
|
184
|
+
quoteScope: null, // task mark --quote-scope
|
|
185
|
+
quoteReason: null, // task apply --quote-reason (why the price is raised)
|
|
186
|
+
// M7 新增 flag
|
|
187
|
+
task: null, // deliver upload --task
|
|
188
|
+
web: null, // deliver upload --web
|
|
189
|
+
fileId: null, // task deliver --file-id(文件交付·与 --result 互斥)
|
|
190
|
+
kind: null, // task request-upload --kind(attachment|deliverable)
|
|
191
|
+
mimeType: null, // task request-upload --mime-type
|
|
192
|
+
sizeBytes: null, // task request-upload --size-bytes(整数·字节数)
|
|
193
|
+
description: null, // capability create --description
|
|
194
|
+
type: null, // capability create --type
|
|
195
|
+
category: null, // capability create --category
|
|
196
|
+
tags: null, // capability create --tags (逗号分隔)
|
|
197
|
+
priceCents: null, // capability create --price-cents(兼容保留·已弃用)
|
|
198
|
+
price: null, // capability create --price(元·面向 agent 的友好单位)
|
|
199
|
+
skills: null, // capability report-skills --skills(JSON 数组字符串)
|
|
200
|
+
skillsFile: null, // capability report-skills --skills-file(JSON 文件路径)
|
|
201
|
+
title: null, // task create --title / capability create --title
|
|
202
|
+
taskTitle: null, // task create --title(同 title)
|
|
203
|
+
bountyCents: null, // task create --bounty-cents
|
|
204
|
+
deliveryHours: null, // task create --delivery-hours
|
|
205
|
+
taskTags: null, // task create --tags (逗号分隔)
|
|
206
|
+
// M2 新增 flag
|
|
207
|
+
file: null, // task add-attachment --file(本地文件路径)
|
|
208
|
+
files: [], // deliver upload --file 可重复(每个 --file 都收进来;file 仍是最后一个)
|
|
209
|
+
visibility: null, // task add-attachment --visibility(public|accepted-only)
|
|
210
|
+
// 5c 新增:task rate 评价
|
|
211
|
+
stars: null, // task rate --stars(1-5 · 星级评分)
|
|
212
|
+
comment: null, // task rate --comment(评语·可选)
|
|
213
|
+
// V0.12.0 M6 新增:task create 邀请参数(对接 M3 邀请字段)
|
|
214
|
+
invite: null, // task create --invite <user_id>(可重复·数组·上限 5,后端校验)
|
|
215
|
+
inviteOnly: false, // task create --invite-only(仅受邀商家可接单)
|
|
216
|
+
autoRedact: false, // task create --auto-redact(v0.23.25:发单时自动保护高置信度的姓名/单位名等)
|
|
217
|
+
autoAccept: false, // poll --auto-accept(v0.19.0:被选中后自动 accept+start)
|
|
218
|
+
exclude: null, // poll --exclude <词1,词2>(自动接单的排除词)
|
|
219
|
+
skipOwn: false, // poll --skip-own(v0.23.31:不要我自己发布的任务的事件)
|
|
220
|
+
now: false, // poll --now(v0.11.2:忽略平台建议的等待时间,立刻看一次)
|
|
221
|
+
// v0.17.0 M2-B:协作沟通
|
|
222
|
+
card: null, // task answer --card <card_id>(缺省取最早未答卡)
|
|
223
|
+
after: null, // task collab --after <id>(整数,只拉该编号之后的记录)
|
|
224
|
+
pick: [], // task answer --pick <题号>=<选项key>(可重复)
|
|
225
|
+
text: [], // task answer --text <题号>=<文字>(可重复)
|
|
226
|
+
// v0.18.0 M3:主页介绍
|
|
227
|
+
stdin: false, // set-profile-intro --stdin(从标准输入读正文)
|
|
228
|
+
clear: false, // set-profile-intro --clear(清空主页介绍)
|
|
229
|
+
out: null, // get-profile-intro --out <路径>(把正文写成文件)
|
|
230
|
+
media: false, // get-profile-intro --media(列出可引用的平台媒体)
|
|
231
|
+
// v0.19.0 M6:本地媒体上传的两个确认开关
|
|
232
|
+
allowOutside: false, // media upload --allow-outside(允许当前目录之外的文件)
|
|
233
|
+
keepLocation: false, // media upload / set-profile-intro --file --keep-location(允许带 GPS 的 JPEG)
|
|
234
|
+
// v0.22.0 M6: Libra dispute ruling commands (all value flags default to null / empty list)
|
|
235
|
+
task: null, // libra * --task <task-id>
|
|
236
|
+
claim: null, // libra quote --claim full|partial:<pct>|libra
|
|
237
|
+
evidence: [], // libra quote / statement / review quote --evidence <file-id> (repeatable)
|
|
238
|
+
pages: [], // libra quote --pages <file-id>:<page ranges> (repeatable)
|
|
239
|
+
minutes: [], // libra quote --minutes <file-id>:<from-to> (repeatable)
|
|
240
|
+
quote: null, // libra file / review file --quote <quote-id>
|
|
241
|
+
confirmAmount: null, // libra file / review file --confirm-amount <yuan>
|
|
242
|
+
statement: null, // libra file --statement "text"
|
|
243
|
+
statementFile: null, // libra file --statement-file <path>
|
|
244
|
+
textFile: null, // libra statement / review file --text-file <path>
|
|
245
|
+
question: null, // libra answer --question <q-id>
|
|
246
|
+
round: null, // libra ruling --round 1|2
|
|
247
|
+
points: null, // libra review quote / file --points 1,3
|
|
248
|
+
// P19 batch: review tags, extra-revision requests, early close of a Libra case
|
|
249
|
+
reviewTags: [], // task rate --tag <key> (repeatable; commas also split)
|
|
250
|
+
caseId: null, // libra concede / settle * --case <case-id> (alternative to --task)
|
|
251
|
+
ask: null, // task continue --ask "what to change this time"
|
|
252
|
+
to: null, // task continue --to orig|public|invite
|
|
253
|
+
base: null, // task continue --base public|accepted-only
|
|
254
|
+
budget: null, // task continue --budget <yuan>
|
|
255
|
+
yes: false, // libra settle accept --yes (kept for the hint wording; --expect-pct is what is sent)
|
|
256
|
+
pct: null, // libra settle propose --pct <refund percentage>
|
|
257
|
+
expectPct: null, // libra concede / settle accept / refund respond|reply --expect-pct <percentage you read>
|
|
258
|
+
action: null, // refund respond|reply --action agree|offer|hold / accept|ask
|
|
259
|
+
offer: null, // libra settle accept|reject|withdraw --offer <offer-id> (default: the pending offer)
|
|
260
|
+
feeCents: null, // task extension request|respond --fee-cents <cents>
|
|
261
|
+
deferDays: null, // task extension request|respond --defer-days <days> (old form, server converts days * 24)
|
|
262
|
+
deferHours: null, // task extension request|respond --defer-hours <hours> (authoritative since the server's v0.24.22)
|
|
263
|
+
decision: null, // task extension respond|confirm --decision <word>
|
|
264
|
+
};
|
|
265
|
+
const positionals = [];
|
|
266
|
+
|
|
267
|
+
const args = [...argv];
|
|
268
|
+
while (args.length > 0) {
|
|
269
|
+
const a = args.shift();
|
|
270
|
+
if (a === '--json') {
|
|
271
|
+
flags.json = true;
|
|
272
|
+
} else if (a === '--no-wait') {
|
|
273
|
+
flags.noWait = true;
|
|
274
|
+
} else if (a === '--code') {
|
|
275
|
+
flags.code = args.shift() ?? null;
|
|
276
|
+
} else if (a === '--product') {
|
|
277
|
+
flags.product = args.shift() ?? null;
|
|
278
|
+
} else if (a === '--resume') {
|
|
279
|
+
flags.resume = true;
|
|
280
|
+
} else if (a === '--list') {
|
|
281
|
+
flags.list = true;
|
|
282
|
+
} else if (a === '--profile') {
|
|
283
|
+
// 同机多 Agent:本命令使用指定 profile 的凭证(含 auth login)
|
|
284
|
+
flags.profile = args.shift() ?? null;
|
|
285
|
+
} else if (a === '--reply-to') {
|
|
286
|
+
flags.replyTo = args.shift() ?? null;
|
|
287
|
+
} else if (a === '--message') {
|
|
288
|
+
flags.message = args.shift() ?? null;
|
|
289
|
+
} else if (a === '--result') {
|
|
290
|
+
flags.result = args.shift() ?? null;
|
|
291
|
+
} else if (a === '--progress') {
|
|
292
|
+
const v = args.shift();
|
|
293
|
+
flags.progress = v != null ? parseInt(v, 10) : null;
|
|
294
|
+
} else if (a === '--note') {
|
|
295
|
+
flags.note = args.shift() ?? null;
|
|
296
|
+
} else if (a === '--models') {
|
|
297
|
+
flags.models = args.shift() ?? null;
|
|
298
|
+
} else if (a === '--tokens') {
|
|
299
|
+
const v = args.shift();
|
|
300
|
+
const n = v != null ? parseInt(v, 10) : NaN;
|
|
301
|
+
flags.tokens = Number.isFinite(n) && n > 0 ? n : null;
|
|
302
|
+
} else if (a === '--reason') {
|
|
303
|
+
flags.reason = args.shift() ?? null;
|
|
304
|
+
} else if (a === '--idempotency-key') {
|
|
305
|
+
flags.idempotencyKey = args.shift() ?? null;
|
|
306
|
+
} else if (a === '--application-id') {
|
|
307
|
+
flags.applicationId = args.shift() ?? null;
|
|
308
|
+
} else if (a === '--error-code') {
|
|
309
|
+
flags.errorCode = args.shift() ?? null;
|
|
310
|
+
} else if (a === '--error-message') {
|
|
311
|
+
flags.errorMessage = args.shift() ?? null;
|
|
312
|
+
} else if (a === '--ref') {
|
|
313
|
+
flags.ref = args.shift() ?? null;
|
|
314
|
+
} else if (a === '--verdict') {
|
|
315
|
+
flags.verdict = args.shift() ?? null;
|
|
316
|
+
} else if (a === '--adopted-text') {
|
|
317
|
+
flags.adoptedText = args.shift() ?? null;
|
|
318
|
+
} else if (a === '--rejected-text') {
|
|
319
|
+
flags.rejectedText = args.shift() ?? null;
|
|
320
|
+
} else if (a === '--quote-cents') {
|
|
321
|
+
// non-digit input becomes NaN; index.js reports it as an argument error
|
|
322
|
+
const v = args.shift();
|
|
323
|
+
flags.quoteCents = v != null ? (/^[0-9]+$/.test(v) ? parseInt(v, 10) : NaN) : null;
|
|
324
|
+
} else if (a === '--quote-reason') {
|
|
325
|
+
flags.quoteReason = args.shift() ?? null;
|
|
326
|
+
} else if (a === '--quote-scope') {
|
|
327
|
+
flags.quoteScope = args.shift() ?? null;
|
|
328
|
+
} else if (a === '--agent-id') {
|
|
329
|
+
flags.agentId = args.shift() ?? null;
|
|
330
|
+
} else if (a === '--deliverable-boundary') {
|
|
331
|
+
flags.deliverableBoundary = args.shift() ?? null;
|
|
332
|
+
} else if (a === '--file-id') {
|
|
333
|
+
// M7:task deliver 文件交付(与 --result 互斥)
|
|
334
|
+
flags.fileId = args.shift() ?? null;
|
|
335
|
+
} else if (a === '--kind') {
|
|
336
|
+
// M7:task request-upload 附件类型(attachment|deliverable)
|
|
337
|
+
flags.kind = args.shift() ?? null;
|
|
338
|
+
} else if (a === '--mime-type') {
|
|
339
|
+
// M7:task request-upload MIME 类型
|
|
340
|
+
flags.mimeType = args.shift() ?? null;
|
|
341
|
+
} else if (a === '--size-bytes') {
|
|
342
|
+
// M7:task request-upload 文件大小(字节·整数)
|
|
343
|
+
const v = args.shift();
|
|
344
|
+
flags.sizeBytes = v != null ? parseInt(v, 10) : null;
|
|
345
|
+
} else if (a === '--description') {
|
|
346
|
+
flags.description = args.shift() ?? null;
|
|
347
|
+
} else if (a === '--type') {
|
|
348
|
+
flags.type = args.shift() ?? null;
|
|
349
|
+
} else if (a === '--category') {
|
|
350
|
+
flags.category = args.shift() ?? null;
|
|
351
|
+
} else if (a === '--tags') {
|
|
352
|
+
const v = args.shift() ?? '';
|
|
353
|
+
// 兼容:task create 和 capability create 都用 --tags
|
|
354
|
+
// 第一次遇到存入 tags,第二次(理论上不会)也存
|
|
355
|
+
flags.tags = v.split(',').map(t => t.trim()).filter(Boolean);
|
|
356
|
+
} else if (a === '--price-cents') {
|
|
357
|
+
const v = args.shift();
|
|
358
|
+
flags.priceCents = v != null ? parseInt(v, 10) : null;
|
|
359
|
+
} else if (a === '--price') {
|
|
360
|
+
// 面向 agent 的友好单位:元(浮点)。index.js 里换算成分。
|
|
361
|
+
const v = args.shift();
|
|
362
|
+
flags.price = v != null ? parseFloat(v) : null;
|
|
363
|
+
} else if (a === '--skills') {
|
|
364
|
+
// capability report-skills:JSON 数组字符串 [{"name","description"}]
|
|
365
|
+
flags.skills = args.shift() ?? null;
|
|
366
|
+
} else if (a === '--skills-file') {
|
|
367
|
+
flags.skillsFile = args.shift() ?? null;
|
|
368
|
+
} else if (a === '--title') {
|
|
369
|
+
flags.title = args.shift() ?? null;
|
|
370
|
+
} else if (a === '--bounty-cents') {
|
|
371
|
+
const v = args.shift();
|
|
372
|
+
flags.bountyCents = v != null ? parseInt(v, 10) : null;
|
|
373
|
+
} else if (a === '--delivery-hours') {
|
|
374
|
+
const v = args.shift();
|
|
375
|
+
flags.deliveryHours = v != null ? parseInt(v, 10) : null;
|
|
376
|
+
} else if (a === '--task') {
|
|
377
|
+
// web hosting: deliver upload --task <task_id>
|
|
378
|
+
flags.task = args.shift() ?? null;
|
|
379
|
+
} else if (a === '--web') {
|
|
380
|
+
// web hosting: deliver upload --web <html or zip path>
|
|
381
|
+
flags.web = args.shift() ?? null;
|
|
382
|
+
} else if (a === '--file') {
|
|
383
|
+
// M2:task add-attachment 本地文件路径;deliver upload --file 可重复(一次交付多个文件)
|
|
384
|
+
flags.file = args.shift() ?? null;
|
|
385
|
+
if (flags.file != null) flags.files.push(flags.file);
|
|
386
|
+
} else if (a === '--visibility') {
|
|
387
|
+
// M2:task add-attachment 附件可见性(public|accepted-only)
|
|
388
|
+
flags.visibility = args.shift() ?? null;
|
|
389
|
+
} else if (a === '--stars') {
|
|
390
|
+
// 5c:task rate 星级评分(1-5)
|
|
391
|
+
const v = args.shift();
|
|
392
|
+
flags.stars = v != null ? parseInt(v, 10) : null;
|
|
393
|
+
} else if (a === '--comment') {
|
|
394
|
+
// 5c:task rate 评语(可选)
|
|
395
|
+
flags.comment = args.shift() ?? null;
|
|
396
|
+
} else if (a === '--invite') {
|
|
397
|
+
// V0.12.0 M6:task create --invite <user_id>(可重复多次·对接 M3 邀请字段·上限 5·后端校验)
|
|
398
|
+
const v = args.shift() ?? null;
|
|
399
|
+
if (v) {
|
|
400
|
+
if (!flags.invite) flags.invite = [];
|
|
401
|
+
flags.invite.push(v);
|
|
402
|
+
}
|
|
403
|
+
} else if (a === '--card') {
|
|
404
|
+
// v0.17.0 M2-B:task answer 指定提问卡
|
|
405
|
+
flags.card = args.shift() ?? null;
|
|
406
|
+
} else if (a === '--after') {
|
|
407
|
+
// v0.17.0 M2-B:task collab 增量游标(非数字 → NaN,由 index.js 报参数错)
|
|
408
|
+
const v = args.shift();
|
|
409
|
+
flags.after = v != null ? parseInt(v, 10) : null;
|
|
410
|
+
} else if (a === '--pick') {
|
|
411
|
+
// v0.17.0 M2-B:可重复,原样存字符串,index.js 用 parseAssign 拆
|
|
412
|
+
const v = args.shift();
|
|
413
|
+
if (v != null) flags.pick.push(v);
|
|
414
|
+
} else if (a === '--text') {
|
|
415
|
+
const v = args.shift();
|
|
416
|
+
if (v != null) flags.text.push(v);
|
|
417
|
+
} else if (a === '--stdin') {
|
|
418
|
+
// v0.18.0 M3:set-profile-intro 从标准输入读正文
|
|
419
|
+
flags.stdin = true;
|
|
420
|
+
} else if (a === '--clear') {
|
|
421
|
+
// v0.18.0 M3:set-profile-intro 清空主页介绍(与给正文互斥)
|
|
422
|
+
flags.clear = true;
|
|
423
|
+
} else if (a === '--out') {
|
|
424
|
+
// v0.18.0 M3:get-profile-intro 把正文写到这个文件
|
|
425
|
+
flags.out = args.shift() ?? null;
|
|
426
|
+
} else if (a === '--media') {
|
|
427
|
+
// v0.18.0 M3:get-profile-intro --media 列出可引用的平台媒体
|
|
428
|
+
flags.media = true;
|
|
429
|
+
} else if (a === '--allow-outside') {
|
|
430
|
+
flags.allowOutside = true;
|
|
431
|
+
} else if (a === '--keep-location') {
|
|
432
|
+
flags.keepLocation = true;
|
|
433
|
+
} else if (a === '--auto-accept') {
|
|
434
|
+
flags.autoAccept = true;
|
|
435
|
+
} else if (a === '--skip-own') {
|
|
436
|
+
flags.skipOwn = true;
|
|
437
|
+
} else if (a === '--now') {
|
|
438
|
+
flags.now = true;
|
|
439
|
+
} else if (a === '--exclude') {
|
|
440
|
+
flags.exclude = (args.shift() ?? '').split(/[,,]/).map((x) => x.trim()).filter(Boolean);
|
|
441
|
+
} else if (a === '--task') {
|
|
442
|
+
flags.task = args.shift() ?? null;
|
|
443
|
+
} else if (a === '--claim') {
|
|
444
|
+
flags.claim = args.shift() ?? null;
|
|
445
|
+
} else if (a === '--evidence') {
|
|
446
|
+
const v = args.shift();
|
|
447
|
+
if (v != null) flags.evidence.push(v);
|
|
448
|
+
} else if (a === '--pages') {
|
|
449
|
+
const v = args.shift();
|
|
450
|
+
if (v != null) flags.pages.push(v);
|
|
451
|
+
} else if (a === '--minutes') {
|
|
452
|
+
const v = args.shift();
|
|
453
|
+
if (v != null) flags.minutes.push(v);
|
|
454
|
+
} else if (a === '--quote') {
|
|
455
|
+
flags.quote = args.shift() ?? null;
|
|
456
|
+
} else if (a === '--confirm-amount') {
|
|
457
|
+
flags.confirmAmount = args.shift() ?? null;
|
|
458
|
+
} else if (a === '--statement') {
|
|
459
|
+
flags.statement = args.shift() ?? null;
|
|
460
|
+
} else if (a === '--statement-file') {
|
|
461
|
+
flags.statementFile = args.shift() ?? null;
|
|
462
|
+
} else if (a === '--text-file') {
|
|
463
|
+
flags.textFile = args.shift() ?? null;
|
|
464
|
+
} else if (a === '--question') {
|
|
465
|
+
flags.question = args.shift() ?? null;
|
|
466
|
+
} else if (a === '--round') {
|
|
467
|
+
flags.round = args.shift() ?? null;
|
|
468
|
+
} else if (a === '--points') {
|
|
469
|
+
flags.points = args.shift() ?? null;
|
|
470
|
+
} else if (a === '--tag') {
|
|
471
|
+
// P19: review tag keys; repeatable, a comma list is accepted too
|
|
472
|
+
const v = args.shift() ?? '';
|
|
473
|
+
for (const t of v.split(',').map((x) => x.trim()).filter(Boolean)) flags.reviewTags.push(t);
|
|
474
|
+
} else if (a === '--case') {
|
|
475
|
+
flags.caseId = args.shift() ?? null;
|
|
476
|
+
} else if (a === '--ask') {
|
|
477
|
+
flags.ask = args.shift() ?? null;
|
|
478
|
+
} else if (a === '--to') {
|
|
479
|
+
flags.to = args.shift() ?? null;
|
|
480
|
+
} else if (a === '--base') {
|
|
481
|
+
flags.base = args.shift() ?? null;
|
|
482
|
+
} else if (a === '--budget') {
|
|
483
|
+
flags.budget = args.shift() ?? null;
|
|
484
|
+
} else if (a === '--yes') {
|
|
485
|
+
flags.yes = true;
|
|
486
|
+
} else if (a === '--pct') {
|
|
487
|
+
flags.pct = args.shift() ?? null;
|
|
488
|
+
} else if (a === '--expect-pct') {
|
|
489
|
+
flags.expectPct = args.shift() ?? null;
|
|
490
|
+
} else if (a === '--action') {
|
|
491
|
+
flags.action = args.shift() ?? null;
|
|
492
|
+
} else if (a === '--offer') {
|
|
493
|
+
flags.offer = args.shift() ?? null;
|
|
494
|
+
} else if (a === '--fee-cents') {
|
|
495
|
+
// non-digit input becomes NaN; the command reports it as an argument error
|
|
496
|
+
const v = args.shift();
|
|
497
|
+
flags.feeCents = v != null ? (/^[0-9]+$/.test(v) ? parseInt(v, 10) : NaN) : null;
|
|
498
|
+
} else if (a === '--defer-days') {
|
|
499
|
+
const v = args.shift();
|
|
500
|
+
flags.deferDays = v != null ? (/^[0-9]+$/.test(v) ? parseInt(v, 10) : NaN) : null;
|
|
501
|
+
} else if (a === '--defer-hours') {
|
|
502
|
+
const v = args.shift();
|
|
503
|
+
flags.deferHours = v != null ? (/^[0-9]+$/.test(v) ? parseInt(v, 10) : NaN) : null;
|
|
504
|
+
} else if (a === '--decision') {
|
|
505
|
+
flags.decision = args.shift() ?? null;
|
|
506
|
+
} else if (a === '--invite-only') {
|
|
507
|
+
// V0.12.0 M6:task create --invite-only(布尔开关·仅受邀商家可接单)
|
|
508
|
+
flags.inviteOnly = true;
|
|
509
|
+
} else if (a === '--auto-redact') {
|
|
510
|
+
// v0.23.25: task create --auto-redact (boolean switch, off by default)
|
|
511
|
+
flags.autoRedact = true;
|
|
512
|
+
} else if (a.startsWith('--')) {
|
|
513
|
+
// 未知开关:暂忽略
|
|
514
|
+
} else {
|
|
515
|
+
positionals.push(a);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const [c0, c1, c2, c3] = positionals;
|
|
520
|
+
let command;
|
|
521
|
+
let aliasOf = null; // set when the typed command is an old name of a renamed one
|
|
522
|
+
|
|
523
|
+
if (!c0) {
|
|
524
|
+
command = 'help';
|
|
525
|
+
} else if (c0 === 'auth' && c1 === 'login') {
|
|
526
|
+
command = 'auth:login';
|
|
527
|
+
} else if (matchRetired(positionals)) {
|
|
528
|
+
// v0.15.1:钱包 / 部分接受已下线,由 index.js 按 retiredKey 给友好提示
|
|
529
|
+
command = 'retired';
|
|
530
|
+
} else if (c0 === 'hall' && (c1 === 'browse' || c1 === undefined)) {
|
|
531
|
+
command = 'hall:browse';
|
|
532
|
+
} else if (c0 === 'ping') {
|
|
533
|
+
command = 'ping';
|
|
534
|
+
} else if (c0 === 'task') {
|
|
535
|
+
switch (c1) {
|
|
536
|
+
// M7 新增:任务信息查询 + 文件能力
|
|
537
|
+
case 'show': command = 'task:show'; break;
|
|
538
|
+
case 'files': command = 'task:files'; break;
|
|
539
|
+
case 'download': command = 'task:download'; break;
|
|
540
|
+
case 'request-upload': command = 'task:request-upload'; break;
|
|
541
|
+
case 'add-attachment': command = 'task:add-attachment'; break; // M2 新增
|
|
542
|
+
case 'set-visibility': command = 'task:set-visibility'; break; // v0.4.5 新增
|
|
543
|
+
// 原有生命周期命令
|
|
544
|
+
case 'create': command = 'task:create'; break;
|
|
545
|
+
case 'continue': command = 'task:continue'; break; // v0.24.0: continue from accepted work
|
|
546
|
+
case 'apply': command = 'task:apply'; break;
|
|
547
|
+
case 'pass': command = 'task:pass'; break;
|
|
548
|
+
case 'select': command = 'task:select'; break;
|
|
549
|
+
case 'accept': command = 'task:accept'; break;
|
|
550
|
+
case 'start': command = 'task:start'; break;
|
|
551
|
+
case 'accept-start': command = 'task:accept-start'; break;
|
|
552
|
+
case 'accept-invited': command = 'task:accept-invited'; break; // v0.11.3: invited seller accepts directly (no apply/select)
|
|
553
|
+
case 'heartbeat': command = 'task:heartbeat'; break;
|
|
554
|
+
case 'deliver': command = 'task:deliver'; break;
|
|
555
|
+
// v0.20.0: `task fail` is the old name of `task abandon` (not retired: it still works, exit code 0)
|
|
556
|
+
case 'fail': command = 'task:abandon'; aliasOf = 'task fail'; break;
|
|
557
|
+
case 'abandon': command = 'task:abandon'; break;
|
|
558
|
+
case 'mark': command = 'task:mark'; break;
|
|
559
|
+
case 'baseline': command = 'task:baseline'; break;
|
|
560
|
+
case 'decline-revision': command = 'task:decline-revision'; break;
|
|
561
|
+
case 'end-revisions': command = 'task:end-revisions'; break;
|
|
562
|
+
case 'confirm': command = 'task:confirm'; break;
|
|
563
|
+
case 'reject': command = 'task:reject'; break;
|
|
564
|
+
case 'revise': command = 'task:revise'; break;
|
|
565
|
+
case 'cancel': command = 'task:cancel'; break;
|
|
566
|
+
case 'rate': command = 'task:rate'; break; // 5c 新增:评价
|
|
567
|
+
// v0.17.0 M2-B:执行期协作沟通
|
|
568
|
+
case 'ask': command = 'task:ask'; break;
|
|
569
|
+
case 'answer': command = 'task:answer'; break;
|
|
570
|
+
case 'note': command = 'task:note'; break;
|
|
571
|
+
case 'collab': command = 'task:collab'; break;
|
|
572
|
+
// P19: ask the seller for one more revision round (sub-command is the 3rd positional)
|
|
573
|
+
case 'extension': command = 'task:extension'; break;
|
|
574
|
+
default: command = 'unknown';
|
|
575
|
+
}
|
|
576
|
+
} else if (c0 === 'capability') {
|
|
577
|
+
switch (c1) {
|
|
578
|
+
case 'create': command = 'capability:create'; break;
|
|
579
|
+
case 'publish': command = 'capability:publish'; break;
|
|
580
|
+
case 'report-skills': command = 'capability:report-skills'; break;
|
|
581
|
+
default: command = 'unknown';
|
|
582
|
+
}
|
|
583
|
+
} else if (c0 === 'qa') {
|
|
584
|
+
// qa <task_id> <message> 或 qa <task_id> --list
|
|
585
|
+
command = flags.list ? 'qa:list' : 'qa:post';
|
|
586
|
+
} else if (c0 === 'autonomy' && c1 === 'tick') {
|
|
587
|
+
command = 'autonomy:tick';
|
|
588
|
+
} else if (c0 === 'user') {
|
|
589
|
+
// v0.20.0 M3: block / unblock another user on every channel
|
|
590
|
+
command = c1 === 'block' ? 'user:block' : (c1 === 'unblock' ? 'user:unblock' : 'unknown');
|
|
591
|
+
} else if (c0 === 'limits') {
|
|
592
|
+
command = 'limits';
|
|
593
|
+
} else if (c0 === 'poll') {
|
|
594
|
+
// M1 v0.10.0:盯单长轮询,无子命令,位置参数只有 'poll' 本身
|
|
595
|
+
command = 'poll';
|
|
596
|
+
} else if (c0 === 'libra') {
|
|
597
|
+
// v0.22.0 M6: stelfield libra quote|file|status|statement|answer|ruling|accept|review quote|review file
|
|
598
|
+
if (c1 === 'review') {
|
|
599
|
+
command = c2 === 'quote' ? 'libra:review-quote' : c2 === 'file' ? 'libra:review-file' : 'unknown';
|
|
600
|
+
} else if (c1 === 'settle') {
|
|
601
|
+
// P19: stelfield libra settle propose|accept|reject|withdraw
|
|
602
|
+
command = ['propose', 'accept', 'reject', 'withdraw'].includes(c2) ? `libra:settle-${c2}` : 'unknown';
|
|
603
|
+
} else if (['quote', 'file', 'status', 'statement', 'answer', 'ruling', 'accept', 'withdraw', 'concede'].includes(c1)) {
|
|
604
|
+
command = `libra:${c1}`;
|
|
605
|
+
} else {
|
|
606
|
+
command = 'unknown';
|
|
607
|
+
}
|
|
608
|
+
} else if (c0 === 'refund') {
|
|
609
|
+
// refund negotiation: stelfield refund status|respond|reply|start|cancel
|
|
610
|
+
command = ['status', 'respond', 'reply', 'start', 'cancel'].includes(c1) ? `refund:${c1}` : 'unknown';
|
|
611
|
+
} else if (c0 === 'profiles') {
|
|
612
|
+
// 列出本机已登录的 profile(同机多 Agent 总览)
|
|
613
|
+
command = 'profiles';
|
|
614
|
+
} else if (c0 === 'agent') {
|
|
615
|
+
switch (c1) {
|
|
616
|
+
case 'set-intro': command = 'agent:set-intro'; break; // V0.12.0 M6
|
|
617
|
+
default: command = 'unknown';
|
|
618
|
+
}
|
|
619
|
+
} else if (c0 === 'deliver') {
|
|
620
|
+
// web hosting: stelfield deliver upload --task <id> --web <path>
|
|
621
|
+
command = c1 === 'upload' ? 'deliver:upload' : 'unknown';
|
|
622
|
+
} else if (c0 === 'media') {
|
|
623
|
+
// v0.19.0 M3: stelfield media upload <file...>
|
|
624
|
+
command = c1 === 'upload' ? 'media:upload' : 'unknown';
|
|
625
|
+
} else if (c0 === 'set-profile-intro') {
|
|
626
|
+
// v0.18.0 M3:写主页介绍。不用 profile 分组,避免与 --profile 凭证目录 / profiles 命令混淆。
|
|
627
|
+
command = 'set-profile-intro';
|
|
628
|
+
} else if (c0 === 'get-profile-intro') {
|
|
629
|
+
// v0.18.0 M3:读主页介绍
|
|
630
|
+
command = 'get-profile-intro';
|
|
631
|
+
} else if (c0 === 'help' || c0 === '--help' || c0 === '-h') {
|
|
632
|
+
command = 'help';
|
|
633
|
+
} else {
|
|
634
|
+
command = 'unknown';
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// raw 保留向后兼容(旧测试用 r.raw 引用位置参数)
|
|
638
|
+
return { command, flags, positionals, raw: positionals, aliasOf, retiredKey: command === 'retired' ? matchRetired(positionals) : null };
|
|
639
|
+
}
|