@kkeve/suite 0.0.1 → 0.0.3

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 (153) hide show
  1. package/README.md +53 -0
  2. package/dist/bin/keve.d.ts +13 -0
  3. package/dist/bin/keve.js +164 -0
  4. package/dist/commands/auth-update.d.ts +16 -0
  5. package/dist/commands/auth-update.js +88 -0
  6. package/dist/commands/clean.d.ts +10 -0
  7. package/dist/commands/clean.js +78 -0
  8. package/dist/commands/config.d.ts +17 -0
  9. package/dist/commands/config.js +99 -0
  10. package/dist/commands/init.d.ts +15 -0
  11. package/dist/commands/init.js +292 -0
  12. package/dist/commands/report.d.ts +12 -0
  13. package/dist/commands/report.js +159 -0
  14. package/dist/commands/run.d.ts +29 -0
  15. package/dist/commands/run.js +417 -0
  16. package/dist/commands/test.d.ts +12 -0
  17. package/dist/commands/test.js +102 -0
  18. package/dist/commands/vitest.d.ts +14 -0
  19. package/dist/commands/vitest.js +166 -0
  20. package/dist/config.d.ts +109 -0
  21. package/dist/config.js +291 -0
  22. package/dist/core/action-log-writer.d.ts +47 -0
  23. package/dist/{tools/write-file.js → core/action-log-writer.js} +42 -26
  24. package/dist/core/auth.d.ts +17 -0
  25. package/dist/core/auth.js +224 -0
  26. package/dist/core/cdp-resolver.d.ts +5 -0
  27. package/dist/core/cdp-resolver.js +93 -0
  28. package/dist/core/generateReport/report.d.ts +19 -0
  29. package/dist/core/generateReport/report.js +91 -0
  30. package/dist/core/generateReport/reportData.d.ts +215 -0
  31. package/dist/core/generateReport/reportData.js +735 -0
  32. package/dist/core/global-setup.d.ts +6 -0
  33. package/dist/core/global-setup.js +113 -0
  34. package/dist/core/keve-aspect.d.ts +54 -0
  35. package/dist/core/keve-aspect.js +52 -0
  36. package/dist/core/keve-decorators.d.ts +51 -0
  37. package/dist/core/keve-decorators.js +95 -0
  38. package/dist/core/keve-registry.d.ts +38 -0
  39. package/dist/core/keve-registry.js +25 -0
  40. package/dist/core/keve-report.d.ts +27 -0
  41. package/dist/core/keve-report.js +357 -0
  42. package/dist/core/keve-test.d.ts +43 -0
  43. package/dist/core/keve-test.js +364 -0
  44. package/dist/core/learned-actions.d.ts +24 -0
  45. package/dist/core/learned-actions.js +65 -0
  46. package/dist/core/pw-config.d.ts +19 -0
  47. package/dist/core/pw-config.js +180 -0
  48. package/dist/index.d.ts +8 -23
  49. package/dist/index.js +36 -43
  50. package/dist/page-agent/agent.d.ts +129 -0
  51. package/dist/page-agent/agent.js +585 -0
  52. package/dist/page-agent/hooks.d.ts +13 -0
  53. package/dist/page-agent/hooks.js +218 -0
  54. package/dist/page-agent/system_prompt.md +240 -0
  55. package/dist/page-agent/tools.d.ts +29 -0
  56. package/dist/page-agent/tools.js +297 -0
  57. package/doc/README.md +124 -0
  58. package/doc/agents/keve-dev-test-agent.md +140 -0
  59. package/doc/agents/keve-test-agent.md +178 -0
  60. package/doc/asserts//346/265/213/350/257/225/345/220/204/351/230/266/346/256/265/350/257/264/346/230/216.png +0 -0
  61. package/doc/keve-agent-demo.html +370 -0
  62. package/doc/skills/keve-bddcase-gen/SKILL.md +332 -0
  63. package/doc/skills/keve-bddcase-gen/references/test-case-rules.md +91 -0
  64. package/doc/skills/keve-bddcase-gen/references/test-cases-example.md +103 -0
  65. package/doc/skills/keve-component-gen/SKILL.md +255 -0
  66. package/doc/skills/keve-component-gen/references/component-mock-strategy.md +74 -0
  67. package/doc/skills/keve-component-gen/references/component-model-template.md +98 -0
  68. package/doc/skills/keve-component-gen/references/component-test-template.md +100 -0
  69. package/doc/skills/keve-component-gen/references/vue-test-utils-guide.md +91 -0
  70. package/doc/skills/keve-e2e-gen/SKILL.md +179 -0
  71. package/doc/skills/keve-e2e-gen/references/code-analysis-guide.md +208 -0
  72. package/doc/skills/keve-e2e-gen/references/decisions.md +36 -0
  73. package/doc/skills/keve-e2e-gen/references/pom-generation-guide.md +140 -0
  74. package/doc/skills/keve-e2e-gen/references/script-generation-guide.md +236 -0
  75. package/doc/skills/keve-e2e-gen/references/test-auditor-guide.md +189 -0
  76. package/doc/skills/keve-e2e-gen/scripts/extract-components.sh +85 -0
  77. package/doc/skills/keve-e2e-gen/scripts/inject-testids.sh +95 -0
  78. package/doc/skills/keve-static-analysis/SKILL.md +362 -0
  79. package/doc/skills/keve-static-analysis/references/codegraph-analysis-guide.md +108 -0
  80. package/doc/skills/keve-static-analysis/references/git-impact-analysis.md +81 -0
  81. package/doc/skills/keve-static-analysis/references/lint-fix-guide.md +69 -0
  82. package/doc/skills/keve-static-analysis/references/runtime-risk-patterns.md +92 -0
  83. package/doc/skills/keve-static-analysis/references/security-scan-guide.md +32 -0
  84. package/doc/skills/keve-static-analysis/references/ts-error-patterns.md +98 -0
  85. package/doc/skills/keve-static-analysis/references/vue-security-guide.md +72 -0
  86. package/doc/skills/keve-test-eval/SKILL.md +78 -0
  87. package/doc/skills/keve-test-eval/references/common-fix-guide.md +166 -0
  88. package/doc/skills/keve-test-eval/references/confidence-data-guide.md +78 -0
  89. package/doc/skills/keve-test-eval/references/fix-loop-guide.md +67 -0
  90. package/doc/skills/keve-unit-gen/SKILL.md +203 -0
  91. package/doc/skills/keve-unit-gen/references/boundary-conditions.md +78 -0
  92. package/doc/skills/keve-unit-gen/references/unit-mock-strategy.md +71 -0
  93. package/doc/skills/keve-unit-gen/references/unit-test-template.md +105 -0
  94. package/package.json +46 -24
  95. package/dist/agent/factory.d.ts +0 -25
  96. package/dist/agent/factory.js +0 -99
  97. package/dist/agent/index.d.ts +0 -3
  98. package/dist/agent/index.js +0 -7
  99. package/dist/agent/loader.d.ts +0 -22
  100. package/dist/agent/loader.js +0 -117
  101. package/dist/agent/types.d.ts +0 -9
  102. package/dist/agent/types.js +0 -2
  103. package/dist/agent-loop/complete-task.d.ts +0 -28
  104. package/dist/agent-loop/complete-task.js +0 -70
  105. package/dist/agent-loop/index.d.ts +0 -16
  106. package/dist/agent-loop/index.js +0 -26
  107. package/dist/agent-loop/loop.d.ts +0 -42
  108. package/dist/agent-loop/loop.js +0 -282
  109. package/dist/agent-loop/runtime.d.ts +0 -40
  110. package/dist/agent-loop/runtime.js +0 -163
  111. package/dist/agent-loop/types.d.ts +0 -161
  112. package/dist/agent-loop/types.js +0 -105
  113. package/dist/context/index.d.ts +0 -8
  114. package/dist/context/index.js +0 -29
  115. package/dist/context/tokenCalculation.d.ts +0 -55
  116. package/dist/context/tokenCalculation.js +0 -95
  117. package/dist/context/truncation.d.ts +0 -67
  118. package/dist/context/truncation.js +0 -142
  119. package/dist/context/types.d.ts +0 -7
  120. package/dist/context/types.js +0 -27
  121. package/dist/llm/OpenAIClient.d.ts +0 -31
  122. package/dist/llm/OpenAIClient.js +0 -455
  123. package/dist/llm/constants.d.ts +0 -2
  124. package/dist/llm/constants.js +0 -6
  125. package/dist/llm/errors.d.ts +0 -25
  126. package/dist/llm/errors.js +0 -51
  127. package/dist/llm/index.d.ts +0 -49
  128. package/dist/llm/index.js +0 -158
  129. package/dist/llm/types.d.ts +0 -124
  130. package/dist/llm/types.js +0 -2
  131. package/dist/llm/utils.d.ts +0 -29
  132. package/dist/llm/utils.js +0 -284
  133. package/dist/skill/frontmatter-parser.d.ts +0 -50
  134. package/dist/skill/frontmatter-parser.js +0 -152
  135. package/dist/skill/index.d.ts +0 -12
  136. package/dist/skill/index.js +0 -17
  137. package/dist/skill/loader.d.ts +0 -96
  138. package/dist/skill/loader.js +0 -256
  139. package/dist/tools/ask-user.d.ts +0 -8
  140. package/dist/tools/ask-user.js +0 -47
  141. package/dist/tools/bash.d.ts +0 -47
  142. package/dist/tools/bash.js +0 -213
  143. package/dist/tools/edit-file.d.ts +0 -9
  144. package/dist/tools/edit-file.js +0 -76
  145. package/dist/tools/glob.d.ts +0 -8
  146. package/dist/tools/glob.js +0 -101
  147. package/dist/tools/grep.d.ts +0 -10
  148. package/dist/tools/grep.js +0 -76
  149. package/dist/tools/index.d.ts +0 -24
  150. package/dist/tools/index.js +0 -47
  151. package/dist/tools/read-files.d.ts +0 -8
  152. package/dist/tools/read-files.js +0 -81
  153. package/dist/tools/write-file.d.ts +0 -8
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Auth utilities — acquire storage state for Playwright tests
3
+ *
4
+ * 1. Existing valid storage-state.json → skip
5
+ * 2. CDP connect to user Chrome → reuse login state
6
+ * 3. Manual browser login → fallback
7
+ */
8
+ export interface AcquireOptions {
9
+ targetUrl?: string;
10
+ storageStatePath?: string;
11
+ logPrefix?: string;
12
+ onLoginDetected?: (url: string) => void;
13
+ onStateSaved?: (cookieCount: number, originCount: number) => void;
14
+ onError?: (msg: string) => void;
15
+ onBrowserFailed?: (msg: string) => void;
16
+ }
17
+ export declare function acquireStorageState(options?: AcquireOptions): Promise<boolean>;
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ /**
3
+ * Auth utilities — acquire storage state for Playwright tests
4
+ *
5
+ * 1. Existing valid storage-state.json → skip
6
+ * 2. CDP connect to user Chrome → reuse login state
7
+ * 3. Manual browser login → fallback
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.acquireStorageState = acquireStorageState;
44
+ const fs = __importStar(require("node:fs"));
45
+ const os = __importStar(require("node:os"));
46
+ const path = __importStar(require("node:path"));
47
+ const node_child_process_1 = require("node:child_process"); // getChromeWsUrl 需要用 lsof 检查端口
48
+ const node_module_1 = require("node:module");
49
+ // ─── Playwright Chromium resolver ───────────────────────────────────────────
50
+ async function getChromium() {
51
+ const cwdRequire = (0, node_module_1.createRequire)(path.resolve(process.cwd(), 'node_modules'));
52
+ const pwAbsolutePath = cwdRequire.resolve('@playwright/test');
53
+ const pwModule = await Promise.resolve(`${pwAbsolutePath}`).then(s => __importStar(require(s)));
54
+ const chromium = pwModule.chromium || pwModule.default?.chromium;
55
+ if (!chromium)
56
+ throw new Error('Failed to resolve chromium from @playwright/test');
57
+ return chromium;
58
+ }
59
+ // ─── Chrome CDP helpers ─────────────────────────────────────────────────────
60
+ function getChromeUserDataDir() {
61
+ const p = os.platform();
62
+ const h = os.homedir();
63
+ if (p === 'darwin')
64
+ return path.join(h, 'Library', 'Application Support', 'Google', 'Chrome');
65
+ if (p === 'win32')
66
+ return path.join(h, 'AppData', 'Local', 'Google', 'Chrome', 'User Data');
67
+ return path.join(h, '.config', 'google-chrome');
68
+ }
69
+ /** 读取 DevToolsActivePort 获取 CDP WebSocket URL(端口须正在监听) */
70
+ function getChromeWsUrl() {
71
+ const portFile = path.join(getChromeUserDataDir(), 'DevToolsActivePort');
72
+ if (!fs.existsSync(portFile))
73
+ return null;
74
+ try {
75
+ const [portStr, wsPath] = fs.readFileSync(portFile, 'utf8').trim().split('\n').map(l => l.trim()).filter(Boolean);
76
+ const port = parseInt(portStr, 10);
77
+ if (!port || !wsPath)
78
+ return null;
79
+ try {
80
+ (0, node_child_process_1.execSync)(`lsof -P -i TCP:${port} 2>/dev/null | grep LISTEN`, { encoding: 'utf8' });
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ return `ws://127.0.0.1:${port}${wsPath}`;
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ /** CDP 连接用户 Chrome → 打开目标页 → 保存 storage-state → 断开
92
+ * 仅连接已运行的 Chrome(keve run 已确保 Chrome 启动 + debug 端口就绪)
93
+ */
94
+ async function authViaCDP(targetUrl, storageStatePath, prefix) {
95
+ const wsUrl = getChromeWsUrl();
96
+ if (!wsUrl) {
97
+ console.log(`${prefix} Chrome CDP not available`);
98
+ return false;
99
+ }
100
+ console.log(`${prefix} Chrome CDP available, connecting...`);
101
+ try {
102
+ const chromium = await getChromium();
103
+ const browser = await chromium.connectOverCDP(wsUrl, { timeout: 10000 });
104
+ const context = browser.contexts()[0];
105
+ if (!context) {
106
+ browser.close();
107
+ return false;
108
+ }
109
+ // 访问目标页面,等登录重定向完成
110
+ const page = await context.newPage();
111
+ await page.goto(targetUrl, { waitUntil: 'networkidle', timeout: 60000 });
112
+ await waitForLoginRedirect(page);
113
+ const onLoginPage = /login|auth|signin/i.test(page.url());
114
+ if (onLoginPage) {
115
+ console.log(`${prefix} CDP connected but target requires login`);
116
+ await page.close();
117
+ browser.close();
118
+ return false;
119
+ }
120
+ // 保存 storage-state
121
+ const cookies = await context.cookies();
122
+ const state = { cookies, origins: [] };
123
+ fs.writeFileSync(storageStatePath, JSON.stringify(state, null, 2));
124
+ await page.close();
125
+ browser.close();
126
+ console.log(`${prefix} CDP auth success (${cookies.length} cookies) → ${storageStatePath}`);
127
+ return cookies.length > 0;
128
+ }
129
+ catch (e) {
130
+ console.warn(`${prefix} CDP connect failed: ${e.message}`);
131
+ return false;
132
+ }
133
+ }
134
+ function isStorageStateValid(statePath, targetDomain) {
135
+ if (!fs.existsSync(statePath))
136
+ return false;
137
+ try {
138
+ const s = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
139
+ if (!s.cookies?.length)
140
+ return false;
141
+ if (targetDomain && !s.cookies.some(c => c.domain?.includes(targetDomain)))
142
+ return false;
143
+ const now = Date.now() / 1000;
144
+ return s.cookies.some(c => !c.expires || c.expires === -1 || c.expires > now);
145
+ }
146
+ catch {
147
+ return false;
148
+ }
149
+ }
150
+ function extractDomain(url) {
151
+ try {
152
+ return new URL(url).hostname;
153
+ }
154
+ catch {
155
+ return undefined;
156
+ }
157
+ }
158
+ async function waitForLoginRedirect(page, maxMs = 30000) {
159
+ for (let i = 0; i < maxMs / 500; i++) {
160
+ if (!/login|auth|signin/i.test(page.url()))
161
+ break;
162
+ await page.waitForTimeout(500);
163
+ }
164
+ await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => { });
165
+ return !/login|auth|signin|sign-in/i.test(page.url());
166
+ }
167
+ /** 手动登录 → 保存 storage-state */
168
+ async function authViaManualLogin(targetUrl, storageStatePath, prefix) {
169
+ const loginUrl = targetUrl.replace(/\/?$/, '/login');
170
+ console.log(`${prefix} Opening browser for manual login: ${loginUrl}`);
171
+ console.log(`${prefix} Please login. Waiting up to 2 minutes...`);
172
+ try {
173
+ const browser = await (await getChromium()).launch({ headless: false });
174
+ const context = await browser.newContext();
175
+ const page = await context.newPage();
176
+ await page.goto(loginUrl, { waitUntil: 'domcontentloaded', timeout: 15000 })
177
+ .catch(() => page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 15000 }));
178
+ let loginDetected = false;
179
+ for (let i = 0; i < 120; i++) {
180
+ if (!/login|auth|signin|sign-in/i.test(page.url())) {
181
+ loginDetected = true;
182
+ break;
183
+ }
184
+ await page.waitForTimeout(1000);
185
+ }
186
+ if (!loginDetected)
187
+ console.log(`${prefix} Timeout (2min), saving current state...`);
188
+ await waitForLoginRedirect(page);
189
+ await context.storageState({ path: storageStatePath });
190
+ const c = JSON.parse(fs.readFileSync(storageStatePath, 'utf-8')).cookies?.length || 0;
191
+ console.log(`${prefix} Auth saved (${c} cookies) → ${storageStatePath}`);
192
+ await browser.close();
193
+ return c > 0;
194
+ }
195
+ catch (e) {
196
+ console.error(`${prefix} Manual login error: ${e.message}`);
197
+ return false;
198
+ }
199
+ }
200
+ async function acquireStorageState(options = {}) {
201
+ const targetUrl = options.targetUrl || process.env.PWGEN_TARGET_URL || 'http://localhost:3000';
202
+ const storageStatePath = options.storageStatePath || path.resolve(process.cwd(), '.auth/storage-state.json');
203
+ const prefix = options.logPrefix || '[keve]';
204
+ const targetDomain = extractDomain(targetUrl);
205
+ const authDir = path.dirname(storageStatePath);
206
+ if (!fs.existsSync(authDir))
207
+ fs.mkdirSync(authDir, { recursive: true });
208
+ // Skip mode
209
+ if (process.env.PWGEN_SKIP_AUTH === '1' || process.env.PWGEN_SKIP_AUTH === 'true') {
210
+ fs.writeFileSync(storageStatePath, JSON.stringify({ cookies: [], origins: [] }, null, 2));
211
+ console.log(`${prefix} PWGEN_SKIP_AUTH=1 → empty storage state saved`);
212
+ return true;
213
+ }
214
+ // Step 1: 已有有效 storage-state
215
+ if (isStorageStateValid(storageStatePath, targetDomain)) {
216
+ console.log(`${prefix} Storage state valid → ${storageStatePath}`);
217
+ return true;
218
+ }
219
+ // Step 2: CDP 连接用户 Chrome
220
+ if (await authViaCDP(targetUrl, storageStatePath, prefix))
221
+ return true;
222
+ // Step 3: 手动登录
223
+ return await authViaManualLogin(targetUrl, storageStatePath, prefix);
224
+ }
@@ -0,0 +1,5 @@
1
+ export interface CdpResolverOptions {
2
+ endpoint?: string;
3
+ wsEndpoint?: string;
4
+ }
5
+ export declare function resolveCdpWsUrl(options: CdpResolverOptions): Promise<string | null>;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resolveCdpWsUrl = resolveCdpWsUrl;
37
+ const fs = __importStar(require("node:fs"));
38
+ const os = __importStar(require("node:os"));
39
+ const path = __importStar(require("node:path"));
40
+ const node_child_process_1 = require("node:child_process");
41
+ // TODO: consolidate doc-reading logic into keve-suite/spec
42
+ function getChromeUserDataDir() {
43
+ const p = os.platform();
44
+ const h = os.homedir();
45
+ if (p === 'darwin')
46
+ return path.join(h, 'Library', 'Application Support', 'Google', 'Chrome');
47
+ if (p === 'win32')
48
+ return path.join(h, 'AppData', 'Local', 'Google', 'Chrome', 'User Data');
49
+ return path.join(h, '.config', 'google-chrome');
50
+ }
51
+ function getChromeWsUrlFromPort() {
52
+ const portFile = path.join(getChromeUserDataDir(), 'DevToolsActivePort');
53
+ if (!fs.existsSync(portFile))
54
+ return null;
55
+ try {
56
+ const [portStr, wsPath] = fs.readFileSync(portFile, 'utf8').trim().split('\n').map(l => l.trim()).filter(Boolean);
57
+ const port = parseInt(portStr, 10);
58
+ if (!port || !wsPath)
59
+ return null;
60
+ try {
61
+ (0, node_child_process_1.execSync)(`lsof -P -i TCP:${port} 2>/dev/null | grep LISTEN`, { encoding: 'utf8' });
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ return `ws://127.0.0.1:${port}${wsPath}`;
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ async function fetchWsUrlFromHttpEndpoint(endpoint) {
73
+ try {
74
+ const url = endpoint.replace(/\/$/, '') + '/json/version';
75
+ const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
76
+ const data = await res.json();
77
+ return data.webSocketDebuggerUrl || null;
78
+ }
79
+ catch (e) {
80
+ console.warn(`[cdp-resolver] Failed to fetch from ${endpoint}: ${e.message}`);
81
+ return null;
82
+ }
83
+ }
84
+ async function resolveCdpWsUrl(options) {
85
+ if (options.wsEndpoint)
86
+ return options.wsEndpoint;
87
+ if (options.endpoint) {
88
+ const wsUrl = await fetchWsUrlFromHttpEndpoint(options.endpoint);
89
+ if (wsUrl)
90
+ return wsUrl;
91
+ }
92
+ return getChromeWsUrlFromPort();
93
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 报告生成统一入口
3
+ *
4
+ * 只生成 report-data.json — 前端 Vue 报告页面消费的唯一数据源。
5
+ * 废弃的产物:execution-summary.json、summary-report.html(数据均已在 report-data.json 中)
6
+ */
7
+ import { type ReportDataOptions } from './reportData';
8
+ export type { ReportDataOptions };
9
+ export interface GenerateReportOptions extends ReportDataOptions {
10
+ }
11
+ export interface GenerateReportResult {
12
+ /** 生成的报告数据(JSON) */
13
+ reportData: any;
14
+ }
15
+ /**
16
+ * 报告生成统一入口:数据生成 → 写入 report-data.json
17
+ */
18
+ export declare function generateReport(opts: GenerateReportOptions): Promise<GenerateReportResult>;
19
+ export declare function reportMain(): Promise<void>;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ /**
3
+ * 报告生成统一入口
4
+ *
5
+ * 只生成 report-data.json — 前端 Vue 报告页面消费的唯一数据源。
6
+ * 废弃的产物:execution-summary.json、summary-report.html(数据均已在 report-data.json 中)
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.generateReport = generateReport;
43
+ exports.reportMain = reportMain;
44
+ const fs = __importStar(require("node:fs"));
45
+ const path = __importStar(require("node:path"));
46
+ const reportData_1 = require("./reportData");
47
+ /**
48
+ * 报告生成统一入口:数据生成 → 写入 report-data.json
49
+ */
50
+ async function generateReport(opts) {
51
+ // Step 1: 生成报告数据(纯计算,无 IO)
52
+ const reportData = await (0, reportData_1.generateReportData)(opts);
53
+ // Step 2: 写入 round-N/report-data.json,latest/ 用 symlink 指向最新轮次(避免 34MB+ 重复拷贝)
54
+ const { roundDir, latestDir, summary } = reportData;
55
+ fs.mkdirSync(roundDir, { recursive: true });
56
+ const reportDataJson = JSON.stringify(reportData, null, 2);
57
+ fs.writeFileSync(path.join(roundDir, 'report-data.json'), reportDataJson);
58
+ // latest → round-N symlink
59
+ try {
60
+ if (fs.existsSync(latestDir)) {
61
+ const stat = fs.lstatSync(latestDir);
62
+ if (stat.isSymbolicLink()) {
63
+ fs.unlinkSync(latestDir); // 已是 symlink,替换
64
+ }
65
+ else {
66
+ fs.rmSync(latestDir, { recursive: true, force: true }); // 旧式目录,清理
67
+ }
68
+ }
69
+ fs.symlinkSync(path.basename(roundDir), latestDir, 'junction');
70
+ }
71
+ catch { /* ignore latest symlink error */ }
72
+ const summaryInner = summary.summary || summary;
73
+ console.log(`\n Report data generated:`);
74
+ console.log(` Round: ${reportData.round}`);
75
+ console.log(` Total: ${summaryInner.total} | Passed: ${summaryInner.passed} | Failed: ${summaryInner.failed}`);
76
+ console.log(` Pass Rate: ${summaryInner.passRate}`);
77
+ return { reportData };
78
+ }
79
+ // ─── CLI Entry ────────────────────────────────────────────────────────
80
+ async function reportMain() {
81
+ const projectRoot = process.env.PROJECT_ROOT
82
+ || (process.argv.find(a => a.startsWith('--project-root='))?.split('=')[1])
83
+ || path.resolve(__dirname, '..');
84
+ const resultsPath = process.argv.find(a => a.startsWith('--results-path='))?.split('=')[1]
85
+ || process.env.TEST_RESULTS_PATH
86
+ || path.join(projectRoot, 'test-artifacts', 'test-results.json');
87
+ const confidencePath = process.argv.find(a => a.startsWith('--confidence-path='))?.split('=')[1];
88
+ const casesPath = process.argv.find(a => a.startsWith('--cases-path='))?.split('=')[1];
89
+ await generateReport({ projectRoot, resultsPath, confidencePath, casesPath });
90
+ console.log(' report-data.json generated');
91
+ }
@@ -0,0 +1,215 @@
1
+ /**
2
+ * KEVE Summary Report Data Generator
3
+ *
4
+ * Reads test-results.json, confidence-data.jsonl, and test-cases.yaml,
5
+ * merges them into a structured report-data.json.
6
+ *
7
+ * This file handles ONLY data processing — no HTML rendering.
8
+ * Rendering is handled by generate-html-renderer.ts.
9
+ */
10
+ export interface ReportDataOptions {
11
+ projectRoot: string;
12
+ resultsPath: string;
13
+ confidencePath?: string;
14
+ casesPath?: string;
15
+ }
16
+ interface TestCaseStep {
17
+ step: string;
18
+ expected: string;
19
+ }
20
+ interface TestCaseDef {
21
+ id: string;
22
+ title: string;
23
+ priority: string;
24
+ type: string;
25
+ precondition: string[];
26
+ steps: TestCaseStep[];
27
+ notes: string;
28
+ }
29
+ interface TestModule {
30
+ name: string;
31
+ description: string;
32
+ cases: TestCaseDef[];
33
+ }
34
+ interface TestCaseYaml {
35
+ version: string;
36
+ mr_id: string;
37
+ description: string;
38
+ docs_url: string;
39
+ generated_at: string;
40
+ modules: TestModule[];
41
+ }
42
+ interface SpecTest {
43
+ status: string;
44
+ duration: number;
45
+ startTime: string;
46
+ error: string;
47
+ errors: string[];
48
+ stdout: string;
49
+ steps: {
50
+ title: string;
51
+ duration: number;
52
+ error: string | null;
53
+ attachments: any[];
54
+ }[];
55
+ attachments: any[];
56
+ }
57
+ interface PlaywrightSpec {
58
+ title: string;
59
+ file: string;
60
+ suitePath: string;
61
+ tests: SpecTest[];
62
+ }
63
+ interface CaseResult {
64
+ caseId: string;
65
+ title: string;
66
+ status: string;
67
+ duration: number;
68
+ startTime: string;
69
+ error: string;
70
+ errors: string[];
71
+ stdout: string;
72
+ pwSteps: any[];
73
+ screenshotPath: string;
74
+ videoPath: string;
75
+ tracePath: string;
76
+ attachments: any[];
77
+ keveScreenshots: {
78
+ label: string;
79
+ path: string;
80
+ }[];
81
+ /** @deprecated 旧版 keveGoals attachment 数据,仅用于向后兼容合并 */
82
+ keveGoals?: any[] | null;
83
+ pwModuleTitle: string;
84
+ /** @keveModel 提取的模块ID(如 'AG') */
85
+ pwModuleId: string;
86
+ /** @keveModel 提取的模块描述(如 '智能应用列表筛选') */
87
+ pwModuleDescription: string;
88
+ errorCategory?: string;
89
+ }
90
+ /**
91
+ * StepReportItem: 单个步骤在报告中的完整数据契约(含截图)。
92
+ * 从 confidence-data.jsonl 的 steps 提取,供前端报告精确渲染。
93
+ */
94
+ export interface StepReportItem {
95
+ step: string;
96
+ expected: string;
97
+ precondition?: string;
98
+ order?: number;
99
+ success?: boolean;
100
+ /** 目标结果截图路径(keveGoal 执行后截图,来自 goalScreenshotPath / goalScreenshotAfter) */
101
+ goalScreenshotPath?: string;
102
+ /** 验证截图路径列表(agent 每步操作后截图,来自 verifyScreenshots / agentScreenshots) */
103
+ verifyScreenshots?: string[];
104
+ /** agent 操作步骤(含每步操作截图路径) */
105
+ actions?: Array<{
106
+ tool?: string;
107
+ role?: string;
108
+ name?: string;
109
+ url?: string;
110
+ text?: string;
111
+ key?: string;
112
+ reason?: string;
113
+ success?: boolean;
114
+ result?: string;
115
+ error?: string;
116
+ /** 工具执行返回结果(如 execute_javascript 的 JS 执行输出) */
117
+ toolOutput?: string;
118
+ evaluation?: string;
119
+ memory?: string;
120
+ nextGoal?: string;
121
+ /** 操作截图路径 */
122
+ screenshotPath?: string;
123
+ }>;
124
+ duration?: number;
125
+ status?: string;
126
+ error?: string | null;
127
+ }
128
+ /**
129
+ * CaseReportItem: 报告数据与前端渲染之间的数据契约。
130
+ * reportData 负责填充所有字段,前端报告只消费这些字段。
131
+ * 任何渲染需要的字段必须在此定义,避免隐式依赖和字段错位。
132
+ */
133
+ export interface CaseReportItem {
134
+ /** 用例ID,如 "NA-10" */
135
+ caseId: string;
136
+ /** 模块标识符(YAML module.name 或 Playwright suitePath 提取) */
137
+ module: string;
138
+ /** 模块描述(YAML module.description 或 module.name) */
139
+ moduleDescription: string;
140
+ /** 用例标题(不含ID前缀,纯文字) */
141
+ title: string;
142
+ /** 优先级 */
143
+ priority: string;
144
+ /** 类型 */
145
+ type: string;
146
+ /** 前置条件列表 */
147
+ precondition: string[];
148
+ /** 统一步骤列表(含截图路径、agent 操作等完整数据) */
149
+ steps: StepReportItem[];
150
+ /** 备注 */
151
+ notes: string;
152
+ /** 执行状态: "passed" | "failed" | "skipped" | "missing" */
153
+ status: string;
154
+ /** 执行耗时(ms) */
155
+ duration: number;
156
+ /** 执行开始时间 */
157
+ startTime: string;
158
+ /** 错误信息 */
159
+ error: string;
160
+ /** 错误列表 */
161
+ errors: string[];
162
+ /** 标准输出 */
163
+ stdout: string;
164
+ /** Playwright 步骤 */
165
+ pwSteps: any[];
166
+ /** AI评估结论: "通过" | "不通过" | "待确认" */
167
+ data: string;
168
+ /** AI评估置信度 0-100 */
169
+ confidence: number;
170
+ /** AI评估推理过程 */
171
+ confidenceReason: string;
172
+ /** 步骤截图路径列表(前端按需加载,不再内嵌 base64) */
173
+ stepScreenshots: {
174
+ path: string;
175
+ }[];
176
+ /** Playwright 失败截图路径(前端按需加载) */
177
+ screenshotPath: string;
178
+ /** 视频路径 */
179
+ videoPath: string;
180
+ /** Trace 路径 */
181
+ tracePath: string;
182
+ /** keveAssert 截图列表(仅保留 label + path,不再内嵌 base64) */
183
+ keveScreenshots: {
184
+ label: string;
185
+ path: string;
186
+ }[];
187
+ /** 错误分类(来自 @keveScene decorator) */
188
+ errorCategory?: string;
189
+ /** AI 观察到的诊断提示(如 disabled 按钮、SSO 重定向等) */
190
+ diagnosticHint?: string;
191
+ }
192
+ declare function parseCaseIdFromTitle(title: string): string | null;
193
+ declare function parseYamlSimple(yamlText: string): TestCaseYaml;
194
+ declare function extractAllSpecs(jsonData: any): PlaywrightSpec[];
195
+ /** Decode Playwright attachment body (may be base64 string, Buffer, or object) */
196
+ export declare function decodeAttachmentBody(body: any): string;
197
+ /** Parse keveGoals from Playwright test attachments */
198
+ export declare function parseKeveGoals(attachments: any[]): any[] | null;
199
+ /** Parse keveAssert results from Playwright test attachments */
200
+ export declare function parseKeveAsserts(attachments: any[]): {
201
+ label: string;
202
+ body: any;
203
+ }[];
204
+ /** Extract keveAssert screenshot paths from Playwright test attachments */
205
+ export declare function parseKeveScreenshots(attachments: any[], taskDir: string): {
206
+ label: string;
207
+ path: string;
208
+ }[];
209
+ export declare function parseGoalScreenshots(attachments: any[], taskDir: string): {
210
+ label: string;
211
+ path: string;
212
+ }[];
213
+ declare function buildCaseResultMap(specs: PlaywrightSpec[], taskDir: string): Record<string, CaseResult>;
214
+ export declare function generateReportData(opts: ReportDataOptions): Promise<any>;
215
+ export { parseCaseIdFromTitle, extractAllSpecs, buildCaseResultMap, parseYamlSimple };