@pauly4010/evalai-sdk 1.8.0 → 1.9.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.
- package/CHANGELOG.md +54 -0
- package/dist/cli/ci.d.ts +45 -0
- package/dist/cli/ci.js +192 -0
- package/dist/cli/diff.d.ts +173 -0
- package/dist/cli/diff.js +680 -0
- package/dist/cli/discover.d.ts +84 -0
- package/dist/cli/discover.js +408 -0
- package/dist/cli/doctor.js +19 -10
- package/dist/cli/env.d.ts +21 -0
- package/dist/cli/env.js +42 -0
- package/dist/cli/explain.js +143 -37
- package/dist/cli/impact-analysis.d.ts +63 -0
- package/dist/cli/impact-analysis.js +251 -0
- package/dist/cli/index.js +173 -0
- package/dist/cli/manifest.d.ts +105 -0
- package/dist/cli/manifest.js +275 -0
- package/dist/cli/migrate.d.ts +41 -0
- package/dist/cli/migrate.js +349 -0
- package/dist/cli/print-config.js +18 -14
- package/dist/cli/run.d.ts +101 -0
- package/dist/cli/run.js +389 -0
- package/dist/cli/workspace.d.ts +28 -0
- package/dist/cli/workspace.js +58 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +30 -5
- package/dist/runtime/adapters/config-to-dsl.d.ts +33 -0
- package/dist/runtime/adapters/config-to-dsl.js +391 -0
- package/dist/runtime/adapters/testsuite-to-dsl.d.ts +63 -0
- package/dist/runtime/adapters/testsuite-to-dsl.js +271 -0
- package/dist/runtime/context.d.ts +26 -0
- package/dist/runtime/context.js +74 -0
- package/dist/runtime/eval.d.ts +46 -0
- package/dist/runtime/eval.js +237 -0
- package/dist/runtime/execution-mode.d.ts +80 -0
- package/dist/runtime/execution-mode.js +353 -0
- package/dist/runtime/executor.d.ts +16 -0
- package/dist/runtime/executor.js +152 -0
- package/dist/runtime/registry.d.ts +78 -0
- package/dist/runtime/registry.js +416 -0
- package/dist/runtime/run-report.d.ts +202 -0
- package/dist/runtime/run-report.js +220 -0
- package/dist/runtime/types.d.ts +356 -0
- package/dist/runtime/types.js +76 -0
- package/dist/testing.d.ts +65 -0
- package/dist/testing.js +42 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -3
package/dist/cli/index.js
CHANGED
|
@@ -10,11 +10,17 @@
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
const baseline_1 = require("./baseline");
|
|
12
12
|
const check_1 = require("./check");
|
|
13
|
+
const ci_1 = require("./ci");
|
|
14
|
+
const diff_1 = require("./diff");
|
|
15
|
+
const discover_1 = require("./discover");
|
|
13
16
|
const doctor_1 = require("./doctor");
|
|
14
17
|
const explain_1 = require("./explain");
|
|
18
|
+
const impact_analysis_1 = require("./impact-analysis");
|
|
15
19
|
const init_1 = require("./init");
|
|
20
|
+
const migrate_1 = require("./migrate");
|
|
16
21
|
const print_config_1 = require("./print-config");
|
|
17
22
|
const regression_gate_1 = require("./regression-gate");
|
|
23
|
+
const run_1 = require("./run");
|
|
18
24
|
const share_1 = require("./share");
|
|
19
25
|
const upgrade_1 = require("./upgrade");
|
|
20
26
|
const argv = process.argv.slice(2);
|
|
@@ -32,6 +38,60 @@ else if (subcommand === "gate") {
|
|
|
32
38
|
const code = (0, regression_gate_1.runGate)(argv.slice(1));
|
|
33
39
|
process.exit(code);
|
|
34
40
|
}
|
|
41
|
+
else if (subcommand === "migrate") {
|
|
42
|
+
// Handle migrate subcommand
|
|
43
|
+
const migrateSubcommand = argv[1];
|
|
44
|
+
if (migrateSubcommand === "config") {
|
|
45
|
+
// Parse migrate config arguments
|
|
46
|
+
let inputPath = "";
|
|
47
|
+
let outputPath = "";
|
|
48
|
+
let verbose = false;
|
|
49
|
+
let helpers = true;
|
|
50
|
+
let preserveIds = true;
|
|
51
|
+
let provenance = true;
|
|
52
|
+
for (let i = 2; i < argv.length; i++) {
|
|
53
|
+
const arg = argv[i];
|
|
54
|
+
if (arg === "--in" || arg === "-i") {
|
|
55
|
+
inputPath = argv[++i];
|
|
56
|
+
}
|
|
57
|
+
else if (arg === "--out" || arg === "-o") {
|
|
58
|
+
outputPath = argv[++i];
|
|
59
|
+
}
|
|
60
|
+
else if (arg === "--verbose" || arg === "-v") {
|
|
61
|
+
verbose = true;
|
|
62
|
+
}
|
|
63
|
+
else if (arg === "--no-helpers") {
|
|
64
|
+
helpers = false;
|
|
65
|
+
}
|
|
66
|
+
else if (arg === "--no-preserve-ids") {
|
|
67
|
+
preserveIds = false;
|
|
68
|
+
}
|
|
69
|
+
else if (arg === "--no-provenance") {
|
|
70
|
+
provenance = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!inputPath || !outputPath) {
|
|
74
|
+
console.error("Error: Both --in and --out options are required");
|
|
75
|
+
console.error("Usage: evalai migrate config --in <input> --out <output> [options]");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
(0, migrate_1.migrateConfig)({
|
|
79
|
+
input: inputPath,
|
|
80
|
+
output: outputPath,
|
|
81
|
+
verbose,
|
|
82
|
+
helpers,
|
|
83
|
+
preserveIds,
|
|
84
|
+
provenance,
|
|
85
|
+
}).catch((err) => {
|
|
86
|
+
console.error(`Migration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.error("Error: Unknown migrate subcommand. Use 'evalai migrate config'");
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
35
95
|
else if (subcommand === "upgrade") {
|
|
36
96
|
const code = (0, upgrade_1.runUpgrade)(argv.slice(1));
|
|
37
97
|
process.exit(code);
|
|
@@ -82,11 +142,111 @@ else if (subcommand === "share") {
|
|
|
82
142
|
process.exit(1);
|
|
83
143
|
});
|
|
84
144
|
}
|
|
145
|
+
else if (subcommand === "discover") {
|
|
146
|
+
// Parse arguments for discover command
|
|
147
|
+
const args = argv.slice(1);
|
|
148
|
+
const manifestFlag = args.includes("--manifest");
|
|
149
|
+
(0, discover_1.discoverSpecs)({ manifest: manifestFlag })
|
|
150
|
+
.then(() => process.exit(0))
|
|
151
|
+
.catch((err) => {
|
|
152
|
+
console.error(`EvalAI ERROR: ${err instanceof Error ? err.message : String(err)}`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else if (subcommand === "impact-analysis") {
|
|
157
|
+
// Parse arguments for impact-analysis command
|
|
158
|
+
const args = argv.slice(1);
|
|
159
|
+
const baseIndex = args.indexOf("--base");
|
|
160
|
+
const changedFilesIndex = args.indexOf("--changed-files");
|
|
161
|
+
const formatIndex = args.indexOf("--format");
|
|
162
|
+
const baseBranch = baseIndex !== -1 ? args[baseIndex + 1] : "main";
|
|
163
|
+
const changedFiles = changedFilesIndex !== -1 ? args[changedFilesIndex + 1]?.split(",") : undefined;
|
|
164
|
+
const format = formatIndex !== -1 ? args[formatIndex + 1] : "human";
|
|
165
|
+
(0, impact_analysis_1.runImpactAnalysisCLI)({ baseBranch, changedFiles, format }).catch((err) => {
|
|
166
|
+
console.error(`EvalAI ERROR: ${err instanceof Error ? err.message : String(err)}`);
|
|
167
|
+
process.exit(2);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
else if (subcommand === "run") {
|
|
171
|
+
// Parse arguments for run command
|
|
172
|
+
const args = argv.slice(1);
|
|
173
|
+
const specIdsIndex = args.indexOf("--spec-ids");
|
|
174
|
+
const impactedOnlyIndex = args.indexOf("--impacted-only");
|
|
175
|
+
const baseIndex = args.indexOf("--base");
|
|
176
|
+
const formatIndex = args.indexOf("--format");
|
|
177
|
+
const writeResultsIndex = args.indexOf("--write-results");
|
|
178
|
+
const specIds = specIdsIndex !== -1 ? args[specIdsIndex + 1]?.split(",") : undefined;
|
|
179
|
+
const impactedOnly = impactedOnlyIndex !== -1;
|
|
180
|
+
const baseBranch = baseIndex !== -1 ? args[baseIndex + 1] : undefined;
|
|
181
|
+
const format = formatIndex !== -1 ? args[formatIndex + 1] : "human";
|
|
182
|
+
const writeResults = writeResultsIndex !== -1;
|
|
183
|
+
(0, run_1.runEvaluationsCLI)({
|
|
184
|
+
specIds,
|
|
185
|
+
impactedOnly: impactedOnly ? !!baseBranch : false,
|
|
186
|
+
baseBranch,
|
|
187
|
+
format,
|
|
188
|
+
writeResults,
|
|
189
|
+
}).catch((err) => {
|
|
190
|
+
console.error(`EvalAI ERROR: ${err instanceof Error ? err.message : String(err)}`);
|
|
191
|
+
process.exit(2);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
else if (subcommand === "diff") {
|
|
195
|
+
// Parse arguments for diff command
|
|
196
|
+
const args = argv.slice(1);
|
|
197
|
+
const baseIndex = args.indexOf("--base");
|
|
198
|
+
const headIndex = args.indexOf("--head");
|
|
199
|
+
const formatIndex = args.indexOf("--format");
|
|
200
|
+
const base = baseIndex !== -1 ? args[baseIndex + 1] : undefined;
|
|
201
|
+
const head = headIndex !== -1 ? args[headIndex + 1] : undefined;
|
|
202
|
+
const format = formatIndex !== -1 ? args[formatIndex + 1] : "human";
|
|
203
|
+
(0, diff_1.runDiffCLI)({ base, head, format }).catch((err) => {
|
|
204
|
+
console.error(`EvalAI ERROR: ${err instanceof Error ? err.message : String(err)}`);
|
|
205
|
+
process.exit(2);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
else if (subcommand === "ci") {
|
|
209
|
+
// Parse arguments for ci command
|
|
210
|
+
const args = argv.slice(1);
|
|
211
|
+
const baseIndex = args.indexOf("--base");
|
|
212
|
+
const impactedOnlyIndex = args.indexOf("--impacted-only");
|
|
213
|
+
const formatIndex = args.indexOf("--format");
|
|
214
|
+
const writeResultsIndex = args.indexOf("--write-results");
|
|
215
|
+
const base = baseIndex !== -1 ? args[baseIndex + 1] : undefined;
|
|
216
|
+
const impactedOnly = impactedOnlyIndex !== -1;
|
|
217
|
+
const format = formatIndex !== -1 ? args[formatIndex + 1] : "human";
|
|
218
|
+
const writeResults = writeResultsIndex !== -1;
|
|
219
|
+
(0, ci_1.runCICLI)({ base, impactedOnly, format, writeResults }).catch((err) => {
|
|
220
|
+
console.error(`EvalAI ERROR: ${err instanceof Error ? err.message : String(err)}`);
|
|
221
|
+
process.exit(2);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
85
224
|
else {
|
|
86
225
|
console.log(`EvalAI CLI
|
|
87
226
|
|
|
88
227
|
Usage:
|
|
89
228
|
evalai init Create evalai.config.json + baseline + CI workflow
|
|
229
|
+
evalai discover Discover behavioral specs in project and show statistics
|
|
230
|
+
evalai discover --manifest Generate evaluation manifest for incremental analysis
|
|
231
|
+
evalai impact-analysis Analyze impact of changes and suggest targeted tests
|
|
232
|
+
--base <branch> Base branch to compare against (default: main)
|
|
233
|
+
--changed-files <files> Comma-separated list of changed files (for CI)
|
|
234
|
+
--format <fmt> Output format: human (default), json
|
|
235
|
+
evalai ci One-command CI loop (manifest → impact → run → diff)
|
|
236
|
+
--base <ref> Base reference for diff (baseline|last|<runId>|<path>|<gitref>)
|
|
237
|
+
--impacted-only Run only specs impacted by changes
|
|
238
|
+
--format <fmt> Output format: human (default), json, github
|
|
239
|
+
--write-results Write run results to .evalai/last-run.json
|
|
240
|
+
evalai run Run evaluation specifications
|
|
241
|
+
--spec-ids <ids> Comma-separated list of spec IDs to run
|
|
242
|
+
--impacted-only Run only specs impacted by changes (requires --base)
|
|
243
|
+
--base <branch> Base branch for impact analysis (with --impacted-only)
|
|
244
|
+
--format <fmt> Output format: human (default), json
|
|
245
|
+
--write-results Write results to .evalai/last-run.json
|
|
246
|
+
evalai diff Compare two run reports and show behavioral changes
|
|
247
|
+
--base <branch> Base branch or report path (default: main)
|
|
248
|
+
--head <path> Head report path (default: .evalai/last-run.json)
|
|
249
|
+
--format <fmt> Output format: human (default), json
|
|
90
250
|
evalai gate [options] Run regression gate (local test-based, no API needed)
|
|
91
251
|
evalai check [options] CI/CD evaluation gate (API-based)
|
|
92
252
|
evalai explain [options] Explain last gate/check failure with root causes + fixes
|
|
@@ -133,6 +293,19 @@ Options for doctor:
|
|
|
133
293
|
|
|
134
294
|
Examples:
|
|
135
295
|
evalai init
|
|
296
|
+
evalai discover
|
|
297
|
+
evalai discover --manifest
|
|
298
|
+
evalai impact-analysis --base main
|
|
299
|
+
evalai impact-analysis --base main --format json
|
|
300
|
+
evalai impact-analysis --changed-files src/utils.ts,datasets/test.json
|
|
301
|
+
evalai run
|
|
302
|
+
evalai run --spec-ids spec1,spec2
|
|
303
|
+
evalai run --impacted-only --base main
|
|
304
|
+
evalai run --format json --write-results
|
|
305
|
+
evalai diff
|
|
306
|
+
evalai diff --base main
|
|
307
|
+
evalai diff --base main --format json
|
|
308
|
+
evalai diff --a .evalai/runs/base.json --b .evalai/last-run.json
|
|
136
309
|
evalai gate
|
|
137
310
|
evalai gate --format json
|
|
138
311
|
evalai explain
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TICKET 2 — Evaluation Manifest Generation
|
|
3
|
+
*
|
|
4
|
+
* Goal: turn discovery output into a stable, versioned, machine-consumable artifact
|
|
5
|
+
* that becomes the input to run / impact / diff.
|
|
6
|
+
*
|
|
7
|
+
* This is the compiler output that everything else consumes.
|
|
8
|
+
*/
|
|
9
|
+
import type { SpecAnalysis } from "./discover";
|
|
10
|
+
import type { ExecutionModeConfig } from "../runtime/execution-mode";
|
|
11
|
+
/**
|
|
12
|
+
* Manifest schema version
|
|
13
|
+
*/
|
|
14
|
+
export declare const MANIFEST_SCHEMA_VERSION = 1;
|
|
15
|
+
/**
|
|
16
|
+
* SDK version from package.json
|
|
17
|
+
*/
|
|
18
|
+
export declare const SDK_VERSION = "1.8.0";
|
|
19
|
+
/**
|
|
20
|
+
* Evaluation Manifest Schema
|
|
21
|
+
*/
|
|
22
|
+
export interface EvaluationManifest {
|
|
23
|
+
/** Schema version for compatibility */
|
|
24
|
+
schemaVersion: number;
|
|
25
|
+
/** When this manifest was generated */
|
|
26
|
+
generatedAt: number;
|
|
27
|
+
/** Project metadata */
|
|
28
|
+
project: {
|
|
29
|
+
name: string;
|
|
30
|
+
root: string;
|
|
31
|
+
namespace: string;
|
|
32
|
+
};
|
|
33
|
+
/** Runtime information */
|
|
34
|
+
runtime: {
|
|
35
|
+
mode: "spec" | "legacy";
|
|
36
|
+
sdkVersion: string;
|
|
37
|
+
};
|
|
38
|
+
/** Spec files with hashes */
|
|
39
|
+
specFiles: SpecFile[];
|
|
40
|
+
/** Individual specifications */
|
|
41
|
+
specs: Spec[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Spec file information
|
|
45
|
+
*/
|
|
46
|
+
export interface SpecFile {
|
|
47
|
+
/** POSIX-relative file path */
|
|
48
|
+
filePath: string;
|
|
49
|
+
/** SHA-256 hash of file content */
|
|
50
|
+
fileHash: string;
|
|
51
|
+
/** Number of specs in this file */
|
|
52
|
+
specCount: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Individual specification
|
|
56
|
+
*/
|
|
57
|
+
export interface Spec {
|
|
58
|
+
/** Stable canonical ID */
|
|
59
|
+
id: string;
|
|
60
|
+
/** Spec name */
|
|
61
|
+
name: string;
|
|
62
|
+
/** Suite path from tags or file structure */
|
|
63
|
+
suitePath: string[];
|
|
64
|
+
/** POSIX-relative file path */
|
|
65
|
+
filePath: string;
|
|
66
|
+
/** Position in file */
|
|
67
|
+
position: {
|
|
68
|
+
line: number;
|
|
69
|
+
column: number;
|
|
70
|
+
};
|
|
71
|
+
/** Tags/categories */
|
|
72
|
+
tags: string[];
|
|
73
|
+
/** Dependencies */
|
|
74
|
+
dependsOn: {
|
|
75
|
+
prompts: string[];
|
|
76
|
+
datasets: string[];
|
|
77
|
+
tools: string[];
|
|
78
|
+
code: string[];
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Lock file for caching
|
|
83
|
+
*/
|
|
84
|
+
export interface ManifestLock {
|
|
85
|
+
/** When lock was generated */
|
|
86
|
+
generatedAt: number;
|
|
87
|
+
/** File hashes for incremental updates */
|
|
88
|
+
fileHashes: Record<string, string>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Generate evaluation manifest from discovery results
|
|
92
|
+
*/
|
|
93
|
+
export declare function generateManifest(specs: SpecAnalysis[], projectRoot: string, projectName: string, executionMode: ExecutionModeConfig): Promise<EvaluationManifest>;
|
|
94
|
+
/**
|
|
95
|
+
* Write manifest to disk
|
|
96
|
+
*/
|
|
97
|
+
export declare function writeManifest(manifest: EvaluationManifest, projectRoot: string): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Read existing manifest
|
|
100
|
+
*/
|
|
101
|
+
export declare function readManifest(projectRoot: string): Promise<EvaluationManifest | null>;
|
|
102
|
+
/**
|
|
103
|
+
* Read existing lock file
|
|
104
|
+
*/
|
|
105
|
+
export declare function readLock(projectRoot: string): Promise<ManifestLock | null>;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TICKET 2 — Evaluation Manifest Generation
|
|
4
|
+
*
|
|
5
|
+
* Goal: turn discovery output into a stable, versioned, machine-consumable artifact
|
|
6
|
+
* that becomes the input to run / impact / diff.
|
|
7
|
+
*
|
|
8
|
+
* This is the compiler output that everything else consumes.
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.SDK_VERSION = exports.MANIFEST_SCHEMA_VERSION = void 0;
|
|
45
|
+
exports.generateManifest = generateManifest;
|
|
46
|
+
exports.writeManifest = writeManifest;
|
|
47
|
+
exports.readManifest = readManifest;
|
|
48
|
+
exports.readLock = readLock;
|
|
49
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
50
|
+
const path = __importStar(require("node:path"));
|
|
51
|
+
const crypto = __importStar(require("node:crypto"));
|
|
52
|
+
/**
|
|
53
|
+
* Manifest schema version
|
|
54
|
+
*/
|
|
55
|
+
exports.MANIFEST_SCHEMA_VERSION = 1;
|
|
56
|
+
/**
|
|
57
|
+
* SDK version from package.json
|
|
58
|
+
*/
|
|
59
|
+
exports.SDK_VERSION = "1.8.0";
|
|
60
|
+
/**
|
|
61
|
+
* Generate evaluation manifest from discovery results
|
|
62
|
+
*/
|
|
63
|
+
async function generateManifest(specs, projectRoot, projectName, executionMode) {
|
|
64
|
+
const generatedAt = Math.floor(Date.now() / 1000);
|
|
65
|
+
const namespace = generateNamespace(projectRoot);
|
|
66
|
+
// Process spec files and specs
|
|
67
|
+
const specFiles = [];
|
|
68
|
+
const processedSpecs = [];
|
|
69
|
+
// Group specs by file
|
|
70
|
+
const specsByFile = new Map();
|
|
71
|
+
for (const spec of specs) {
|
|
72
|
+
const normalizedPath = normalizePath(spec.file, projectRoot);
|
|
73
|
+
if (!specsByFile.has(normalizedPath)) {
|
|
74
|
+
specsByFile.set(normalizedPath, []);
|
|
75
|
+
}
|
|
76
|
+
specsByFile.get(normalizedPath).push(spec);
|
|
77
|
+
}
|
|
78
|
+
// Process each file
|
|
79
|
+
for (const [filePath, fileSpecs] of specsByFile) {
|
|
80
|
+
const absolutePath = path.join(projectRoot, filePath);
|
|
81
|
+
const fileHash = await hashFile(absolutePath);
|
|
82
|
+
specFiles.push({
|
|
83
|
+
filePath,
|
|
84
|
+
fileHash,
|
|
85
|
+
specCount: fileSpecs.length,
|
|
86
|
+
});
|
|
87
|
+
// Process individual specs
|
|
88
|
+
for (const spec of fileSpecs) {
|
|
89
|
+
const processedSpec = await processSpec(spec, filePath, projectRoot);
|
|
90
|
+
processedSpecs.push(processedSpec);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
schemaVersion: exports.MANIFEST_SCHEMA_VERSION,
|
|
95
|
+
generatedAt,
|
|
96
|
+
project: {
|
|
97
|
+
name: projectName,
|
|
98
|
+
root: ".",
|
|
99
|
+
namespace,
|
|
100
|
+
},
|
|
101
|
+
runtime: {
|
|
102
|
+
mode: executionMode.mode,
|
|
103
|
+
sdkVersion: exports.SDK_VERSION,
|
|
104
|
+
},
|
|
105
|
+
specFiles,
|
|
106
|
+
specs: processedSpecs,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Process individual specification
|
|
111
|
+
*/
|
|
112
|
+
async function processSpec(spec, filePath, projectRoot) {
|
|
113
|
+
const absolutePath = path.join(projectRoot, filePath);
|
|
114
|
+
const content = await fs.readFile(absolutePath, "utf-8");
|
|
115
|
+
// Extract position from AST analysis (simplified for now)
|
|
116
|
+
const position = extractPosition(content, spec.name);
|
|
117
|
+
// Extract dependencies from content
|
|
118
|
+
const dependsOn = extractDependencies(content);
|
|
119
|
+
// Generate suite path from tags or file structure
|
|
120
|
+
const suitePath = generateSuitePath(spec.tags, filePath);
|
|
121
|
+
return {
|
|
122
|
+
id: spec.id,
|
|
123
|
+
name: spec.name,
|
|
124
|
+
suitePath,
|
|
125
|
+
filePath: normalizePath(spec.file, projectRoot),
|
|
126
|
+
position,
|
|
127
|
+
tags: spec.tags,
|
|
128
|
+
dependsOn,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Extract position from content (simplified implementation)
|
|
133
|
+
*/
|
|
134
|
+
function extractPosition(content, specName) {
|
|
135
|
+
const lines = content.split("\n");
|
|
136
|
+
const specPattern = new RegExp(`defineEval\\s*\\(\\s*["'\`]${specName}["'\`]`, "g");
|
|
137
|
+
let match = null;
|
|
138
|
+
let line = 1;
|
|
139
|
+
let column = 1;
|
|
140
|
+
for (let i = 0; i < lines.length; i++) {
|
|
141
|
+
const lineContent = lines[i];
|
|
142
|
+
specPattern.lastIndex = 0;
|
|
143
|
+
match = specPattern.exec(lineContent);
|
|
144
|
+
if (match) {
|
|
145
|
+
line = i + 1;
|
|
146
|
+
column = match.index + 1;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { line, column };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Extract dependencies from content
|
|
154
|
+
*/
|
|
155
|
+
function extractDependencies(content) {
|
|
156
|
+
const dependsOn = {
|
|
157
|
+
prompts: [],
|
|
158
|
+
datasets: [],
|
|
159
|
+
tools: [],
|
|
160
|
+
code: [],
|
|
161
|
+
};
|
|
162
|
+
// Extract from dependsOn option if present
|
|
163
|
+
const dependsOnMatch = content.match(/dependsOn\s*:\s*({[^}]+})/s);
|
|
164
|
+
if (dependsOnMatch) {
|
|
165
|
+
try {
|
|
166
|
+
const deps = eval(`(${dependsOnMatch[1]})`);
|
|
167
|
+
return {
|
|
168
|
+
prompts: deps.prompts || [],
|
|
169
|
+
datasets: deps.datasets || [],
|
|
170
|
+
tools: deps.tools || [],
|
|
171
|
+
code: deps.code || [],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
// Fall back to simple extraction
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Simple extraction as fallback
|
|
179
|
+
const patterns = {
|
|
180
|
+
prompts: /["']([^"']*\.md)["']/g,
|
|
181
|
+
datasets: /["']([^"']*\.json)["']/g,
|
|
182
|
+
tools: /["']([^"']*\.ts)["']/g,
|
|
183
|
+
code: /import.*from\s*["']([^"']+)["']/g,
|
|
184
|
+
};
|
|
185
|
+
for (const [type, pattern] of Object.entries(patterns)) {
|
|
186
|
+
let match;
|
|
187
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
188
|
+
dependsOn[type].push(match[1]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return dependsOn;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Generate suite path from tags or file structure
|
|
195
|
+
*/
|
|
196
|
+
function generateSuitePath(tags, filePath) {
|
|
197
|
+
// Use tags as primary suite path
|
|
198
|
+
if (tags.length > 0) {
|
|
199
|
+
return [tags[0]];
|
|
200
|
+
}
|
|
201
|
+
// Fall back to file structure
|
|
202
|
+
const parts = filePath.split("/");
|
|
203
|
+
if (parts.length > 1) {
|
|
204
|
+
return [parts[0]];
|
|
205
|
+
}
|
|
206
|
+
return ["general"];
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Generate namespace from project root
|
|
210
|
+
*/
|
|
211
|
+
function generateNamespace(projectRoot) {
|
|
212
|
+
const hash = crypto.createHash("sha256");
|
|
213
|
+
hash.update(projectRoot);
|
|
214
|
+
return hash.digest("hex").slice(0, 8);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Normalize path to POSIX format
|
|
218
|
+
*/
|
|
219
|
+
function normalizePath(filePath, projectRoot) {
|
|
220
|
+
const relativePath = path.relative(projectRoot, filePath);
|
|
221
|
+
return relativePath.replace(/\\/g, "/");
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Hash file content
|
|
225
|
+
*/
|
|
226
|
+
async function hashFile(filePath) {
|
|
227
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
228
|
+
const hash = crypto.createHash("sha256");
|
|
229
|
+
hash.update(content);
|
|
230
|
+
return `sha256:${hash.digest("hex")}`;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Write manifest to disk
|
|
234
|
+
*/
|
|
235
|
+
async function writeManifest(manifest, projectRoot) {
|
|
236
|
+
const evalaiDir = path.join(projectRoot, ".evalai");
|
|
237
|
+
// Ensure .evalai directory exists
|
|
238
|
+
await fs.mkdir(evalaiDir, { recursive: true });
|
|
239
|
+
// Write manifest.json
|
|
240
|
+
const manifestPath = path.join(evalaiDir, "manifest.json");
|
|
241
|
+
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
242
|
+
// Write lock file
|
|
243
|
+
const lock = {
|
|
244
|
+
generatedAt: manifest.generatedAt,
|
|
245
|
+
fileHashes: Object.fromEntries(manifest.specFiles.map((f) => [f.filePath, f.fileHash])),
|
|
246
|
+
};
|
|
247
|
+
const lockPath = path.join(evalaiDir, "manifest.lock.json");
|
|
248
|
+
await fs.writeFile(lockPath, JSON.stringify(lock, null, 2), "utf-8");
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Read existing manifest
|
|
252
|
+
*/
|
|
253
|
+
async function readManifest(projectRoot) {
|
|
254
|
+
const manifestPath = path.join(projectRoot, ".evalai", "manifest.json");
|
|
255
|
+
try {
|
|
256
|
+
const content = await fs.readFile(manifestPath, "utf-8");
|
|
257
|
+
return JSON.parse(content);
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Read existing lock file
|
|
265
|
+
*/
|
|
266
|
+
async function readLock(projectRoot) {
|
|
267
|
+
const lockPath = path.join(projectRoot, ".evalai", "manifest.lock.json");
|
|
268
|
+
try {
|
|
269
|
+
const content = await fs.readFile(lockPath, "utf-8");
|
|
270
|
+
return JSON.parse(content);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMPAT-203: Config → DSL migration generator (file-based)
|
|
3
|
+
*
|
|
4
|
+
* CLI command: evalai migrate config --in evalai.config.json --out eval/legacy.spec.ts
|
|
5
|
+
* Generates defineEval() calls with comments and TODOs for manual completion
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
/**
|
|
9
|
+
* Migration options
|
|
10
|
+
*/
|
|
11
|
+
interface MigrateOptions {
|
|
12
|
+
/** Input config file path */
|
|
13
|
+
input: string;
|
|
14
|
+
/** Output DSL file path */
|
|
15
|
+
output: string;
|
|
16
|
+
/** Include detailed comments */
|
|
17
|
+
verbose?: boolean;
|
|
18
|
+
/** Generate helper functions */
|
|
19
|
+
helpers?: boolean;
|
|
20
|
+
/** Preserve original test IDs */
|
|
21
|
+
preserveIds?: boolean;
|
|
22
|
+
/** Include provenance metadata */
|
|
23
|
+
provenance?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Main migration function
|
|
27
|
+
*/
|
|
28
|
+
export declare function migrateConfig(options: MigrateOptions): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* CLI command definition
|
|
31
|
+
*/
|
|
32
|
+
export declare function createMigrateCommand(): Command;
|
|
33
|
+
/**
|
|
34
|
+
* Validate config file structure
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateConfigFile(filePath: string): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Show migration preview without writing files
|
|
39
|
+
*/
|
|
40
|
+
export declare function previewMigration(filePath: string): Promise<void>;
|
|
41
|
+
export {};
|