speccore 8.3.46 → 8.3.47

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 (33) hide show
  1. package/dist/cli.js +7 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/analyze.d.ts.map +1 -1
  4. package/dist/commands/analyze.js +6 -1
  5. package/dist/commands/analyze.js.map +1 -1
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +62 -12
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/verify.d.ts +6 -0
  10. package/dist/commands/verify.d.ts.map +1 -1
  11. package/dist/commands/verify.js +143 -0
  12. package/dist/commands/verify.js.map +1 -1
  13. package/dist/core/ui-verify/index.d.ts +8 -0
  14. package/dist/core/ui-verify/index.d.ts.map +1 -0
  15. package/dist/core/ui-verify/index.js +29 -0
  16. package/dist/core/ui-verify/index.js.map +1 -0
  17. package/dist/core/ui-verify/report-generator.d.ts +6 -0
  18. package/dist/core/ui-verify/report-generator.d.ts.map +1 -0
  19. package/dist/core/ui-verify/report-generator.js +209 -0
  20. package/dist/core/ui-verify/report-generator.js.map +1 -0
  21. package/dist/core/ui-verify/smoke-engine.d.ts +16 -0
  22. package/dist/core/ui-verify/smoke-engine.d.ts.map +1 -0
  23. package/dist/core/ui-verify/smoke-engine.js +297 -0
  24. package/dist/core/ui-verify/smoke-engine.js.map +1 -0
  25. package/dist/core/ui-verify/types.d.ts +100 -0
  26. package/dist/core/ui-verify/types.d.ts.map +1 -0
  27. package/dist/core/ui-verify/types.js +8 -0
  28. package/dist/core/ui-verify/types.js.map +1 -0
  29. package/dist/core/ui-verify/visual-engine.d.ts +21 -0
  30. package/dist/core/ui-verify/visual-engine.d.ts.map +1 -0
  31. package/dist/core/ui-verify/visual-engine.js +177 -0
  32. package/dist/core/ui-verify/visual-engine.js.map +1 -0
  33. package/package.json +3 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/ui-verify/types.ts"],"names":[],"mappings":";AAAA;;;;GAIG"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Visual Engine — 视觉模型分析引擎
