@epoint-testtech/ep-stage-skill 0.0.3-alpha.2 → 0.0.4-alpha.0

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.
Files changed (109) hide show
  1. package/SKILL.md +2 -2
  2. package/codex-skill/ep-stage/glue-create-project/SKILL.md +186 -0
  3. package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +199 -0
  4. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-schema.md +112 -0
  5. package/codex-skill/ep-stage/glue-run-test/SKILL.md +249 -0
  6. package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +145 -0
  7. package/codex-skill/ep-stage/{glue-test → glue-run-test}/scripts/generate-crud-spec.mjs +3 -3
  8. package/codex-skill/ep-stage/recording-to-glue/SKILL.md +1 -0
  9. package/codex-skill/ep-stage/scripts/validate-skill.mjs +29 -7
  10. package/dist/src/cli/dev/extract-contract.d.ts +14 -0
  11. package/dist/src/cli/dev/extract-contract.d.ts.map +1 -0
  12. package/dist/src/cli/dev/extract-contract.js +114 -0
  13. package/dist/src/cli/generate-crud-contract.js +7 -77
  14. package/dist/src/cli/generate-playwright-tests.d.ts +0 -28
  15. package/dist/src/cli/generate-playwright-tests.d.ts.map +1 -1
  16. package/dist/src/cli/generate-playwright-tests.js +4 -81
  17. package/dist/src/cli/generate-testcase.d.ts +83 -0
  18. package/dist/src/cli/generate-testcase.d.ts.map +1 -0
  19. package/dist/src/cli/generate-testcase.js +197 -0
  20. package/dist/src/cli/index.d.ts +18 -0
  21. package/dist/src/cli/index.d.ts.map +1 -0
  22. package/dist/src/cli/index.js +55 -0
  23. package/dist/src/cli/probe.d.ts +44 -0
  24. package/dist/src/cli/probe.d.ts.map +1 -0
  25. package/dist/src/cli/probe.js +221 -0
  26. package/dist/src/cli/run-gap-pipeline.js +4 -0
  27. package/dist/src/cli/run.d.ts +63 -0
  28. package/dist/src/cli/run.d.ts.map +1 -0
  29. package/dist/src/cli/run.js +116 -0
  30. package/dist/src/cli/spec.d.ts +45 -0
  31. package/dist/src/cli/spec.d.ts.map +1 -0
  32. package/dist/src/cli/spec.js +74 -0
  33. package/dist/src/context/stage-context.d.ts +72 -8
  34. package/dist/src/context/stage-context.d.ts.map +1 -1
  35. package/dist/src/context/stage-context.js +61 -15
  36. package/dist/src/index.d.ts +2 -2
  37. package/dist/src/index.d.ts.map +1 -1
  38. package/dist/src/index.js +1 -1
  39. package/dist/src/testcase/testcase-generator.d.ts.map +1 -1
  40. package/dist/src/testcase/testcase-generator.js +4 -0
  41. package/dist/src/testcase/testcase-v2.d.ts +50 -0
  42. package/dist/src/testcase/testcase-v2.d.ts.map +1 -0
  43. package/dist/src/testcase/testcase-v2.js +1 -0
  44. package/dist/src/util/credentials.d.ts +12 -0
  45. package/dist/src/util/credentials.d.ts.map +1 -0
  46. package/dist/src/util/credentials.js +19 -0
  47. package/dist/src/util/i18n-testcase.d.ts +8 -0
  48. package/dist/src/util/i18n-testcase.d.ts.map +1 -0
  49. package/dist/src/util/i18n-testcase.js +55 -0
  50. package/dist/src/util/softlink.d.ts +33 -0
  51. package/dist/src/util/softlink.d.ts.map +1 -0
  52. package/dist/src/util/softlink.js +43 -0
  53. package/dist/src/validation/credentials.d.ts +19 -0
  54. package/dist/src/validation/credentials.d.ts.map +1 -0
  55. package/dist/src/validation/credentials.js +38 -0
  56. package/dist/src/validation/index.d.ts +5 -0
  57. package/dist/src/validation/index.d.ts.map +1 -0
  58. package/dist/src/validation/index.js +3 -0
  59. package/dist/src/validation/projects-index.d.ts +13 -0
  60. package/dist/src/validation/projects-index.d.ts.map +1 -0
  61. package/dist/src/validation/projects-index.js +37 -0
  62. package/dist/src/validation/testcase.d.ts +13 -0
  63. package/dist/src/validation/testcase.d.ts.map +1 -0
  64. package/dist/src/validation/testcase.js +53 -0
  65. package/dist/test/cli/extract-contract.test.d.ts +2 -0
  66. package/dist/test/cli/extract-contract.test.d.ts.map +1 -0
  67. package/dist/test/cli/extract-contract.test.js +32 -0
  68. package/dist/test/cli/generate-testcase.test.d.ts +2 -0
  69. package/dist/test/cli/generate-testcase.test.d.ts.map +1 -0
  70. package/dist/test/cli/generate-testcase.test.js +130 -0
  71. package/dist/test/cli/index.test.d.ts +2 -0
  72. package/dist/test/cli/index.test.d.ts.map +1 -0
  73. package/dist/test/cli/index.test.js +93 -0
  74. package/dist/test/cli/run.test.d.ts +2 -0
  75. package/dist/test/cli/run.test.d.ts.map +1 -0
  76. package/dist/test/cli/run.test.js +149 -0
  77. package/dist/test/cli/spec.test.d.ts +2 -0
  78. package/dist/test/cli/spec.test.d.ts.map +1 -0
  79. package/dist/test/cli/spec.test.js +196 -0
  80. package/dist/test/stage-context.test.js +145 -13
  81. package/dist/test/util/credentials.test.d.ts +2 -0
  82. package/dist/test/util/credentials.test.d.ts.map +1 -0
  83. package/dist/test/util/credentials.test.js +64 -0
  84. package/dist/test/util/i18n-testcase.test.d.ts +2 -0
  85. package/dist/test/util/i18n-testcase.test.d.ts.map +1 -0
  86. package/dist/test/util/i18n-testcase.test.js +119 -0
  87. package/dist/test/util/softlink.test.d.ts +2 -0
  88. package/dist/test/util/softlink.test.d.ts.map +1 -0
  89. package/dist/test/util/softlink.test.js +82 -0
  90. package/dist/test/validation/credentials.test.d.ts +2 -0
  91. package/dist/test/validation/credentials.test.d.ts.map +1 -0
  92. package/dist/test/validation/credentials.test.js +72 -0
  93. package/dist/test/validation/projects-index.test.d.ts +2 -0
  94. package/dist/test/validation/projects-index.test.d.ts.map +1 -0
  95. package/dist/test/validation/projects-index.test.js +48 -0
  96. package/dist/test/validation/testcase.test.d.ts +2 -0
  97. package/dist/test/validation/testcase.test.d.ts.map +1 -0
  98. package/dist/test/validation/testcase.test.js +129 -0
  99. package/docs/README.md +6 -6
  100. package/docs/mvp-usage-guide.md +3 -3
  101. package/package.json +9 -4
  102. package/codex-skill/ep-stage/create-project/SKILL.md +0 -59
  103. package/codex-skill/ep-stage/glue-test/SKILL.md +0 -258
  104. package/codex-skill/ep-stage/glue-test/references/crud-pipeline.md +0 -139
  105. package/codex-skill/ep-stage/glue-testcase/SKILL.md +0 -31
  106. package/codex-skill/ep-stage/glue-testcase/references/testcase-schema.md +0 -67
  107. /package/codex-skill/ep-stage/{glue-testcase → glue-generate-testcase}/examples/observable-testcase.json +0 -0
  108. /package/codex-skill/ep-stage/{glue-test → glue-run-test}/references/gap-review-protocol.md +0 -0
  109. /package/codex-skill/ep-stage/{glue-test → glue-run-test}/references/harness-principles.md +0 -0
