@akagilnc/pi-workflow-roles 0.1.5593 → 0.1.5598
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/acp-host/production-host.js +168 -394
- package/dist/headless-host/production-host.js +168 -394
- package/dist/merger-contracts.js +6 -2
- package/dist/migrate-book-topology.js +4846 -10449
- package/dist/package-contracts/worker-output.js +1 -6
- package/dist/packaged-role-registry.js +5 -4
- package/dist/public-cli/invocation.js +20 -53
- package/dist/public-cli/main.js +18046 -18197
- package/dist/public-cli/option-definitions.js +2 -2
- package/dist/public-cli/post-admission.js +2 -2
- package/dist/public-cli/run-lifecycle.js +2 -29
- package/dist/role-run-relocation.js +3 -4
- package/package.json +1 -1
- package/scripts/build-package.mjs +0 -1
- package/src/host-contracts.ts +0 -3
- package/src/merger-contracts.ts +6 -2
- package/src/merger-role.ts +11 -1
- package/src/navigator-attendance.ts +2 -2
- package/src/package-contracts/worker-output.ts +0 -8
- package/src/packaged-role-registry.ts +4 -3
- package/src/public-cli/invocation.ts +25 -72
- package/src/public-cli/option-definitions.ts +2 -2
- package/src/public-cli/post-admission.ts +2 -2
- package/src/public-cli/run-lifecycle.ts +2 -39
- package/src/role-run-relocation.ts +3 -4
- package/src/role-runtime-dependencies.ts +0 -2
- package/src/role-runtime.ts +0 -16
- package/src/worker-role.ts +11 -78
- package/dist/package-contracts/fixer-packet.js +0 -77
- package/packets/fixer-prerequisites.json +0 -6
- package/src/package-contracts/fixer-packet.ts +0 -91
|
@@ -45,8 +45,6 @@ export function createRoleRuntimeDependencies(packageRoot: string): RoleRuntimeD
|
|
|
45
45
|
packageRoot,
|
|
46
46
|
loadRoleReferenceMaterials: loadPackagedRoleReferenceMaterials,
|
|
47
47
|
loadRoleSoul: loadRegisteredRoleSoul,
|
|
48
|
-
loadFixPacket: (path) => readFile(path, "utf8"),
|
|
49
|
-
loadCoderTask: (path) => readFile(path, "utf8"),
|
|
50
48
|
loadDoctorCase,
|
|
51
49
|
loadNotarySourceRun: loadNotarySourceRunLocator,
|
|
52
50
|
loadMergerInput: async (path) => JSON.parse(await readFile(path, "utf8")),
|
package/src/role-runtime.ts
CHANGED
|
@@ -346,8 +346,6 @@ export {
|
|
|
346
346
|
} from "./worker-role.ts";
|
|
347
347
|
export { fixerOutputSchema, validateFixerOutput } from "./package-contracts/fixer-output.ts";
|
|
348
348
|
export type { FixerBlocker, FixerClassResult, FixerPhase, FixerTestEvidence } from "./package-contracts/fixer-output.ts";
|
|
349
|
-
export { fixerPrerequisiteSchema, fixerPrerequisitesSchema, parseFixerPrerequisites, validateFixerPrerequisites } from "./package-contracts/fixer-packet.ts";
|
|
350
|
-
export type { FixerInvocationInput, FixerPrerequisite } from "./package-contracts/fixer-packet.ts";
|
|
351
349
|
export {
|
|
352
350
|
AUDITOR_SOUL_ROLES,
|
|
353
351
|
AK_ROLE_AUDITOR_SUBJECT_ENV,
|
|
@@ -460,8 +458,6 @@ export type RoleRuntimeDependencies = {
|
|
|
460
458
|
loadRoleReferenceMaterials?(role: PackagedRole): Promise<string>;
|
|
461
459
|
/** One soul loader. The role argument selects the registry record. */
|
|
462
460
|
loadRoleSoul(role: PackagedRole): Promise<string>;
|
|
463
|
-
loadFixPacket?(path: string): Promise<string>;
|
|
464
|
-
loadCoderTask?(path: string): Promise<string>;
|
|
465
461
|
loadNotarySourceRun?(path: string): Promise<import("./notary-contracts.ts").NotarySourceRunLocator>;
|
|
466
462
|
loadDoctorCase?(path: string): Promise<import("./doctor-contracts.ts").DoctorCase>;
|
|
467
463
|
loadMergerInput?(path: string): Promise<unknown>;
|
|
@@ -1459,12 +1455,6 @@ export function createRoleRuntimeExtension(
|
|
|
1459
1455
|
roleHost,
|
|
1460
1456
|
{
|
|
1461
1457
|
loadSoul: () => requireRoleSoul("fixer"),
|
|
1462
|
-
async loadPacket(path) {
|
|
1463
|
-
if (dependencies.loadFixPacket === undefined) {
|
|
1464
|
-
throw new Error("Fixer packet loader is not configured");
|
|
1465
|
-
}
|
|
1466
|
-
return dependencies.loadFixPacket(path);
|
|
1467
|
-
},
|
|
1468
1458
|
},
|
|
1469
1459
|
hostActions,
|
|
1470
1460
|
{ unfinishedReasonBounceLimit: deliveryLimit },
|
|
@@ -1473,12 +1463,6 @@ export function createRoleRuntimeExtension(
|
|
|
1473
1463
|
roleHost,
|
|
1474
1464
|
{
|
|
1475
1465
|
loadSoul: () => requireRoleSoul("coder"),
|
|
1476
|
-
async loadTask(path) {
|
|
1477
|
-
if (dependencies.loadCoderTask === undefined) {
|
|
1478
|
-
throw new Error("Coder task loader is not configured");
|
|
1479
|
-
}
|
|
1480
|
-
return dependencies.loadCoderTask(path);
|
|
1481
|
-
},
|
|
1482
1466
|
},
|
|
1483
1467
|
hostActions,
|
|
1484
1468
|
{ unfinishedReasonBounceLimit: deliveryLimit },
|
package/src/worker-role.ts
CHANGED
|
@@ -10,11 +10,6 @@ import {
|
|
|
10
10
|
import { validateFixerOutput, type FixerPhase } from "./package-contracts/fixer-output.ts";
|
|
11
11
|
import { registerFiledSubmissionTool } from "./filed-submission.ts";
|
|
12
12
|
import { roleSubmissionDeclaration } from "./role-submission-declarations.ts";
|
|
13
|
-
import {
|
|
14
|
-
FixerPacketValidationError,
|
|
15
|
-
parseFixerPrerequisites,
|
|
16
|
-
type FixerInvocationInput,
|
|
17
|
-
} from "./package-contracts/fixer-packet.ts";
|
|
18
13
|
import {
|
|
19
14
|
createWorkerSubmissionGate,
|
|
20
15
|
WorkerCommitReminderError,
|
|
@@ -37,21 +32,8 @@ export {
|
|
|
37
32
|
validateAcceptedWorkerDetails,
|
|
38
33
|
} from "./package-contracts/worker-output.ts";
|
|
39
34
|
export type { WorkerOutput, FixerOutput, CoderOutput };
|
|
35
|
+
/** #1168: phase only — dispatch text is the first message, not a packet flag. */
|
|
40
36
|
export const FIXER_FLAG_DEFINITIONS = {
|
|
41
|
-
packet: {
|
|
42
|
-
name: "ak-fix-packet",
|
|
43
|
-
definition: {
|
|
44
|
-
description: "Path to opaque prose instructions for the Fixer",
|
|
45
|
-
type: "string" as const,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
prerequisites: {
|
|
49
|
-
name: "ak-fixer-prerequisites",
|
|
50
|
-
definition: {
|
|
51
|
-
description: "Optional path to a JSON array of typed Fixer prerequisites",
|
|
52
|
-
type: "string" as const,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
37
|
phase: {
|
|
56
38
|
name: "ak-fixer-phase",
|
|
57
39
|
definition: {
|
|
@@ -73,12 +55,10 @@ export type WorkerRoleHostActions = HostGatekeeperActions;
|
|
|
73
55
|
|
|
74
56
|
export type FixerRoleDependencies = {
|
|
75
57
|
loadSoul(): Promise<string>;
|
|
76
|
-
loadPacket(path: string): Promise<string>;
|
|
77
58
|
};
|
|
78
59
|
|
|
79
60
|
export type CoderRoleDependencies = {
|
|
80
61
|
loadSoul(): Promise<string>;
|
|
81
|
-
loadTask(path: string): Promise<string>;
|
|
82
62
|
};
|
|
83
63
|
|
|
84
64
|
export type WorkerRoleRuntime = {
|
|
@@ -178,9 +158,6 @@ export function createFixerRoleRuntime(
|
|
|
178
158
|
options?: { readonly unfinishedReasonBounceLimit?: number },
|
|
179
159
|
): WorkerRoleRuntime {
|
|
180
160
|
let soul: string | undefined;
|
|
181
|
-
let packet: FixerInvocationInput | undefined;
|
|
182
|
-
let packetPath: string | undefined;
|
|
183
|
-
let prerequisitesPath: string | undefined;
|
|
184
161
|
let phase: WorkerPhase | undefined;
|
|
185
162
|
let lifecycleRegistered = false;
|
|
186
163
|
// #1132: ADR 0050 缺理由催全次数. The in-process seam passes the value it
|
|
@@ -190,14 +167,6 @@ export function createFixerRoleRuntime(
|
|
|
190
167
|
?? deliveryLimitFromEnv(process.env),
|
|
191
168
|
});
|
|
192
169
|
|
|
193
|
-
pi.registerFlag(
|
|
194
|
-
FIXER_FLAG_DEFINITIONS.packet.name,
|
|
195
|
-
FIXER_FLAG_DEFINITIONS.packet.definition,
|
|
196
|
-
);
|
|
197
|
-
pi.registerFlag(
|
|
198
|
-
FIXER_FLAG_DEFINITIONS.prerequisites.name,
|
|
199
|
-
FIXER_FLAG_DEFINITIONS.prerequisites.definition,
|
|
200
|
-
);
|
|
201
170
|
pi.registerFlag(
|
|
202
171
|
FIXER_FLAG_DEFINITIONS.phase.name,
|
|
203
172
|
FIXER_FLAG_DEFINITIONS.phase.definition,
|
|
@@ -214,33 +183,12 @@ export function createFixerRoleRuntime(
|
|
|
214
183
|
);
|
|
215
184
|
}
|
|
216
185
|
phase = selectedPhase;
|
|
217
|
-
const resolvedPacketPath = pi.getFlag(FIXER_FLAG_DEFINITIONS.packet.name);
|
|
218
|
-
if (typeof resolvedPacketPath !== "string" || resolvedPacketPath.trim().length === 0) {
|
|
219
|
-
throw new Error("Fixer role requires --ak-fix-packet");
|
|
220
|
-
}
|
|
221
|
-
packetPath = resolvedPacketPath;
|
|
222
|
-
const instructions = await dependencies.loadPacket(packetPath);
|
|
223
|
-
if (instructions.trim().length === 0) {
|
|
224
|
-
throw new FixerPacketValidationError(
|
|
225
|
-
new Error("Fixer instructions must be nonblank"),
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
const resolvedPrerequisitesPath = pi.getFlag(FIXER_FLAG_DEFINITIONS.prerequisites.name);
|
|
229
|
-
if (resolvedPrerequisitesPath !== undefined && (typeof resolvedPrerequisitesPath !== "string" || resolvedPrerequisitesPath.trim().length === 0)) {
|
|
230
|
-
throw new Error("Fixer --ak-fixer-prerequisites path must be nonblank when supplied");
|
|
231
|
-
}
|
|
232
|
-
prerequisitesPath =
|
|
233
|
-
typeof resolvedPrerequisitesPath === "string" ? resolvedPrerequisitesPath : undefined;
|
|
234
|
-
const prerequisites = prerequisitesPath !== undefined
|
|
235
|
-
? parseFixerPrerequisites(await dependencies.loadPacket(prerequisitesPath))
|
|
236
|
-
: Object.freeze([]);
|
|
237
|
-
packet = Object.freeze({ instructions, prerequisites });
|
|
238
186
|
|
|
239
187
|
if (!lifecycleRegistered) {
|
|
240
188
|
lifecycleRegistered = true;
|
|
241
189
|
registerWorkerSubmission(pi, "fixer", {
|
|
242
|
-
notReady: "
|
|
243
|
-
ready: () =>
|
|
190
|
+
notReady: "修内司阶段未装载",
|
|
191
|
+
ready: () => phase !== undefined,
|
|
244
192
|
phase: () => phase,
|
|
245
193
|
project: (parameters, current) => deepFreeze(validateFixerOutput(parameters, current)),
|
|
246
194
|
submissionGate,
|
|
@@ -257,17 +205,12 @@ export function createFixerRoleRuntime(
|
|
|
257
205
|
reason: fixerBashSeatbeltDenyReason(matched),
|
|
258
206
|
};
|
|
259
207
|
});
|
|
208
|
+
// #1168: soul + phase only; dispatch text is the first message.
|
|
260
209
|
pi.on("before_agent_start", (event) => {
|
|
261
210
|
if (soul === undefined) throw new Error("修内司职分未装载");
|
|
262
|
-
if (packetPath === undefined) throw new Error("修内司修理包路径未装载");
|
|
263
|
-
// Path delivery only — body is self-fetched; no inline duplicate of flag bytes (#632).
|
|
264
|
-
const prerequisitesBlock =
|
|
265
|
-
prerequisitesPath === undefined
|
|
266
|
-
? ""
|
|
267
|
-
: `\n\n<fixer_prerequisites_path>\n${prerequisitesPath}\n</fixer_prerequisites_path>`;
|
|
268
211
|
return {
|
|
269
212
|
systemPrompt:
|
|
270
|
-
`${event.systemPrompt}\n\n<fixer_soul>\n${soul}\n</fixer_soul>\n\n<fixer_phase>\n${phase ?? ""}\n</fixer_phase
|
|
213
|
+
`${event.systemPrompt}\n\n<fixer_soul>\n${soul}\n</fixer_soul>\n\n<fixer_phase>\n${phase ?? ""}\n</fixer_phase>`,
|
|
271
214
|
};
|
|
272
215
|
});
|
|
273
216
|
}
|
|
@@ -285,7 +228,6 @@ export function createCoderRoleRuntime(
|
|
|
285
228
|
options?: { readonly unfinishedReasonBounceLimit?: number },
|
|
286
229
|
): WorkerRoleRuntime {
|
|
287
230
|
let soul: string | undefined;
|
|
288
|
-
let task: string | undefined;
|
|
289
231
|
let phase: WorkerPhase | undefined;
|
|
290
232
|
let lifecycleRegistered = false;
|
|
291
233
|
// #1132: same resolved ceiling as the fixer gate. Absent = Pi child env.
|
|
@@ -294,10 +236,6 @@ export function createCoderRoleRuntime(
|
|
|
294
236
|
?? deliveryLimitFromEnv(process.env),
|
|
295
237
|
});
|
|
296
238
|
|
|
297
|
-
pi.registerFlag("ak-coder-task", {
|
|
298
|
-
description: "Markdown task assigned to the coder role",
|
|
299
|
-
type: "string",
|
|
300
|
-
});
|
|
301
239
|
pi.registerFlag("ak-coder-phase", {
|
|
302
240
|
description:
|
|
303
241
|
"Coder phase: plan (inspect and propose an implementation plan; no edits or commits) or apply (execute the approved plan and verify the first implementation)",
|
|
@@ -305,7 +243,7 @@ export function createCoderRoleRuntime(
|
|
|
305
243
|
});
|
|
306
244
|
|
|
307
245
|
return {
|
|
308
|
-
async activate(
|
|
246
|
+
async activate() {
|
|
309
247
|
soul = (await dependencies.loadSoul()).trim();
|
|
310
248
|
if (soul.length === 0) throw new Error("Coder soul is empty");
|
|
311
249
|
const selectedPhase = pi.getFlag("ak-coder-phase");
|
|
@@ -315,28 +253,23 @@ export function createCoderRoleRuntime(
|
|
|
315
253
|
);
|
|
316
254
|
}
|
|
317
255
|
phase = selectedPhase;
|
|
318
|
-
const taskPath = pi.getFlag("ak-coder-task");
|
|
319
|
-
if (typeof taskPath !== "string" || taskPath.trim().length === 0) {
|
|
320
|
-
throw new Error("Coder role requires --ak-coder-task");
|
|
321
|
-
}
|
|
322
|
-
task = (await dependencies.loadTask(taskPath)).trim();
|
|
323
|
-
if (task.length === 0) throw new Error("Coder task is empty");
|
|
324
256
|
|
|
325
257
|
if (!lifecycleRegistered) {
|
|
326
258
|
lifecycleRegistered = true;
|
|
327
259
|
registerWorkerSubmission(pi, "coder", {
|
|
328
|
-
notReady: "
|
|
329
|
-
ready: () =>
|
|
260
|
+
notReady: "将作监阶段未装载",
|
|
261
|
+
ready: () => phase !== undefined,
|
|
330
262
|
phase: () => phase,
|
|
331
263
|
project: (parameters, current) => validateWorkerOutput(parameters, current, "Coder"),
|
|
332
264
|
submissionGate,
|
|
333
265
|
hostActions,
|
|
334
266
|
});
|
|
335
|
-
|
|
267
|
+
// #1168: soul + phase only; dispatch text is the first message.
|
|
268
|
+
pi.on("before_agent_start", (event) => {
|
|
336
269
|
if (soul === undefined) throw new Error("将作监职分未装载");
|
|
337
270
|
return {
|
|
338
271
|
systemPrompt:
|
|
339
|
-
`${event.systemPrompt}\n\n<coder_soul>\n${soul}\n</coder_soul>\n\n<coder_phase>\n${phase ?? ""}\n</coder_phase
|
|
272
|
+
`${event.systemPrompt}\n\n<coder_soul>\n${soul}\n</coder_soul>\n\n<coder_phase>\n${phase ?? ""}\n</coder_phase>`,
|
|
340
273
|
};
|
|
341
274
|
});
|
|
342
275
|
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { Type } from "typebox";
|
|
2
|
-
import { Value } from "typebox/value";
|
|
3
|
-
import { isRecord } from "../unknown-value.js";
|
|
4
|
-
const FIXER_PREREQUISITE_ID_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._-]*$";
|
|
5
|
-
const fixerPrerequisiteSchema = Type.Object({
|
|
6
|
-
id: Type.String({ pattern: FIXER_PREREQUISITE_ID_PATTERN }),
|
|
7
|
-
requirement: Type.String({ pattern: "\\S" })
|
|
8
|
-
});
|
|
9
|
-
const fixerPrerequisitesSchema = Type.Array(fixerPrerequisiteSchema);
|
|
10
|
-
function causeMessage(cause) {
|
|
11
|
-
if (cause instanceof Error) return cause.message;
|
|
12
|
-
if (typeof cause === "string") return cause;
|
|
13
|
-
try {
|
|
14
|
-
return JSON.stringify(cause) ?? String(cause);
|
|
15
|
-
} catch {
|
|
16
|
-
return String(cause);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
class FixerPacketValidationError extends Error {
|
|
20
|
-
code = "AK_INVALID_FIX_PACKET";
|
|
21
|
-
constructor(cause) {
|
|
22
|
-
const prefix = "Fixer prerequisites or instructions violate the invocation contract";
|
|
23
|
-
super(
|
|
24
|
-
cause === void 0 ? prefix : `${prefix}: ${causeMessage(cause)}`,
|
|
25
|
-
cause === void 0 ? void 0 : { cause }
|
|
26
|
-
);
|
|
27
|
-
this.name = "FixerPacketValidationError";
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function fail(cause) {
|
|
31
|
-
throw new FixerPacketValidationError(cause);
|
|
32
|
-
}
|
|
33
|
-
function parseFailure(value) {
|
|
34
|
-
if (!Array.isArray(value)) fail(new Error("Fixer prerequisites must be a JSON array"));
|
|
35
|
-
for (const entry of value) {
|
|
36
|
-
if (!isRecord(entry)) {
|
|
37
|
-
fail(new Error("Fixer prerequisite entry must be an object with id and requirement fields"));
|
|
38
|
-
}
|
|
39
|
-
if (typeof entry.id !== "string" || !new RegExp(FIXER_PREREQUISITE_ID_PATTERN).test(entry.id)) {
|
|
40
|
-
fail(new Error(`Fixer prerequisite id violates pattern ${FIXER_PREREQUISITE_ID_PATTERN}`));
|
|
41
|
-
}
|
|
42
|
-
if (typeof entry.requirement !== "string" || !/\S/.test(entry.requirement)) {
|
|
43
|
-
fail(new Error("Fixer prerequisite requirement must be nonblank"));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
fail(new Error("Fixer prerequisites violate the attachment schema"));
|
|
47
|
-
}
|
|
48
|
-
function validateFixerPrerequisites(value) {
|
|
49
|
-
if (!Value.Check(fixerPrerequisitesSchema, value)) parseFailure(value);
|
|
50
|
-
const entries = value;
|
|
51
|
-
const ids = /* @__PURE__ */ new Set();
|
|
52
|
-
const prerequisites = entries.map((entry) => {
|
|
53
|
-
if (ids.has(entry.id)) {
|
|
54
|
-
fail(new Error(`Fixer prerequisites contain duplicate id: ${entry.id}`));
|
|
55
|
-
}
|
|
56
|
-
ids.add(entry.id);
|
|
57
|
-
return Object.freeze({ id: entry.id, requirement: entry.requirement });
|
|
58
|
-
});
|
|
59
|
-
return Object.freeze(prerequisites);
|
|
60
|
-
}
|
|
61
|
-
function parseFixerPrerequisites(source) {
|
|
62
|
-
let decoded;
|
|
63
|
-
try {
|
|
64
|
-
decoded = JSON.parse(source);
|
|
65
|
-
} catch (error) {
|
|
66
|
-
fail(error);
|
|
67
|
-
}
|
|
68
|
-
return validateFixerPrerequisites(decoded);
|
|
69
|
-
}
|
|
70
|
-
export {
|
|
71
|
-
FIXER_PREREQUISITE_ID_PATTERN,
|
|
72
|
-
FixerPacketValidationError,
|
|
73
|
-
fixerPrerequisiteSchema,
|
|
74
|
-
fixerPrerequisitesSchema,
|
|
75
|
-
parseFixerPrerequisites,
|
|
76
|
-
validateFixerPrerequisites
|
|
77
|
-
};
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { Type, type Static } from "typebox";
|
|
2
|
-
import { Value } from "typebox/value";
|
|
3
|
-
|
|
4
|
-
import { isRecord } from "../unknown-value.ts";
|
|
5
|
-
|
|
6
|
-
export const FIXER_PREREQUISITE_ID_PATTERN = "^[A-Za-z0-9][A-Za-z0-9._-]*$";
|
|
7
|
-
|
|
8
|
-
export const fixerPrerequisiteSchema = Type.Object({
|
|
9
|
-
id: Type.String({ pattern: FIXER_PREREQUISITE_ID_PATTERN }),
|
|
10
|
-
requirement: Type.String({ pattern: "\\S" }),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
export const fixerPrerequisitesSchema = Type.Array(fixerPrerequisiteSchema);
|
|
14
|
-
export type FixerPrerequisite = Readonly<Static<typeof fixerPrerequisiteSchema>>;
|
|
15
|
-
export type FixerInvocationInput = Readonly<{
|
|
16
|
-
instructions: string;
|
|
17
|
-
prerequisites: readonly FixerPrerequisite[];
|
|
18
|
-
}>;
|
|
19
|
-
|
|
20
|
-
function causeMessage(cause: unknown): string {
|
|
21
|
-
if (cause instanceof Error) return cause.message;
|
|
22
|
-
if (typeof cause === "string") return cause;
|
|
23
|
-
try {
|
|
24
|
-
return JSON.stringify(cause) ?? String(cause);
|
|
25
|
-
} catch {
|
|
26
|
-
return String(cause);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export class FixerPacketValidationError extends Error {
|
|
31
|
-
readonly code = "AK_INVALID_FIX_PACKET";
|
|
32
|
-
|
|
33
|
-
constructor(cause?: unknown) {
|
|
34
|
-
const prefix = "Fixer prerequisites or instructions violate the invocation contract";
|
|
35
|
-
super(
|
|
36
|
-
cause === undefined ? prefix : `${prefix}: ${causeMessage(cause)}`,
|
|
37
|
-
cause === undefined ? undefined : { cause },
|
|
38
|
-
);
|
|
39
|
-
this.name = "FixerPacketValidationError";
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function fail(cause: unknown): never {
|
|
44
|
-
throw new FixerPacketValidationError(cause);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function parseFailure(value: unknown): never {
|
|
48
|
-
if (!Array.isArray(value)) fail(new Error("Fixer prerequisites must be a JSON array"));
|
|
49
|
-
for (const entry of value) {
|
|
50
|
-
if (!isRecord(entry)) {
|
|
51
|
-
fail(new Error("Fixer prerequisite entry must be an object with id and requirement fields"));
|
|
52
|
-
}
|
|
53
|
-
if (
|
|
54
|
-
typeof (entry as Record<string, unknown>).id !== "string" ||
|
|
55
|
-
!(new RegExp(FIXER_PREREQUISITE_ID_PATTERN)).test((entry as { id: string }).id)
|
|
56
|
-
) {
|
|
57
|
-
fail(new Error(`Fixer prerequisite id violates pattern ${FIXER_PREREQUISITE_ID_PATTERN}`));
|
|
58
|
-
}
|
|
59
|
-
if (
|
|
60
|
-
typeof (entry as Record<string, unknown>).requirement !== "string" ||
|
|
61
|
-
!/\S/.test((entry as { requirement: string }).requirement)
|
|
62
|
-
) {
|
|
63
|
-
fail(new Error("Fixer prerequisite requirement must be nonblank"));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
fail(new Error("Fixer prerequisites violate the attachment schema"));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function validateFixerPrerequisites(value: unknown): readonly FixerPrerequisite[] {
|
|
70
|
-
if (!Value.Check(fixerPrerequisitesSchema, value)) parseFailure(value);
|
|
71
|
-
const entries = value as Static<typeof fixerPrerequisitesSchema>;
|
|
72
|
-
const ids = new Set<string>();
|
|
73
|
-
const prerequisites = entries.map((entry) => {
|
|
74
|
-
if (ids.has(entry.id)) {
|
|
75
|
-
fail(new Error(`Fixer prerequisites contain duplicate id: ${entry.id}`));
|
|
76
|
-
}
|
|
77
|
-
ids.add(entry.id);
|
|
78
|
-
return Object.freeze({ id: entry.id, requirement: entry.requirement });
|
|
79
|
-
});
|
|
80
|
-
return Object.freeze(prerequisites);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function parseFixerPrerequisites(source: string): readonly FixerPrerequisite[] {
|
|
84
|
-
let decoded: unknown;
|
|
85
|
-
try {
|
|
86
|
-
decoded = JSON.parse(source);
|
|
87
|
-
} catch (error) {
|
|
88
|
-
fail(error);
|
|
89
|
-
}
|
|
90
|
-
return validateFixerPrerequisites(decoded);
|
|
91
|
-
}
|