@yizhuan-cli/cli 0.1.14 → 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 +2 -2
- package/package.json +1 -1
- package/resources/yizhuan-cli/SKILL.md +0 -3
- package/resources/yizhuan-cli/manifest.json +4 -4
- package/src/doctor.js +9 -2
- package/src/index.js +24 -77
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@
|
|
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@
|
|
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
|
@@ -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.
|
|
5
|
-
"compatibleCliVersion": "0.1.
|
|
6
|
-
"source": "npm:@yizhuan-cli/cli@0.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": "
|
|
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
|
-
|
|
57
|
+
const expectedCliVersion = officialManifest.compatibleCliVersion
|
|
58
|
+
checks.push(packageJson.version === expectedCliVersion
|
|
57
59
|
? check('pass', 'cli_version', `CLI ${packageJson.version}`)
|
|
58
|
-
: check(
|
|
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
|
-
|
|
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(
|
|
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]
|
|
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]
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|