@yizhuan-cli/cli 0.1.5-beta.0 → 0.1.7-beta.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/README.md +6 -2
- package/package.json +1 -1
- package/src/index.js +45 -5
- package/src/params.test.js +63 -1
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ yizhuan execute --ability accounts --params '{"mode":"public_search","platform":
|
|
|
40
40
|
yizhuan execute --ability radar --params '{"mode":"following"}'
|
|
41
41
|
yizhuan execute --ability radar --mode timeline --timeRange 7d
|
|
42
42
|
yizhuan execute --ability radar --params '{"mode":"unfollow","articleId":"205169870"}'
|
|
43
|
+
yizhuan execute --ability radar --json-file params.json
|
|
43
44
|
```
|
|
44
45
|
|
|
45
46
|
### PowerShell 参数说明
|
|
@@ -47,8 +48,8 @@ yizhuan execute --ability radar --params '{"mode":"unfollow","articleId":"205169
|
|
|
47
48
|
PowerShell 对 JSON 双引号转义不友好。推荐以下写法:
|
|
48
49
|
|
|
49
50
|
```powershell
|
|
50
|
-
#
|
|
51
|
-
yizhuan execute --ability hot_topics --
|
|
51
|
+
# Agent 和跨 Shell 最稳妥:参数文件是唯一输入源
|
|
52
|
+
yizhuan execute --ability hot_topics --json-file params.json
|
|
52
53
|
|
|
53
54
|
# 单引号包 JSON
|
|
54
55
|
yizhuan execute --ability hot_topics --params '{"platform":"douyin"}'
|
|
@@ -57,6 +58,8 @@ yizhuan execute --ability hot_topics --params '{"platform":"douyin"}'
|
|
|
57
58
|
yizhuan execute --ability hot_topics --params platform=douyin
|
|
58
59
|
```
|
|
59
60
|
|
|
61
|
+
`execute --json-file` 不能与 `--params`、`--query` 或独立参数混用。CLI 会拒绝未知参数和游离的 `key=value`,避免参数丢失后执行默认能力。
|
|
62
|
+
|
|
60
63
|
请避免 bash 风格的:
|
|
61
64
|
|
|
62
65
|
```powershell
|
|
@@ -68,6 +71,7 @@ yizhuan execute --ability hot_topics --params "{\"platform\":\"douyin\"}"
|
|
|
68
71
|
|
|
69
72
|
- 作品查询:`yizhuan execute --ability works --query "AI 相关的热门作品有哪些"`
|
|
70
73
|
- 作者查询:`yizhuan execute --ability accounts --query "查询张三这个作者"`
|
|
74
|
+
- 作者指标筛选:`yizhuan execute --ability author_query --params '{"platform_id":2,"min_follower":500000,"min_total_likes":100000}'`
|
|
71
75
|
- 作者全网搜索:`yizhuan execute --ability accounts --params '{"mode":"public_search","platform":"toutiao","keyword":"张三"}'`
|
|
72
76
|
- 关注全网作者:`yizhuan execute --ability accounts --params '{"mode":"follow_public_author","platform":"toutiao","keyword":"张三","extAuthorId":"author-1"}'`
|
|
73
77
|
- 热点查询:`yizhuan execute --ability hot_topics --platform douyin`
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -48,7 +48,8 @@ function printHelp() {
|
|
|
48
48
|
Usage:
|
|
49
49
|
yizhuan --help
|
|
50
50
|
yizhuan --version
|
|
51
|
-
yizhuan execute --ability <ability> [--query <text>] [--params <json>]
|
|
51
|
+
yizhuan execute --ability <ability> [--query <text>] [--params <json>]
|
|
52
|
+
yizhuan execute --ability <ability> --json-file <params-file>
|
|
52
53
|
yizhuan query --json '<request JSON>' [--format json|table|markdown]
|
|
53
54
|
yizhuan query --json-file <path> [--format json|table|markdown]
|
|
54
55
|
yizhuan config path
|
|
@@ -65,7 +66,8 @@ Examples:
|
|
|
65
66
|
|
|
66
67
|
Notes:
|
|
67
68
|
- PowerShell 推荐优先使用 --platform / --mode 等独立参数,或单引号 JSON:--params '{"platform":"douyin"}'
|
|
68
|
-
-
|
|
69
|
+
- Agent 和所有 Shell 推荐使用 execute --json-file params.json,避免 JSON 引号被终端改写
|
|
70
|
+
- --json-file 是唯一参数源,不能与 --params、--query 或独立参数混用
|
|
69
71
|
- bash/zsh 也可用:--params '{"platform":"douyin"}' 或 --platform douyin
|
|
70
72
|
- 兼容 key=value:--params platform=douyin
|
|
71
73
|
|
|
@@ -131,7 +133,7 @@ function readFlagValue(argv, startIndex, key) {
|
|
|
131
133
|
let raw = argv[index]
|
|
132
134
|
if (raw == null) return { value: true, nextIndex: startIndex - 1 }
|
|
133
135
|
|
|
134
|
-
if (key === 'params' && /[{\[]/.test(raw)) {
|
|
136
|
+
if ((key === 'params' || key === 'json') && /[{\[]/.test(raw)) {
|
|
135
137
|
let joined = raw
|
|
136
138
|
while (!isBalancedJsonLike(joined) && index + 1 < argv.length && !String(argv[index + 1]).startsWith('--')) {
|
|
137
139
|
index += 1
|
|
@@ -155,13 +157,13 @@ export function parseArgs(argv) {
|
|
|
155
157
|
const body = value.slice(2)
|
|
156
158
|
const eqIndex = body.indexOf('=')
|
|
157
159
|
if (eqIndex > 0) {
|
|
158
|
-
const key = body.slice(0, eqIndex)
|
|
160
|
+
const key = body.slice(0, eqIndex).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
|
|
159
161
|
const raw = body.slice(eqIndex + 1)
|
|
160
162
|
args[key] = raw
|
|
161
163
|
continue
|
|
162
164
|
}
|
|
163
165
|
|
|
164
|
-
const key = body
|
|
166
|
+
const key = body.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
|
|
165
167
|
const next = argv[index + 1]
|
|
166
168
|
if (!next || next.startsWith('--')) {
|
|
167
169
|
args[key] = true
|
|
@@ -287,6 +289,22 @@ function assignFlagParams(params, args) {
|
|
|
287
289
|
}
|
|
288
290
|
|
|
289
291
|
export function buildExecuteParams(args) {
|
|
292
|
+
if (args.jsonFile) {
|
|
293
|
+
const conflictingKeys = ['params', 'query', ...PARAM_FLAG_KEYS].filter(
|
|
294
|
+
(key) => args[key] != null && args[key] !== false
|
|
295
|
+
)
|
|
296
|
+
if (conflictingKeys.length > 0) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
`execute --json-file 不能与其他参数输入混用:${conflictingKeys.map((key) => `--${key}`).join(', ')}`
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const jsonText = fs.readFileSync(path.resolve(String(args.jsonFile)), 'utf8')
|
|
303
|
+
const fileParams = parseParams(jsonText)
|
|
304
|
+
if (!fileParams) throw new Error('execute --json-file 必须包含 JSON 参数对象')
|
|
305
|
+
return fileParams
|
|
306
|
+
}
|
|
307
|
+
|
|
290
308
|
const params = {
|
|
291
309
|
...(parseParams(args.params) || {})
|
|
292
310
|
}
|
|
@@ -294,7 +312,28 @@ export function buildExecuteParams(args) {
|
|
|
294
312
|
return Object.keys(params).length > 0 ? params : undefined
|
|
295
313
|
}
|
|
296
314
|
|
|
315
|
+
export function assertCommandArgs(args, command, allowedFlags, positionalCount = 1) {
|
|
316
|
+
const extraPositionals = args._.slice(positionalCount)
|
|
317
|
+
if (extraPositionals.length > 0) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`${command} 存在未识别的位置参数:${extraPositionals.join(' ')}。多个参数必须写入同一个 JSON 对象`
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const unknownFlags = Object.keys(args).filter(
|
|
324
|
+
(key) => key !== '_' && !allowedFlags.has(key)
|
|
325
|
+
)
|
|
326
|
+
if (unknownFlags.length > 0) {
|
|
327
|
+
throw new Error(`${command} 存在未知参数:${unknownFlags.map((key) => `--${key}`).join(', ')}`)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
297
331
|
async function execute(args) {
|
|
332
|
+
assertCommandArgs(
|
|
333
|
+
args,
|
|
334
|
+
'execute',
|
|
335
|
+
new Set(['ability', 'query', 'params', 'jsonFile', ...PARAM_FLAG_KEYS])
|
|
336
|
+
)
|
|
298
337
|
const ability = args.ability
|
|
299
338
|
if (!ABILITIES.has(ability)) {
|
|
300
339
|
throw new Error(`未知 ability:${ability || ''},请执行 yizhuan --help 查看可用能力`)
|
|
@@ -336,6 +375,7 @@ function renderQuery(payload, format) {
|
|
|
336
375
|
}
|
|
337
376
|
|
|
338
377
|
async function query(args) {
|
|
378
|
+
assertCommandArgs(args, 'query', new Set(['json', 'jsonFile', 'params', 'format']))
|
|
339
379
|
const jsonText = args.jsonFile
|
|
340
380
|
? fs.readFileSync(path.resolve(String(args.jsonFile)), 'utf8')
|
|
341
381
|
: args.json === '-'
|
package/src/params.test.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import test from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import {
|
|
3
|
+
import { rmSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { assertCommandArgs, buildExecuteParams, parseArgs, parseParams } from './index.js'
|
|
4
6
|
|
|
5
7
|
test('parseParams accepts standard JSON', () => {
|
|
6
8
|
assert.deepEqual(parseParams('{"platform":"douyin"}'), { platform: 'douyin' })
|
|
@@ -46,9 +48,69 @@ test('buildExecuteParams prefers explicit params keys over flags', () => {
|
|
|
46
48
|
)
|
|
47
49
|
})
|
|
48
50
|
|
|
51
|
+
test('buildExecuteParams reads execute params from --json-file', (t) => {
|
|
52
|
+
const fixture = new URL(`./params-${process.pid}-${Date.now()}.json`, import.meta.url)
|
|
53
|
+
t.after(() => rmSync(fixture))
|
|
54
|
+
writeFileSync(
|
|
55
|
+
fixture,
|
|
56
|
+
JSON.stringify({
|
|
57
|
+
mode: 'follow',
|
|
58
|
+
platform: 'baijiahao',
|
|
59
|
+
extAuthorId: '1659153597622724'
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
assert.deepEqual(buildExecuteParams({ jsonFile: fileURLToPath(fixture) }), {
|
|
64
|
+
mode: 'follow',
|
|
65
|
+
platform: 'baijiahao',
|
|
66
|
+
extAuthorId: '1659153597622724'
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('buildExecuteParams rejects mixed --json-file and flag inputs', (t) => {
|
|
71
|
+
const fixture = new URL(`./params-${process.pid}-${Date.now()}.json`, import.meta.url)
|
|
72
|
+
t.after(() => rmSync(fixture))
|
|
73
|
+
writeFileSync(fixture, '{"mode":"following"}')
|
|
74
|
+
|
|
75
|
+
assert.throws(
|
|
76
|
+
() => buildExecuteParams({ jsonFile: fileURLToPath(fixture), mode: 'follow' }),
|
|
77
|
+
/不能与其他参数输入混用/
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
|
|
49
81
|
test('parseArgs preserves query JSON supplied after --json', () => {
|
|
50
82
|
assert.deepEqual(
|
|
51
83
|
parseArgs(['query', '--json', '{"intent":"author_search","platform":"douyin"}']),
|
|
52
84
|
{ _: ['query'], json: '{"intent":"author_search","platform":"douyin"}' }
|
|
53
85
|
)
|
|
54
86
|
})
|
|
87
|
+
|
|
88
|
+
test('parseArgs maps --json-file to the property used by query', () => {
|
|
89
|
+
assert.deepEqual(
|
|
90
|
+
parseArgs(['query', '--json-file', 'request.json']),
|
|
91
|
+
{ _: ['query'], jsonFile: 'request.json' }
|
|
92
|
+
)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('parseArgs preserves query JSON split into multiple argv fragments', () => {
|
|
96
|
+
assert.deepEqual(
|
|
97
|
+
parseArgs(['query', '--json', '{"intent":', '"author_search",', '"platform":"douyin"}']),
|
|
98
|
+
{ _: ['query'], json: '{"intent":"author_search","platform":"douyin"}' }
|
|
99
|
+
)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('assertCommandArgs rejects extra key=value positionals', () => {
|
|
103
|
+
const args = parseArgs([
|
|
104
|
+
'execute',
|
|
105
|
+
'--ability',
|
|
106
|
+
'radar',
|
|
107
|
+
'--params',
|
|
108
|
+
'mode=follow',
|
|
109
|
+
'platform=baijiahao'
|
|
110
|
+
])
|
|
111
|
+
|
|
112
|
+
assert.throws(
|
|
113
|
+
() => assertCommandArgs(args, 'execute', new Set(['ability', 'params'])),
|
|
114
|
+
/未识别的位置参数/
|
|
115
|
+
)
|
|
116
|
+
})
|