@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.
@@ -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 tools = Object.create(null);
84
+ const callers = new Map();
78
85
  const allTools = [];
79
- for (const { name, jsName, description } of parse(toolsJson)) {
80
- const fn = caller(name);
81
- // 两个名字归一化成同一个标识符时,第一个赢
82
- if (!(jsName in tools)) {
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, "tools", { value: tools, enumerable: true });
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 ErrorCtor(payload));
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(tools, console);
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 outputSchema = Type.Object({
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, outputSchema, doneSchema]);
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
- jsName: toScriptIdentifier(tool.name),
497
- description: tool.description
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
- `(async (tools, console) => {${start.code}
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
- };
@@ -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<ToolResult> {
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
- return {
783
- content: [{ type: "text", text: verdict.text }],
784
- details: {
785
- status: verdict.status,
786
- totalChecks: poll.checks.length,
787
- checks: poll.checks,
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
+ });