@yizhuan-cli/cli 0.1.14-beta.1 → 0.1.15

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
@@ -7,13 +7,13 @@
7
7
  需要 Node.js 22 或更高版本。
8
8
 
9
9
  ```bash
10
- npm install -g @yizhuan-cli/cli@0.1.13
10
+ npm install -g @yizhuan-cli/cli@0.1.15
11
11
  ```
12
12
 
13
13
  如果发布在私有 npm registry,请加上对应 registry:
14
14
 
15
15
  ```bash
16
- npm install -g @yizhuan-cli/cli@0.1.13 --registry=https://your-private-registry.example
16
+ npm install -g @yizhuan-cli/cli@0.1.15 --registry=https://your-private-registry.example
17
17
  ```
18
18
 
19
19
  ## 配置
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yizhuan-cli/cli",
3
- "version": "0.1.14-beta.1",
3
+ "version": "0.1.15",
4
4
  "description": "易撰命令行工具,用于通过 API Key 查询易撰真实数据。",
5
5
  "private": false,
6
6
  "type": "module",
@@ -45,8 +45,6 @@ yizhuan doctor
45
45
  | 作者查询 | `yizhuan execute --ability accounts --query "张三"` |
46
46
  | 作者指标筛选 | `yizhuan execute --ability author_query --params '{"platform_id":2,"min_follower":500000,"min_total_likes":100000}'` |
47
47
  | 作者全网搜索 | `yizhuan query --json '{"intent":"author_search","scope":"public","platform":"douyin","keyword":"张三"}'` |
48
- | 微信公众号作者 | `yizhuan execute --ability accounts --params '{"mode":"public_search","platform":"wechat","keyword":"科技"}'` |
49
- | 微信公众号作品 | `yizhuan execute --ability works --params '{"mode":"search","platform":"wechat","platform_id":9,"keyword":"人工智能"}'` |
50
48
  | 热点 | `yizhuan query --json '{"intent":"hot_topics","scope":"platform","platform":"douyin","timeRange":"today"}'` |
51
49
  | 我的关注 | `yizhuan execute --ability radar --params '{"mode":"following"}'` |
52
50
  | 竞品动态 | `yizhuan execute --ability radar --mode timeline --timeRange 7d` |
@@ -65,7 +63,6 @@ yizhuan doctor
65
63
  - 平台热点必须严格按 CLI 返回的 `data.items` / `rank` 顺序展示,该顺序与 Web 端平台榜单一致;`hot` 仅为展示指标,禁止按 `hot` 或格式化后的“万”值二次排序。
66
64
  - 使用 Markdown 表格展示热点时,必须将标题中的 `|` 转义为 `\|`,不得让标题内容错位到热度列。
67
65
  - 全网作者搜索使用 `intent=author_search`、`scope=public`,并提供 `platform` 和 `keyword`。
68
- - 微信公众号暂不支持全网搜索作品。用户提出该请求时必须明确说明不支持,不得改用作品库检索并把 `empty` 解释为全网没有相关作品;微信公众号作者全网搜索仍可使用 `author_search`。
69
66
  - 查询指定作者最近作品时,直接使用 `intent=works_search`,同时提供 `platform`、`authorName` 和 `timeRange`;不得先用 `author_search` 的 `workCount` 判断该作者是否有作品。
70
67
  - `author_search` 返回的 `extAuthorId` 是平台外部作者 ID,不是易撰内部 `authorId`。需要按外部 ID 查询作品时只能传 `extAuthorId` / `ext_author_id`(兼容 `author_id`),必须同时提供 `platform`,由服务端完成内部 ID 转换;禁止把它直接作为内部 `authorId` 使用。
71
68
  - 公开作者结果中的 `workCount: 0` 可能是上游未返回作品数后的缺省值,不代表作者没有作品;是否有作品必须以 `works_search` 的结果为准。
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "name": "yizhuan-cli",
4
- "version": "0.1.14-beta.1",
5
- "compatibleCliVersion": "0.1.14-beta.1",
6
- "source": "npm:@yizhuan-cli/cli@0.1.14-beta.1",
4
+ "version": "0.1.15",
5
+ "compatibleCliVersion": "0.1.15",
6
+ "source": "npm:@yizhuan-cli/cli@0.1.15",
7
7
  "digestAlgorithm": "sha256",
