@yizhuan-cli/cli 0.1.3 → 0.1.4
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 +35 -11
- package/package.json +4 -2
- package/src/index.js +246 -27
- package/src/params.test.js +47 -0
- package/src/version.test.js +22 -0
package/README.md
CHANGED
|
@@ -34,23 +34,47 @@ yizhuan config path
|
|
|
34
34
|
yizhuan config show
|
|
35
35
|
yizhuan execute --ability membership
|
|
36
36
|
yizhuan execute --ability works --query "百家号昨天新增了哪些作品"
|
|
37
|
-
yizhuan execute --ability
|
|
38
|
-
yizhuan execute --ability
|
|
39
|
-
yizhuan execute --ability
|
|
40
|
-
yizhuan execute --ability radar --params
|
|
37
|
+
yizhuan execute --ability hot_topics --platform douyin
|
|
38
|
+
yizhuan execute --ability hot_topics --params '{"platform":"douyin"}'
|
|
39
|
+
yizhuan execute --ability accounts --params '{"mode":"public_search","platform":"toutiao","keyword":"张三"}'
|
|
40
|
+
yizhuan execute --ability radar --params '{"mode":"following"}'
|
|
41
|
+
yizhuan execute --ability radar --mode timeline --timeRange 7d
|
|
42
|
+
yizhuan execute --ability radar --params '{"mode":"unfollow","articleId":"205169870"}'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### PowerShell 参数说明
|
|
46
|
+
|
|
47
|
+
PowerShell 对 JSON 双引号转义不友好。推荐以下写法:
|
|
48
|
+
|
|
49
|
+
```powershell
|
|
50
|
+
# 最稳妥:独立参数
|
|
51
|
+
yizhuan execute --ability hot_topics --platform douyin
|
|
52
|
+
|
|
53
|
+
# 单引号包 JSON
|
|
54
|
+
yizhuan execute --ability hot_topics --params '{"platform":"douyin"}'
|
|
55
|
+
|
|
56
|
+
# key=value
|
|
57
|
+
yizhuan execute --ability hot_topics --params platform=douyin
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
请避免 bash 风格的:
|
|
61
|
+
|
|
62
|
+
```powershell
|
|
63
|
+
# 在 PowerShell 中容易失败
|
|
64
|
+
yizhuan execute --ability hot_topics --params "{\"platform\":\"douyin\"}"
|
|
41
65
|
```
|
|
42
66
|
|
|
43
67
|
## 支持的典型能力
|
|
44
68
|
|
|
45
69
|
- 作品查询:`yizhuan execute --ability works --query "AI 相关的热门作品有哪些"`
|
|
46
70
|
- 作者查询:`yizhuan execute --ability accounts --query "查询张三这个作者"`
|
|
47
|
-
- 作者全网搜索:`yizhuan execute --ability accounts --params
|
|
48
|
-
- 关注全网作者:`yizhuan execute --ability accounts --params
|
|
49
|
-
- 热点查询:`yizhuan execute --ability hot_topics --
|
|
50
|
-
- 雷达查询:`yizhuan execute --ability radar --params
|
|
51
|
-
- 竞品动态:`yizhuan execute --ability radar --
|
|
52
|
-
- 竞品动态筛选:`yizhuan execute --ability radar --params
|
|
53
|
-
- 取消关注雷达作者:先查询关注列表拿到 `articleId`,再执行 `yizhuan execute --ability radar --params
|
|
71
|
+
- 作者全网搜索:`yizhuan execute --ability accounts --params '{"mode":"public_search","platform":"toutiao","keyword":"张三"}'`
|
|
72
|
+
- 关注全网作者:`yizhuan execute --ability accounts --params '{"mode":"follow_public_author","platform":"toutiao","keyword":"张三","extAuthorId":"author-1"}'`
|
|
73
|
+
- 热点查询:`yizhuan execute --ability hot_topics --platform douyin`
|
|
74
|
+
- 雷达查询:`yizhuan execute --ability radar --params '{"mode":"following"}'`
|
|
75
|
+
- 竞品动态:`yizhuan execute --ability radar --mode timeline --timeRange 7d`
|
|
76
|
+
- 竞品动态筛选:`yizhuan execute --ability radar --params '{"mode":"timeline","timeRange":"7d","platform":"baijiahao","keyword":"AI"}'`
|
|
77
|
+
- 取消关注雷达作者:先查询关注列表拿到 `articleId`,再执行 `yizhuan execute --ability radar --params '{"mode":"unfollow","articleId":"205169870"}'`
|
|
54
78
|
- 会员查询:`yizhuan execute --ability membership --query "我的会员什么时候到期"`
|
|
55
79
|
|
|
56
80
|
如果你是通过 Codex、Cursor、Claude Code 这类 Agent 使用 CLI,也可以直接用自然语言提问,例如:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yizhuan-cli/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "易撰命令行工具,用于通过 API Key 查询易撰真实数据。",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
"scripts": {
|
|
15
15
|
"start": "node src/index.js",
|
|
16
16
|
"pack:dry-run": "npm pack --dry-run",
|
|
17
|
-
"smoke": "node src/index.js --help && node src/
|
|
17
|
+
"smoke": "node src/index.js --help && node --test src/version.test.js",
|
|
18
|
+
"test:params": "node --test src/params.test.js",
|
|
19
|
+
"test:version": "node --test src/version.test.js"
|
|
18
20
|
},
|
|
19
21
|
"engines": {
|
|
20
22
|
"node": ">=22"
|
package/src/index.js
CHANGED
|
@@ -25,22 +25,47 @@ const ABILITIES = new Set([
|
|
|
25
25
|
'favorites_query'
|
|
26
26
|
])
|
|
27
27
|
|
|
28
|
+
const PARAM_FLAG_KEYS = [
|
|
29
|
+
'platform',
|
|
30
|
+
'mode',
|
|
31
|
+
'keyword',
|
|
32
|
+
'page',
|
|
33
|
+
'pageSize',
|
|
34
|
+
'timeRange',
|
|
35
|
+
'date',
|
|
36
|
+
'startDate',
|
|
37
|
+
'endDate',
|
|
38
|
+
'articleId',
|
|
39
|
+
'authorId',
|
|
40
|
+
'extAuthorId',
|
|
41
|
+
'collectionType',
|
|
42
|
+
'metric'
|
|
43
|
+
]
|
|
44
|
+
|
|
28
45
|
function printHelp() {
|
|
29
46
|
console.log(`易撰 CLI
|
|
30
47
|
|
|
31
48
|
Usage:
|
|
32
49
|
yizhuan --help
|
|
33
50
|
yizhuan --version
|
|
34
|
-
yizhuan execute --ability <ability> [--query <text>] [--params <json>]
|
|
51
|
+
yizhuan execute --ability <ability> [--query <text>] [--params <json>] [--platform <name>]
|
|
52
|
+
yizhuan query --json '<request JSON>' [--format json|table|markdown]
|
|
35
53
|
yizhuan config path
|
|
36
54
|
yizhuan config show
|
|
37
55
|
|
|
38
56
|
Examples:
|
|
39
57
|
yizhuan execute --ability works --query "百家号昨天新增了哪些作品"
|
|
40
|
-
yizhuan execute --ability
|
|
41
|
-
yizhuan execute --ability
|
|
42
|
-
yizhuan execute --ability
|
|
43
|
-
yizhuan execute --ability radar --params
|
|
58
|
+
yizhuan execute --ability hot_topics --platform douyin
|
|
59
|
+
yizhuan execute --ability hot_topics --params '{"platform":"douyin"}'
|
|
60
|
+
yizhuan execute --ability accounts --params '{"mode":"public_search","platform":"toutiao","keyword":"张三"}'
|
|
61
|
+
yizhuan execute --ability radar --params '{"mode":"following"}'
|
|
62
|
+
yizhuan execute --ability radar --mode timeline --timeRange 7d
|
|
63
|
+
yizhuan execute --ability radar --params '{"mode":"unfollow","articleId":"205169870"}'
|
|
64
|
+
|
|
65
|
+
Notes:
|
|
66
|
+
- PowerShell 推荐优先使用 --platform / --mode 等独立参数,或单引号 JSON:--params '{"platform":"douyin"}'
|
|
67
|
+
- bash/zsh 也可用:--params '{"platform":"douyin"}' 或 --platform douyin
|
|
68
|
+
- 兼容 key=value:--params platform=douyin
|
|
44
69
|
|
|
45
70
|
Abilities:
|
|
46
71
|
works 作品查询
|
|
@@ -65,22 +90,84 @@ function readPackageVersion() {
|
|
|
65
90
|
}
|
|
66
91
|
}
|
|
67
92
|
|
|
93
|
+
function isBalancedJsonLike(text) {
|
|
94
|
+
let brace = 0
|
|
95
|
+
let bracket = 0
|
|
96
|
+
let quote = null
|
|
97
|
+
let escaped = false
|
|
98
|
+
|
|
99
|
+
for (const char of text) {
|
|
100
|
+
if (escaped) {
|
|
101
|
+
escaped = false
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
if (char === '\\') {
|
|
105
|
+
escaped = true
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
if (quote) {
|
|
109
|
+
if (char === quote) quote = null
|
|
110
|
+
continue
|
|
111
|
+
}
|
|
112
|
+
if (char === '"' || char === "'") {
|
|
113
|
+
quote = char
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
if (char === '{') brace += 1
|
|
117
|
+
if (char === '}') brace -= 1
|
|
118
|
+
if (char === '[') bracket += 1
|
|
119
|
+
if (char === ']') bracket -= 1
|
|
120
|
+
if (brace < 0 || bracket < 0) return false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return brace === 0 && bracket === 0 && quote === null
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function readFlagValue(argv, startIndex, key) {
|
|
127
|
+
let index = startIndex
|
|
128
|
+
let raw = argv[index]
|
|
129
|
+
if (raw == null) return { value: true, nextIndex: startIndex - 1 }
|
|
130
|
+
|
|
131
|
+
if (key === 'params' && /[{\[]/.test(raw)) {
|
|
132
|
+
let joined = raw
|
|
133
|
+
while (!isBalancedJsonLike(joined) && index + 1 < argv.length && !String(argv[index + 1]).startsWith('--')) {
|
|
134
|
+
index += 1
|
|
135
|
+
joined += argv[index]
|
|
136
|
+
}
|
|
137
|
+
return { value: joined, nextIndex: index }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { value: raw, nextIndex: index }
|
|
141
|
+
}
|
|
142
|
+
|
|
68
143
|
function parseArgs(argv) {
|
|
69
144
|
const args = { _: [] }
|
|
70
145
|
for (let index = 0; index < argv.length; index += 1) {
|
|
71
146
|
const value = argv[index]
|
|
72
|
-
if (value.startsWith('--')) {
|
|
73
|
-
const key = value.slice(2)
|
|
74
|
-
const next = argv[index + 1]
|
|
75
|
-
if (!next || next.startsWith('--')) {
|
|
76
|
-
args[key] = true
|
|
77
|
-
} else {
|
|
78
|
-
args[key] = next
|
|
79
|
-
index += 1
|
|
80
|
-
}
|
|
81
|
-
} else {
|
|
147
|
+
if (!value.startsWith('--')) {
|
|
82
148
|
args._.push(value)
|
|
149
|
+
continue
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const body = value.slice(2)
|
|
153
|
+
const eqIndex = body.indexOf('=')
|
|
154
|
+
if (eqIndex > 0) {
|
|
155
|
+
const key = body.slice(0, eqIndex)
|
|
156
|
+
const raw = body.slice(eqIndex + 1)
|
|
157
|
+
args[key] = raw
|
|
158
|
+
continue
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const key = body
|
|
162
|
+
const next = argv[index + 1]
|
|
163
|
+
if (!next || next.startsWith('--')) {
|
|
164
|
+
args[key] = true
|
|
165
|
+
continue
|
|
83
166
|
}
|
|
167
|
+
|
|
168
|
+
const read = readFlagValue(argv, index + 1, key)
|
|
169
|
+
args[key] = read.value
|
|
170
|
+
index = read.nextIndex
|
|
84
171
|
}
|
|
85
172
|
return args
|
|
86
173
|
}
|
|
@@ -101,13 +188,107 @@ function readConfig() {
|
|
|
101
188
|
}
|
|
102
189
|
}
|
|
103
190
|
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
191
|
+
function stripWrappingQuotes(text) {
|
|
192
|
+
const value = String(text).trim()
|
|
193
|
+
if (
|
|
194
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
195
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
196
|
+
) {
|
|
197
|
+
return value.slice(1, -1)
|
|
198
|
+
}
|
|
199
|
+
return value
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function coerceScalar(value) {
|
|
203
|
+
const text = stripWrappingQuotes(value).trim()
|
|
204
|
+
if (text === 'true') return true
|
|
205
|
+
if (text === 'false') return false
|
|
206
|
+
if (text === 'null') return null
|
|
207
|
+
if (text !== '' && /^-?\d+(\.\d+)?$/.test(text)) return Number(text)
|
|
208
|
+
return text
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function sanitizeJsonLike(text) {
|
|
212
|
+
return String(text)
|
|
213
|
+
.replace(/^\uFEFF/, '')
|
|
214
|
+
.replace(/[\u201C\u201D]/g, '"')
|
|
215
|
+
.replace(/[\u2018\u2019]/g, "'")
|
|
216
|
+
.replace(/\\+"/g, '"')
|
|
217
|
+
.replace(/\\+'/g, "'")
|
|
218
|
+
.replace(/\\([{}:[\]:,])/g, '$1')
|
|
219
|
+
.replace(/\\/g, '')
|
|
220
|
+
.replace(/\s+/g, ' ')
|
|
221
|
+
.trim()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function parseLooseObject(text) {
|
|
225
|
+
let body = String(text).trim()
|
|
226
|
+
if (!body) return undefined
|
|
227
|
+
|
|
228
|
+
if ((body.startsWith('{') && body.endsWith('}')) || (body.startsWith('[') && body.endsWith(']'))) {
|
|
229
|
+
body = body.slice(1, -1).trim()
|
|
110
230
|
}
|
|
231
|
+
if (!body) return {}
|
|
232
|
+
|
|
233
|
+
const pairs = body
|
|
234
|
+
.split(',')
|
|
235
|
+
.map((part) => part.trim())
|
|
236
|
+
.filter(Boolean)
|
|
237
|
+
if (!pairs.length) return undefined
|
|
238
|
+
|
|
239
|
+
const result = {}
|
|
240
|
+
for (const pair of pairs) {
|
|
241
|
+
const matched = pair.match(
|
|
242
|
+
/^(?:["']?)([A-Za-z_][\w-]*)(?:["']?)\s*[:=]\s*(.+)$/
|
|
243
|
+
)
|
|
244
|
+
if (!matched) return undefined
|
|
245
|
+
result[matched[1]] = coerceScalar(matched[2])
|
|
246
|
+
}
|
|
247
|
+
return result
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function parseParams(value) {
|
|
251
|
+
if (value == null || value === true || value === false) return undefined
|
|
252
|
+
if (typeof value === 'object' && !Array.isArray(value)) return value
|
|
253
|
+
|
|
254
|
+
const original = String(value).trim()
|
|
255
|
+
if (!original) return undefined
|
|
256
|
+
|
|
257
|
+
const candidates = [original, sanitizeJsonLike(original), original.replace(/\\/g, '')]
|
|
258
|
+
for (const candidate of candidates) {
|
|
259
|
+
try {
|
|
260
|
+
const parsed = JSON.parse(candidate)
|
|
261
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed
|
|
262
|
+
} catch {
|
|
263
|
+
// continue
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
for (const candidate of candidates) {
|
|
268
|
+
const loose = parseLooseObject(candidate)
|
|
269
|
+
if (loose) return loose
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
throw new Error(
|
|
273
|
+
'--params 必须是 JSON 或 key=value。PowerShell 推荐:--platform douyin 或 --params \'{"platform":"douyin"}\''
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function assignFlagParams(params, args) {
|
|
278
|
+
for (const key of PARAM_FLAG_KEYS) {
|
|
279
|
+
if (args[key] == null || args[key] === true) continue
|
|
280
|
+
if (params[key] != null) continue
|
|
281
|
+
params[key] = coerceScalar(args[key])
|
|
282
|
+
}
|
|
283
|
+
return params
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function buildExecuteParams(args) {
|
|
287
|
+
const params = {
|
|
288
|
+
...(parseParams(args.params) || {})
|
|
289
|
+
}
|
|
290
|
+
assignFlagParams(params, args)
|
|
291
|
+
return Object.keys(params).length > 0 ? params : undefined
|
|
111
292
|
}
|
|
112
293
|
|
|
113
294
|
async function execute(args) {
|
|
@@ -127,8 +308,8 @@ async function execute(args) {
|
|
|
127
308
|
body: JSON.stringify({
|
|
128
309
|
json: {
|
|
129
310
|
ability,
|
|
130
|
-
query: args.query,
|
|
131
|
-
params:
|
|
311
|
+
query: typeof args.query === 'string' ? args.query : undefined,
|
|
312
|
+
params: buildExecuteParams(args)
|
|
132
313
|
}
|
|
133
314
|
})
|
|
134
315
|
})
|
|
@@ -142,6 +323,28 @@ async function execute(args) {
|
|
|
142
323
|
console.log(JSON.stringify(payload.result?.data?.json ?? payload, null, 2))
|
|
143
324
|
}
|
|
144
325
|
|
|
326
|
+
function renderQuery(payload, format) {
|
|
327
|
+
if (format === 'json' || !format) return JSON.stringify(payload, null, 2)
|
|
328
|
+
if (payload.status === 'error') return `${payload.error.code}: ${payload.error.message}`
|
|
329
|
+
const items = payload.data?.items
|
|
330
|
+
if (!Array.isArray(items)) return JSON.stringify(payload, null, 2)
|
|
331
|
+
if (format === 'markdown') return items.map((item, index) => `| ${index + 1} | ${item.title || item.name || item.nickname || ''} |`).join('\n')
|
|
332
|
+
return items.map((item, index) => `${index + 1}\t${item.title || item.name || item.nickname || ''}`).join('\n')
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async function query(args) {
|
|
336
|
+
const request = parseParams(args.json || args.params)
|
|
337
|
+
if (!request) throw new Error('query 必须提供 --json 请求对象')
|
|
338
|
+
const config = readConfig()
|
|
339
|
+
const response = await fetch(`${config.apiBaseUrl}/api/agent.query`, {
|
|
340
|
+
method: 'POST', headers: { authorization: config.apiKey, 'content-type': 'application/json', 'x-device-type': 'pc' },
|
|
341
|
+
body: JSON.stringify({ json: request })
|
|
342
|
+
})
|
|
343
|
+
const text = await response.text()
|
|
344
|
+
if (!response.ok) throw new Error(`请求失败 HTTP ${response.status}:${text}`)
|
|
345
|
+
console.log(renderQuery(JSON.parse(text).result?.data?.json ?? JSON.parse(text), args.format || 'json'))
|
|
346
|
+
}
|
|
347
|
+
|
|
145
348
|
async function main() {
|
|
146
349
|
const args = parseArgs(process.argv.slice(2))
|
|
147
350
|
const command = args._[0]
|
|
@@ -171,11 +374,27 @@ async function main() {
|
|
|
171
374
|
await execute(args)
|
|
172
375
|
return
|
|
173
376
|
}
|
|
377
|
+
if (command === 'query') {
|
|
378
|
+
await query(args)
|
|
379
|
+
return
|
|
380
|
+
}
|
|
174
381
|
|
|
175
382
|
throw new Error(`未知命令:${command}`)
|
|
176
383
|
}
|
|
177
384
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
385
|
+
const isDirectRun = (() => {
|
|
386
|
+
const entry = process.argv[1]
|
|
387
|
+
if (!entry) return false
|
|
388
|
+
try {
|
|
389
|
+
return path.resolve(entry) === fileURLToPath(import.meta.url)
|
|
390
|
+
} catch {
|
|
391
|
+
return false
|
|
392
|
+
}
|
|
393
|
+
})()
|
|
394
|
+
|
|
395
|
+
if (isDirectRun) {
|
|
396
|
+
main().catch((error) => {
|
|
397
|
+
console.error(error.message)
|
|
398
|
+
process.exit(1)
|
|
399
|
+
})
|
|
400
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import test from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { buildExecuteParams, parseParams } from './index.js'
|
|
4
|
+
|
|
5
|
+
test('parseParams accepts standard JSON', () => {
|
|
6
|
+
assert.deepEqual(parseParams('{"platform":"douyin"}'), { platform: 'douyin' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
test('parseParams repairs PowerShell-mangled escaped JSON fragments', () => {
|
|
10
|
+
assert.deepEqual(parseParams('{\\platform\\:\\douyin\\}'), { platform: 'douyin' })
|
|
11
|
+
assert.deepEqual(parseParams('{"platform":"douyin"}'.replaceAll('"', '\\"')), {
|
|
12
|
+
platform: 'douyin'
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('parseParams accepts key=value and loose objects', () => {
|
|
17
|
+
assert.deepEqual(parseParams('platform=douyin'), { platform: 'douyin' })
|
|
18
|
+
assert.deepEqual(parseParams('{platform:douyin,mode:list}'), {
|
|
19
|
+
platform: 'douyin',
|
|
20
|
+
mode: 'list'
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('buildExecuteParams merges top-level flags', () => {
|
|
25
|
+
assert.deepEqual(
|
|
26
|
+
buildExecuteParams({
|
|
27
|
+
params: '{"mode":"timeline"}',
|
|
28
|
+
platform: 'douyin',
|
|
29
|
+
timeRange: '7d'
|
|
30
|
+
}),
|
|
31
|
+
{
|
|
32
|
+
mode: 'timeline',
|
|
33
|
+
platform: 'douyin',
|
|
34
|
+
timeRange: '7d'
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('buildExecuteParams prefers explicit params keys over flags', () => {
|
|
40
|
+
assert.deepEqual(
|
|
41
|
+
buildExecuteParams({
|
|
42
|
+
params: 'platform=weibo',
|
|
43
|
+
platform: 'douyin'
|
|
44
|
+
}),
|
|
45
|
+
{ platform: 'weibo' }
|
|
46
|
+
)
|
|
47
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { execFileSync } from 'node:child_process'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import test from 'node:test'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
|
|
8
|
+
const CLI_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
9
|
+
const ENTRY_PATH = path.join(CLI_DIR, 'src', 'index.js')
|
|
10
|
+
const PACKAGE_PATH = path.join(CLI_DIR, 'package.json')
|
|
11
|
+
const expectedVersion = JSON.parse(fs.readFileSync(PACKAGE_PATH, 'utf8')).version
|
|
12
|
+
|
|
13
|
+
for (const flag of ['--version', '-v']) {
|
|
14
|
+
test(`${flag} prints the package version`, () => {
|
|
15
|
+
const output = execFileSync(process.execPath, [ENTRY_PATH, flag], {
|
|
16
|
+
encoding: 'utf8'
|
|
17
|
+
}).trim()
|
|
18
|
+
|
|
19
|
+
assert.equal(output, expectedVersion)
|
|
20
|
+
assert.notEqual(output, '')
|
|
21
|
+
})
|
|
22
|
+
}
|