@yizhuan-cli/cli 0.1.16-beta.2 → 0.1.17-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 +9 -0
- package/package.json +1 -1
- package/resources/yizhuan-cli/SKILL.md +12 -1
- package/resources/yizhuan-cli/manifest.json +4 -4
- package/src/doctor.js +1 -1
- package/src/index.js +106 -26
- package/src/skill.js +8 -6
package/README.md
CHANGED
|
@@ -36,6 +36,8 @@ yizhuan config path
|
|
|
36
36
|
yizhuan config show
|
|
37
37
|
yizhuan execute --ability membership
|
|
38
38
|
yizhuan execute --ability works --query "百家号昨天新增了哪些作品"
|
|
39
|
+
yizhuan execute --ability works --params '{"mode":"comments","platform":"douyin","workId":"123456","count":20}'
|
|
40
|
+
yizhuan execute --ability works --params '{"mode":"comments","platform":"douyin","title":"作品标题","count":20}'
|
|
39
41
|
yizhuan execute --ability hot_topics --platform douyin
|
|
40
42
|
yizhuan execute --ability hot_topics --params '{"platform":"douyin"}'
|
|
41
43
|
yizhuan execute --ability accounts --params '{"mode":"public_search","platform":"toutiao","keyword":"张三"}'
|
|
@@ -72,6 +74,9 @@ yizhuan execute --ability hot_topics --params "{\"platform\":\"douyin\"}"
|
|
|
72
74
|
## 支持的典型能力
|
|
73
75
|
|
|
74
76
|
- 作品查询:`yizhuan execute --ability works --query "AI 相关的热门作品有哪些"`
|
|
77
|
+
- 作品评论:`yizhuan execute --ability works --params '{"mode":"comments","platform":"douyin","workId":"123456","count":20}'`
|
|
78
|
+
- 按标题查询评论:`yizhuan execute --ability works --params '{"mode":"comments","platform":"douyin","title":"作品标题"}'`;标题必须唯一匹配,多个候选时 CLI 会要求补充条件。
|
|
79
|
+
- 微信公众号/视频号评论:`yizhuan execute --ability works --params '{"mode":"comments","platform":"weixin","workId":"https://mp.weixin.qq.com/s/文章链接","count":20}'`;这两个平台的 `workId` 使用作品链接。
|
|
75
80
|
- 作者查询:`yizhuan execute --ability accounts --query "查询张三这个作者"`
|
|
76
81
|
- 作者指标筛选:`yizhuan execute --ability author_query --params '{"platform_id":2,"min_follower":500000,"min_total_likes":100000}'`
|
|
77
82
|
- 作者全网搜索:`yizhuan execute --ability accounts --params '{"mode":"public_search","platform":"toutiao","keyword":"张三"}'`
|
|
@@ -82,6 +87,8 @@ yizhuan execute --ability hot_topics --params "{\"platform\":\"douyin\"}"
|
|
|
82
87
|
- 竞品动态筛选:`yizhuan execute --ability radar --params '{"mode":"timeline","timeRange":"7d","platform":"baijiahao","keyword":"AI"}'`
|
|
83
88
|
- 取消关注雷达作者:先查询关注列表拿到 `articleId`,再执行 `yizhuan execute --ability radar --params '{"mode":"unfollow","articleId":"205169870"}'`
|
|
84
89
|
- 会员查询:`yizhuan execute --ability membership --query "我的会员什么时候到期"`
|
|
90
|
+
- 内容提取:`yizhuan execute --ability content_extract --url "https://example.com/article"`
|
|
91
|
+
- 易撰作品内容提取:`yizhuan execute --ability content_extract --params '{"contentId":12345}'`
|
|
85
92
|
|
|
86
93
|
如果你是通过 Codex、Cursor、Claude Code 这类 Agent 使用 CLI,也可以直接用自然语言提问,例如:
|
|
87
94
|
|
|
@@ -90,3 +97,5 @@ yizhuan execute --ability hot_topics --params "{\"platform\":\"douyin\"}"
|
|
|
90
97
|
- `查询 AI 相关的热门文章`
|
|
91
98
|
- `取消关注陈某某先生`
|
|
92
99
|
- `我的会员什么时候到期`
|
|
100
|
+
|
|
101
|
+
纯创作请求(例如“写一篇中秋文章”)由当前 Agent 完成,不调用 `content_extract`;CLI 只提供真实内容素材,不负责生成、改写或发布。
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yizhuan-cli
|
|
3
3
|
description: |
|
|
4
|
-
安装、配置并使用易撰 CLI(yizhuan
|
|
4
|
+
安装、配置并使用易撰 CLI(yizhuan 命令)查询真实数据、提取文章/视频/图文内容的全量 Skill;若已安装用户明确指定平台对应的 yizhuan-<platform> Skill,必须优先使用对应的拆分平台 Skill。业务数据禁止臆造,文章创作由当前 Agent 完成。
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# 易撰 CLI
|
|
@@ -40,6 +40,9 @@ yizhuan doctor
|
|
|
40
40
|
| 配置状态 | `yizhuan config show` |
|
|
41
41
|
| 作品 | `yizhuan execute --ability works --query "百家号昨天新增了哪些作品"` |
|
|
42
42
|
| 作品检索 | `yizhuan execute --ability works --params '{"mode":"search","keyword":"AI"}'` |
|
|
43
|
+
| 作品评论(明确 ID) | `yizhuan execute --ability works --params '{"mode":"comments","platform":"douyin","workId":"123456","count":20}'` |
|
|
44
|
+
| 作品评论(按标题定位) | `yizhuan execute --ability works --params '{"mode":"comments","platform":"douyin","title":"作品标题"}'` |
|
|
45
|
+
| 微信公众号/视频号评论 | `yizhuan execute --ability works --params '{"mode":"comments","platform":"weixin","workId":"https://mp.weixin.qq.com/s/文章链接","count":20}'` |
|
|
43
46
|
| 指定作者作品 | `yizhuan query --json '{"intent":"works_search","scope":"public","platform":"toutiao","authorName":"大名府名人","timeRange":"3d"}'` |
|
|
44
47
|
| 高级作品筛选 | `yizhuan query --json '{"intent":"works_search","scope":"public","platform":"weibo","categories":["娱乐","情感"],"content_type":"video","min_views":100000,"min_comments":10000,"sort_by":"views","order":"desc"}'` |
|
|
45
48
|
| 作者查询 | `yizhuan execute --ability accounts --query "张三"` |
|
|
@@ -53,10 +56,14 @@ yizhuan doctor
|
|
|
53
56
|
| 会员 | `yizhuan execute --ability membership` |
|
|
54
57
|
| 我的灵感 / 灵感库 / 收藏作品 | `yizhuan execute --ability favorites` |
|
|
55
58
|
| 一键检测 | 将标题和正文写入临时 JSON 文件后执行 `yizhuan execute --ability detection --json-file <file>` |
|
|
59
|
+
| 提取文章/视频/图文 | `yizhuan execute --ability content_extract --params '{"url":"https://..."}'` |
|
|
56
60
|
|
|
57
61
|
## 调用规则
|
|
58
62
|
|
|
59
63
|
- 需要真实易撰数据时必须调用 CLI,不直接猜测结果。
|
|
64
|
+
- 用户只说“写一篇中秋文章”等纯创作请求时,不调用 CLI,由当前 Agent 直接创作。
|
|
65
|
+
- 用户要求提取文章正文、视频口播文字或图文素材时,使用 `content_extract`;来源可以是 `url` 或易撰 `contentId`。
|
|
66
|
+
- 用户要求“根据易撰热门内容创作”时,先调用查询/提取能力获取真实素材,再由当前 Agent 完成创作;CLI 不生成、改写、生成标题或发布内容。
|
|
60
67
|
- 一键检测的原创度检测当前只有百度引擎。展示检测结果时使用 `data.originalScore` 和 `data.originalEngine`;不得把历史兼容字段 `_360`、`sogou`、`zonghe` 展示为检测引擎或额外得分。
|
|
61
68
|
- 一键检测支持 `submit`、`get` 和 `wait` 模式。`wait` 必须按照服务端 `pollAfterSeconds` 轮询;pending、failed、timeout 状态必须如实报告,成功、失败或超时后均清理包含标题和正文的临时 JSON 文件。
|
|
62
69
|
- PowerShell 使用单引号包裹 JSON。终端可能改写引号时,将不含密钥的请求对象写入文件,再使用 `--json-file`。
|
|
@@ -71,6 +78,10 @@ yizhuan doctor
|
|
|
71
78
|
- 关注公开作者时,先用 `author_search` 取得并选定完整作者资料,再调用 `accounts` 的 `mode=follow_public_author`;必须传该结果的 `platform`、`extAuthorId` 和 `nickname`。`nickname` 必须是公开搜索结果中的作者名称,绝不能将 `extAuthorId` 作为名称写入。
|
|
72
79
|
- 公开作者结果中的 `workCount: 0` 可能是上游未返回作品数后的缺省值,不代表作者没有作品;是否有作品必须以 `works_search` 的结果为准。
|
|
73
80
|
- 作品复合筛选优先使用统一协议 `intent=works_search`。平台使用 `platform`,分类使用 `categories`,内容类型使用 `content_type`,最低阅读/播放和评论分别使用 `min_views`、`min_comments`;需要固定顺序时显式传 `sort_by` 和 `order`。不得把作品查询改成作者、雷达或热点查询。
|
|
81
|
+
- 查询作品评论使用 `works` 的 `mode=comments`。有明确平台作品 ID 时传 `platform` + `workId`;只有作品标题时传 `platform` + `title`,CLI 会先检索作品,只有标题唯一匹配且能解析平台作品 ID 才继续查询。多个候选返回 `AMBIGUOUS_WORK`,不得自行猜测。
|
|
82
|
+
- 微信公众号和微信视频号评论通过登录采集机获取,平台分别传 `weixin`/`wxgzh` 和 `wxsph`/`视频号`,`workId` 必须是作品链接;这两个平台暂不支持 `cursor` 和 `parentId`。
|
|
83
|
+
- 作品评论默认返回一页、最多 50 条,未传 `count` 时按 20 条处理。需要继续翻页时,必须将上一页返回的 `data.cursor` 原样传回,依据 `data.hasMore` 停止;不得把评论数 `metrics.commentCount` 当作评论正文,也不得声称结果是“最新”或“热门”。
|
|
84
|
+
- 作品评论返回 `data.items[].text`、`user`、`likeCount`、`replyCount`、`ipLabel`、`createdAt` 等字段。查询评论回复时可额外传 `parentId`。评论发布、删除、点赞不属于本 Skill 能力。
|
|
74
85
|
- 当前 CLI 不支持按单篇作品 URL 或平台侧 `articleId` 精确查询作品详情。用户提供作品链接或文章 ID 时,直接说明该能力暂不支持;不得将完整链接或纯文章 ID 改作 `keyword` 搜索,也不得把这种搜索返回的 `empty` 解读为全网不存在该作品。
|
|
75
86
|
- 作品查询返回后必须逐项核对用户要求的分类、类型和指标阈值;低于阈值或缺少必要指标的项目不得作为符合条件的结果展示,也不得擅自放宽条件。展示总数必须使用返回的 `data.total`,不能把当前页条数当成总数。
|
|
76
87
|
- 作者累计指标筛选使用 `author_query`,不得改成作品指标或擅自降低阈值。必须逐项映射用户给出的所有条件:粉丝使用 `min_follower` / `max_follower`,点赞使用 `min_total_likes` / `max_total_likes`,阅读使用 `min_total_views` / `max_total_views`,关注使用 `min_friends` / `max_friends`,作品数使用 `min_work_count` / `max_work_count`;复合条件不得遗漏任何字段。例如“百家号粉丝大于50万,点赞大于10万”必须同时传 `platform_id:2`、`min_follower:500000` 和 `min_total_likes:100000`。
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"name": "yizhuan-cli",
|
|
4
|
-
"version": "0.1.16
|
|
5
|
-
"compatibleCliVersion": "0.1.16
|
|
6
|
-
"source": "npm:@yizhuan-cli/cli@0.1.16
|
|
4
|
+
"version": "0.1.16",
|
|
5
|
+
"compatibleCliVersion": "0.1.16",
|
|
6
|
+
"source": "npm:@yizhuan-cli/cli@0.1.16",
|
|
7
7
|
"digestAlgorithm": "sha256",
|
|
8
8
|
"files": [
|
|
9
9
|
{
|
|
10
10
|
"path": "SKILL.md",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "b58b68be1bd53fb3c9c572e8ccefcf000ad837c4f54d5269959d23f2c12150d4"
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
14
|
}
|
package/src/doctor.js
CHANGED
|
@@ -210,7 +210,7 @@ export function inspectSkill(
|
|
|
210
210
|
}
|
|
211
211
|
return check('pass', 'skill', `正式 Skill ${metadata.version} 来源和摘要有效:${resolved.target}`)
|
|
212
212
|
} catch (error) {
|
|
213
|
-
return check('fail', 'skill', `正式 Skill 校验失败:${error.message}`, '
|
|
213
|
+
return check('fail', 'skill', `正式 Skill 校验失败:${error.message}`, '人工确认后运行 yizhuan skill sync --repair,或移走冲突内容后重新同步。')
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
|
package/src/index.js
CHANGED
|
@@ -26,7 +26,8 @@ const ABILITIES = new Set([
|
|
|
26
26
|
'work_query',
|
|
27
27
|
'membership_info',
|
|
28
28
|
'favorites_query',
|
|
29
|
-
'detection'
|
|
29
|
+
'detection',
|
|
30
|
+
'content_extract'
|
|
30
31
|
])
|
|
31
32
|
|
|
32
33
|
const PARAM_FLAG_KEYS = [
|
|
@@ -43,7 +44,9 @@ const PARAM_FLAG_KEYS = [
|
|
|
43
44
|
'authorId',
|
|
44
45
|
'extAuthorId',
|
|
45
46
|
'collectionType',
|
|
46
|
-
'metric'
|
|
47
|
+
'metric',
|
|
48
|
+
'url', 'contentType', 'contentId', 'content_id', 'platformCode', 'platform_code',
|
|
49
|
+
'outputFormat', 'output_format', 'includeImages', 'includeTranscriptSegments', 'source'
|
|
47
50
|
]
|
|
48
51
|
|
|
49
52
|
function printHelp() {
|
|
@@ -59,17 +62,20 @@ Usage:
|
|
|
59
62
|
yizhuan config init [--api-key-stdin] [--api-base-url <url>] [--force]
|
|
60
63
|
yizhuan config path
|
|
61
64
|
yizhuan config show
|
|
62
|
-
yizhuan skill sync [--global]
|
|
65
|
+
yizhuan skill sync [--global] [--host <name>] [--repair]
|
|
63
66
|
yizhuan doctor [--json]
|
|
64
67
|
|
|
65
68
|
Examples:
|
|
66
69
|
yizhuan execute --ability works --query "百家号昨天新增了哪些作品"
|
|
70
|
+
yizhuan execute --ability works --params '{"mode":"comments","platform":"douyin","workId":"123456","count":20}'
|
|
71
|
+
yizhuan execute --ability works --params '{"mode":"comments","platform":"douyin","title":"作品标题","count":20}'
|
|
67
72
|
yizhuan execute --ability hot_topics --platform douyin
|
|
68
73
|
yizhuan execute --ability hot_topics --params '{"platform":"douyin"}'
|
|
69
74
|
yizhuan execute --ability accounts --params '{"mode":"public_search","platform":"toutiao","keyword":"张三"}'
|
|
70
75
|
yizhuan execute --ability radar --params '{"mode":"following"}'
|
|
71
76
|
yizhuan execute --ability radar --mode timeline --timeRange 7d
|
|
72
77
|
yizhuan execute --ability detection --json-file detection.json
|
|
78
|
+
yizhuan execute --ability content_extract --url "https://example.com/article"
|
|
73
79
|
yizhuan execute --ability radar --params '{"mode":"unfollow","articleId":"205169870"}'
|
|
74
80
|
|
|
75
81
|
Notes:
|
|
@@ -92,6 +98,7 @@ Abilities:
|
|
|
92
98
|
public_author_search 作者全网搜索,兼容低层能力
|
|
93
99
|
public_work_search 作品全网搜索,兼容低层能力
|
|
94
100
|
detection 一键检测(原创/风险)
|
|
101
|
+
content_extract 提取文章、视频和图文内容
|
|
95
102
|
|
|
96
103
|
Config:
|
|
97
104
|
${CONFIG_PATH}
|
|
@@ -220,7 +227,18 @@ function readConfig() {
|
|
|
220
227
|
|
|
221
228
|
return {
|
|
222
229
|
apiBaseUrl: String(config.apiBaseUrl).replace(/\/+$/, ''),
|
|
223
|
-
apiKey: String(config.apiKey)
|
|
230
|
+
apiKey: String(config.apiKey),
|
|
231
|
+
telemetryEnabled: process.env.YIZHUAN_TELEMETRY !== '0'
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function buildRequestHeaders(config) {
|
|
236
|
+
return {
|
|
237
|
+
authorization: config.apiKey,
|
|
238
|
+
'content-type': 'application/json',
|
|
239
|
+
'x-device-type': 'pc',
|
|
240
|
+
'x-yizhuan-cli-version': readPackageVersion(),
|
|
241
|
+
'x-yizhuan-telemetry': config.telemetryEnabled ? '1' : '0'
|
|
224
242
|
}
|
|
225
243
|
}
|
|
226
244
|
|
|
@@ -235,7 +253,16 @@ function stripWrappingQuotes(text) {
|
|
|
235
253
|
return value
|
|
236
254
|
}
|
|
237
255
|
|
|
238
|
-
|
|
256
|
+
// Platform work IDs can exceed Number.MAX_SAFE_INTEGER (Douyin aweme IDs are
|
|
257
|
+
// commonly 19 digits). Keep them opaque even when params use loose key=value
|
|
258
|
+
// parsing; JSON input already preserves quoted strings.
|
|
259
|
+
const OPAQUE_ID_KEYS = new Set([
|
|
260
|
+
'extAuthorId',
|
|
261
|
+
'ext_author_id',
|
|
262
|
+
'author_id',
|
|
263
|
+
'workId',
|
|
264
|
+
'work_id'
|
|
265
|
+
])
|
|
239
266
|
|
|
240
267
|
function coerceScalar(value, key) {
|
|
241
268
|
const text = stripWrappingQuotes(value).replace(/\\_/g, '_').trim()
|
|
@@ -323,6 +350,65 @@ function assignFlagParams(params, args) {
|
|
|
323
350
|
return params
|
|
324
351
|
}
|
|
325
352
|
|
|
353
|
+
const AUTHOR_RANKING_METRICS = [
|
|
354
|
+
{
|
|
355
|
+
key: 'friendsCount',
|
|
356
|
+
label: '关注数',
|
|
357
|
+
patterns: [/关注/, /friends(?:Count|_count)?/i]
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
key: 'followerCount',
|
|
361
|
+
label: '粉丝数',
|
|
362
|
+
patterns: [/粉丝/, /follower(?:Count|_count)?/i]
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
key: 'totalViews',
|
|
366
|
+
label: '阅读数',
|
|
367
|
+
patterns: [/阅读|播放/, /(?:total)?views?/i]
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
key: 'totalLikes',
|
|
371
|
+
label: '点赞数',
|
|
372
|
+
patterns: [/点赞/, /(?:total)?likes?/i]
|
|
373
|
+
}
|
|
374
|
+
]
|
|
375
|
+
|
|
376
|
+
function rankingMetric(query, params) {
|
|
377
|
+
const text = [
|
|
378
|
+
query,
|
|
379
|
+
params?.metric,
|
|
380
|
+
params?.sortBy,
|
|
381
|
+
params?.sort_by,
|
|
382
|
+
params?.order
|
|
383
|
+
].filter(Boolean).join(' ')
|
|
384
|
+
if (!/(最高|最多|最大|第一|排行|排名|top)/i.test(text)) return undefined
|
|
385
|
+
return AUTHOR_RANKING_METRICS.find((metric) => metric.patterns.some((pattern) => pattern.test(text)))
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function annotateUnavailableAuthorRanking(result, { query, params } = {}) {
|
|
389
|
+
const metric = rankingMetric(query, params)
|
|
390
|
+
const items = result?.data?.items
|
|
391
|
+
if (!metric || !Array.isArray(items) || items.length === 0) return result
|
|
392
|
+
|
|
393
|
+
const allValuesAreZero = items.every((item) => Number(item?.[metric.key]) === 0)
|
|
394
|
+
if (!allValuesAreZero) return result
|
|
395
|
+
|
|
396
|
+
const message = `查询到的作者${metric.label}均为 0,无法据此排序或判断最高作者`
|
|
397
|
+
return {
|
|
398
|
+
...result,
|
|
399
|
+
message: result.message || message,
|
|
400
|
+
data: {
|
|
401
|
+
...result.data,
|
|
402
|
+
ranking: {
|
|
403
|
+
metric: metric.key,
|
|
404
|
+
status: 'unavailable',
|
|
405
|
+
reason: 'all_returned_values_zero',
|
|
406
|
+
message
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
326
412
|
export function buildExecuteParams(args) {
|
|
327
413
|
if (args.jsonFile) {
|
|
328
414
|
const conflictingKeys = ['params', 'query', ...PARAM_FLAG_KEYS].filter(
|
|
@@ -372,19 +458,16 @@ async function execute(args) {
|
|
|
372
458
|
throw new Error(`未知 ability:${ability || ''},请执行 yizhuan --help 查看可用能力`)
|
|
373
459
|
}
|
|
374
460
|
|
|
461
|
+
const requestParams = buildExecuteParams(args)
|
|
375
462
|
const config = readConfig()
|
|
376
463
|
const response = await fetch(`${config.apiBaseUrl}/api/cli.execute`, {
|
|
377
464
|
method: 'POST',
|
|
378
|
-
headers:
|
|
379
|
-
authorization: config.apiKey,
|
|
380
|
-
'content-type': 'application/json',
|
|
381
|
-
'x-device-type': 'pc'
|
|
382
|
-
},
|
|
465
|
+
headers: buildRequestHeaders(config),
|
|
383
466
|
body: JSON.stringify({
|
|
384
467
|
json: {
|
|
385
468
|
ability,
|
|
386
469
|
query: typeof args.query === 'string' ? args.query : undefined,
|
|
387
|
-
params:
|
|
470
|
+
params: requestParams
|
|
388
471
|
}
|
|
389
472
|
})
|
|
390
473
|
})
|
|
@@ -395,7 +478,6 @@ async function execute(args) {
|
|
|
395
478
|
}
|
|
396
479
|
|
|
397
480
|
let result = payloadResult(JSON.parse(text))
|
|
398
|
-
const requestParams = buildExecuteParams(args)
|
|
399
481
|
if (
|
|
400
482
|
ability === 'detection' &&
|
|
401
483
|
requestParams?.wait === true &&
|
|
@@ -412,11 +494,7 @@ async function execute(args) {
|
|
|
412
494
|
if (Date.now() >= deadline) break
|
|
413
495
|
const poll = await fetch(`${config.apiBaseUrl}/api/cli.execute`, {
|
|
414
496
|
method: 'POST',
|
|
415
|
-
headers:
|
|
416
|
-
authorization: config.apiKey,
|
|
417
|
-
'content-type': 'application/json',
|
|
418
|
-
'x-device-type': 'pc'
|
|
419
|
-
},
|
|
497
|
+
headers: buildRequestHeaders(config),
|
|
420
498
|
body: JSON.stringify({
|
|
421
499
|
json: { ability: 'detection', params: { mode: 'get', id: result.data.id } }
|
|
422
500
|
})
|
|
@@ -426,6 +504,10 @@ async function execute(args) {
|
|
|
426
504
|
result = payloadResult(JSON.parse(pollText))
|
|
427
505
|
}
|
|
428
506
|
}
|
|
507
|
+
result = annotateUnavailableAuthorRanking(result, {
|
|
508
|
+
query: typeof args.query === 'string' ? args.query : undefined,
|
|
509
|
+
params: requestParams
|
|
510
|
+
})
|
|
429
511
|
console.log(JSON.stringify(result, null, 2))
|
|
430
512
|
}
|
|
431
513
|
|
|
@@ -459,18 +541,16 @@ async function query(args) {
|
|
|
459
541
|
const config = readConfig()
|
|
460
542
|
const response = await fetch(`${config.apiBaseUrl}/api/agent.query`, {
|
|
461
543
|
method: 'POST',
|
|
462
|
-
headers:
|
|
463
|
-
authorization: config.apiKey,
|
|
464
|
-
'content-type': 'application/json',
|
|
465
|
-
'x-device-type': 'pc'
|
|
466
|
-
},
|
|
544
|
+
headers: buildRequestHeaders(config),
|
|
467
545
|
body: JSON.stringify({ json: request })
|
|
468
546
|
})
|
|
469
547
|
const text = await response.text()
|
|
470
548
|
if (!response.ok) throw new Error(`请求失败 HTTP ${response.status}:${text}`)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
549
|
+
const result = annotateUnavailableAuthorRanking(payloadResult(JSON.parse(text)), {
|
|
550
|
+
query: request.query || request.keyword,
|
|
551
|
+
params: request
|
|
552
|
+
})
|
|
553
|
+
console.log(renderQuery(result, args.format || 'json'))
|
|
474
554
|
}
|
|
475
555
|
|
|
476
556
|
async function main() {
|
|
@@ -510,7 +590,7 @@ async function main() {
|
|
|
510
590
|
}
|
|
511
591
|
|
|
512
592
|
if (command === 'skill' && args._[1] === 'sync') {
|
|
513
|
-
assertCommandArgs(args, 'skill sync', new Set(['global', 'host', 'help']), 2)
|
|
593
|
+
assertCommandArgs(args, 'skill sync', new Set(['global', 'host', 'repair', 'help']), 2)
|
|
514
594
|
await runSkillSync(args)
|
|
515
595
|
return
|
|
516
596
|
}
|
package/src/skill.js
CHANGED
|
@@ -90,7 +90,7 @@ export function loadOfficialSkill(resourceDir = DEFAULT_RESOURCE_DIR) {
|
|
|
90
90
|
return { manifest, resourceDir }
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
export function readSkillMetadata(target) {
|
|
93
|
+
export function readSkillMetadata(target, { allowModified = false } = {}) {
|
|
94
94
|
const metadataPath = path.join(target, SKILL_METADATA_FILE)
|
|
95
95
|
if (!fs.existsSync(metadataPath)) {
|
|
96
96
|
throw new Error(`目标存在未受易撰管理的同名内容,已停止同步:${target}`)
|
|
@@ -111,7 +111,7 @@ export function readSkillMetadata(target) {
|
|
|
111
111
|
}
|
|
112
112
|
for (const file of metadata.files) {
|
|
113
113
|
const installedPath = safeResourcePath(target, file.path)
|
|
114
|
-
if (!fs.existsSync(installedPath) || sha256File(installedPath) !== file.sha256) {
|
|
114
|
+
if (!allowModified && (!fs.existsSync(installedPath) || sha256File(installedPath) !== file.sha256)) {
|
|
115
115
|
throw new Error(`已安装 Skill 包含未知修改,已停止覆盖:${file.path}`)
|
|
116
116
|
}
|
|
117
117
|
}
|
|
@@ -182,14 +182,15 @@ export function syncOfficialSkill(
|
|
|
182
182
|
{
|
|
183
183
|
loadBundle = loadOfficialSkill,
|
|
184
184
|
installBundle = installStagedSkill,
|
|
185
|
-
now = () => new Date()
|
|
185
|
+
now = () => new Date(),
|
|
186
|
+
repair = false
|
|
186
187
|
} = {}
|
|
187
188
|
) {
|
|
188
189
|
const bundle = loadBundle()
|
|
189
190
|
const { manifest } = bundle
|
|
190
191
|
let installed
|
|
191
192
|
if (fs.existsSync(resolved.target)) {
|
|
192
|
-
installed = readSkillMetadata(resolved.target)
|
|
193
|
+
installed = readSkillMetadata(resolved.target, { allowModified: repair })
|
|
193
194
|
const comparison = compareVersions(installed.version, manifest.version)
|
|
194
195
|
if (comparison > 0) {
|
|
195
196
|
return { action: 'newer', metadata: installed, target: resolved.target }
|
|
@@ -257,11 +258,12 @@ export function resolveSkillTarget(
|
|
|
257
258
|
|
|
258
259
|
export function printSkillSyncHelp() {
|
|
259
260
|
console.log(`Usage:
|
|
260
|
-
yizhuan skill sync [--global] [--host <name>]
|
|
261
|
+
yizhuan skill sync [--global] [--host <name>] [--repair]
|
|
261
262
|
|
|
262
263
|
Options:
|
|
263
264
|
--global 安装到当前受支持宿主的用户级 Skill 目录
|
|
264
265
|
--host 明确指定 codex、agents、trae、claude 或 cursor
|
|
266
|
+
--repair 仅覆盖带官方托管元数据但内容校验失败的 Skill
|
|
265
267
|
--help 显示本帮助`)
|
|
266
268
|
}
|
|
267
269
|
|
|
@@ -272,7 +274,7 @@ export async function runSkillSync(args) {
|
|
|
272
274
|
}
|
|
273
275
|
|
|
274
276
|
const resolved = resolveSkillTarget({ global: Boolean(args.global), host: args.host })
|
|
275
|
-
const result = syncOfficialSkill(resolved)
|
|
277
|
+
const result = syncOfficialSkill(resolved, { repair: Boolean(args.repair) })
|
|
276
278
|
const actionText = {
|
|
277
279
|
installed: '已安装',
|
|
278
280
|
updated: '已更新',
|