@yizhuan-cli/cli 0.1.6-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 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 --platform douyin
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yizhuan-cli/cli",
3
- "version": "0.1.6-beta.0",
3
+ "version": "0.1.7-beta.0",
4
4
  "description": "易撰命令行工具,用于通过 API Key 查询易撰真实数据。",
5
5
  "private": false,
6
6
  "type": "module",
@@ -11,6 +11,13 @@
11
11
  "src",
12
12
  "README.md"
13
13
  ],
14
+ "scripts": {
15
+ "start": "node src/index.js",
16
+ "pack:dry-run": "npm pack --dry-run",
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"
20
+ },
14
21
  "engines": {
15
22
  "node": ">=22"
16
23
  },
@@ -22,12 +29,5 @@
22
29
  "license": "UNLICENSED",
23
30
  "publishConfig": {
24
31
  "access": "public"
25
- },
26
- "scripts": {
27
- "start": "node src/index.js",
28
- "pack:dry-run": "npm pack --dry-run",
29
- "smoke": "node src/index.js --help && node --test src/version.test.js",
30
- "test:params": "node --test src/params.test.js",
31
- "test:version": "node --test src/version.test.js"
32
32
  }
33
- }
33
+ }
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>] [--platform <name>]
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
- - 所有 Shell 都可用 --json-file request.json,避免 JSON 引号被终端改写
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
 
@@ -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 === '-'
@@ -1,6 +1,8 @@
1
1
  import test from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
- import { buildExecuteParams, parseArgs, parseParams } from './index.js'
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,6 +48,36 @@ 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"}']),
@@ -66,3 +98,19 @@ test('parseArgs preserves query JSON split into multiple argv fragments', () =>
66
98
  { _: ['query'], json: '{"intent":"author_search","platform":"douyin"}' }
67
99
  )
68
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
+ })