@@ -0,0 +1,221 @@
1
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { extractCodeListSummary } from '../extractors/code-list.js';
4
+ import { extractHtmlPage } from '../extractors/html-page.js';
5
+ import { extractJavaAction } from '../extractors/java-action.js';
6
+ import { extractSpecYaml } from '../extractors/spec-yaml.js';
7
+ import { buildCrudBusinessModuleContract } from '../normalizers/crud-contract.js';
8
+ import { collectActionCandidates } from '../gap-planner/action-candidates.js';
9
+ import { createSkeletonCoverage } from '../gap-planner/skeleton-coverage.js';
10
+ import { planAgentInferredWorkflows } from '../gap-planner/plan-agent-workflows.js';
11
+ import { summarizePageStructure } from '../capability/page-structure.js';
12
+ import { createCoverageDiff } from '../capability/coverage-diff.js';
13
+ /**
14
+ * 收集指定目录下的 HTML 物料文件。
15
+ *
16
+ * @param dir - 物料目录。
17
+ * @returns 排序后的绝对路径数组。
18
+ */
19
+ function htmlFiles(dir) {
20
+ return readdirSync(dir)
21
+ .filter((fileName) => fileName.endsWith('.html'))
22
+ .sort()
23
+ .map((fileName) => path.join(dir, fileName));
24
+ }
25
+ /**
26
+ * 收集指定目录下的 *Action.java 物料文件。
27
+ *
28
+ * @param dir - 物料目录。
29
+ * @returns 排序后的绝对路径数组。
30
+ */
31
+ function javaFiles(dir) {
32
+ return readdirSync(dir)
33
+ .filter((fileName) => fileName.endsWith('Action.java'))
34
+ .sort()
35
+ .map((fileName) => path.join(dir, fileName));
36
+ }
37
+ /**
38
+ * 解析 docs 目录下的 spec.yaml;缺失时返回最小空骨架。
39
+ *
40
+ * @param docsDir - 需求文档目录。
41
+ * @param moduleId - 模块 ID。
42
+ * @param codeListSummary - 已解析的 code_list 摘要。
43
+ * @returns ExtractedSpecYaml。
44
+ */
45
+ function resolveSpec(docsDir, moduleId, codeListSummary) {
46
+ const specPath = path.join(docsDir, 'spec.yaml');
47
+ if (existsSync(specPath))
48
+ return extractSpecYaml(specPath);
49
+ return {
50
+ path: specPath,
51
+ module: { id: moduleId, label: codeListSummary.moduleLabel },
52
+ fields: [],
53
+ businessRules: [],
54
+ };
55
+ }
56
+ /**
57
+ * 推断 requiredRole 默认值。
58
+ *
59
+ * 设计:testcase v2 schema 要求 cases[].requiredRole / uncoveredCandidates[].requiredRole 必填。
60
+ * 当前 BusinessRuleContract 不带 role 字段,hints.scenarioMatrix 也未覆盖角色;
61
+ * 回落到 'admin',后续由 generate-testcase 子命令的 --required-role / hints 覆盖。
62
+ *
63
+ * @returns 默认 requiredRole 字面量。
64
+ */
65
+ function deriveRequiredRole() {
66
+ return 'admin';
67
+ }
68
+ /**
69
+ * 从 contract 推导出 v2 schema 要求的 requiredRoles 集合。
70
+ *
71
+ * 目前只生成单一默认角色;后续可由 hints / scenario decision 注入更丰富的角色矩阵。
72
+ *
73
+ * @returns 至少包含一个默认 admin 的角色清单。
74
+ */
75
+ function deriveRequiredRoles() {
76
+ return [{ role: 'admin', hint: undefined }];
77
+ }
78
+ /**
79
+ * 把 coverageDiff 中已覆盖动作映射成 testcase v2 coveredActions 段。
80
+ *
81
+ * @param contract - CRUD 契约(用于 evidence kind 提示)。
82
+ * @param coverageDiff - coverage diff trace。
83
+ * @returns coveredActions 数组(含 evidence <kind>=<ref>)。
84
+ */
85
+ function mapCoveredActions(contract, coverageDiff) {
86
+ return coverageDiff.covered.map((item) => ({
87
+ actionId: item.actionId,
88
+ kind: item.label,
89
+ evidence: [
90
+ `contract=${contract.contractVersion}`,
91
+ `skeleton=${coverageDiff.skeletonCoverageId}`,
92
+ ],
93
+ }));
94
+ }
95
+ /**
96
+ * 把 coverageDiff 中未覆盖动作映射成 v2 uncoveredCandidates 段。
97
+ *
98
+ * @param contract - CRUD 契约(用于 evidence kind 提示)。
99
+ * @param coverageDiff - coverage diff trace。
100
+ * @param requiredRole - 默认 role;后续 spec/run 子命令可改写。
101
+ * @returns uncoveredCandidates 数组(含 requiredRole/businessIntent/assertionExpectation/evidence)。
102
+ */
103
+ function mapUncoveredCandidates(contract, coverageDiff, requiredRole) {
104
+ const status = (diffStatus) => {
105
+ if (diffStatus === 'gap-candidate')
106
+ return 'planned';
107
+ if (diffStatus === 'partially-covered')
108
+ return 'candidate';
109
+ return 'unresolved';
110
+ };
111
+ return [
112
+ ...coverageDiff.gapCandidates,
113
+ ...coverageDiff.partiallyCovered,
114
+ ...coverageDiff.unresolved,
115
+ ].map((item) => ({
116
+ actionId: item.actionId,
117
+ label: item.label,
118
+ status: status(item.status),
119
+ requiredRole,
120
+ businessIntent: item.reason,
121
+ assertionExpectation: `验证 ${item.label} 完成后业务断言(待 run 子命令推理细化)`,
122
+ evidence: [`contract=${contract.contractVersion}`, `skeleton=${coverageDiff.skeletonCoverageId}`],
123
+ }));
124
+ }
125
+ /**
126
+ * 装配 testcase v2 顶层结构。
127
+ *
128
+ * @param input - 装配所需要素。
129
+ * @returns TestcaseV2。
130
+ */
131
+ function assembleTestcaseV2(input) {
132
+ const requiredRoles = deriveRequiredRoles();
133
+ const defaultRole = deriveRequiredRole();
134
+ const coveredActions = mapCoveredActions(input.contract, input.coverageDiff);
135
+ const uncoveredCandidates = mapUncoveredCandidates(input.contract, input.coverageDiff, defaultRole);
136
+ const caseEvidence = [
137
+ `contract=${input.contract.contractVersion}`,
138
+ `skeleton=${input.coverageDiff.skeletonCoverageId}`,
139
+ ];
140
+ const cases = ['create', 'read', 'update', 'delete'].map((kind) => ({
141
+ caseId: `${input.contract.module.id}.${kind}`,
142
+ title: `${input.contract.module.label} ${kind}`,
143
+ scenario: input.scenario,
144
+ reviewStatus: 'needs_review',
145
+ requiredRole: defaultRole,
146
+ evidence: caseEvidence,
147
+ steps: [`执行 ${kind} 主流程(详见 .spec.ts)`],
148
+ assertions: [`验证 ${kind} 完成后业务断言`],
149
+ unresolved: [],
150
+ }));
151
+ return {
152
+ schemaVersion: 'v2',
153
+ scenario: input.scenario,
154
+ moduleId: input.contract.module.id,
155
+ moduleName: input.contract.module.label,
156
+ menuPath: input.menuPath,
157
+ requiredSystems: [{ url: '', systemName: undefined }],
158
+ requiredRoles,
159
+ coveredActions,
160
+ uncoveredCandidates,
161
+ cases,
162
+ reasoningSummary: {
163
+ conclusion: `基于 ${input.contract.contractVersion} 契约和 ${input.coverageDiff.skeletonCoverageId} 骨架覆盖度生成 ${cases.length} 条胶水用例。`,
164
+ evidenceChain: [
165
+ `contract=${input.contract.contractVersion}`,
166
+ `skeleton=${input.coverageDiff.skeletonCoverageId}`,
167
+ `codeList=${path.basename(input.codeListPath)}`,
168
+ ],
169
+ alternatives: [],
170
+ confidence: input.coverageDiff.reasoningSummary.confidence,
171
+ risks: input.coverageDiff.reasoningSummary.risks,
172
+ needsHumanReview: true,
173
+ },
174
+ };
175
+ }
176
+ /**
177
+ * probe + coverage + testcase v2 装配的统一入口(REQ-CHAIN-01)。
178
+ *
179
+ * 把 run-gap-pipeline.ts 与 generate-testcase.ts 共用的物料解析 → contract → skeleton coverage →
180
+ * coverage diff 段抽成单一函数,避免 1881 行 run-gap-pipeline.ts 与新 CLI 双份维护。
181
+ *
182
+ * 注意:本函数只做钻探与装配,不写文件;写盘由调用方负责(保持 I/O 边界清晰)。
183
+ *
184
+ * @param args - 物料路径 + 场景。
185
+ * @returns contract + coverageDiff + 装配好的 TestcaseV2。
186
+ */
187
+ export function runProbeAndCoverage(args) {
188
+ const hints = args.hintsPath
189
+ ? JSON.parse(readFileSync(args.hintsPath, 'utf8'))
190
+ : undefined;
191
+ const codeList = extractCodeListSummary(args.codeListPath);
192
+ const pages = htmlFiles(args.webappDir).map(extractHtmlPage);
193
+ const actions = javaFiles(args.javaActionsDir).map(extractJavaAction);
194
+ const pageStructures = pages.map(summarizePageStructure);
195
+ const contract = buildCrudBusinessModuleContract({
196
+ moduleId: args.moduleId,
197
+ spec: resolveSpec(args.docsDir, args.moduleId, codeList),
198
+ codeList,
199
+ pages,
200
+ actions,
201
+ hints,
202
+ });
203
+ const skeletonCoverage = createSkeletonCoverage(contract, hints);
204
+ const actionCandidates = collectActionCandidates(contract);
205
+ const baselineWorkflows = planAgentInferredWorkflows({ contract, actions, hints });
206
+ const coverageDiff = createCoverageDiff({
207
+ moduleId: args.moduleId,
208
+ actionCandidates,
209
+ skeletonCoverage,
210
+ baselineWorkflows,
211
+ pageStructures,
212
+ });
213
+ const testcaseV2 = assembleTestcaseV2({
214
+ contract,
215
+ coverageDiff,
216
+ scenario: args.scenario,
217
+ menuPath: args.menuPath ?? codeList.menu ?? '',
218
+ codeListPath: args.codeListPath,
219
+ });
220
+ return { contract, coverageDiff, testcaseV2 };
221
+ }
@@ -1217,6 +1217,10 @@ async function main() {
1217
1217
  gate: { status: 'confirmed', confirmedBy: 'policy', reason: '确定性 extractor 已完成' },
1218
1218
  };
