@trim21/personal-pi-extensions 0.1.678 → 0.1.680
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 +25 -12
- package/package.json +1 -1
- package/src/claude-code/files.ts +12 -2
- package/src/codemode/declarations.ts +35 -12
- package/src/codemode/fs.ts +138 -0
- package/src/codemode/prelude.ts +44 -17
- package/src/codemode/protocol.ts +17 -2
- package/src/codemode/tool.ts +69 -11
- package/src/codemode/worker.js +48 -30
- package/src/codemode/worker.ts +3 -13
- package/src/gh/base.ts +62 -12
- package/src/gh/schemas.ts +230 -0
- package/src/gh/tools/download-release-assets.ts +51 -44
- package/src/gh/tools/get-workflow-jobs.ts +29 -18
- package/src/gh/tools/read-ci-logs.ts +37 -23
- package/src/gh/tools/read-issue-comments.ts +31 -24
- package/src/gh/tools/read-issue.ts +30 -22
- package/src/gh/tools/read-pr-comments.ts +62 -58
- package/src/gh/tools/read-pr-status.ts +31 -20
- package/src/gh/tools/read-pr.ts +30 -22
- package/src/gh/tools/wait-commit-checks.ts +35 -31
- package/src/gh/tools/wait-pr-checks.ts +30 -23
- package/src/index.ts +5 -2
- package/src/lib/tool-bus.ts +128 -8
- package/src/opencode/files.ts +12 -2
package/src/codemode/worker.js
CHANGED
|
@@ -13,6 +13,13 @@ var PRELUDE_SOURCE = String.raw`(function (bridge, toolsJson, storeJson) {
|
|
|
13
13
|
const promiseThen = Promise.prototype.then;
|
|
14
14
|
const ErrorCtor = Error;
|
|
15
15
|
const TypeErrorCtor = TypeError;
|
|
16
|
+
// 嵌套调用失败统一用它 reject:脚本能按 instanceof 区分「工具失败」与自身运行期错误
|
|
17
|
+
class CallFailedError extends ErrorCtor {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "CallFailedError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
16
23
|
const pending = new Map();
|
|
17
24
|
let nextId = 1;
|
|
18
25
|
let finished = false;
|
|
@@ -74,20 +81,36 @@ var PRELUDE_SOURCE = String.raw`(function (bridge, toolsJson, storeJson) {
|
|
|
74
81
|
});
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
const
|
|
84
|
+
const callers = new Map();
|
|
78
85
|
const allTools = [];
|
|
79
|
-
for (const { name,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
tools[jsName] = fn;
|
|
84
|
-
allTools.push(Object.freeze({ name: jsName, description }));
|
|
85
|
-
}
|
|
86
|
-
if (!(name in tools)) tools[name] = fn;
|
|
86
|
+
for (const { name, description } of parse(toolsJson)) {
|
|
87
|
+
if (callers.has(name)) continue;
|
|
88
|
+
callers.set(name, caller(name));
|
|
89
|
+
allTools.push(Object.freeze({ name, description }));
|
|
87
90
|
}
|
|
88
|
-
Object.freeze(tools);
|
|
89
91
|
Object.freeze(allTools);
|
|
90
92
|
|
|
93
|
+
function call(name, args) {
|
|
94
|
+
const fn = callers.get(name);
|
|
95
|
+
if (fn === undefined) {
|
|
96
|
+
return Promise.reject(
|
|
97
|
+
new CallFailedError('Tool "' + String(name) + '" is not available in codemode.'),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return fn(args);
|
|
101
|
+
}
|
|
102
|
+
Object.freeze(call);
|
|
103
|
+
|
|
104
|
+
// 脚本的文件原语:与工具走同一条桥,但它们是内建能力而不是工具——不进 ALL_TOOLS,
|
|
105
|
+
// 也不出现在工具描述的工具重载里(声明单独渲染),写审批与已读记账由宿主负责。
|
|
106
|
+
// 脚本侧是 Node 风格的位置参数,过桥仍是一个可校验的对象。
|
|
107
|
+
const readFile = caller("fs.read");
|
|
108
|
+
const writeFile = caller("fs.write");
|
|
109
|
+
const fs = Object.freeze({
|
|
110
|
+
read: (path) => readFile({ path }),
|
|
111
|
+
write: (path, content) => writeFile({ path, content }),
|
|
112
|
+
});
|
|
113
|
+
|
|
91
114
|
// key -> JSON 文本;容量按 key 与 JSON 的字符数计
|
|
92
115
|
const stored = new Map();
|
|
93
116
|
const writes = new Map();
|
|
@@ -227,8 +250,10 @@ var PRELUDE_SOURCE = String.raw`(function (bridge, toolsJson, storeJson) {
|
|
|
227
250
|
}
|
|
228
251
|
Object.freeze(console);
|
|
229
252
|
|
|
230
|
-
Object.defineProperty(globalThis, "
|
|
253
|
+
Object.defineProperty(globalThis, "call", { value: call, enumerable: true });
|
|
254
|
+
Object.defineProperty(globalThis, "CallFailedError", { value: CallFailedError, enumerable: true });
|
|
231
255
|
Object.defineProperty(globalThis, "ALL_TOOLS", { value: allTools, enumerable: true });
|
|
256
|
+
Object.defineProperty(globalThis, "fs", { value: fs, enumerable: true });
|
|
232
257
|
Object.defineProperty(globalThis, "console", { value: console, enumerable: true });
|
|
233
258
|
Object.defineProperty(globalThis, "text", { value: text, enumerable: true });
|
|
234
259
|
Object.defineProperty(globalThis, "image", { value: image, enumerable: true });
|
|
@@ -240,7 +265,7 @@ var PRELUDE_SOURCE = String.raw`(function (bridge, toolsJson, storeJson) {
|
|
|
240
265
|
if (!entry) return;
|
|
241
266
|
pending.delete(id);
|
|
242
267
|
if (!ok) {
|
|
243
|
-
entry.reject(new
|
|
268
|
+
entry.reject(new CallFailedError(payload));
|
|
244
269
|
return;
|
|
245
270
|
}
|
|
246
271
|
let value;
|
|
@@ -255,7 +280,7 @@ var PRELUDE_SOURCE = String.raw`(function (bridge, toolsJson, storeJson) {
|
|
|
255
280
|
run(fn) {
|
|
256
281
|
let promise;
|
|
257
282
|
try {
|
|
258
|
-
promise = fn(
|
|
283
|
+
promise = fn();
|
|
259
284
|
} catch (error) {
|
|
260
285
|
done(false, describeError(error));
|
|
261
286
|
return;
|
|
@@ -295,13 +320,16 @@ var PRELUDE_SOURCE = String.raw`(function (bridge, toolsJson, storeJson) {
|
|
|
295
320
|
// src/codemode/protocol.ts
|
|
296
321
|
import { Type } from "typebox";
|
|
297
322
|
import { Value } from "typebox/value";
|
|
323
|
+
var MEMORY_LIMIT_BYTES = 2 * 1024 ** 3;
|
|
298
324
|
var outputItemSchema = Type.Union([
|
|
299
325
|
Type.Object({ type: Type.Literal("text"), text: Type.String() }),
|
|
300
326
|
Type.Object({ type: Type.Literal("image"), data: Type.String(), mimeType: Type.String() })
|
|
301
327
|
]);
|
|
302
328
|
var toolDeclSchema = Type.Object({
|
|
303
329
|
name: Type.String(),
|
|
304
|
-
description: Type.Optional(Type.String())
|
|
330
|
+
description: Type.Optional(Type.String()),
|
|
331
|
+
/** 工具的 structuredSchema(TypeBox schema):脚本侧据此渲染 call() 的返回类型。 */
|
|
332
|
+
structuredSchema: Type.Optional(Type.Unknown())
|
|
305
333
|
});
|
|
306
334
|
var startSchema = Type.Object({
|
|
307
335
|
t: Type.Literal("start"),
|
|
@@ -329,7 +357,7 @@ var callSchema = Type.Object({
|
|
|
329
357
|
name: Type.String(),
|
|
330
358
|
args: Type.Unknown()
|
|
331
359
|
});
|
|
332
|
-
var
|
|
360
|
+
var outputFrameSchema = Type.Object({
|
|
333
361
|
t: Type.Literal("output"),
|
|
334
362
|
items: Type.Array(outputItemSchema)
|
|
335
363
|
});
|
|
@@ -358,7 +386,7 @@ var doneSchema = Type.Union([
|
|
|
358
386
|
})
|
|
359
387
|
]);
|
|
360
388
|
var hostMessageSchema = Type.Union([startSchema, resultSchema]);
|
|
361
|
-
var workerMessageSchema = Type.Union([callSchema,
|
|
389
|
+
var workerMessageSchema = Type.Union([callSchema, outputFrameSchema, doneSchema]);
|
|
362
390
|
function decode(schema, value) {
|
|
363
391
|
if (!Value.Check(schema, value)) {
|
|
364
392
|
const preview = JSON.stringify(value).slice(0, 200);
|
|
@@ -371,10 +399,6 @@ function decodeHostMessage(value) {
|
|
|
371
399
|
}
|
|
372
400
|
|
|
373
401
|
// src/codemode/worker.ts
|
|
374
|
-
var MEMORY_LIMIT_BYTES = 512 * 1024 * 1024;
|
|
375
|
-
function toScriptIdentifier(name) {
|
|
376
|
-
return name.replaceAll(/[^A-Za-z0-9_$]/g, "_").replaceAll(/^\d/g, "_");
|
|
377
|
-
}
|
|
378
402
|
function discardOutput(memory) {
|
|
379
403
|
return {
|
|
380
404
|
fd_write(_fd, iovsPtr, iovsLen, nwrittenPtr) {
|
|
@@ -493,8 +517,8 @@ async function runScript(wasm, start) {
|
|
|
493
517
|
JSON.stringify(
|
|
494
518
|
start.tools.map((tool) => ({
|
|
495
519
|
name: tool.name,
|
|
496
|
-
|
|
497
|
-
|
|
520
|
+
description: tool.description,
|
|
521
|
+
structuredSchema: tool.structuredSchema
|
|
498
522
|
}))
|
|
499
523
|
)
|
|
500
524
|
),
|
|
@@ -530,11 +554,8 @@ async function runScript(wasm, start) {
|
|
|
530
554
|
drain();
|
|
531
555
|
});
|
|
532
556
|
try {
|
|
533
|
-
const fn = vm.evalCode(
|
|
534
|
-
|
|
535
|
-
})`,
|
|
536
|
-
"codemode.js"
|
|
537
|
-
);
|
|
557
|
+
const fn = vm.evalCode(`(async () => {${start.code}
|
|
558
|
+
})`, "codemode.js");
|
|
538
559
|
vm.callFunction(run, api, fn).dispose();
|
|
539
560
|
fn.dispose();
|
|
540
561
|
drain();
|
|
@@ -580,6 +601,3 @@ function main() {
|
|
|
580
601
|
});
|
|
581
602
|
}
|
|
582
603
|
main();
|
|
583
|
-
export {
|
|
584
|
-
toScriptIdentifier
|
|
585
|
-
};
|
package/src/codemode/worker.ts
CHANGED
|
@@ -17,20 +17,13 @@ import { PRELUDE_SOURCE } from "./prelude.js";
|
|
|
17
17
|
import {
|
|
18
18
|
decodeHostMessage,
|
|
19
19
|
type HostMessage,
|
|
20
|
+
MEMORY_LIMIT_BYTES,
|
|
20
21
|
type ScriptError,
|
|
21
22
|
type StoreWrites,
|
|
22
23
|
type WorkerBootstrap,
|
|
23
24
|
type WorkerMessage,
|
|
24
25
|
} from "./protocol.js";
|
|
25
26
|
|
|
26
|
-
/** QuickJS VM 的堆上限:超量分配在脚本里变成 InternalError,而不是拖垮宿主。 */
|
|
27
|
-
const MEMORY_LIMIT_BYTES = 512 * 1024 * 1024;
|
|
28
|
-
|
|
29
|
-
/** 工具名到脚本标识符的归一化:非法字符换成 `_`,数字开头补 `_`。 */
|
|
30
|
-
export function toScriptIdentifier(name: string): string {
|
|
31
|
-
return name.replaceAll(/[^A-Za-z0-9_$]/g, "_").replaceAll(/^\d/g, "_");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
27
|
/**
|
|
35
28
|
* QuickJS 把引擎诊断写到 fd 1 / 2,那会直接进 pi 的 TUI;按写入长度回报并丢弃内容,
|
|
36
29
|
* 避免 libc 重试。
|
|
@@ -178,8 +171,8 @@ async function runScript(wasm: object, start: Extract<HostMessage, { t: "start"
|
|
|
178
171
|
JSON.stringify(
|
|
179
172
|
start.tools.map((tool) => ({
|
|
180
173
|
name: tool.name,
|
|
181
|
-
jsName: toScriptIdentifier(tool.name),
|
|
182
174
|
description: tool.description,
|
|
175
|
+
structuredSchema: tool.structuredSchema,
|
|
183
176
|
})),
|
|
184
177
|
),
|
|
185
178
|
),
|
|
@@ -225,10 +218,7 @@ async function runScript(wasm: object, start: Extract<HostMessage, { t: "start"
|
|
|
225
218
|
|
|
226
219
|
try {
|
|
227
220
|
// 前缀与脚本首行共用一行,报错行号与用户写的脚本一致
|
|
228
|
-
const fn: JSValueHandle = vm.evalCode(
|
|
229
|
-
`(async (tools, console) => {${start.code}\n})`,
|
|
230
|
-
"codemode.js",
|
|
231
|
-
);
|
|
221
|
+
const fn: JSValueHandle = vm.evalCode(`(async () => {${start.code}\n})`, "codemode.js");
|
|
232
222
|
vm.callFunction(run, api, fn).dispose();
|
|
233
223
|
fn.dispose();
|
|
234
224
|
drain();
|
package/src/gh/base.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
import { delimiter, join } from "node:path";
|
|
11
11
|
|
|
12
|
-
import { Type } from "typebox";
|
|
12
|
+
import { type Static, type TSchema, Type } from "typebox";
|
|
13
13
|
import { Value } from "typebox/value";
|
|
14
14
|
|
|
15
15
|
import { egress } from "../lib/egress.js";
|
|
@@ -25,7 +25,10 @@ import {
|
|
|
25
25
|
type GithubSearch,
|
|
26
26
|
renderHits,
|
|
27
27
|
} from "../lib/github.js";
|
|
28
|
+
import { parseWithSchema } from "../lib/parse-with-schema.js";
|
|
28
29
|
import { type ToolPendant } from "../lib/pendant.js";
|
|
30
|
+
import { type StructuredResult } from "../lib/tool-bus.js";
|
|
31
|
+
import type { checksVerdictSchema } from "./schemas.js";
|
|
29
32
|
|
|
30
33
|
/** A tool result: what the model sees plus the structured details payload. */
|
|
31
34
|
export interface ToolResult {
|
|
@@ -33,6 +36,24 @@ export interface ToolResult {
|
|
|
33
36
|
details: Record<string, unknown>;
|
|
34
37
|
}
|
|
35
38
|
|
|
39
|
+
/**
|
|
40
|
+
* 带结构化结果的结果:`structuredResult` 的成功支类型由 `T` 决定。声明了 structuredSchema
|
|
41
|
+
* 的工具用它,交给 codemode 脚本按名字调用时解包。它不改变 `content` 与 `details`。
|
|
42
|
+
*/
|
|
43
|
+
export type WithStructured<T> = ToolResult & { structuredResult: StructuredResult<T> };
|
|
44
|
+
|
|
45
|
+
/** 某个 `structuredSchema` 对应的工具结果:成功支与 schema 的静态类型对齐。 */
|
|
46
|
+
export type StructuredResultOf<TSchema_ extends TSchema> = WithStructured<Static<TSchema_>>;
|
|
47
|
+
|
|
48
|
+
/** 结构化失败(Result 的失败支):与具体的输出类型无关,任何结构化工具都能用。 */
|
|
49
|
+
export interface StructuredFailure {
|
|
50
|
+
ok: false;
|
|
51
|
+
error: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** 结构化失败的结果:不必抛异常就能把「没找到」这类结果告诉脚本。 */
|
|
55
|
+
export type StructuredFailureResult = ToolResult & { structuredResult: StructuredFailure };
|
|
56
|
+
|
|
36
57
|
/** What a toolcall handler receives from the framework. */
|
|
37
58
|
export interface ToolCall<Params> {
|
|
38
59
|
params: Params;
|
|
@@ -239,6 +260,32 @@ export function toToolResultJson(json: string, input?: unknown): ToolResult {
|
|
|
239
260
|
};
|
|
240
261
|
}
|
|
241
262
|
|
|
263
|
+
/** 给结果附上结构化结果(成功支):`value` 就是脚本解包后拿到的东西。 */
|
|
264
|
+
export function withStructuredResult<T>(result: ToolResult, value: T): WithStructured<T> {
|
|
265
|
+
return { ...result, structuredResult: { ok: true, value } };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* 透传型工具的成功结果:`gh --json` 的原始输出既是给模型的文本(整段 JSON,不经行
|
|
270
|
+
* 截断),也按 `schema` 解析成结构化结果。解析失败会抛错——schema 与 GitHub 实际返回
|
|
271
|
+
* 漂移时必须显式失败,不能悄悄少给字段。
|
|
272
|
+
*/
|
|
273
|
+
export function toStructuredJsonResult<T extends TSchema>(
|
|
274
|
+
json: string,
|
|
275
|
+
input: unknown,
|
|
276
|
+
schema: T,
|
|
277
|
+
): StructuredResultOf<T> {
|
|
278
|
+
return withStructuredResult(
|
|
279
|
+
toToolResultJson(json, input),
|
|
280
|
+
parseWithSchema(schema, JSON.parse(json)),
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** 结构化失败(Result 的失败支):不必抛异常就能把「没找到」这类结果告诉脚本。 */
|
|
285
|
+
export function structuredFailure(error: string): StructuredFailure {
|
|
286
|
+
return { ok: false, error };
|
|
287
|
+
}
|
|
288
|
+
|
|
242
289
|
/**
|
|
243
290
|
* Pendant subtitle for a tool result: `repo=x/y` (when provided) plus the
|
|
244
291
|
* tool's id parameter, e.g. `repo=x/y number=123`. Returns undefined when
|
|
@@ -747,7 +794,7 @@ export async function waitChecksReport(options: {
|
|
|
747
794
|
onUpdate: ((msg: ToolResult) => void) | undefined;
|
|
748
795
|
params: unknown;
|
|
749
796
|
pendant?: ToolPendant;
|
|
750
|
-
}): Promise<
|
|
797
|
+
}): Promise<StructuredResultOf<typeof checksVerdictSchema>> {
|
|
751
798
|
const { subject, owner, repo, headSha, failFast, event, signal, onUpdate, params, pendant } =
|
|
752
799
|
options;
|
|
753
800
|
|
|
@@ -779,15 +826,18 @@ export async function waitChecksReport(options: {
|
|
|
779
826
|
}
|
|
780
827
|
|
|
781
828
|
const verdict = renderChecksVerdict({ subject, poll, actionJobs, enrichmentError });
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
failedJobs: verdict.failedJobs,
|
|
789
|
-
input: params,
|
|
790
|
-
...(pendant && { pendant }),
|
|
791
|
-
},
|
|
829
|
+
const payload = {
|
|
830
|
+
status: verdict.status,
|
|
831
|
+
totalChecks: poll.checks.length,
|
|
832
|
+
// 复制成可变数组:结构化结果的类型要与 structuredSchema 的静态类型一致
|
|
833
|
+
checks: [...poll.checks],
|
|
834
|
+
failedJobs: [...verdict.failedJobs],
|
|
792
835
|
};
|
|
836
|
+
return withStructuredResult(
|
|
837
|
+
{
|
|
838
|
+
content: [{ type: "text", text: verdict.text }],
|
|
839
|
+
details: { ...payload, input: params, ...(pendant && { pendant }) },
|
|
840
|
+
},
|
|
841
|
+
payload,
|
|
842
|
+
);
|
|
793
843
|
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gh-readonly 工具的结构化结果 schema(TypeBox)。
|
|
3
|
+
*
|
|
4
|
+
* 两个用途:codemode 脚本侧据此渲染 `call()` 的返回类型;工具总线在运行期用它复核
|
|
5
|
+
* `structuredResult.value`。因此 schema 必须**允许额外字段**(GitHub 的 JSON 只多不少,
|
|
6
|
+
* 多出来的字段不该让工具失败),只对工具本身已经依赖的字段设 required。
|
|
7
|
+
*
|
|
8
|
+
* 字段名取自 GitHub 的两种传输:`gh ... view --json`(GraphQL,actor 叫 `author` /
|
|
9
|
+
* `user`)与 REST(`/pulls/:n/comments` 等,用了 `diff_hunk` / `submitted_at` 这类
|
|
10
|
+
* 下划线命名)。评论与评审条目因此放宽成两者的并集。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Type } from "typebox";
|
|
14
|
+
|
|
15
|
+
// ── 共享片段 ─────────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
/** GitHub 的 actor(author / assignee / mergedBy / review 作者)。 */
|
|
18
|
+
export const ghActorSchema = Type.Object({
|
|
19
|
+
login: Type.Optional(Type.String()),
|
|
20
|
+
name: Type.Optional(Type.String()),
|
|
21
|
+
is_bot: Type.Optional(Type.Boolean()),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const ghLabelSchema = Type.Object({
|
|
25
|
+
name: Type.String(),
|
|
26
|
+
color: Type.Optional(Type.String()),
|
|
27
|
+
description: Type.Optional(Type.String()),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const ghMilestoneSchema = Type.Object({
|
|
31
|
+
title: Type.String(),
|
|
32
|
+
number: Type.Optional(Type.Number()),
|
|
33
|
+
dueOn: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** 评论条目:GraphQL 的 `author`/`createdAt` 与 REST 的 `user`/`diff_hunk` 并集。 */
|
|
37
|
+
export const ghCommentSchema = Type.Object({
|
|
38
|
+
id: Type.Optional(Type.Union([Type.String(), Type.Number()])),
|
|
39
|
+
body: Type.Optional(Type.String()),
|
|
40
|
+
author: Type.Optional(Type.Union([ghActorSchema, Type.Null()])),
|
|
41
|
+
user: Type.Optional(Type.Union([ghActorSchema, Type.Null()])),
|
|
42
|
+
path: Type.Optional(Type.String()),
|
|
43
|
+
line: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
44
|
+
diff_hunk: Type.Optional(Type.String()),
|
|
45
|
+
createdAt: Type.Optional(Type.String()),
|
|
46
|
+
url: Type.Optional(Type.String()),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/** 评审条目:GraphQL 的 `reviews` 与 REST 的 `/pulls/:n/reviews` 并集。 */
|
|
50
|
+
export const ghReviewSchema = Type.Object({
|
|
51
|
+
id: Type.Optional(Type.Union([Type.String(), Type.Number()])),
|
|
52
|
+
body: Type.Optional(Type.String()),
|
|
53
|
+
author: Type.Optional(Type.Union([ghActorSchema, Type.Null()])),
|
|
54
|
+
user: Type.Optional(Type.Union([ghActorSchema, Type.Null()])),
|
|
55
|
+
state: Type.Optional(Type.String()),
|
|
56
|
+
createdAt: Type.Optional(Type.String()),
|
|
57
|
+
submitted_at: Type.Optional(Type.String()),
|
|
58
|
+
url: Type.Optional(Type.String()),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/** 待评审人:可能是用户(login)也可能是团队(slug)。 */
|
|
62
|
+
export const ghReviewRequestSchema = Type.Object({
|
|
63
|
+
login: Type.Optional(Type.String()),
|
|
64
|
+
name: Type.Optional(Type.String()),
|
|
65
|
+
slug: Type.Optional(Type.String()),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
/** `read-github-pr-status` / 两个 wait 工具共用的检查桶。 */
|
|
69
|
+
export const checkBucketSchema = Type.Union([
|
|
70
|
+
Type.Literal("pass"),
|
|
71
|
+
Type.Literal("fail"),
|
|
72
|
+
Type.Literal("pending"),
|
|
73
|
+
Type.Literal("skipped"),
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
// ── 各工具的输出 ─────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/** `read-github-issue`:`gh issue view --json` 的输出。 */
|
|
79
|
+
export const issueViewSchema = Type.Object({
|
|
80
|
+
number: Type.Number(),
|
|
81
|
+
title: Type.String(),
|
|
82
|
+
state: Type.String(),
|
|
83
|
+
body: Type.Optional(Type.String()),
|
|
84
|
+
author: Type.Optional(Type.Union([ghActorSchema, Type.Null()])),
|
|
85
|
+
createdAt: Type.Optional(Type.String()),
|
|
86
|
+
updatedAt: Type.Optional(Type.String()),
|
|
87
|
+
closedAt: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
88
|
+
url: Type.Optional(Type.String()),
|
|
89
|
+
labels: Type.Optional(Type.Array(ghLabelSchema)),
|
|
90
|
+
assignees: Type.Optional(Type.Array(ghActorSchema)),
|
|
91
|
+
comments: Type.Optional(Type.Array(ghCommentSchema)),
|
|
92
|
+
milestone: Type.Optional(Type.Union([ghMilestoneSchema, Type.Null()])),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/** `read-github-pr`:`gh pr view --json` 的输出,比 issue 多出合并与 diff 统计。 */
|
|
96
|
+
export const prViewSchema = Type.Object({
|
|
97
|
+
number: Type.Number(),
|
|
98
|
+
title: Type.String(),
|
|
99
|
+
state: Type.String(),
|
|
100
|
+
body: Type.Optional(Type.String()),
|
|
101
|
+
author: Type.Optional(Type.Union([ghActorSchema, Type.Null()])),
|
|
102
|
+
createdAt: Type.Optional(Type.String()),
|
|
103
|
+
updatedAt: Type.Optional(Type.String()),
|
|
104
|
+
closedAt: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
105
|
+
mergedAt: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
106
|
+
mergedBy: Type.Optional(Type.Union([ghActorSchema, Type.Null()])),
|
|
107
|
+
headRefName: Type.Optional(Type.String()),
|
|
108
|
+
baseRefName: Type.Optional(Type.String()),
|
|
109
|
+
url: Type.Optional(Type.String()),
|
|
110
|
+
additions: Type.Optional(Type.Number()),
|
|
111
|
+
deletions: Type.Optional(Type.Number()),
|
|
112
|
+
changedFiles: Type.Optional(Type.Number()),
|
|
113
|
+
labels: Type.Optional(Type.Array(ghLabelSchema)),
|
|
114
|
+
assignees: Type.Optional(Type.Array(ghActorSchema)),
|
|
115
|
+
reviewRequests: Type.Optional(Type.Array(ghReviewRequestSchema)),
|
|
116
|
+
reviews: Type.Optional(Type.Array(ghReviewSchema)),
|
|
117
|
+
comments: Type.Optional(Type.Array(ghCommentSchema)),
|
|
118
|
+
milestone: Type.Optional(Type.Union([ghMilestoneSchema, Type.Null()])),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/** `read-github-issue-comments`:`gh issue view --json comments`。 */
|
|
122
|
+
export const issueCommentsSchema = Type.Object({
|
|
123
|
+
comments: Type.Array(ghCommentSchema),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* `read-github-pr-comments`:默认模式是 `gh pr view --json comments`(只有 comments),
|
|
128
|
+
* `reviews: true` 时走 REST 同时给出 reviews 与行内评论。两个字段都可选。
|
|
129
|
+
*/
|
|
130
|
+
export const prCommentsSchema = Type.Object({
|
|
131
|
+
comments: Type.Optional(Type.Array(ghCommentSchema)),
|
|
132
|
+
reviews: Type.Optional(Type.Array(ghReviewSchema)),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
/** `read-github-pr-status`:工具自己拼的 `{pr, repo, head_sha, checks}`。 */
|
|
136
|
+
export const prStatusSchema = Type.Object({
|
|
137
|
+
pr: Type.Number(),
|
|
138
|
+
repo: Type.String(),
|
|
139
|
+
head_sha: Type.String(),
|
|
140
|
+
checks: Type.Array(
|
|
141
|
+
Type.Object({
|
|
142
|
+
name: Type.String(),
|
|
143
|
+
bucket: checkBucketSchema,
|
|
144
|
+
event: Type.Union([Type.String(), Type.Null()]),
|
|
145
|
+
run_id: Type.Union([Type.Number(), Type.Null()]),
|
|
146
|
+
job_id: Type.Union([Type.Number(), Type.Null()]),
|
|
147
|
+
url: Type.Union([Type.String(), Type.Null()]),
|
|
148
|
+
}),
|
|
149
|
+
),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
export const workflowJobsSchema = Type.Object({
|
|
153
|
+
total_count: Type.Number(),
|
|
154
|
+
jobs: Type.Array(
|
|
155
|
+
Type.Object({
|
|
156
|
+
id: Type.Number(),
|
|
157
|
+
run_id: Type.Number(),
|
|
158
|
+
run_url: Type.String(),
|
|
159
|
+
name: Type.String(),
|
|
160
|
+
status: Type.String(),
|
|
161
|
+
conclusion: Type.Union([Type.String(), Type.Null()]),
|
|
162
|
+
html_url: Type.Union([Type.String(), Type.Null()]),
|
|
163
|
+
steps: Type.Array(
|
|
164
|
+
Type.Object({
|
|
165
|
+
name: Type.String(),
|
|
166
|
+
number: Type.Number(),
|
|
167
|
+
status: Type.String(),
|
|
168
|
+
conclusion: Type.Union([Type.String(), Type.Null()]),
|
|
169
|
+
started_at: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
170
|
+
}),
|
|
171
|
+
),
|
|
172
|
+
}),
|
|
173
|
+
),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
/** `read-github-ci-logs`:`CiLogsJobIndex`(步骤带它在原始日志里的行号范围)。 */
|
|
177
|
+
export const ciLogsSchema = Type.Object({
|
|
178
|
+
name: Type.String(),
|
|
179
|
+
id: Type.Number(),
|
|
180
|
+
status: Type.String(),
|
|
181
|
+
conclusion: Type.Union([Type.String(), Type.Null()]),
|
|
182
|
+
log_file: Type.String(),
|
|
183
|
+
steps: Type.Array(
|
|
184
|
+
Type.Object({
|
|
185
|
+
number: Type.Number(),
|
|
186
|
+
name: Type.String(),
|
|
187
|
+
conclusion: Type.Union([Type.String(), Type.Null()]),
|
|
188
|
+
start_line: Type.Optional(Type.Number()),
|
|
189
|
+
end_line: Type.Optional(Type.Number()),
|
|
190
|
+
}),
|
|
191
|
+
),
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
/** `download-github-release-assets`:下载目录里此刻的文件清单。 */
|
|
195
|
+
export const releaseDownloadSchema = Type.Object({
|
|
196
|
+
repo: Type.String(),
|
|
197
|
+
tag: Type.String(),
|
|
198
|
+
dir: Type.String(),
|
|
199
|
+
files: Type.Array(
|
|
200
|
+
Type.Object({ name: Type.String(), path: Type.String(), bytes: Type.Number() }),
|
|
201
|
+
),
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
/** 两个 wait 工具共享的判定结果(`waitChecksReport` 的 payload)。 */
|
|
205
|
+
export const checksVerdictSchema = Type.Object({
|
|
206
|
+
status: Type.Union([Type.Literal("success"), Type.Literal("failure"), Type.Literal("pending")]),
|
|
207
|
+
totalChecks: Type.Number(),
|
|
208
|
+
checks: Type.Array(
|
|
209
|
+
Type.Object({
|
|
210
|
+
name: Type.String(),
|
|
211
|
+
bucket: checkBucketSchema,
|
|
212
|
+
startedAt: Type.Union([Type.String(), Type.Null()]),
|
|
213
|
+
link: Type.Union([Type.String(), Type.Null()]),
|
|
214
|
+
event: Type.Union([Type.String(), Type.Null()]),
|
|
215
|
+
runId: Type.Union([Type.Number(), Type.Null()]),
|
|
216
|
+
jobId: Type.Union([Type.Number(), Type.Null()]),
|
|
217
|
+
}),
|
|
218
|
+
),
|
|
219
|
+
failedJobs: Type.Array(
|
|
220
|
+
Type.Object({
|
|
221
|
+
runId: Type.Number(),
|
|
222
|
+
runName: Type.String(),
|
|
223
|
+
runUrl: Type.String(),
|
|
224
|
+
jobId: Type.Number(),
|
|
225
|
+
jobName: Type.String(),
|
|
226
|
+
conclusion: Type.String(),
|
|
227
|
+
jobUrl: Type.Optional(Type.String()),
|
|
228
|
+
}),
|
|
229
|
+
),
|
|
230
|
+
});
|