8
8
  "files": [
9
9
  {
10
10
  "path": "SKILL.md",
11
- "sha256": "4c7a2c189e1b86577a8b3ff290d7cc1031a4cf3fb3e3e075560f4b672adf90dc"
11
+ "sha256": "4fcca6c6a4bc98ffabab267174bf369e617e58f2bd297b20132d29138fdcad19"
12
12
  }
13
13
  ]
14
14
  }
package/src/doctor.js CHANGED
@@ -40,6 +40,7 @@ export function inspectLocalEnvironment(
40
40
  } = {},
41
41
  {
42
42
  packageJson = JSON.parse(fs.readFileSync(PACKAGE_PATH, 'utf8')),
43
+ officialManifest = loadOfficialSkill().manifest,
43
44
  exists = fs.existsSync,
44
45
  readFile = fs.readFileSync,
45
46
  stat = fs.statSync,
@@ -53,9 +54,15 @@ export function inspectLocalEnvironment(
53
54
  ? check('pass', 'node', `Node.js ${nodeVersion} 满足 >=${minimumNode}`)
54
55
  : check('fail', 'node', `Node.js ${nodeVersion} 不满足 ${packageJson.engines?.node || '版本要求'}`, `安装 Node.js ${minimumNode || 22} 或更高版本。`))
55
56
 
56
- checks.push(packageJson.version === '0.1.13'
57
+ const expectedCliVersion = officialManifest.compatibleCliVersion
58
+ checks.push(packageJson.version === expectedCliVersion
57
59
  ? check('pass', 'cli_version', `CLI ${packageJson.version}`)
58
- : check('fail', 'cli_version', `CLI 版本为 ${packageJson.version},预期 0.1.13`, '安装 @yizhuan-cli/cli@0.1.13。'))
60
+ : check(
61
+ 'fail',
62
+ 'cli_version',
63
+ `CLI 版本为 ${packageJson.version},内置正式 Skill 要求 ${expectedCliVersion}`,
64
+ '重新安装 @yizhuan-cli/cli@latest。'
65
+ ))
59
66
 
60
67
  if (!exists(configPath)) {
61
68
  checks.push(check('fail', 'config', `配置不存在:${configPath}`, '运行 yizhuan config init。'))
package/src/index.js CHANGED
@@ -17,7 +17,6 @@ const ABILITIES = new Set([
17
17
  'membership',
18
18
  'favorites',
19
19
  'public_author_search',
20
- 'public_work_search',
21
20
  'author_query',
22
21
  'statistics',
23
22
  'content_search',
@@ -25,8 +24,8 @@ const ABILITIES = new Set([
25
24
  'hot_topic_analysis',
26
25
  'work_query',
27
26
  'membership_info',
28
- 'favorites_query',
29
- 'detection'
27
+ 'favorites_query'
28
+ ,'detection'
30
29
  ])
31
30
 
32
31
  const PARAM_FLAG_KEYS = [
@@ -90,7 +89,6 @@ Abilities:
90
89
  membership 会员查询
91
90
  favorites 收藏查询
92
91
  public_author_search 作者全网搜索,兼容低层能力
93
- public_work_search 作品全网搜索,兼容低层能力
94
92
  detection 一键检测(原创/风险)
95
93
 
96
94
  Config:
@@ -148,31 +146,13 @@ function readFlagValue(argv, startIndex, key) {
148
146
 
149
147
  if ((key === 'params' || key === 'json') && /[{\[]/.test(raw)) {
150
148
  let joined = raw
151
- while (
152
- !isBalancedJsonLike(joined) &&
153
- index + 1 < argv.length &&
154
- !String(argv[index + 1]).startsWith('--')
155
- ) {
149
+ while (!isBalancedJsonLike(joined) && index + 1 < argv.length && !String(argv[index + 1]).startsWith('--')) {
156
150
  index += 1
157
151
  joined += argv[index]
158
152
  }
159
153
  return { value: joined, nextIndex: index }
160
154
  }
161
155
 
162
- // Accept comma-separated key=value fragments commonly pasted into PowerShell.
163
- if (key === 'json' && /^[A-Za-z_][\w-]*\s*[:=]/.test(String(raw))) {
164
- let joined = raw
165
- while (
166
- index + 1 < argv.length &&
167
- !String(argv[index + 1]).startsWith('--') &&
168
- /^[A-Za-z_][\w-]*\s*[:=]/.test(String(argv[index + 1]))
169
- ) {
170
- index += 1
171
- joined += ` ${argv[index]}`
172
- }
173
- return { value: joined, nextIndex: index }
174
- }
175
-
176
156
  return { value: raw, nextIndex: index }
177
157
  }
178
158
 
@@ -235,11 +215,8 @@ function stripWrappingQuotes(text) {
235
215
  return value
236
216
  }
237
217
 
238
- const OPAQUE_ID_KEYS = new Set(['extAuthorId', 'ext_author_id', 'author_id'])
239
-
240
- function coerceScalar(value, key) {
241
- const text = stripWrappingQuotes(value).replace(/\\_/g, '_').trim()
242
- if (OPAQUE_ID_KEYS.has(key)) return text
218
+ function coerceScalar(value) {
219
+ const text = stripWrappingQuotes(value).trim()
243
220
  if (text === 'true') return true
244
221
  if (text === 'false') return false
245
222
  if (text === 'null') return null
@@ -264,10 +241,7 @@ function parseLooseObject(text) {
264
241
  let body = String(text).trim()
265
242
  if (!body) return undefined
266
243
 
267
- if (
268
- (body.startsWith('{') && body.endsWith('}')) ||
269
- (body.startsWith('[') && body.endsWith(']'))
270
- ) {
244
+ if ((body.startsWith('{') && body.endsWith('}')) || (body.startsWith('[') && body.endsWith(']'))) {
271
245
  body = body.slice(1, -1).trim()
272
246
  }
273
247
  if (!body) return {}
@@ -280,9 +254,11 @@ function parseLooseObject(text) {
280
254
 
281
255
  const result = {}
282
256
  for (const pair of pairs) {
283
- const matched = pair.match(/^(?:["']?)([A-Za-z_][\w-]*)(?:["']?)\s*[:=]\s*(.+)$/)
257
+ const matched = pair.match(
258
+ /^(?:["']?)([A-Za-z_][\w-]*)(?:["']?)\s*[:=]\s*(.+)$/
259
+ )
284
260
  if (!matched) return undefined
285
- result[matched[1]] = coerceScalar(matched[2], matched[1])
261
+ result[matched[1]] = coerceScalar(matched[2])
286
262
  }
287
263
  return result
288
264
  }
@@ -318,7 +294,7 @@ function assignFlagParams(params, args) {
318
294
  for (const key of PARAM_FLAG_KEYS) {
319
295
  if (args[key] == null || args[key] === true) continue
320
296
  if (params[key] != null) continue
321
- params[key] = coerceScalar(args[key], key)
297
+ params[key] = coerceScalar(args[key])
322
298
  }
323
299
  return params
324
300
  }
@@ -355,7 +331,9 @@ export function assertCommandArgs(args, command, allowedFlags, positionalCount =
355
331
  )
356
332
  }
357
333
 
358
- const unknownFlags = Object.keys(args).filter((key) => key !== '_' && !allowedFlags.has(key))
334
+ const unknownFlags = Object.keys(args).filter(
335
+ (key) => key !== '_' && !allowedFlags.has(key)
336
+ )
359
337
  if (unknownFlags.length > 0) {
360
338
  throw new Error(`${command} 存在未知参数:${unknownFlags.map((key) => `--${key}`).join(', ')}`)
361
339
  }
@@ -396,30 +374,16 @@ async function execute(args) {
396
374
 
397
375
  let result = payloadResult(JSON.parse(text))
398
376
  const requestParams = buildExecuteParams(args)
399
- if (
400
- ability === 'detection' &&
401
- requestParams?.wait === true &&
402
- result?.status === 'pending' &&
403
- result?.data?.id
404
- ) {
405
- const timeoutSeconds =
406
- Number(requestParams.timeoutSeconds) > 0 ? Number(requestParams.timeoutSeconds) : 120
377
+ if (ability === 'detection' && requestParams?.wait === true && result?.status === 'pending' && result?.data?.id) {
378
+ const timeoutSeconds = Number(requestParams.timeoutSeconds) > 0 ? Number(requestParams.timeoutSeconds) : 120
407
379
  const deadline = Date.now() + timeoutSeconds * 1000
408
380
  while (Date.now() < deadline && result.status === 'pending') {
409
- await new Promise((resolve) =>
410
- setTimeout(resolve, Math.max(500, Number(result.data.pollAfterSeconds || 3) * 1000))
411
- )
381
+ await new Promise((resolve) => setTimeout(resolve, Math.max(500, Number(result.data.pollAfterSeconds || 3) * 1000)))
412
382
  if (Date.now() >= deadline) break
413
383
  const poll = await fetch(`${config.apiBaseUrl}/api/cli.execute`, {
414
384
  method: 'POST',
415
- headers: {
416
- authorization: config.apiKey,
417
- 'content-type': 'application/json',
418
- 'x-device-type': 'pc'
419
- },
420
- body: JSON.stringify({
421
- json: { ability: 'detection', params: { mode: 'get', id: result.data.id } }
422
- })
385
+ headers: { authorization: config.apiKey, 'content-type': 'application/json', 'x-device-type': 'pc' },
386
+ body: JSON.stringify({ json: { ability: 'detection', params: { mode: 'get', id: result.data.id } } })
423
387
  })
424
388
  const pollText = await poll.text()
425
389
  if (!poll.ok) throw new Error(`请求失败 HTTP ${poll.status}:${pollText}`)
@@ -438,13 +402,8 @@ function renderQuery(payload, format) {
438
402
  if (payload.status === 'error') return `${payload.error.code}: ${payload.error.message}`
439
403
  const items = payload.data?.items
440
404
  if (!Array.isArray(items)) return JSON.stringify(payload, null, 2)
441
- if (format === 'markdown')
442
- return items
443
- .map((item, index) => `| ${index + 1} | ${item.title || item.name || item.nickname || ''} |`)
444
- .join('\n')
445
- return items
446
- .map((item, index) => `${index + 1}\t${item.title || item.name || item.nickname || ''}`)
447
- .join('\n')
405
+ if (format === 'markdown') return items.map((item, index) => `| ${index + 1} | ${item.title || item.name || item.nickname || ''} |`).join('\n')
406
+ return items.map((item, index) => `${index + 1}\t${item.title || item.name || item.nickname || ''}`).join('\n')
448
407
  }
449
408
 
450
409
  async function query(args) {
@@ -458,19 +417,12 @@ async function query(args) {
458
417
  if (!request) throw new Error('query 必须提供 --json 请求对象')
459
418
  const config = readConfig()
460
419
  const response = await fetch(`${config.apiBaseUrl}/api/agent.query`, {
461
- method: 'POST',
462
- headers: {
463
- authorization: config.apiKey,
464
- 'content-type': 'application/json',
465
- 'x-device-type': 'pc'
466
- },
420
+ method: 'POST', headers: { authorization: config.apiKey, 'content-type': 'application/json', 'x-device-type': 'pc' },
467
421
  body: JSON.stringify({ json: request })
468
422
  })
469
423
  const text = await response.text()
470
424
  if (!response.ok) throw new Error(`请求失败 HTTP ${response.status}:${text}`)
471
- console.log(
472
- renderQuery(JSON.parse(text).result?.data?.json ?? JSON.parse(text), args.format || 'json')
473
- )
425
+ console.log(renderQuery(JSON.parse(text).result?.data?.json ?? JSON.parse(text), args.format || 'json'))
474
426
  }
475
427
 
476
428
  async function main() {
@@ -493,12 +445,7 @@ async function main() {
493
445
  }
494
446
 
495
447
  if (command === 'config' && args._[1] === 'init') {
496
- assertCommandArgs(
497
- args,
498
- 'config init',
499
- new Set(['apiBaseUrl', 'apiKey', 'apiKeyStdin', 'force', 'help']),
500
- 2
501
- )
448
+ assertCommandArgs(args, 'config init', new Set(['apiBaseUrl', 'apiKey', 'apiKeyStdin', 'force', 'help']), 2)
502
449
  await runConfigInit(args)
503
450
  return
504
451
  }