1219
1219
  recordStage(writer, stageSummaries, 'evidence-extraction', evidenceTrace);
1220
+ // TODO(Phase 3 convergence):本段(contract 提取 → coverage diff → testcase 生成)的规范实现已迁到
1221
+ // `src/cli/probe.ts` 的 runProbeAndCoverage / assembleTestcaseV2。保留此处的内联调用是 Task 1.6 为
1222
+ // 避免现有 vitest fixture 联动断裂做的过渡。Phase 3.1 task 完成 generator 升级后应替换为
1223
+ // import { runProbeAndCoverage } from './probe.js' 一次调用,并删除此段重复。
1220
1224
  const contract = buildCrudBusinessModuleContract({
1221
1225
  moduleId,
1222
1226
  spec,
@@ -0,0 +1,63 @@
1
+ /**
2
+ * run 子命令参数(REQ-CHAIN-02 / REQ-SPEC-01)。
3
+ *
4
+ * - spec:crud.spec.ts 路径(必填,由 spec 子命令产出,v1 或已含 AI 追加段 v2);
5
+ * - testcase:testcase.json5 路径(必填,提供 uncoveredCandidates 与 cases[].requiredRole);
6
+ * - headless:是否启动无头浏览器,真实业务系统通常 false;
7
+ * - projectDir:报告目录基址,默认 process.cwd()。
8
+ */
9
+ export type RunArgs = {
10
+ spec: string;
11
+ testcase: string;
12
+ headless: boolean;
13
+ projectDir?: string;
14
+ };
15
+ /**
16
+ * run 子命令产物。
17
+ *
18
+ * @property specPath - 写盘后的 .spec.ts(含 AI 追加段)。
19
+ * @property reportDir - 本次 run 产出的报告目录。
20
+ * @property version - 'v1-with-planned-stubs' 表示当前 run.ts 阶段产物(已追加 planned 占位但未跑真实 Playwright + AI 推理);
21
+ * 'v2' 保留给 Phase 5 真机验收阶段迁移完整 runtime 后使用。
22
+ */
23
+ export type RunResult = {
24
+ specPath: string;
25
+ reportDir: string;
26
+ version: 'v1' | 'v1-with-planned-stubs' | 'v2';
27
+ };
28
+ /**
29
+ * AI 追加段分隔标记。重跑时 detect 已有标记则覆盖(保证幂等)。
30
+ */
31
+ export declare const AI_APPEND_MARKER = "// === AI \u94BB\u63A2\u8FFD\u52A0\uFF08\u57FA\u4E8E uncoveredCandidates[].status=planned\uFF09===";
32
+ /**
33
+ * 解析 run 子命令参数。
34
+ *
35
+ * @param argv - 已剥离子命令名的命令行参数。
36
+ * @returns RunArgs。
37
+ * @throws 缺少 --spec / --testcase 或参数对非法时抛错。
38
+ */
39
+ export declare function parseRunArgs(argv: string[]): RunArgs;
40
+ /**
41
+ * run 子命令主体(REQ-CHAIN-02 / REQ-SPEC-01)。
42
+ *
43
+ * 完整设计(spec §5.4 / brief Step 6):
44
+ * 1. 按 cases[].requiredRole 用 credentials.json5 登录;
45
+ * 2. 跑 v1 胶水部分 spec;
46
+ * 3. 对 uncoveredCandidates 里 status='planned' 的项做 AI 实时推理(含 requiredRole 切换);
47
+ * 4. 追加段以 AI_APPEND_MARKER 分隔;重跑检测已有段则覆盖(幂等);
48
+ * 5. 报告落 e2e/glue-code-mvp/glue-report/<timestamp>/;
49
+ * 6. 回写 projects.index.json5 的 systems[].credentialsCache 与 requirements[].lastRun*。
50
+ *
51
+ * 当前 Sub-step 6 范围:
52
+ * - 完整实现「步骤 4」(AI 追加段幂等覆盖契约);
53
+ * - 完整实现「步骤 5」(报告目录生成);
54
+ * - 步骤 1 / 2 / 3 / 6 的 Playwright runtime + AI 推理 + 索引回写在 Phase 5 真机验收阶段
55
+ * 迁移 run-gap-pipeline.ts 的 runtime runner 实现接入(brief 已说明 AI 推理在 Phase 5 证)。
56
+ *
57
+ * @param args - 已解析参数。
58
+ * @returns version: 'v1-with-planned-stubs' — 真实的 v2(Playwright 执行 + AI 推理填充 planned 项)
59
+ * 待 Phase 5 真机验收阶段从 run-gap-pipeline.ts 的 runtime runner 迁移过来。当前返回 'v1-with-planned-stubs'
60
+ * 而非 'v2' 是为了让下游 skill(glue-run-test 等)能区分"真 v2"与"stub 形态"。
61
+ */
62
+ export declare function runRun(args: RunArgs): Promise<RunResult>;
63
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,IAAI,GAAG,uBAAuB,GAAG,IAAI,CAAC;CAChD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,uGAA+D,CAAC;AAE7F;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAoBpD;AAyCD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAoB9D"}
@@ -0,0 +1,116 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import JSON5 from 'json5';
4
+ import { validateTestcase } from '../validation/testcase.js';
5
+ /**
6
+ * AI 追加段分隔标记。重跑时 detect 已有标记则覆盖(保证幂等)。
7
+ */
8
+ export const AI_APPEND_MARKER = '// === AI 钻探追加(基于 uncoveredCandidates[].status=planned)===';
9
+ /**
10
+ * 解析 run 子命令参数。
11
+ *
12
+ * @param argv - 已剥离子命令名的命令行参数。
13
+ * @returns RunArgs。
14
+ * @throws 缺少 --spec / --testcase 或参数对非法时抛错。
15
+ */
16
+ export function parseRunArgs(argv) {
17
+ const result = {};
18
+ const normalizedArgv = argv.filter((arg) => arg !== '--');
19
+ for (let index = 0; index < normalizedArgv.length; index += 2) {
20
+ const key = normalizedArgv[index];
21
+ const value = normalizedArgv[index + 1];
22
+ if (!key?.startsWith('--') || !value) {
23
+ throw new Error(`无效参数对: ${key ?? '<empty>'}`);
24
+ }
25
+ result[key.slice(2)] = value;
26
+ }
27
+ if (!result.spec)
28
+ throw new Error('缺少 --spec');
29
+ if (!result.testcase)
30
+ throw new Error('缺少 --testcase');
31
+ return {
32
+ spec: result.spec,
33
+ testcase: result.testcase,
34
+ // headless 默认 true;--headless false 时关闭无头。
35
+ headless: result.headless !== 'false',
36
+ projectDir: result['project-dir'],
37
+ };
38
+ }
39
+ /**
40
+ * 渲染 AI 追加段:每条 planned uncoveredCandidate 输出一个占位 test,含
41
+ * requiredRole / businessIntent / assertionExpectation 三条注释(供 AI runtime
42
+ * 推理时拓展为真实业务步骤)。
43
+ *
44
+ * 当前实现只生成占位骨架;真正的 AI runtime 推理在 Phase 5 真机验收阶段接入
45
+ * (参考 run-gap-pipeline.ts 的 planAgentInferredWorkflows 调用链)。本 Sub-step
46
+ * 聚焦于「追加段幂等覆盖」契约,确保重跑不会无限叠加注释体。
47
+ *
48
+ * @param planned - status='planned' 的 uncoveredCandidates。
49
+ * @returns 拼接后的追加段(不含 marker,由调用方串接)。
50
+ */
51
+ function renderAppendedTests(planned) {
52
+ return planned
53
+ .map((item) => {
54
+ return [
55
+ `test(${JSON.stringify(item.label)}, async ({ page: _page }) => {`,
56
+ ` // actionId: ${item.actionId}`,
57
+ ` // requiredRole: ${item.requiredRole}`,
58
+ ` // businessIntent: ${item.businessIntent}`,
59
+ ` // assertionExpectation: ${item.assertionExpectation}`,
60
+ ` // TODO: AI runtime 推理后填入实际 Playwright 步骤`,
61
+ `});`,
62
+ ].join('\n');
63
+ })
64
+ .join('\n\n');
65
+ }
66
+ /**
67
+ * 截断现有 spec 的 AI 追加段(用于重跑覆盖)。
68
+ *
69
+ * @param specBody - 现有 spec 文件全文。
70
+ * @returns 截断到 AI_APPEND_MARKER 之前的基线 spec。
71
+ */
72
+ function truncateAtMarker(specBody) {
73
+ const markerIndex = specBody.indexOf(AI_APPEND_MARKER);
74
+ return markerIndex >= 0 ? specBody.slice(0, markerIndex).trimEnd() : specBody.trimEnd();
75
+ }
76
+ /**
77
+ * run 子命令主体(REQ-CHAIN-02 / REQ-SPEC-01)。
78
+ *
79
+ * 完整设计(spec §5.4 / brief Step 6):
80
+ * 1. 按 cases[].requiredRole 用 credentials.json5 登录;
81
+ * 2. 跑 v1 胶水部分 spec;
82
+ * 3. 对 uncoveredCandidates 里 status='planned' 的项做 AI 实时推理(含 requiredRole 切换);
83
+ * 4. 追加段以 AI_APPEND_MARKER 分隔;重跑检测已有段则覆盖(幂等);
84
+ * 5. 报告落 e2e/glue-code-mvp/glue-report/<timestamp>/;
85
+ * 6. 回写 projects.index.json5 的 systems[].credentialsCache 与 requirements[].lastRun*。
86
+ *
87
+ * 当前 Sub-step 6 范围:
88
+ * - 完整实现「步骤 4」(AI 追加段幂等覆盖契约);
89
+ * - 完整实现「步骤 5」(报告目录生成);
90
+ * - 步骤 1 / 2 / 3 / 6 的 Playwright runtime + AI 推理 + 索引回写在 Phase 5 真机验收阶段
91
+ * 迁移 run-gap-pipeline.ts 的 runtime runner 实现接入(brief 已说明 AI 推理在 Phase 5 证)。
92
+ *
93
+ * @param args - 已解析参数。
94
+ * @returns version: 'v1-with-planned-stubs' — 真实的 v2(Playwright 执行 + AI 推理填充 planned 项)
95
+ * 待 Phase 5 真机验收阶段从 run-gap-pipeline.ts 的 runtime runner 迁移过来。当前返回 'v1-with-planned-stubs'
96
+ * 而非 'v2' 是为了让下游 skill(glue-run-test 等)能区分"真 v2"与"stub 形态"。
97
+ */
98
+ export async function runRun(args) {
99
+ if (!existsSync(args.spec))
100
+ throw new Error(`spec 不存在: ${args.spec}`);
101
+ const testcaseRaw = JSON5.parse(readFileSync(args.testcase, 'utf8'));
102
+ const testcase = validateTestcase(testcaseRaw);
103
+ const planned = testcase.uncoveredCandidates.filter((item) => item.status === 'planned');
104
+ const currentSpec = readFileSync(args.spec, 'utf8');
105
+ const baseSpec = truncateAtMarker(currentSpec);
106
+ if (planned.length > 0) {
107
+ const appendedTests = renderAppendedTests(planned);
108
+ writeFileSync(args.spec, `${baseSpec}\n\n${AI_APPEND_MARKER}\n${appendedTests}\n`, 'utf8');
109
+ }
110
+ else {
111
+ // 没有 planned 项时,仍然把 spec 收敛到 baseSpec(保证幂等:重跑能擦掉旧追加段)。
112
+ writeFileSync(args.spec, `${baseSpec}\n`, 'utf8');
113
+ }
114
+ const reportDir = path.join(args.projectDir ?? process.cwd(), 'e2e/glue-code-mvp/glue-report', new Date().toISOString().replace(/[:.]/g, '-'));
115
+ return { specPath: args.spec, reportDir, version: 'v1-with-planned-stubs' };
116
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * spec 子命令参数(REQ-SPEC-01)。
3
+ *
4
+ * - testcase:testcase.json5 路径(必填,由 testcase 子命令产出);
5
+ * - out:.spec.ts 输出路径(必填,通常为 <tests-dir>/crud.spec.ts)。
6
+ */
7
+ export type SpecArgs = {
8
+ testcase: string;
9
+ out: string;
10
+ };
11
+ /**
12
+ * spec 子命令产物。
13
+ *
14
+ * @property specPath - 写盘后的 .spec.ts 路径。
15
+ * @property version - 'v1' 表示只含 coveredActions 胶水部分,待 run 子命令追加 v2。
16
+ */
17
+ export type SpecResult = {
18
+ specPath: string;
19
+ version: 'v1';
20
+ };
21
+ /**
22
+ * 解析 spec 子命令参数。
23
+ *
24
+ * @param argv - 已剥离子命令名的命令行参数。
25
+ * @returns SpecArgs。
26
+ * @throws 缺少必填参数或参数对非法时抛错。
27
+ */
28
+ export declare function parseSpecArgs(argv: string[]): SpecArgs;
29
+ /**
30
+ * spec 子命令主体(REQ-SPEC-01):只生成 v1 初始版 .spec.ts,不启动浏览器。
31
+ *
32
+ * v1 = 仅 coveredActions 胶水部分(顶部注释 `// === 胶水模式生成 ===`);
33
+ * run 子命令后续在底部追加 AI 推理段产 v2。
34
+ *
35
+ * 实现:
36
+ * 1. 校验 testcase.json5 通过 validateTestcase(v2 schema);
37
+ * 2. 加载 contract.json(位于 <tests-dir>/.ep-stage/contract.json;不存在时报错引导用户先跑 testcase 子命令);
38
+ * 3. 调 generateStageSkeletonCrudSpec 生成基线骨架脚本;
39
+ * 4. 头部插入 SKELETON_MARKER_LINE 标记,写盘。
40
+ *
41
+ * @param args - 解析后的参数。
42
+ * @returns SpecResult(含 version='v1' 显式标识阶段)。
43
+ */
44
+ export declare function runSpec(args: SpecArgs): SpecResult;
45
+ //# sourceMappingURL=spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../../src/cli/spec.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,IAAI,CAAC;CACf,CAAC;AAIF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,CActD;AAeD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAkBlD"}
@@ -0,0 +1,74 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import JSON5 from 'json5';
4
+ import { generateStageSkeletonCrudSpec } from '../generators/stage-skeleton-script.js';
5
+ import { validateTestcase } from '../validation/testcase.js';
6
+ const SKELETON_MARKER_LINE = '// === 胶水模式生成(基于 contract + coveredActions)===';
7
+ /**
8
+ * 解析 spec 子命令参数。
9
+ *
10
+ * @param argv - 已剥离子命令名的命令行参数。
11
+ * @returns SpecArgs。
12
+ * @throws 缺少必填参数或参数对非法时抛错。
13
+ */
14
+ export function parseSpecArgs(argv) {
15
+ const result = {};
16
+ const normalizedArgv = argv.filter((arg) => arg !== '--');
17
+ for (let index = 0; index < normalizedArgv.length; index += 2) {
18
+ const key = normalizedArgv[index];
19
+ const value = normalizedArgv[index + 1];
20
+ if (!key?.startsWith('--') || !value) {
21
+ throw new Error(`无效参数对: ${key ?? '<empty>'}`);
22
+ }
23
+ result[key.slice(2)] = value;
24
+ }
25
+ if (!result.testcase)
26
+ throw new Error('缺少 --testcase');
27
+ if (!result.out)
28
+ throw new Error('缺少 --out');
29
+ return { testcase: result.testcase, out: result.out };
30
+ }
31
+ /**
32
+ * 定位 testcase 旁挂的 contract.json:
33
+ * <tests-dir>/.ep-stage/contract.json(由 testcase 子命令的 probe 内部写入,
34
+ * 当前 Sub-step 4 未保存该文件;本函数兜底返回 null,调用方需先用 dev/extract-contract 显式生成)。
35
+ *
36
+ * @param testcasePath - testcase.json5 绝对路径。
37
+ * @returns contract.json 绝对路径或 null。
38
+ */
39
+ function locateContract(testcasePath) {
40
+ const candidate = path.join(path.dirname(testcasePath), '.ep-stage', 'contract.json');
41
+ return existsSync(candidate) ? candidate : null;
42
+ }
43
+ /**
44
+ * spec 子命令主体(REQ-SPEC-01):只生成 v1 初始版 .spec.ts,不启动浏览器。
45
+ *
46
+ * v1 = 仅 coveredActions 胶水部分(顶部注释 `// === 胶水模式生成 ===`);
47
+ * run 子命令后续在底部追加 AI 推理段产 v2。
48
+ *
49
+ * 实现:
50
+ * 1. 校验 testcase.json5 通过 validateTestcase(v2 schema);
51
+ * 2. 加载 contract.json(位于 <tests-dir>/.ep-stage/contract.json;不存在时报错引导用户先跑 testcase 子命令);
52
+ * 3. 调 generateStageSkeletonCrudSpec 生成基线骨架脚本;
53
+ * 4. 头部插入 SKELETON_MARKER_LINE 标记,写盘。
54
+ *
55
+ * @param args - 解析后的参数。
56
+ * @returns SpecResult(含 version='v1' 显式标识阶段)。
57
+ */
58
+ export function runSpec(args) {
59
+ const testcaseRaw = JSON5.parse(readFileSync(args.testcase, 'utf8'));
60
+ const testcase = validateTestcase(testcaseRaw);
61
+ const contractPath = locateContract(args.testcase);
62
+ if (!contractPath) {
63
+ throw new Error(`未找到 ${path.dirname(args.testcase)}/.ep-stage/contract.json。请先运行 \`ep-stage-skill testcase --code-list <p> --tests-dir <p>\` 生成 contract.json。`);
64
+ }
65
+ const contract = JSON.parse(readFileSync(contractPath, 'utf8'));
66
+ const script = generateStageSkeletonCrudSpec({
67
+ contract,
68
+ menuNavigation: testcase.menuPath,
69
+ });
70
+ const body = `${SKELETON_MARKER_LINE}\n${script}`;
71
+ mkdirSync(path.dirname(args.out), { recursive: true });
72
+ writeFileSync(args.out, body, 'utf8');
73
+ return { specPath: args.out, version: 'v1' };
74
+ }
@@ -10,19 +10,58 @@ export type StageContext = {
10
10
  codeListPaths: string[];
11
11
  stageContextPath?: string;
12
12
  };
13
+ /**
14
+ * projects.index.json5 顶层 systems[] 条目(REQ-DATA-01 修订 / spec §4.1)。
15
+ *
16
+ * 系统级凭据缓存按 (url, role) 维度组织,跨多个 project 共用。
17
+ * knownRoles 与 credentialsCache 必填,可为空数组 / 空对象(spec 要求字段存在)。
18
+ */
19
+ export type SystemEntry = {
20
+ url: string;
21
+ knownRoles: string[];
22
+ credentialsCache: Record<string, {
23
+ username: string;
24
+ password: string;
25
+ lastUsed: string;
26
+ }>;
27
+ };
28
+ /**
29
+ * projects.index.json5 中 projects[].requirements[] 条目(REQ-DATA-01 修订 / spec §4.1)。
30
+ */
31
+ export type RequirementEntry = {
32
+ name: string;
33
+ requirementDir: string;
34
+ testsDir: string;
35
+ lastRunAt?: string;
36
+ lastRunStatus?: 'passed' | 'failed' | 'partial' | 'never';
37
+ };
38
+ /**
39
+ * projects.index.json5 中 projects[] 条目(REQ-DATA-01 修订 / spec §4.1)。
40
+ *
41
+ * stageCreateVersion 承接砍掉的 stage-context.md 元信息;requirements[] 承载需求级状态。
42
+ * 旧字段 envPath/knowledgeRoot/codeListPaths/stageContextPath 已被 schema 拒绝,类型层不再声明。
43
+ * 兼容性:旧数据若残留这些字段,由 normalizeProjectIndexEntry 内的 graceful-read 路径解析,
44
+ * 不污染类型契约。
45
+ */
13
46
  export type ProjectIndexEntry = {
14
47
  projectName: string;
15
48
  projectDir: string;
16
49
  mode: StageContextMode;
17
- envPath?: string;
18
- knowledgeRoot?: string;
19
- codeListPaths?: string[];
20
- stageContextPath?: string;
50
+ createdAt?: string;
51
+ stageCreateVersion: string;
52
+ requirements: RequirementEntry[];
21
53
  };
22
- export type ProjectIndex = {
23
- version?: 1;
54
+ /**
55
+ * projects.index.json5 顶层结构(REQ-DATA-01 修订 / spec §4.1)。
56
+ *
57
+ * 顶层 systems[] 承接多项目共享的系统级凭据缓存。
58
+ */
59
+ export type ProjectsIndex = {
24
60
  projects: ProjectIndexEntry[];
61
+ systems: SystemEntry[];
25
62
  };
63
+ /** @deprecated 旧名,使用 ProjectsIndex;保留仅为外部 import 兼容。 */
64
+ export type ProjectIndex = ProjectsIndex;
26
65
  export type ResolveStageContextInput = {
27
66
  stageContextPath?: string;
28
67
  projectDir?: string;
@@ -34,27 +73,52 @@ export type ResolveStageContextResult = {
34
73
  context: StageContext;
35
74
  trace: ObservableStagePayload;
36
75
  };
76
+ export type LookupProjectByRequirementInput = {
77
+ requirementPath: string;
78
+ homeDir?: string;
79
+ };
37
80
  /**
38
81
  * 读取项目级 stage-context.md,并将相对路径归一到项目目录。
39
82
  *
83
+ * @deprecated stage-context.md 已砍(REQ-ENV-01 修订);新链路应从 projects.index.json5 反查
84
+ * ({@link lookupProjectByRequirement})。本函数保留仅为零回归兼容,
85
+ * 不要在新代码里调用。
40
86
  * @param stageContextPath - stage-context.md 文件路径。
41
87
  * @returns 归一化后的项目上下文。
42
88
  * @throws 当 frontmatter 缺少必要字段或格式不合法时抛出错误。
43
89
  */
44
90
  export declare function readStageContext(stageContextPath: string): StageContext;
45
91
  /**
46
- * 写入用户级 ep-stage 项目索引。
92
+ * 写入用户级 ep-stage 项目索引(REQ-DATA-01 修订 / spec §4.1)。
47
93
  *
48
- * @param input - HOME 目录和项目列表。
94
+ * schema:顶层 `{ projects: [...], systems: [...] }`,无 `version` 字段;
95
+ * projects[] 条目不再写入 envPath/knowledgeRoot/codeListPaths/stageContextPath 等 legacy 字段。
96
+ *
97
+ * @param input - HOME 目录、项目列表,以及可选的 systems[](不传按空数组写入)。
49
98
  * @returns 写入后的索引文件路径。
50
99
  */
51
100
  export declare function writeProjectIndex(input: {
52
101
  homeDir?: string;
53
102
  projects: ProjectIndexEntry[];
103
+ systems?: SystemEntry[];
54
104
  }): string;
105
+ /**
106
+ * 从 `~/.ep-stage/projects.index.json5` 反查匹配的 ProjectIndexEntry(REQ-DATA-01 修订 / spec §5.2 §5.3)。
107
+ *
108
+ * 匹配优先级:①精确 requirementDir / testsDir 命中;②精确 projectDir 命中。
109
+ * 用于 glue-generate-testcase / glue-run-test skill 的「反查项目脚手架」步骤——
110
+ * 给定一个需求目录或项目目录,回到对应的 ProjectIndexEntry(不再依赖砍掉的 stage-context.md)。
111
+ *
112
+ * @param input - 需求路径或项目目录 + 可选 homeDir。
113
+ * @returns 匹配的 ProjectIndexEntry,未匹配返回 null。
114
+ */
115
+ export declare function lookupProjectByRequirement(input: LookupProjectByRequirementInput): ProjectIndexEntry | null;
55
116
  /**
56
117
  * 按固定优先级解析 ep-stage 项目上下文,并返回可审阅 trace。
57
118
  *
119
+ * @deprecated 旧链路依赖 stage-context.md 与 codeListPaths 匹配,stage-context.md 已砍
120
+ * (REQ-ENV-01 修订)。保留实现仅为零回归兼容;新代码请使用
121
+ * {@link lookupProjectByRequirement}。
58
122
  * @param input - 显式路径、当前目录、code_list 路径和可选 HOME。
59
123
  * @returns 解析出的项目上下文与 context-resolution trace。
60
124
  * @throws 当无法定位任何上下文来源时抛出错误。
@@ -1 +1 @@
1
- {"version":3,"file":"stage-context.d.ts","sourceRoot":"","sources":["../../../src/context/stage-context.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAE/E,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,CAAC,EAAE,CAAC,CAAC;IACZ,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,sBAAsB,CAAC;CAC/B,CAAC;AAWF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,CAqBvE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B,GAAG,MAAM,CAST;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,yBAAyB,CA6D9F"}
1
+ {"version":3,"file":"stage-context.d.ts","sourceRoot":"","sources":["../../../src/context/stage-context.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAE/E,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5F,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;CAC3D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,gBAAgB,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,gBAAgB,EAAE,CAAC;CAClC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC;AAEzC,MAAM,MAAM,wBAAwB,GAAG;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,sBAAsB,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAuBF;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,CAqBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CACzB,GAAG,MAAM,CAST;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,+BAA+B,GACrC,iBAAiB,GAAG,IAAI,CAmB1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,yBAAyB,CA8D9F"}