@kkeve/suite 0.0.15 → 0.0.18

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 (115) hide show
  1. package/dist/bin/keve.js +2 -1
  2. package/dist/commands/clean.js +2 -2
  3. package/dist/commands/config.js +7 -7
  4. package/dist/commands/init.js +2 -2
  5. package/dist/commands/report.js +7 -7
  6. package/dist/commands/run.js +12 -11
  7. package/dist/commands/test.js +7 -7
  8. package/dist/config.js +7 -7
  9. package/dist/core/action-log-writer.js +4 -1
  10. package/dist/core/decorator/keve-decorators.d.ts +128 -0
  11. package/dist/core/decorator/keve-decorators.js +254 -0
  12. package/dist/core/decorator/keve-host.d.ts +40 -0
  13. package/dist/core/decorator/keve-host.js +33 -0
  14. package/dist/core/{keve-registry.d.ts → decorator/keve-registry.d.ts} +19 -0
  15. package/dist/core/{keve-registry.js → decorator/keve-registry.js} +13 -1
  16. package/dist/core/generateReport/report.d.ts +1 -1
  17. package/dist/core/generateReport/report.js +2 -2
  18. package/dist/core/generateReport/reportData.d.ts +3 -0
  19. package/dist/core/generateReport/reportData.js +49 -15
  20. package/dist/core/judge/confidence.d.ts +119 -0
  21. package/dist/core/judge/confidence.js +295 -0
  22. package/dist/core/judge/goal-heuristics.d.ts +32 -0
  23. package/dist/core/judge/goal-heuristics.js +128 -0
  24. package/dist/core/judge/pngCodec.d.ts +23 -0
  25. package/dist/core/judge/pngCodec.js +215 -0
  26. package/dist/core/judge/screenshotDiff.d.ts +112 -0
  27. package/dist/core/judge/screenshotDiff.js +420 -0
  28. package/dist/core/page-object/definePage.d.ts +95 -0
  29. package/dist/core/page-object/definePage.js +62 -0
  30. package/dist/core/page-object/elementResolver.d.ts +32 -0
  31. package/dist/core/page-object/elementResolver.js +236 -0
  32. package/dist/core/page-object/engine-types.d.ts +140 -0
  33. package/dist/core/page-object/engine-types.js +34 -0
  34. package/dist/core/sso-cookie.d.ts +18 -0
  35. package/dist/core/sso-cookie.js +10 -0
  36. package/dist/dsl/index.d.ts +15 -0
  37. package/dist/dsl/index.js +29 -0
  38. package/dist/dsl/locatorSource.d.ts +25 -0
  39. package/dist/dsl/locatorSource.js +323 -0
  40. package/dist/dsl/ops.d.ts +46 -0
  41. package/dist/dsl/ops.js +95 -0
  42. package/dist/dsl/toDecorator.d.ts +98 -0
  43. package/dist/dsl/toDecorator.js +747 -0
  44. package/dist/engine-cypress/adapter.d.ts +108 -0
  45. package/dist/engine-cypress/adapter.js +514 -0
  46. package/dist/engine-cypress/agent-page.d.ts +159 -0
  47. package/dist/engine-cypress/agent-page.js +566 -0
  48. package/dist/engine-cypress/agentDom.d.ts +22 -0
  49. package/dist/engine-cypress/agentDom.js +590 -0
  50. package/dist/engine-cypress/bridge.d.ts +39 -0
  51. package/dist/engine-cypress/bridge.js +598 -0
  52. package/dist/engine-cypress/cdp.d.ts +78 -0
  53. package/dist/engine-cypress/cdp.js +339 -0
  54. package/dist/engine-cypress/host.d.ts +22 -0
  55. package/dist/engine-cypress/host.js +480 -0
  56. package/dist/engine-cypress/index.d.ts +26 -0
  57. package/dist/engine-cypress/index.js +58 -0
  58. package/dist/engine-cypress/keveGoal.d.ts +43 -0
  59. package/dist/engine-cypress/keveGoal.js +265 -0
  60. package/dist/engine-cypress/pageOps.d.ts +41 -0
  61. package/dist/engine-cypress/pageOps.js +439 -0
  62. package/dist/engine-cypress/reporter.d.ts +20 -0
  63. package/dist/engine-cypress/reporter.js +128 -0
  64. package/dist/engine-cypress/setup.d.ts +21 -0
  65. package/dist/engine-cypress/setup.js +27 -0
  66. package/dist/engine-cypress/shotBuffer.d.ts +40 -0
  67. package/dist/engine-cypress/shotBuffer.js +41 -0
  68. package/dist/engine-cypress/state.d.ts +56 -0
  69. package/dist/engine-cypress/state.js +81 -0
  70. package/dist/engine-playwright/engineAdapter.d.ts +78 -0
  71. package/dist/engine-playwright/engineAdapter.js +371 -0
  72. package/dist/engine-playwright/global-setup.d.ts +28 -0
  73. package/dist/engine-playwright/global-setup.js +142 -0
  74. package/dist/{core → engine-playwright}/keve-report.d.ts +0 -1
  75. package/dist/engine-playwright/keve-report.js +137 -0
  76. package/dist/{core → engine-playwright}/keve-test.d.ts +10 -8
  77. package/dist/{core → engine-playwright}/keve-test.js +204 -200
  78. package/dist/{core → engine-playwright}/pw-config.js +8 -7
  79. package/dist/index.d.ts +13 -8
  80. package/dist/index.js +43 -28
  81. package/dist/page-agent/agent.d.ts +82 -7
  82. package/dist/page-agent/agent.js +252 -88
  83. package/dist/page-agent/diagnostics.d.ts +184 -0
  84. package/dist/page-agent/diagnostics.js +377 -0
  85. package/dist/page-agent/hooks.d.ts +3 -2
  86. package/dist/page-agent/hooks.js +4 -3
  87. package/dist/page-agent/page-like.d.ts +116 -0
  88. package/dist/page-agent/page-like.js +14 -0
  89. package/dist/page-agent/playwright-page.d.ts +20 -0
  90. package/dist/page-agent/playwright-page.js +98 -0
  91. package/dist/page-agent/system-prompt.d.ts +7 -0
  92. package/dist/page-agent/system-prompt.js +10 -0
  93. package/dist/page-agent/tools.d.ts +1 -1
  94. package/dist/page-agent/tools.js +144 -8
  95. package/package.json +24 -11
  96. package/scripts/gen-system-prompt.mjs +36 -0
  97. package/scripts/run-dsl-task.mjs +652 -0
  98. package/scripts/runtime/cypress-preprocessor.mjs +44 -0
  99. package/scripts/runtime/cypress.config.mjs +35 -0
  100. package/scripts/runtime/playwright.config.mjs +41 -0
  101. package/scripts/verify-shot-diff.mjs +243 -0
  102. package/dist/commands/auth-update.d.ts +0 -16
  103. package/dist/commands/auth-update.js +0 -88
  104. package/dist/core/auth.d.ts +0 -17
  105. package/dist/core/auth.js +0 -224
  106. package/dist/core/cdp-resolver.d.ts +0 -5
  107. package/dist/core/cdp-resolver.js +0 -93
  108. package/dist/core/global-setup.d.ts +0 -6
  109. package/dist/core/global-setup.js +0 -123
  110. package/dist/core/keve-decorators.d.ts +0 -51
  111. package/dist/core/keve-decorators.js +0 -95
  112. package/dist/core/keve-report.js +0 -376
  113. /package/dist/core/{keve-aspect.d.ts → decorator/keve-aspect.d.ts} +0 -0
  114. /package/dist/core/{keve-aspect.js → decorator/keve-aspect.js} +0 -0
  115. /package/dist/{core → engine-playwright}/pw-config.d.ts +0 -0