3
+ *
4
+ * 对比基准图与当前截图,调用视觉模型分析差异
5
+ */
6
+ import { VisualResult, SmokeResult } from './types';
7
+ interface VisualOptions {
8
+ baselineDir: string;
9
+ screenshotDir: string;
10
+ diffDir: string;
11
+ threshold: 'strict' | 'normal' | 'loose';
12
+ updateBaseline: boolean;
13
+ visualModel?: string;
14
+ }
15
+ export declare function runVisualCheck(smokeResults: SmokeResult[], options: VisualOptions): Promise<VisualResult[]>;
16
+ /**
17
+ * 检查视觉模型 API 是否可用
18
+ */
19
+ export declare function isVisualModelAvailable(): Promise<boolean>;
20
+ export {};
21
+ //# sourceMappingURL=visual-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-engine.d.ts","sourceRoot":"","sources":["../../../src/core/ui-verify/visual-engine.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,YAAY,EAAe,WAAW,EAAE,MAAM,SAAS,CAAC;AAEjE,UAAU,aAAa;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACzC,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,cAAc,CAClC,YAAY,EAAE,WAAW,EAAE,EAC3B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC,CAuEzB;AA6HD;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,OAAO,CAAC,CAG/D"}
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ /**
3
+ * Visual Engine — 视觉模型分析引擎
4
+ *
5
+ * 对比基准图与当前截图,调用视觉模型分析差异
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.runVisualCheck = runVisualCheck;
9
+ exports.isVisualModelAvailable = isVisualModelAvailable;
10
+ const path_1 = require("path");
11
+ const fs_extra_1 = require("fs-extra");
12
+ const logger_1 = require("../../utils/logger");
13
+ async function runVisualCheck(smokeResults, options) {
14
+ const results = [];
15
+ await (0, fs_extra_1.ensureDir)(options.baselineDir);
16
+ await (0, fs_extra_1.ensureDir)(options.diffDir);
17
+ for (const smoke of smokeResults) {
18
+ if (!smoke.screenshot) {
19
+ logger_1.logger.warn(` ⚠️ ${smoke.scenarioName} 无截图,跳过视觉检查`);
20
+ continue;
21
+ }
22
+ const scenarioSafeName = smoke.scenarioName.replace(/[^a-z0-9]/gi, '_').toLowerCase();
23
+ const baselinePath = (0, path_1.join)(options.baselineDir, `${scenarioSafeName}.png`);
24
+ const currentPath = smoke.screenshot;
25
+ const diffPath = (0, path_1.join)(options.diffDir, `${scenarioSafeName}_diff.png`);
26
+ logger_1.logger.info(` 🖼️ 视觉检查: ${smoke.scenarioName}`);
27
+ // 检查是否存在基准图
28
+ const hasBaseline = await (0, fs_extra_1.pathExists)(baselinePath);
29
+ if (!hasBaseline) {
30
+ // 首次运行,保存为基准图
31
+ await (0, fs_extra_1.copy)(currentPath, baselinePath);
32
+ results.push({
33
+ scenarioName: smoke.scenarioName,
34
+ passed: true,
35
+ baselinePath,
36
+ currentPath,
37
+ analysis: '首次运行,已保存为基准图',
38
+ issues: [],
39
+ });
40
+ logger_1.logger.info(` ✅ 已保存基准图`);
41
+ continue;
42
+ }
43
+ if (options.updateBaseline) {
44
+ // 强制更新基准图
45
+ await (0, fs_extra_1.copy)(currentPath, baselinePath);
46
+ results.push({
47
+ scenarioName: smoke.scenarioName,
48
+ passed: true,
49
+ baselinePath,
50
+ currentPath,
51
+ analysis: '基准图已更新',
52
+ issues: [],
53
+ });
54
+ logger_1.logger.info(` ✅ 基准图已更新`);
55
+ continue;
56
+ }
57
+ // 调用视觉模型分析差异
58
+ const visualResult = await analyzeWithVisualModel({
59
+ baselinePath,
60
+ currentPath,
61
+ diffPath,
62
+ scenarioName: smoke.scenarioName,
63
+ threshold: options.threshold,
64
+ model: options.visualModel,
65
+ });
66
+ results.push(visualResult);
67
+ const icon = visualResult.passed ? '✅' : '⚠️';
68
+ logger_1.logger.info(` ${icon} ${visualResult.passed ? '通过' : '发现问题'} (${visualResult.issues.length} 项)`);
69
+ }
70
+ return results;
71
+ }
72
+ async function analyzeWithVisualModel(options) {
73
+ try {
74
+ // 读取图片为 base64
75
+ const baselineBase64 = await imageToBase64(options.baselinePath);
76
+ const currentBase64 = await imageToBase64(options.currentPath);
77
+ // 调用视觉模型 API
78
+ const response = await callVisualModel({
79
+ baselineBase64,
80
+ currentBase64,
81
+ scenarioName: options.scenarioName,
82
+ model: options.model,
83
+ });
84
+ // 解析响应
85
+ const { passed, analysis, issues } = parseVisualResponse(response, options.threshold);
86
+ return {
87
+ scenarioName: options.scenarioName,
88
+ passed,
89
+ baselinePath: options.baselinePath,
90
+ currentPath: options.currentPath,
91
+ diffPath: options.diffPath,
92
+ analysis,
93
+ issues,
94
+ };
95
+ }
96
+ catch (error) {
97
+ const message = error instanceof Error ? error.message : String(error);
98
+ logger_1.logger.error(` ❌ 视觉模型调用失败: ${message}`);
99
+ // 失败时返回警告而非阻断(避免网络问题导致测试失败)
100
+ return {
101
+ scenarioName: options.scenarioName,
102
+ passed: options.threshold !== 'strict',
103
+ baselinePath: options.baselinePath,
104
+ currentPath: options.currentPath,
105
+ analysis: `视觉模型调用失败: ${message}`,
106
+ issues: [{
107
+ severity: 'warning',
108
+ description: `视觉模型调用失败: ${message}`,
109
+ }],
110
+ };
111
+ }
112
+ }
113
+ async function imageToBase64(path) {
114
+ const buffer = await (0, fs_extra_1.readFile)(path);
115
+ return buffer.toString('base64');
116
+ }
117
+ async function callVisualModel(request) {
118
+ // TODO: 实现具体的视觉模型调用
119
+ // 当前为占位实现,后续接入 GPT-4o / Claude 3.5 Sonnet / Qwen-VL
120
+ const model = request.model || 'gpt-4o';
121
+ logger_1.logger.info(` 🤖 调用视觉模型: ${model}`);
122
+ // 这里应该调用实际的 AI API
123
+ // 示例(OpenAI GPT-4o):
124
+ // const response = await openai.chat.completions.create({
125
+ // model: 'gpt-4o',
126
+ // messages: [
127
+ // {
128
+ // role: 'system',
129
+ // content: '你是一个 UI 视觉测试专家。对比两张截图,分析差异。'
130
+ // },
131
+ // {
132
+ // role: 'user',
133
+ // content: [
134
+ // { type: 'text', text: `对比 "${request.scenarioName}" 的基准图和当前图,分析是否有视觉差异。` },
135
+ // { type: 'image_url', image_url: { url: `data:image/png;base64,${request.baselineBase64}` } },
136
+ // { type: 'image_url', image_url: { url: `data:image/png;base64,${request.currentBase64}` } },
137
+ // ]
138
+ // }
139
+ // ]
140
+ // });
141
+ // 临时返回模拟结果(后续替换为真实调用)
142
+ return JSON.stringify({
143
+ passed: true,
144
+ analysis: '视觉模型分析完成,未发现明显差异。',
145
+ issues: [],
146
+ });
147
+ }
148
+ function parseVisualResponse(response, threshold) {
149
+ try {
150
+ const parsed = JSON.parse(response);
151
+ return {
152
+ passed: parsed.passed ?? true,
153
+ analysis: parsed.analysis || '未提供分析',
154
+ issues: (parsed.issues || []).map((i) => ({
155
+ severity: i.severity || 'info',
156
+ description: i.description || '',
157
+ region: i.region,
158
+ })),
159
+ };
160
+ }
161
+ catch {
162
+ // 非 JSON 响应,视为纯文本分析
163
+ return {
164
+ passed: threshold !== 'strict',
165
+ analysis: response,
166
+ issues: [],
167
+ };
168
+ }
169
+ }
170
+ /**
171
+ * 检查视觉模型 API 是否可用
172
+ */
173
+ async function isVisualModelAvailable() {
174
+ // TODO: 检查 API key 是否配置
175
+ return false; // 当前未实现真实调用
176
+ }
177
+ //# sourceMappingURL=visual-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-engine.js","sourceRoot":"","sources":["../../../src/core/ui-verify/visual-engine.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speccore",
3
- "version": "8.3.46",
3
+ "version": "8.3.47",
4
4
  "description": "SpecCore - Code by Spec, Not by Vibe.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -38,7 +38,8 @@
38
38
  "fs-extra": "^11.2.0",
39
39
  "glob": "^10.3.10",
40
40
  "js-yaml": "^4.1.0",
41
- "jszip": "^3.10.1"
41
+ "jszip": "^3.10.1",
42
+ "playwright": "^1.62.1"
42
43
  },
43
44
  "devDependencies": {
44
45
  "@types/fs-extra": "^11.0.4",