@yizhuan-cli/cli 0.1.17-beta.1 → 0.1.17-beta.2
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/package.json +2 -1
- package/resources/yizhuan-cli/SKILL.md +10 -0
- package/resources/yizhuan-cli/manifest.json +4 -4
- package/src/index.js +19 -5
- package/src/usage.js +60 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yizhuan-cli/cli",
|
|
3
|
-
"version": "0.1.17-beta.
|
|
3
|
+
"version": "0.1.17-beta.2",
|
|
4
4
|
"description": "易撰命令行工具,用于通过 API Key 查询易撰真实数据。",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"src/config.js",
|
|
13
13
|
"src/skill.js",
|
|
14
14
|
"src/doctor.js",
|
|
15
|
+
"src/usage.js",
|
|
15
16
|
"resources",
|
|
16
17
|
"README.md"
|
|
17
18
|
],
|
|
@@ -124,3 +124,13 @@ try {
|
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
只有元数据参与使用统计,不额外上传用户问题、关键词、链接或正文。尊重用户现有的 `YIZHUAN_TELEMETRY=0` 设置,不启用或覆盖它。安装、配置、doctor、help 和 version 不作为业务使用;来源设置无需额外询问用户。来源标签不是触发新业务调用或定时任务的授权。
|
|
127
|
+
|
|
128
|
+
## 任务关联与中文功能统计
|
|
129
|
+
|
|
130
|
+
使用支持任务关联的新版本 CLI 时,每次明确用户目标或自动任务运行生成一个随机 UUID 作为 taskId。同一目标内的 execute/query 共用 `--task-id`,新的独立目标更换 ID;不要把整个聊天会话当作一次任务。使用 `yizhuan task start --task-id <uuid>` 建立任务,任务结束使用 `yizhuan task finish --task-id <uuid> --status completed|failed|cancelled`。自由任务不必声明目标路径;已明确固定流程时,可在 start 使用 `--goal-feature-ids works_search,content_extract,content_detection` 声明内部功能标识。
|
|
131
|
+
|
|
132
|
+
每次业务操作生成独立 UUID 并传入 `--operation-id`;同一操作的技术重试复用该 ID,用户重新执行则换 ID。仅在后一步实际使用了前一步的输出时,传入 `--parent-operation-ids <前序操作UUID,...>`;不能仅因先后执行就声明连接。串行任务可传 `--sequence 0`、`--sequence 1` 等顺序,并行步骤不要伪造成串行顺序。分页通常是新的业务查询操作,CLI 内置检测轮询自动复用操作标识且不重复计数。
|
|
133
|
+
|
|
134
|
+
也可通过当前任务子进程的 `YIZHUAN_TASK_ID`、`YIZHUAN_OPERATION_ID`、`YIZHUAN_PARENT_OPERATION_IDS`、`YIZHUAN_SEQUENCE` 传递;调用结束恢复旧值,避免继承到新任务。关联标识只能是随机 UUID,不包含用户提问、关键词或任何业务内容。关闭 telemetry 时不要额外执行统计任务 start/finish;旧版本 CLI 不支持这些参数时正常使用业务命令,关联标为未知,不捏造路径。
|
|
135
|
+
|
|
136
|
+
统计和用户解释统一使用“作品查询”“作者查询”“内容提取”“一键检测”等中文功能名称。命令名和内部标识只用于实现,不作为面向用户的功能名称。
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"name": "yizhuan-cli",
|
|
4
|
-
"version": "0.1.17-beta.
|
|
5
|
-
"compatibleCliVersion": "0.1.17-beta.
|
|
6
|
-
"source": "npm:@yizhuan-cli/cli@0.1.17-beta.
|
|
4
|
+
"version": "0.1.17-beta.2",
|
|
5
|
+
"compatibleCliVersion": "0.1.17-beta.2",
|
|
6
|
+
"source": "npm:@yizhuan-cli/cli@0.1.17-beta.2",
|
|
7
7
|
"digestAlgorithm": "sha256",
|
|
8
8
|
"files": [
|
|
9
9
|
{
|
|
10
10
|
"path": "SKILL.md",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "c19151583a93ae02008f1ff30b13d1ae2ed71ec143f9ef7b0ed47ac2648a18e6"
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
14
|
}
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
5
5
|
import { getConfigPath, maskApiKey, runConfigInit } from './config.js'
|
|
6
6
|
import { runSkillSync } from './skill.js'
|
|
7
7
|
import { runDoctor } from './doctor.js'
|
|
8
|
+
import { USAGE_FLAGS, usageHeaders, runUsageTask } from './usage.js'
|
|
8
9
|
|
|
9
10
|
const CLI_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
10
11
|
const PACKAGE_PATH = path.join(CLI_DIR, 'package.json')
|
|
@@ -64,6 +65,8 @@ Usage:
|
|
|
64
65
|
yizhuan config show
|
|
65
66
|
yizhuan skill sync [--global] [--host <name>] [--repair]
|
|
66
67
|
yizhuan doctor [--json]
|
|
68
|
+
yizhuan task start [--task-id <uuid>] [--goal-feature-ids <功能标识列表>]
|
|
69
|
+
yizhuan task finish --task-id <uuid> [--status completed|failed|cancelled]
|
|
67
70
|
|
|
68
71
|
Examples:
|
|
69
72
|
yizhuan execute --ability works --query "百家号昨天新增了哪些作品"
|
|
@@ -105,6 +108,10 @@ Config:
|
|
|
105
108
|
可通过环境变量 YIZHUAN_CONFIG_PATH 指定独立配置文件
|
|
106
109
|
YIZHUAN_TRIGGER=user_initiated 标识用户主动请求,automated 标识自动任务;未设置为 unknown
|
|
107
110
|
YIZHUAN_TELEMETRY=0 关闭使用元数据埋点
|
|
111
|
+
YIZHUAN_TASK_ID=<uuid> 关联同一次用户任务,新的目标使用新 ID
|
|
112
|
+
YIZHUAN_OPERATION_ID=<uuid> 标识一次操作,技术重试复用,新操作更换
|
|
113
|
+
YIZHUAN_PARENT_OPERATION_IDS=<uuid,...> 声明实际使用的前序输出,不能只按时间猜测
|
|
114
|
+
YIZHUAN_SEQUENCE=<整数> 声明任务顺序;也支持 --task-id / --operation-id / --parent-operation-ids / --sequence
|
|
108
115
|
`)
|
|
109
116
|
}
|
|
110
117
|
|
|
@@ -455,7 +462,7 @@ async function execute(args) {
|
|
|
455
462
|
assertCommandArgs(
|
|
456
463
|
args,
|
|
457
464
|
'execute',
|
|
458
|
-
new Set(['ability', 'query', 'params', 'jsonFile', ...PARAM_FLAG_KEYS])
|
|
465
|
+
new Set(['ability', 'query', 'params', 'jsonFile', ...PARAM_FLAG_KEYS, ...USAGE_FLAGS])
|
|
459
466
|
)
|
|
460
467
|
const ability = args.ability
|
|
461
468
|
if (!ABILITIES.has(ability)) {
|
|
@@ -464,9 +471,10 @@ async function execute(args) {
|
|
|
464
471
|
|
|
465
472
|
const requestParams = buildExecuteParams(args)
|
|
466
473
|
const config = readConfig()
|
|
474
|
+
const requestHeaders = { ...buildRequestHeaders(config), ...usageHeaders(args) }
|
|
467
475
|
const response = await fetch(`${config.apiBaseUrl}/api/cli.execute`, {
|
|
468
476
|
method: 'POST',
|
|
469
|
-
headers:
|
|
477
|
+
headers: requestHeaders,
|
|
470
478
|
body: JSON.stringify({
|
|
471
479
|
json: {
|
|
472
480
|
ability,
|
|
@@ -498,7 +506,7 @@ async function execute(args) {
|
|
|
498
506
|
if (Date.now() >= deadline) break
|
|
499
507
|
const poll = await fetch(`${config.apiBaseUrl}/api/cli.execute`, {
|
|
500
508
|
method: 'POST',
|
|
501
|
-
headers: { ...
|
|
509
|
+
headers: { ...requestHeaders, 'x-yizhuan-trigger': 'polling' },
|
|
502
510
|
body: JSON.stringify({
|
|
503
511
|
json: { ability: 'detection', params: { mode: 'get', id: result.data.id } }
|
|
504
512
|
})
|
|
@@ -534,7 +542,7 @@ function renderQuery(payload, format) {
|
|
|
534
542
|
}
|
|
535
543
|
|
|
536
544
|
async function query(args) {
|
|
537
|
-
assertCommandArgs(args, 'query', new Set(['json', 'jsonFile', 'params', 'format']))
|
|
545
|
+
assertCommandArgs(args, 'query', new Set(['json', 'jsonFile', 'params', 'format', ...USAGE_FLAGS]))
|
|
538
546
|
const jsonText = args.jsonFile
|
|
539
547
|
? fs.readFileSync(path.resolve(String(args.jsonFile)), 'utf8')
|
|
540
548
|
: args.json === '-'
|
|
@@ -545,7 +553,7 @@ async function query(args) {
|
|
|
545
553
|
const config = readConfig()
|
|
546
554
|
const response = await fetch(`${config.apiBaseUrl}/api/agent.query`, {
|
|
547
555
|
method: 'POST',
|
|
548
|
-
headers: buildRequestHeaders(config),
|
|
556
|
+
headers: { ...buildRequestHeaders(config), ...usageHeaders(args) },
|
|
549
557
|
body: JSON.stringify({ json: request })
|
|
550
558
|
})
|
|
551
559
|
const text = await response.text()
|
|
@@ -610,6 +618,12 @@ async function main() {
|
|
|
610
618
|
await execute(args)
|
|
611
619
|
return
|
|
612
620
|
}
|
|
621
|
+
if (command === 'task') {
|
|
622
|
+
assertCommandArgs(args, 'task', new Set(['taskId', 'status', 'goalFeatureIds']), 2)
|
|
623
|
+
const config = readConfig()
|
|
624
|
+
await runUsageTask(args, config, buildRequestHeaders(config))
|
|
625
|
+
return
|
|
626
|
+
}
|
|
613
627
|
if (command === 'query') {
|
|
614
628
|
await query(args)
|
|
615
629
|
return
|
package/src/usage.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
export const USAGE_FLAGS = ['taskId', 'operationId', 'parentOperationIds', 'sequence']
|
|
4
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
|
5
|
+
export function usageHeaders(args = {}, env = process.env) {
|
|
6
|
+
const values = {
|
|
7
|
+
taskId: args.taskId ?? env.YIZHUAN_TASK_ID,
|
|
8
|
+
operationId: args.operationId ?? env.YIZHUAN_OPERATION_ID ?? randomUUID(),
|
|
9
|
+
parentOperationIds: args.parentOperationIds ?? env.YIZHUAN_PARENT_OPERATION_IDS,
|
|
10
|
+
sequence: args.sequence ?? env.YIZHUAN_SEQUENCE
|
|
11
|
+
}
|
|
12
|
+
for (const field of ['taskId', 'operationId'])
|
|
13
|
+
if (values[field] !== undefined && !uuid.test(String(values[field])))
|
|
14
|
+
throw new Error(`${field} 必须是 UUID,不能使用用户提问或业务内容`)
|
|
15
|
+
const parents = values.parentOperationIds
|
|
16
|
+
? String(values.parentOperationIds).split(',').filter(Boolean)
|
|
17
|
+
: []
|
|
18
|
+
if (parents.length > 20 || parents.some((id) => !uuid.test(id)))
|
|
19
|
+
throw new Error('parentOperationIds 必须是最多20个逗号分隔的 UUID')
|
|
20
|
+
if (parents.length && !values.taskId) throw new Error('关联前序操作必须同时提供 taskId')
|
|
21
|
+
if (
|
|
22
|
+
values.sequence !== undefined &&
|
|
23
|
+
(!/^\d+$/.test(String(values.sequence)) || Number(values.sequence) > 1000000)
|
|
24
|
+
)
|
|
25
|
+
throw new Error('sequence 必须是0到1000000之间的整数')
|
|
26
|
+
return {
|
|
27
|
+
'x-yizhuan-operation-id': String(values.operationId),
|
|
28
|
+
...(values.taskId ? { 'x-yizhuan-task-id': String(values.taskId) } : {}),
|
|
29
|
+
...(parents.length
|
|
30
|
+
? { 'x-yizhuan-parent-operation-ids': [...new Set(parents)].join(',') }
|
|
31
|
+
: {}),
|
|
32
|
+
...(values.sequence !== undefined ? { 'x-yizhuan-sequence': String(values.sequence) } : {})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function runUsageTask(args, config, headers) {
|
|
37
|
+
const action = args._[1]
|
|
38
|
+
const taskId =
|
|
39
|
+
args.taskId ?? process.env.YIZHUAN_TASK_ID ?? (action === 'start' ? randomUUID() : undefined)
|
|
40
|
+
if (!taskId || !uuid.test(taskId))
|
|
41
|
+
throw new Error('需要 UUID 格式的 --task-id 或 YIZHUAN_TASK_ID')
|
|
42
|
+
if (!['start', 'finish'].includes(action)) throw new Error('task 支持 start 或 finish')
|
|
43
|
+
const status = args.status ?? 'completed'
|
|
44
|
+
if (action === 'finish' && !['completed', 'failed', 'cancelled'].includes(status))
|
|
45
|
+
throw new Error('status 支持 completed、failed、cancelled')
|
|
46
|
+
const goalFeatureIds = args.goalFeatureIds ? String(args.goalFeatureIds).split(',') : undefined
|
|
47
|
+
const response = await fetch(
|
|
48
|
+
`${config.apiBaseUrl}/api/cli.${action === 'start' ? 'startUsageTask' : 'finishUsageTask'}`,
|
|
49
|
+
{
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers,
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
json: action === 'start' ? { taskId, goalFeatureIds } : { taskId, status }
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
const payload = await response.json()
|
|
58
|
+
if (!response.ok) throw new Error(`任务统计请求失败 HTTP ${response.status}`)
|
|
59
|
+
console.log(JSON.stringify({ taskId, ...(payload.result?.data?.json ?? payload) }, null, 2))
|
|
60
|
+
}
|