@coderpp/yunxiao-cli 0.1.8 → 0.1.10
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 +101 -1
- package/dist/src/cli.js +63 -2
- package/dist/src/client.d.ts +46 -0
- package/dist/src/client.js +180 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @coderpp/yunxiao-cli
|
|
2
2
|
|
|
3
|
-
用于评审和合并云效 Codeup
|
|
3
|
+
用于评审和合并云效 Codeup 合并请求、重试云效流水线任务、查询项目协作工作项并维护项目成员的命令行工具。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
@@ -69,6 +69,38 @@ npm version 0.1.1
|
|
|
69
69
|
git push origin main v0.1.1
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
## 技能同步
|
|
73
|
+
|
|
74
|
+
当前仓库的技能位于 `.agents/skills`,可以同步到常用技能目录:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm run skills:sync
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
同步目标:
|
|
81
|
+
|
|
82
|
+
- `~/pp-note/.agents/skills`
|
|
83
|
+
- `~/pp-note/.claude/skills`
|
|
84
|
+
- `~/.hermes/skills`
|
|
85
|
+
|
|
86
|
+
如果希望技能文件修改后自动同步,启动 watcher:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm run skills:watch
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Watcher 会先同步一次,然后持续监听 `.agents/skills` 文件变化;同名技能会覆盖,目标目录里的其他技能不会被删除。
|
|
93
|
+
|
|
94
|
+
如果希望后台常驻自动同步:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm run skills:watch:start
|
|
98
|
+
npm run skills:watch:status
|
|
99
|
+
npm run skills:watch:stop
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
后台 watcher 的 PID 和日志保存在 `.git/skill-sync-watch.*`,不会提交到仓库。
|
|
103
|
+
|
|
72
104
|
## 配置
|
|
73
105
|
|
|
74
106
|
不要把个人访问令牌写入代码或提交到仓库。建议通过环境变量提供:
|
|
@@ -186,6 +218,9 @@ node dist/src/index.js mr list --output json
|
|
|
186
218
|
node dist/src/index.js mr review 2813489 12 --output table
|
|
187
219
|
node dist/src/index.js mr release yunxiao-cli release/1.2.3 master --yes --create-only --output json
|
|
188
220
|
node dist/src/index.js mr patches 2813489 12 --output json
|
|
221
|
+
node dist/src/index.js pipeline failed-runs --hours 24 --output json
|
|
222
|
+
node dist/src/index.js pipeline runs 123 --status FAIL --output json
|
|
223
|
+
node dist/src/index.js pipeline retry-job 123 456 789 --yes --output table
|
|
189
224
|
```
|
|
190
225
|
|
|
191
226
|
支持的格式:
|
|
@@ -254,6 +289,62 @@ node dist/src/index.js member list --per-page 100
|
|
|
254
289
|
node dist/src/index.js member get <memberId> --output json
|
|
255
290
|
```
|
|
256
291
|
|
|
292
|
+
## 流水线命令
|
|
293
|
+
|
|
294
|
+
查询最近失败流水线运行实例,并且同一个流水线只保留最新一次失败运行:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
node dist/src/index.js pipeline failed-runs --hours 24 --output table
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
默认查询最近 24 小时,等价于:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
node dist/src/index.js pipeline failed-runs --hours 24
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
也可以用毫秒时间戳指定范围:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
node dist/src/index.js pipeline failed-runs \
|
|
310
|
+
--start-time 1777358348118 \
|
|
311
|
+
--end-time 1777444748118 \
|
|
312
|
+
--output json
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
查询流水线运行实例列表:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
node dist/src/index.js pipeline runs <pipelineId> --status FAIL --per-page 30
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
可用过滤参数:
|
|
322
|
+
|
|
323
|
+
- `--page`、`--per-page`
|
|
324
|
+
- `--start-time`、`--end-time`:毫秒时间戳,例如 `1729178040000`
|
|
325
|
+
- `--status`:`FAIL`、`SUCCESS`、`RUNNING`
|
|
326
|
+
- `--trigger-mode`:触发方式,常见值为 `1` 人工触发、`2` 定时触发、`3` 代码提交触发、`5` 流水线触发、`6` Webhook 触发
|
|
327
|
+
|
|
328
|
+
获取单次流水线运行详情:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
node dist/src/index.js pipeline run <pipelineId> <pipelineRunId> --output json
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
运行详情里包含阶段和任务信息,重试任务时需要从返回结果里的 `stages[].stageInfo.jobs[].id` 获取 `jobId`。
|
|
335
|
+
|
|
336
|
+
重试指定流水线任务:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
node dist/src/index.js pipeline retry-job <pipelineId> <pipelineRunId> <jobId> --yes
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
`pipeline` 也可以简写为 `pl`,`retry-job` 也可以简写为 `retry`:
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
node dist/src/index.js pl retry <pipelineId> <pipelineRunId> <jobId> --yes
|
|
346
|
+
```
|
|
347
|
+
|
|
257
348
|
## 工作项命令
|
|
258
349
|
|
|
259
350
|
查询当前用户待办工作项:
|
|
@@ -270,6 +361,14 @@ node dist/src/index.js workitem list --project-ids project-id-1,project-id-2 --a
|
|
|
270
361
|
node dist/src/index.js workitem list --project-ids project-id-1,project-id-2 --assigned-to user-id --state done
|
|
271
362
|
```
|
|
272
363
|
|
|
364
|
+
按创建人查询工作项:
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
node dist/src/index.js workitem list --project-ids project-id-1,project-id-2 --created-by user-id
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
`--created-by` 支持逗号分隔多个用户 ID,也可以使用等价参数 `--creator`。
|
|
371
|
+
|
|
273
372
|
按时间范围查询工作项:
|
|
274
373
|
|
|
275
374
|
```bash
|
|
@@ -335,5 +434,6 @@ node dist/src/index.js workitem comment <workitemId> --content "处理完成,
|
|
|
335
434
|
|
|
336
435
|
- `project member-add`
|
|
337
436
|
- `project member-remove`
|
|
437
|
+
- `pipeline retry-job`
|
|
338
438
|
- `workitem update`
|
|
339
439
|
- `workitem comment`
|
package/dist/src/cli.js
CHANGED
|
@@ -42,6 +42,9 @@ async function runCommand(scope, command, parsed, client, env) {
|
|
|
42
42
|
return runProjectCommand(command, parsed, client);
|
|
43
43
|
case "member":
|
|
44
44
|
return runMemberCommand(command, parsed, client);
|
|
45
|
+
case "pipeline":
|
|
46
|
+
case "pl":
|
|
47
|
+
return runPipelineCommand(command, parsed, client);
|
|
45
48
|
case "workitem":
|
|
46
49
|
case "wi":
|
|
47
50
|
return runWorkitemCommand(command, parsed, client, env);
|
|
@@ -141,6 +144,29 @@ async function runMemberCommand(command, parsed, client) {
|
|
|
141
144
|
throw new Error(`Unknown member command: ${command}`);
|
|
142
145
|
}
|
|
143
146
|
}
|
|
147
|
+
async function runPipelineCommand(command, parsed, client) {
|
|
148
|
+
if (command === "failed-runs" || command === "failed" || command === "latest-failed-runs") {
|
|
149
|
+
return client.listLatestFailedPipelineRuns(latestFailedPipelineRunsOptions(parsed.flags));
|
|
150
|
+
}
|
|
151
|
+
const pipelineId = requiredPositional(parsed.positionals[2], "pipelineId");
|
|
152
|
+
const pipelineRunId = parsed.positionals[3];
|
|
153
|
+
const jobId = parsed.positionals[4];
|
|
154
|
+
switch (command) {
|
|
155
|
+
case "runs":
|
|
156
|
+
case "list-runs":
|
|
157
|
+
return client.listPipelineRuns(pipelineId, pipelineRunsOptions(parsed.flags));
|
|
158
|
+
case "run":
|
|
159
|
+
case "get-run":
|
|
160
|
+
case "get":
|
|
161
|
+
return client.getPipelineRun(pipelineId, requiredPositional(pipelineRunId, "pipelineRunId"));
|
|
162
|
+
case "retry-job":
|
|
163
|
+
case "retry":
|
|
164
|
+
requireConfirmation(parsed.flags);
|
|
165
|
+
return client.retryPipelineJobRun(pipelineId, requiredPositional(pipelineRunId, "pipelineRunId"), requiredPositional(jobId, "jobId"));
|
|
166
|
+
default:
|
|
167
|
+
throw new Error(`Unknown pipeline command: ${command}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
144
170
|
async function runWorkitemCommand(command, parsed, client, env) {
|
|
145
171
|
const workitemId = parsed.positionals[2];
|
|
146
172
|
switch (command) {
|
|
@@ -242,6 +268,24 @@ function projectSearchOptions(flags) {
|
|
|
242
268
|
sort: optionalString(flags.sort) ?? "desc"
|
|
243
269
|
};
|
|
244
270
|
}
|
|
271
|
+
function pipelineRunsOptions(flags) {
|
|
272
|
+
return {
|
|
273
|
+
page: optionalNumber(flags.page),
|
|
274
|
+
perPage: optionalNumber(flags["per-page"]),
|
|
275
|
+
startTime: optionalNumber(flags["start-time"]),
|
|
276
|
+
endTime: optionalNumber(flags["end-time"]) ?? optionalNumber(flags["end-tme"]),
|
|
277
|
+
status: optionalString(flags.status),
|
|
278
|
+
triggerMode: optionalNumber(flags["trigger-mode"])
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function latestFailedPipelineRunsOptions(flags) {
|
|
282
|
+
return {
|
|
283
|
+
hours: optionalNumber(flags.hours) ?? 24,
|
|
284
|
+
startTime: optionalNumber(flags["start-time"]),
|
|
285
|
+
endTime: optionalNumber(flags["end-time"]) ?? optionalNumber(flags["end-tme"]),
|
|
286
|
+
perPage: optionalNumber(flags["per-page"]) ?? 30
|
|
287
|
+
};
|
|
288
|
+
}
|
|
245
289
|
async function searchWorkitems(client, flags, env) {
|
|
246
290
|
const projectIds = workitemProjectIds(flags, env);
|
|
247
291
|
const perPage = optionalNumber(flags["per-page"]) ?? 20;
|
|
@@ -277,7 +321,7 @@ function workitemConditions(flags) {
|
|
|
277
321
|
const rawConditions = optionalString(flags.conditions);
|
|
278
322
|
const filters = [];
|
|
279
323
|
const assignedTo = optionalString(flags["assigned-to"]) ?? optionalString(flags.assignee);
|
|
280
|
-
const creator = optionalString(flags.creator);
|
|
324
|
+
const creator = optionalString(flags["created-by"]) ?? optionalString(flags.creator);
|
|
281
325
|
const statusIds = optionalString(flags["status-ids"]);
|
|
282
326
|
const subject = optionalString(flags.subject);
|
|
283
327
|
const from = optionalString(flags.from) ?? optionalString(flags["created-after"]);
|
|
@@ -413,6 +457,9 @@ function formatTable(rows) {
|
|
|
413
457
|
}
|
|
414
458
|
const normalizedRows = rows.map((row) => (isRecord(row) ? row : { value: row }));
|
|
415
459
|
const preferredKeys = [
|
|
460
|
+
"pipelineName",
|
|
461
|
+
"pipelineRunId",
|
|
462
|
+
"pipelineId",
|
|
416
463
|
"localId",
|
|
417
464
|
"projectId",
|
|
418
465
|
"queryProjectId",
|
|
@@ -423,7 +470,17 @@ function formatTable(rows) {
|
|
|
423
470
|
"title",
|
|
424
471
|
"state",
|
|
425
472
|
"status",
|
|
473
|
+
"triggerMode",
|
|
474
|
+
"triggerModeName",
|
|
475
|
+
"startTime",
|
|
476
|
+
"startTimeText",
|
|
477
|
+
"endTime",
|
|
478
|
+
"endTimeText",
|
|
479
|
+
"createTime",
|
|
480
|
+
"gmtCreate",
|
|
426
481
|
"assignedTo",
|
|
482
|
+
"creator",
|
|
483
|
+
"createdBy",
|
|
427
484
|
"userId",
|
|
428
485
|
"userName",
|
|
429
486
|
"roleName",
|
|
@@ -648,7 +705,11 @@ Usage:
|
|
|
648
705
|
yunxiao project member-remove <projectId> --user-ids user1,user2 [--role-id project.participant] --yes
|
|
649
706
|
yunxiao member list [--page 1] [--per-page 100] [--output table|json]
|
|
650
707
|
yunxiao member get <memberId> [--output table|json]
|
|
651
|
-
yunxiao
|
|
708
|
+
yunxiao pipeline failed-runs [--hours 24] [--output table|json]
|
|
709
|
+
yunxiao pipeline runs <pipelineId> [--status FAIL] [--start-time 1729178040000] [--end-time 1729181640000] [--output table|json]
|
|
710
|
+
yunxiao pipeline run <pipelineId> <pipelineRunId> [--output table|json]
|
|
711
|
+
yunxiao pipeline retry-job <pipelineId> <pipelineRunId> <jobId> --yes [--output table|json]
|
|
712
|
+
yunxiao workitem list --project-ids p1,p2 [--assigned-to userId] [--created-by userId] [--state todo|doing|done] [--from "2026-04-01 00:00:00"] [--to "2026-04-30 23:59:59"]
|
|
652
713
|
yunxiao workitem mine --project-ids p1,p2 [--state todo|doing|done] [--output table|json]
|
|
653
714
|
yunxiao workitem get <workitemId> [--output table|json]
|
|
654
715
|
yunxiao workitem update <workitemId> --yes [--assigned-to userId] [--participants user1,user2] [--due-date "2026-05-01 18:00:00"] [--field key=value]
|
package/dist/src/client.d.ts
CHANGED
|
@@ -84,6 +84,43 @@ export interface CreateWorkitemCommentOptions {
|
|
|
84
84
|
parentId?: string;
|
|
85
85
|
}
|
|
86
86
|
export type UpdateWorkitemOptions = Record<string, unknown>;
|
|
87
|
+
export interface ListPipelineRunsOptions {
|
|
88
|
+
page?: number;
|
|
89
|
+
perPage?: number;
|
|
90
|
+
startTime?: number;
|
|
91
|
+
endTime?: number;
|
|
92
|
+
status?: "FAIL" | "SUCCESS" | "RUNNING" | string;
|
|
93
|
+
triggerMode?: number;
|
|
94
|
+
}
|
|
95
|
+
export interface ListPipelinesOptions {
|
|
96
|
+
createStartTime?: number;
|
|
97
|
+
createEndTime?: number;
|
|
98
|
+
executeStartTime?: number;
|
|
99
|
+
executeEndTime?: number;
|
|
100
|
+
pipelineName?: string;
|
|
101
|
+
statusList?: string;
|
|
102
|
+
page?: number;
|
|
103
|
+
perPage?: number;
|
|
104
|
+
}
|
|
105
|
+
export interface ListLatestFailedPipelineRunsOptions {
|
|
106
|
+
hours?: number;
|
|
107
|
+
startTime?: number;
|
|
108
|
+
endTime?: number;
|
|
109
|
+
perPage?: number;
|
|
110
|
+
}
|
|
111
|
+
export interface LatestFailedPipelineRun {
|
|
112
|
+
pipelineId: number | string;
|
|
113
|
+
pipelineName: string;
|
|
114
|
+
pipelineRunId: number | string;
|
|
115
|
+
status?: string;
|
|
116
|
+
triggerMode?: number | string;
|
|
117
|
+
triggerModeName: string;
|
|
118
|
+
startTime?: number | string;
|
|
119
|
+
startTimeText: string;
|
|
120
|
+
endTime?: number | string;
|
|
121
|
+
endTimeText: string;
|
|
122
|
+
creatorAccountId?: string;
|
|
123
|
+
}
|
|
87
124
|
export interface RepositorySummary {
|
|
88
125
|
id: number | string;
|
|
89
126
|
name?: string;
|
|
@@ -157,6 +194,11 @@ export declare class YunxiaoClient {
|
|
|
157
194
|
listWorkitemComments(workitemId: string): Promise<unknown>;
|
|
158
195
|
createWorkitemComment(workitemId: string, options: CreateWorkitemCommentOptions): Promise<unknown>;
|
|
159
196
|
getWorkitemTimeline(workitemId: string): Promise<unknown>;
|
|
197
|
+
listPipelineRuns(pipelineId: string, options?: ListPipelineRunsOptions): Promise<unknown>;
|
|
198
|
+
listPipelines(options?: ListPipelinesOptions): Promise<unknown>;
|
|
199
|
+
listLatestFailedPipelineRuns(options?: ListLatestFailedPipelineRunsOptions): Promise<LatestFailedPipelineRun[]>;
|
|
200
|
+
getPipelineRun(pipelineId: string, pipelineRunId: string): Promise<unknown>;
|
|
201
|
+
retryPipelineJobRun(pipelineId: string, pipelineRunId: string, jobId: string): Promise<unknown>;
|
|
160
202
|
createChangeRequest(repositoryId: string, options: CreateChangeRequestOptions): Promise<unknown>;
|
|
161
203
|
reviewChangeRequest(repositoryId: string, localId: number, options?: ReviewChangeRequestOptions): Promise<unknown>;
|
|
162
204
|
mergeChangeRequest(repositoryId: string, localId: number, options?: MergeChangeRequestOptions): Promise<unknown>;
|
|
@@ -172,5 +214,9 @@ export declare class YunxiaoClient {
|
|
|
172
214
|
private workitemsSearchPath;
|
|
173
215
|
private projectPath;
|
|
174
216
|
private platformMembersPath;
|
|
217
|
+
private pipelinesPath;
|
|
218
|
+
private pipelinePath;
|
|
219
|
+
private listAllPipelines;
|
|
220
|
+
private listAllPipelineRuns;
|
|
175
221
|
private request;
|
|
176
222
|
}
|
package/dist/src/client.js
CHANGED
|
@@ -109,6 +109,81 @@ export class YunxiaoClient {
|
|
|
109
109
|
]);
|
|
110
110
|
return { workitem, activities, comments };
|
|
111
111
|
}
|
|
112
|
+
listPipelineRuns(pipelineId, options = {}) {
|
|
113
|
+
return this.request("GET", `${this.pipelinePath(pipelineId)}/runs`, {
|
|
114
|
+
page: options.page,
|
|
115
|
+
perPage: options.perPage,
|
|
116
|
+
startTime: options.startTime,
|
|
117
|
+
endTme: options.endTime,
|
|
118
|
+
status: options.status,
|
|
119
|
+
triggerMode: options.triggerMode
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
listPipelines(options = {}) {
|
|
123
|
+
return this.request("GET", this.pipelinesPath(), {
|
|
124
|
+
page: options.page,
|
|
125
|
+
perPage: options.perPage,
|
|
126
|
+
executeStartTime: options.executeStartTime,
|
|
127
|
+
executeEndTime: options.executeEndTime,
|
|
128
|
+
statusList: options.statusList,
|
|
129
|
+
pipelineName: options.pipelineName,
|
|
130
|
+
createStartTime: options.createStartTime,
|
|
131
|
+
createEndTime: options.createEndTime
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async listLatestFailedPipelineRuns(options = {}) {
|
|
135
|
+
const endTime = options.endTime ?? Date.now();
|
|
136
|
+
const startTime = options.startTime ?? endTime - (options.hours ?? 24) * 60 * 60 * 1000;
|
|
137
|
+
const perPage = options.perPage ?? 30;
|
|
138
|
+
const pipelines = await this.listAllPipelines({
|
|
139
|
+
page: 1,
|
|
140
|
+
perPage,
|
|
141
|
+
executeStartTime: startTime,
|
|
142
|
+
executeEndTime: endTime,
|
|
143
|
+
statusList: "FAIL"
|
|
144
|
+
});
|
|
145
|
+
const uniquePipelines = uniqueBy(pipelines, (pipeline) => String(recordValue(pipeline, "pipelineId") ?? recordValue(pipeline, "id")));
|
|
146
|
+
const results = [];
|
|
147
|
+
for (const pipeline of uniquePipelines) {
|
|
148
|
+
const pipelineId = recordValue(pipeline, "pipelineId") ?? recordValue(pipeline, "id");
|
|
149
|
+
if (pipelineId === undefined || pipelineId === null || pipelineId === "") {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const runs = await this.listAllPipelineRuns(String(pipelineId), {
|
|
153
|
+
page: 1,
|
|
154
|
+
perPage,
|
|
155
|
+
startTime,
|
|
156
|
+
endTime,
|
|
157
|
+
status: "FAIL"
|
|
158
|
+
});
|
|
159
|
+
const latestRun = runs
|
|
160
|
+
.filter((run) => timestampInRange(recordValue(run, "startTime"), startTime, endTime))
|
|
161
|
+
.sort((left, right) => Number(recordValue(right, "startTime") ?? 0) - Number(recordValue(left, "startTime") ?? 0))[0];
|
|
162
|
+
if (!latestRun) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
results.push({
|
|
166
|
+
pipelineId: normalizeNumericId(pipelineId),
|
|
167
|
+
pipelineName: stringRecordValue(pipeline, "pipelineName") ?? stringRecordValue(pipeline, "name") ?? "",
|
|
168
|
+
pipelineRunId: normalizeNumericId(recordValue(latestRun, "pipelineRunId") ?? ""),
|
|
169
|
+
status: stringRecordValue(latestRun, "status"),
|
|
170
|
+
triggerMode: numericOrStringValue(recordValue(latestRun, "triggerMode")),
|
|
171
|
+
triggerModeName: triggerModeName(recordValue(latestRun, "triggerMode")),
|
|
172
|
+
startTime: numericOrStringValue(recordValue(latestRun, "startTime")),
|
|
173
|
+
startTimeText: formatTimestamp(recordValue(latestRun, "startTime")),
|
|
174
|
+
endTime: numericOrStringValue(recordValue(latestRun, "endTime")),
|
|
175
|
+
endTimeText: formatTimestamp(recordValue(latestRun, "endTime")),
|
|
176
|
+
creatorAccountId: stringRecordValue(latestRun, "creatorAccountId")
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return results.sort((left, right) => Number(right.startTime ?? 0) - Number(left.startTime ?? 0));
|
|
180
|
+
}
|
|
181
|
+
getPipelineRun(pipelineId, pipelineRunId) {
|
|
182
|
+
return this.request("GET", `${this.pipelinePath(pipelineId)}/runs/${encodePathSegment(pipelineRunId)}`);
|
|
183
|
+
}
|
|
184
|
+
retryPipelineJobRun(pipelineId, pipelineRunId, jobId) {
|
|
185
|
+
return this.request("PUT", `${this.pipelinePath(pipelineId)}/pipelineRuns/${encodePathSegment(pipelineRunId)}/jobs/${encodePathSegment(jobId)}/retry`);
|
|
186
|
+
}
|
|
112
187
|
createChangeRequest(repositoryId, options) {
|
|
113
188
|
return this.request("POST", `${this.repositoryPath(repositoryId)}/changeRequests`, undefined, {
|
|
114
189
|
createFrom: options.createFrom ?? "WEB",
|
|
@@ -252,6 +327,42 @@ export class YunxiaoClient {
|
|
|
252
327
|
}
|
|
253
328
|
return "/oapi/v1/platform/members";
|
|
254
329
|
}
|
|
330
|
+
pipelinesPath() {
|
|
331
|
+
if (this.edition === "center") {
|
|
332
|
+
return `/oapi/v1/flow/organizations/${encodePathSegment(this.organizationId ?? "")}/pipelines`;
|
|
333
|
+
}
|
|
334
|
+
return "/oapi/v1/flow/pipelines";
|
|
335
|
+
}
|
|
336
|
+
pipelinePath(pipelineId) {
|
|
337
|
+
const encodedPipelineId = encodePathSegment(pipelineId);
|
|
338
|
+
return `${this.pipelinesPath()}/${encodedPipelineId}`;
|
|
339
|
+
}
|
|
340
|
+
async listAllPipelines(options) {
|
|
341
|
+
const rows = [];
|
|
342
|
+
const perPage = options.perPage ?? 30;
|
|
343
|
+
for (let page = options.page ?? 1;; page += 1) {
|
|
344
|
+
const response = await this.listPipelines({ ...options, page, perPage });
|
|
345
|
+
const pageRows = arrayRecords(response);
|
|
346
|
+
rows.push(...pageRows);
|
|
347
|
+
if (pageRows.length < perPage) {
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return rows;
|
|
352
|
+
}
|
|
353
|
+
async listAllPipelineRuns(pipelineId, options) {
|
|
354
|
+
const rows = [];
|
|
355
|
+
const perPage = options.perPage ?? 30;
|
|
356
|
+
for (let page = options.page ?? 1;; page += 1) {
|
|
357
|
+
const response = await this.listPipelineRuns(pipelineId, { ...options, page, perPage });
|
|
358
|
+
const pageRows = arrayRecords(response);
|
|
359
|
+
rows.push(...pageRows);
|
|
360
|
+
if (pageRows.length < perPage) {
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return rows;
|
|
365
|
+
}
|
|
255
366
|
async request(method, path, query, body) {
|
|
256
367
|
const url = new URL(`https://${this.domain}${path}`);
|
|
257
368
|
for (const [key, value] of Object.entries(query ?? {})) {
|
|
@@ -286,6 +397,75 @@ function stripProtocol(domain) {
|
|
|
286
397
|
function encodePathSegment(value) {
|
|
287
398
|
return encodeURIComponent(value);
|
|
288
399
|
}
|
|
400
|
+
function arrayRecords(value) {
|
|
401
|
+
return Array.isArray(value) ? value.filter(isRecord) : [];
|
|
402
|
+
}
|
|
403
|
+
function isRecord(value) {
|
|
404
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
405
|
+
}
|
|
406
|
+
function recordValue(record, key) {
|
|
407
|
+
return record[key];
|
|
408
|
+
}
|
|
409
|
+
function stringRecordValue(record, key) {
|
|
410
|
+
const value = record[key];
|
|
411
|
+
return typeof value === "string" ? value : undefined;
|
|
412
|
+
}
|
|
413
|
+
function uniqueBy(values, keyFn) {
|
|
414
|
+
const seen = new Set();
|
|
415
|
+
const result = [];
|
|
416
|
+
for (const value of values) {
|
|
417
|
+
const key = keyFn(value);
|
|
418
|
+
if (!key || seen.has(key)) {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
seen.add(key);
|
|
422
|
+
result.push(value);
|
|
423
|
+
}
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
function timestampInRange(value, startTime, endTime) {
|
|
427
|
+
const timestamp = Number(value);
|
|
428
|
+
return Number.isFinite(timestamp) && timestamp >= startTime && timestamp <= endTime;
|
|
429
|
+
}
|
|
430
|
+
function normalizeNumericId(value) {
|
|
431
|
+
const numberValue = Number(value);
|
|
432
|
+
return Number.isInteger(numberValue) ? numberValue : String(value);
|
|
433
|
+
}
|
|
434
|
+
function numericOrStringValue(value) {
|
|
435
|
+
if (value === undefined || value === null || value === "") {
|
|
436
|
+
return undefined;
|
|
437
|
+
}
|
|
438
|
+
const numberValue = Number(value);
|
|
439
|
+
return Number.isFinite(numberValue) ? numberValue : String(value);
|
|
440
|
+
}
|
|
441
|
+
function formatTimestamp(value) {
|
|
442
|
+
const timestamp = Number(value);
|
|
443
|
+
if (!Number.isFinite(timestamp)) {
|
|
444
|
+
return "";
|
|
445
|
+
}
|
|
446
|
+
const date = new Date(timestamp);
|
|
447
|
+
if (Number.isNaN(date.getTime())) {
|
|
448
|
+
return "";
|
|
449
|
+
}
|
|
450
|
+
const pad = (part) => String(part).padStart(2, "0");
|
|
451
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
452
|
+
}
|
|
453
|
+
function triggerModeName(value) {
|
|
454
|
+
switch (Number(value)) {
|
|
455
|
+
case 1:
|
|
456
|
+
return "人工触发";
|
|
457
|
+
case 2:
|
|
458
|
+
return "定时触发";
|
|
459
|
+
case 3:
|
|
460
|
+
return "代码提交触发";
|
|
461
|
+
case 5:
|
|
462
|
+
return "流水线触发";
|
|
463
|
+
case 6:
|
|
464
|
+
return "Webhook触发";
|
|
465
|
+
default:
|
|
466
|
+
return value === undefined || value === null ? "" : String(value);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
289
469
|
function compactBody(body) {
|
|
290
470
|
return Object.fromEntries(Object.entries(body).filter(([, value]) => value !== undefined && value !== null && value !== ""));
|
|
291
471
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderpp/yunxiao-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "A CLI for Yunxiao merge request, project member, and workitem workflows.",
|
|
3
|
+
"version": "0.1.10",
|
|
4
|
+
"description": "A CLI for Yunxiao merge request, pipeline retry, project member, and workitem workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
"build": "tsc -p tsconfig.json",
|
|
19
19
|
"pack:dry-run": "npm pack --dry-run",
|
|
20
20
|
"prepack": "npm run build",
|
|
21
|
+
"skills:sync": "scripts/sync-skills.sh",
|
|
22
|
+
"skills:watch": "scripts/watch-skills.sh",
|
|
23
|
+
"skills:watch:start": "scripts/start-skill-sync-watch.sh",
|
|
24
|
+
"skills:watch:status": "scripts/status-skill-sync-watch.sh",
|
|
25
|
+
"skills:watch:stop": "scripts/stop-skill-sync-watch.sh",
|
|
21
26
|
"test": "tsc -p tsconfig.json && node --test dist/test/*.test.js"
|
|
22
27
|
},
|
|
23
28
|
"publishConfig": {
|