package/dist/bin/keve.js CHANGED
@@ -47,10 +47,11 @@ var __importStar = (this && this.__importStar) || (function () {
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
48
  const commander_1 = require("commander");
49
49
  const path = __importStar(require("node:path"));
50
+ const fs = __importStar(require("node:fs"));
50
51
  // package.json is in the package root, not in dist/
51
52
  // __dirname → dist/bin/ → go up 2 levels to package root
52
53
  const pkgPath = path.resolve(__dirname, '..', '..', 'package.json');
53
- const pkg = require(pkgPath);
54
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
54
55
  const program = new commander_1.Command();
55
56
  program
56
57
  .name('keve')
@@ -43,7 +43,7 @@ exports.clean = clean;
43
43
  const fs = __importStar(require("node:fs"));
44
44
  const path = __importStar(require("node:path"));
45
45
  const chalk_1 = __importDefault(require("chalk"));
46
- const config_1 = require("../config");
46
+ const config_js_1 = require("../config.js");
47
47
  function removeDir(dir, dryRun) {
48
48
  if (!fs.existsSync(dir))
49
49
  return;
@@ -55,7 +55,7 @@ function removeDir(dir, dryRun) {
55
55
  console.log(chalk_1.default.green(` ✓ Removed: ${path.relative(process.cwd(), dir)}`));
56
56
  }
57
57
  async function clean(options) {
58
- const keveDir = (0, config_1.findKeveDir)();
58
+ const keveDir = (0, config_js_1.findKeveDir)();
59
59
  if (!keveDir) {
60
60
  console.error(chalk_1.default.red(' ✗ .keve/ directory not found'));
61
61
  process.exit(1);
@@ -43,13 +43,13 @@ exports.config = config;
43
43
  const fs = __importStar(require("node:fs"));
44
44
  const path = __importStar(require("node:path"));
45
45
  const chalk_1 = __importDefault(require("chalk"));
46
- const config_1 = require("../config");
47
- const pw_config_1 = require("../core/pw-config");
46
+ const config_js_1 = require("../config.js");
47
+ const pw_config_js_1 = require("../engine-playwright/pw-config.js");
48
48
  async function config(options) {
49
- const keveDir = (0, config_1.findKeveDir)();
50
- const taskDir = (0, config_1.resolveTaskDir)(keveDir);
49
+ const keveDir = (0, config_js_1.findKeveDir)();
50
+ const taskDir = (0, config_js_1.resolveTaskDir)(keveDir);
51
51
  if (options.show) {
52
- const configPath = (0, pw_config_1.findExistingConfig)(keveDir, taskDir);
52
+ const configPath = (0, pw_config_js_1.findExistingConfig)(keveDir, taskDir);
53
53
  if (configPath) {
54
54
  console.log(chalk_1.default.green(` Config: ${configPath}`));
55
55
  console.log(fs.readFileSync(configPath, 'utf-8'));
@@ -69,14 +69,14 @@ async function config(options) {
69
69
  timeout: options.timeout ? parseInt(options.timeout, 10) : undefined,
70
70
  retries: options.retries ? parseInt(options.retries, 10) : undefined,
71
71
  };
72
- const content = (0, pw_config_1.generatePwConfig)(pwOptions);
72
+ const content = (0, pw_config_js_1.generatePwConfig)(pwOptions);
73
73
  if (options.print) {
74
74
  console.log(content);
75
75
  return;
76
76
  }
77
77
  const outputPath = options.output
78
78
  ? path.resolve(options.output)
79
- : (0, pw_config_1.resolveConfigOutputPath)();
79
+ : (0, pw_config_js_1.resolveConfigOutputPath)();
80
80
  console.log(chalk_1.default.cyan('\n⚙️ keve config\n'));
81
81
  if (fs.existsSync(outputPath) && !options.force) {
82
82
  console.log(chalk_1.default.yellow(` ⚠ ${path.relative(process.cwd(), outputPath)} already exists. Use --force to overwrite.`));
@@ -50,7 +50,7 @@ const fs = __importStar(require("node:fs"));
50
50
  const os = __importStar(require("node:os"));
51
51
  const path = __importStar(require("node:path"));
52
52
  const chalk_1 = __importDefault(require("chalk"));
53
- const pw_config_1 = require("../core/pw-config");
53
+ const pw_config_js_1 = require("../engine-playwright/pw-config.js");
54
54
  // Skills bundled in this package — copied to global ~/.codeflicker/skills/
55
55
  const KEVE_SKILLS = [
56
56
  'keve-bddcase-gen',
@@ -254,7 +254,7 @@ async function init(options) {
254
254
  webServer: !options.cdp,
255
255
  };
256
256
  if (!fs.existsSync(pwConfigPath) || options.force) {
257
- fs.writeFileSync(pwConfigPath, (0, pw_config_1.generatePwConfig)(pwOptions), 'utf-8');
257
+ fs.writeFileSync(pwConfigPath, (0, pw_config_js_1.generatePwConfig)(pwOptions), 'utf-8');
258
258
  console.log(chalk_1.default.green(' ✓ Created keve-test.config.ts'));
259
259
  console.log(chalk_1.default.gray(` baseURL: ${pwOptions.baseUrl || '(from env)'}`));
260
260
  console.log(chalk_1.default.gray(` CDP: ${pwOptions.cdp ? 'enabled' : 'disabled'}`));
@@ -46,20 +46,20 @@ exports.report = report;
46
46
  const fs = __importStar(require("node:fs"));
47
47
  const path = __importStar(require("node:path"));
48
48
  const chalk_1 = __importDefault(require("chalk"));
49
- const config_1 = require("../config");
50
- const index_1 = require("../index");
49
+ const config_js_1 = require("../config.js");
50
+ const index_js_1 = require("../index.js");
51
51
  /**
52
52
  * Resolve paths for report inputs
53
53
  */
54
54
  function resolvePaths(options) {
55
- const keveDir = (0, config_1.findKeveDir)();
55
+ const keveDir = (0, config_js_1.findKeveDir)();
56
56
  // Determine round
57
57
  let round = null;
58
58
  if (options.round) {
59
59
  round = parseInt(options.round, 10);
60
60
  }
61
61
  else if (keveDir) {
62
- const branchDir = (0, config_1.getBranchDir)(keveDir);
62
+ const branchDir = (0, config_js_1.getBranchDir)(keveDir);
63
63
  const artifactsBase = branchDir ? path.join(branchDir, 'test-artifacts') : path.join(keveDir, 'test-artifacts');
64
64
  if (fs.existsSync(artifactsBase)) {
65
65
  const dirs = fs.readdirSync(artifactsBase)
@@ -73,7 +73,7 @@ function resolvePaths(options) {
73
73
  // Resolve test-results.json
74
74
  let resultsPath = options.results;
75
75
  if (!resultsPath && keveDir && round !== null) {
76
- const branchDir = (0, config_1.getBranchDir)(keveDir);
76
+ const branchDir = (0, config_js_1.getBranchDir)(keveDir);
77
77
  const resultBase = branchDir ? path.join(branchDir, 'test-artifacts', `round-${round}`) : path.join(keveDir, 'test-artifacts', `round-${round}`);
78
78
  const candidate = path.join(resultBase, 'test-results.json');
79
79
  if (fs.existsSync(candidate))
@@ -96,7 +96,7 @@ function resolvePaths(options) {
96
96
  // Resolve confidence-data.jsonl
97
97
  let confidencePath = '';
98
98
  if (keveDir && round !== null) {
99
- const branchDir = (0, config_1.getBranchDir)(keveDir);
99
+ const branchDir = (0, config_js_1.getBranchDir)(keveDir);
100
100
  const resultBase = branchDir ? path.join(branchDir, 'test-artifacts', `round-${round}`) : path.join(keveDir, 'test-artifacts', `round-${round}`);
101
101
  const candidate = path.join(resultBase, 'confidence-data.jsonl');
102
102
  if (fs.existsSync(candidate))
@@ -117,7 +117,7 @@ async function invokeReport(options) {
117
117
  const projectRoot = keveDir || path.dirname(resultsPath);
118
118
  console.log(chalk_1.default.cyan('\n📊 keve report\n'));
119
119
  try {
120
- const reportData = await (0, index_1.generateReportData)({
120
+ const reportData = await (0, index_js_1.generateReportData)({
121
121
  projectRoot,
122
122
  resultsPath,
123
123
  confidencePath: confidencePath || undefined,
@@ -50,9 +50,9 @@ const path = __importStar(require("node:path"));
50
50
  const os = __importStar(require("node:os"));
51
51
  const node_child_process_1 = require("node:child_process");
52
52
  const chalk_1 = __importDefault(require("chalk"));
53
- const config_1 = require("../config");
54
- const report_1 = require("../core/generateReport/report");
55
- const reportData_1 = require("../core/generateReport/reportData");
53
+ const config_js_1 = require("../config.js");
54
+ const report_js_1 = require("../core/generateReport/report.js");
55
+ const reportData_js_1 = require("../core/generateReport/reportData.js");
56
56
  function isPlaywrightBrowserInstalled() {
57
57
  const cacheDir = os.platform() === 'darwin'
58
58
  ? path.join(os.homedir(), 'Library/Caches/ms-playwright')
@@ -95,7 +95,7 @@ function findPlaywrightConfig(keveDir, optionsConfig) {
95
95
  console.error(chalk_1.default.red(` ✗ Config not found: ${optionsConfig}`));
96
96
  return null;
97
97
  }
98
- const taskDir = (0, config_1.resolveTaskDir)(keveDir);
98
+ const taskDir = (0, config_js_1.resolveTaskDir)(keveDir);
99
99
  const searchPaths = [
100
100
  taskDir ? path.join(taskDir, 'keve-test.config.mjs') : null,
101
101
  taskDir ? path.join(taskDir, 'keve-test.config.ts') : null,
@@ -139,7 +139,7 @@ class KeveRunError extends Error {
139
139
  }
140
140
  exports.KeveRunError = KeveRunError;
141
141
  async function run(options) {
142
- const keveDir = (0, config_1.findKeveDir)();
142
+ const keveDir = (0, config_js_1.findKeveDir)();
143
143
  const cwd = process.cwd();
144
144
  const configPath = findPlaywrightConfig(keveDir || '', options.config);
145
145
  if (!configPath) {
@@ -150,12 +150,13 @@ async function run(options) {
150
150
  pwConfig = await new Function('return import(' + JSON.stringify(configPath) + ')')();
151
151
  }
152
152
  catch (e) {
153
- throw new KeveRunError(`Failed to import Playwright config: ${e.message}`);
153
+ // 包含 config 路径和完整错误堆栈,便于定位 "Invalid left-hand side" 等语法错误
154
+ throw new KeveRunError(`Failed to import Playwright config (${configPath}): ${e.message}\n${e.stack || ''}`);
154
155
  }
155
156
  const configDefault = pwConfig?.default || pwConfig;
156
157
  const taskDir = process.env.KEVE_TASK_DIR
157
158
  ? path.resolve(process.env.KEVE_TASK_DIR)
158
- : (0, config_1.resolveTaskDir)(keveDir) || cwd;
159
+ : (0, config_js_1.resolveTaskDir)(keveDir) || cwd;
159
160
  let resultDir = '';
160
161
  let resultsFile = '';
161
162
  if (configDefault?.reporter) {
@@ -173,7 +174,7 @@ async function run(options) {
173
174
  round = parseInt(options.round, 10);
174
175
  }
175
176
  else {
176
- round = (0, config_1.getNextRoundFromState)(taskDir);
177
+ round = (0, config_js_1.getNextRoundFromState)(taskDir);
177
178
  }
178
179
  resultDir = path.join(taskDir, 'test-artifacts', `round-${round}`);
179
180
  resultsFile = path.join(resultDir, 'test-results.json');
@@ -265,7 +266,7 @@ async function run(options) {
265
266
  console.log(chalk_1.default.green(`\n ✓ Round ${round} complete`));
266
267
  console.log(chalk_1.default.gray(` Tests: ${stats.expected || 0} expected, ${stats.actual || 0} actual`));
267
268
  console.log(chalk_1.default.gray(` Passed: ${passed}, Failed: ${failed}, Skipped: ${skipped}`));
268
- (0, config_1.completeRound)(taskDir, round, { passed, failed, skipped });
269
+ (0, config_js_1.completeRound)(taskDir, round, { passed, failed, skipped });
269
270
  stripBase64FromTestResults(resultsFile, resultDir);
270
271
  // ── 生成 report-data.json:在 Playwright 进程结束后统一生成,避免 keve-reporter 的 onEnd 与 JSON reporter 的 race condition ──
271
272
  // keve-reporter.ts 的 onEnd 在 Playwright 进程内部执行,此时 JSON reporter 可能尚未完全刷新 test-results.json,
@@ -275,7 +276,7 @@ async function run(options) {
275
276
  try {
276
277
  const confidencePath = path.join(resultDir, 'confidence-data.jsonl');
277
278
  const casesPath = path.join(taskDir, 'cases', 'test-cases.yaml');
278
- const reportData = await (0, reportData_1.generateReportData)({
279
+ const reportData = await (0, reportData_js_1.generateReportData)({
279
280
  projectRoot: taskDir,
280
281
  resultsPath: resultsFile,
281
282
  confidencePath: fs.existsSync(confidencePath) ? confidencePath : undefined,
@@ -399,7 +400,7 @@ async function runGenerateReport(resultDir, resultsFile, keveDir, taskDir) {
399
400
  const confidenceFile = path.join(resultDir, 'confidence-data.jsonl');
400
401
  console.log(chalk_1.default.gray(' Step 1: Generating report data...'));
401
402
  try {
402
- const { reportData } = await (0, report_1.generateReport)({
403
+ const { reportData } = await (0, report_js_1.generateReport)({
403
404
  projectRoot,
404
405
  resultsPath: resultsFile,
405
406
  confidencePath: fs.existsSync(confidenceFile) ? confidenceFile : undefined,
@@ -44,12 +44,12 @@ exports.test = test;
44
44
  const fs = __importStar(require("node:fs"));
45
45
  const path = __importStar(require("node:path"));
46
46
  const chalk_1 = __importDefault(require("chalk"));
47
- const config_1 = require("../config");
48
- const run_1 = require("./run");
49
- const report_1 = require("./report");
47
+ const config_js_1 = require("../config.js");
48
+ const run_js_1 = require("./run.js");
49
+ const report_js_1 = require("./report.js");
50
50
  async function test(options) {
51
51
  const maxRounds = parseInt(options.maxRounds || '3', 10);
52
- const keveDir = (0, config_1.findKeveDir)();
52
+ const keveDir = (0, config_js_1.findKeveDir)();
53
53
  console.log(chalk_1.default.cyan('\n🔄 keve test — Full pipeline\n'));
54
54
  console.log(chalk_1.default.gray(` Max rounds: ${maxRounds}`));
55
55
  for (let round = 1; round <= maxRounds; round++) {
@@ -57,14 +57,14 @@ async function test(options) {
57
57
  // Step 1: Run tests
58
58
  console.log(chalk_1.default.yellow(' [1/2] Running tests...'));
59
59
  try {
60
- await (0, run_1.run)({
60
+ await (0, run_js_1.run)({
61
61
  config: options.config,
62
62
  grep: options.grep,
63
63
  verbose: options.verbose,
64
64
  });
65
65
  }
66
66
  catch (e) {
67
- if (e instanceof run_1.KeveRunError) {
67
+ if (e instanceof run_js_1.KeveRunError) {
68
68
  console.error(chalk_1.default.red(` ✗ Run failed: ${e.message}`));
69
69
  break;
70
70
  }
@@ -72,7 +72,7 @@ async function test(options) {
72
72
  }
73
73
  // Step 2: Generate report
74
74
  console.log(chalk_1.default.yellow(' [2/2] Generating report...'));
75
- await (0, report_1.report)({});
75
+ await (0, report_js_1.report)({});
76
76
  // Check if all passed — if so, we're done
77
77
  if (keveDir) {
78
78
  const summaryPath = path.join(keveDir, 'reports', 'latest', 'execution-summary.json');
package/dist/config.js CHANGED
@@ -51,10 +51,10 @@ exports.readRunState = readRunState;
51
51
  exports.writeRunState = writeRunState;
52
52
  exports.getNextRoundFromState = getNextRoundFromState;
53
53
  exports.completeRound = completeRound;
54
- const fs = __importStar(require("fs"));
55
- const path = __importStar(require("path"));
54
+ const fs = __importStar(require("node:fs"));
55
+ const path = __importStar(require("node:path"));
56
56
  const yaml = __importStar(require("yaml"));
57
- const child_process_1 = require("child_process");
57
+ const node_child_process_1 = require("node:child_process");
58
58
  const DEFAULT_CONFIG = {
59
59
  project: {
60
60
  name: '',
@@ -166,7 +166,7 @@ function getBranchDir(keveDir) {
166
166
  */
167
167
  function getCurrentBranch() {
168
168
  try {
169
- return (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
169
+ return (0, node_child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
170
170
  }
171
171
  catch {
172
172
  return null;
@@ -177,7 +177,7 @@ function getCurrentBranch() {
177
177
  */
178
178
  function getGitUserName() {
179
179
  try {
180
- return (0, child_process_1.execSync)('git config user.name', { encoding: 'utf-8' }).trim();
180
+ return (0, node_child_process_1.execSync)('git config user.name', { encoding: 'utf-8' }).trim();
181
181
  }
182
182
  catch {
183
183
  return process.env.KEVE_USER_NAME || '';
@@ -188,7 +188,7 @@ function getGitUserName() {
188
188
  */
189
189
  function getGitRepoName() {
190
190
  try {
191
- const url = (0, child_process_1.execSync)('git remote get-url origin', { encoding: 'utf-8' }).trim();
191
+ const url = (0, node_child_process_1.execSync)('git remote get-url origin', { encoding: 'utf-8' }).trim();
192
192
  const parts = url.replace(/\.git$/, '').split('/');
193
193
  return parts[parts.length - 1] || '';
194
194
  }
@@ -201,7 +201,7 @@ function getGitRepoName() {
201
201
  */
202
202
  function getGitBranchName() {
203
203
  try {
204
- return (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
204
+ return (0, node_child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
205
205
  }
206
206
  catch {
207
207
  return '';
@@ -46,7 +46,10 @@ class ActionLogWriter {
46
46
  filePath;
47
47
  constructor(taskDir) {
48
48
  const dir = taskDir || process.env.KEVE_TASK_DIR || '.keve';
49
- const logDir = path.join(dir, 'test-artifacts', `round-${process.env.KEVE_ROUND || 'latest'}`);
49
+ // 优先写入 runner 注入的当前轮次目录;缺失时回退旧布局。
50
+ const logDir = process.env.KEVE_RESULT_DIR
51
+ ? path.resolve(process.env.KEVE_RESULT_DIR)
52
+ : path.join(dir, 'test-artifacts', `round-${process.env.KEVE_ROUND || 'latest'}`);
50
53
  fs.mkdirSync(logDir, { recursive: true });
51
54
  this.filePath = path.join(logDir, 'action-log.jsonl');
52
55
  }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * keve-decorators — @keveModel + @keveScene 装饰器
3
+ *
4
+ * 使用 TC39 Stage 3 装饰器 API (Playwright 1.58+ 默认支持)
5
+ *
6
+ * 执行顺序(TC39 Stage 3 规范):
7
+ * 1. Method decorator 函数调用 → 注册 addInitializer
8
+ * 2. Class decorator 函数调用 → 注册 addInitializer
9
+ * 3. Method addInitializers 执行 → @keveScene 注册场景到 WeakMap
10
+ * 4. Class addInitializer 执行 → @keveModel 读取 WeakMap 并调用 test.describe
11
+ *
12
+ * 两层装饰器 + 函数调用:
13
+ * Model (模块) → test.describe → @keveModel(id, desc, { setup, teardown, precondition?, before? })
14
+ * Scene (场景) → test → @keveScene(id, desc, { tag?, precondition?, before?, afterHook? })
15
+ * Goal (步骤) → 步骤元数据 → keveGoal() 函数调用(在方法体内,支持 precondition)
16
+ *
17
+ * @keveGroup 已消除:
18
+ * - setup → @keveModel setup 选项(独立 Page,所有 Scene 前执行)
19
+ * - teardown → @keveModel teardown 选项(所有 Scene 后执行,扫描 E2E_ 前缀)
20
+ * - identity → Scene 内 keveGoal(fn) 中调用 globalThis.switchIdentity
21
+ * - afterHook → @keveScene afterHook 选项(Scene 后保证执行)
22
+ */
23
+ import { type KeveGoalMeta } from './keve-registry.js';
24
+ import { type KeveHost } from './keve-host.js';
25
+ /**
26
+ * 注册宿主(推荐入口,引擎无关)。
27
+ */
28
+ export declare function setKeveTestHost(host: KeveHost): void;
29
+ /**
30
+ * Playwright 兼容适配:把 @playwright/test 的 test 对象包成 KeveHost。
31
+ * 保留该名称是为了不破坏既有调用(keve-test.ts / 外部脚本)。
32
+ */
33
+ export declare function setKeveTest(t: any): void;
34
+ /** Get the current keve test instance (Playwright 兼容查询;Cypress 下返回宿主) */
35
+ export declare function getKeveTest(): KeveHost;
36
+ /** 是否已注册宿主 */
37
+ export declare function hasKeveTest(): boolean;
38
+ export type { KeveGoalMeta, KeveEvalMeta, ErrorCategory } from './keve-registry.js';
39
+ export interface KeveModelOptions {
40
+ /**
41
+ * Module 级 setup:在所有 Scene 之前执行一次。
42
+ * 拥有独立 Page(完成后销毁,不与 Scene 共享)。
43
+ * 用 keveGoal(fn) → API 成功零 AI 开销,失败 AI 自愈。
44
+ * 签名:async ({ page, keveGoal }) => { ... }
45
+ * Playwright 将其作为独立 test() 执行,因此可以解构 fixture。
46
+ */
47
+ setup?: Function;
48
+ /**
49
+ * Module 级 teardown:在所有 Scene 之后执行一次。
50
+ * 保证执行(即使 Scene 失败/超时)。
51
+ * 无 Playwright fixture —— 纯异步函数,用于 API 清理、E2E_ 前缀扫描等。
52
+ * 签名:async () => { ... }
53
+ */
54
+ teardown?: Function;
55
+ /**
56
+ * Module 级前置条件(声明式描述):进入任何 Scene 之前必须满足的条件列表。
57
+ * 仅作元数据声明,供 Reporter/平台侧展示与归因(blocked ≠ fail)。
58
+ * 需要可执行校验时用 before。
59
+ */
60
+ precondition?: string[];
61
+ /**
62
+ * Module 级可执行前置校验:在所有 Scene 之前执行一次(beforeAll)。
63
+ * 返回 false 或抛错 → 整个 Model 下所有 Scene 跳过(skip,不是 fail)。
64
+ * 无 Playwright fixture —— 纯异步函数(可用自有 API client 做环境检查)。
65
+ * 签名:async () => boolean
66
+ */
67
+ before?: () => Promise<boolean>;
68
+ }
69
+ export interface KeveSceneOptions {
70
+ /** 场景标签(可选,用于分类过滤) */
71
+ tag?: string;
72
+ /**
73
+ * 用例级前置条件(声明式描述):本 Scene 执行前必须满足的条件列表。
74
+ * 仅作元数据声明,供 Reporter/平台侧展示与归因(对应旧系统 stepGroup.extraInfo.precondition)。
75
+ * 需要可执行校验时用 before。
76
+ */
77
+ precondition?: string[];
78
+ /**
79
+ * Scene 级可执行前置校验:本 Scene 执行前调用一次(beforeEach,仅对本 Scene 生效)。
80
+ * 返回 false 或抛错 → 本 Scene 跳过(skip,不是 fail)。
81
+ * 无 Playwright fixture —— 纯异步函数。
82
+ * 签名:async () => boolean
83
+ */
84
+ before?: () => Promise<boolean>;
85
+ /**
86
+ * Scene 级 afterHook:Scene 执行后保证调用(即使失败/超时)。
87
+ * 无 Playwright fixture —— 纯异步函数,用于 API 清理等。
88
+ * 签名:async () => { ... }
89
+ */
90
+ afterHook?: Function;
91
+ }
92
+ export interface KeveSceneMeta {
93
+ id: string;
94
+ description: string;
95
+ title: string;
96
+ code: string;
97
+ goals: KeveGoalMeta[];
98
+ fn: Function;
99
+ /** 用例级前置条件描述列表 */
100
+ precondition?: string[];
101
+ /** 可执行前置校验 */
102
+ before?: () => Promise<boolean>;
103
+ afterHook?: Function;
104
+ }
105
+ /**
106
+ * @keveModel(id, description, options?) — Class decorator (模块层)
107
+ * Uses context.addInitializer to defer test.describe() registration
108
+ * until AFTER all @keveScene addInitializers have run.
109
+ *
110
+ * options.setup: 第一个 test() 调用,拥有独立 Page + keveGoal
111
+ * options.teardown: test.afterAll 回调,保证执行
112
+ */
113
+ export declare function keveModel(id: string, description: string, options?: KeveModelOptions): <T extends {
114
+ new (...args: any[]): {};
115
+ }>(ctor: T, context: ClassDecoratorContext) => T;
116
+ /**
117
+ * @keveScene(id, description, options?) — Method decorator (场景层, static methods only)
118
+ * Captures fn.toString() as the complete scene script.
119
+ * Registers scene metadata to both WeakMap and global Map.
120
+ * If options.afterHook is provided, registers it in sceneAfterHooks Map
121
+ * for afterEach consumption.
122
+ *
123
+ * Returns the original function unchanged — Playwright resolves fixture dependencies
124
+ * by parsing fn.toString() / AST. Wrapping would break this resolution.
125
+ */
126
+ export declare function keveScene(id: string, description: string, options?: KeveSceneOptions): (fn: any, context: ClassMethodDecoratorContext) => any;
127
+ /** 获取 Model 下注册的所有 Scene */
128
+ export declare function getModelScenes(ctor: Function): KeveSceneMeta[] | null;