@sanity/ailf 3.2.0 → 3.3.1
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/dist/_vendor/ailf-core/examples/index.d.ts +8 -8
- package/dist/_vendor/ailf-core/examples/index.js +8 -8
- package/dist/_vendor/ailf-shared/feature-flags.d.ts +59 -0
- package/dist/_vendor/ailf-shared/feature-flags.js +44 -0
- package/dist/_vendor/ailf-shared/index.d.ts +1 -0
- package/dist/_vendor/ailf-shared/index.js +1 -0
- package/dist/adapters/config-sources/ailf-resolver.d.ts +55 -0
- package/dist/adapters/config-sources/ailf-resolver.js +147 -0
- package/dist/adapters/config-sources/ts-config-loader.js +7 -0
- package/dist/adapters/task-sources/repo-schemas.d.ts +35 -5
- package/dist/adapters/task-sources/repo-schemas.js +25 -3
- package/dist/adapters/task-sources/task-file-loader.js +3 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +19 -5
- package/dist/commands/pipeline-action.js +51 -6
- package/dist/commands/pipeline.js +1 -1
- package/dist/commands/validate-tasks.d.ts +14 -3
- package/dist/commands/validate-tasks.js +125 -81
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/pipeline/compiler/config-loader.js +6 -1
- package/dist/pipeline/compiler/preset-loader.js +3 -0
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* validate-tasks command — standalone validation of task files.
|
|
3
3
|
*
|
|
4
|
-
* Validates .ailf/tasks/*.yaml files against the
|
|
5
|
-
* running the full pipeline. Useful for
|
|
6
|
-
* in external repos.
|
|
4
|
+
* Validates .ailf/tasks/*.yaml and .ailf/tasks/*.task.ts files against the
|
|
5
|
+
* CanonicalTaskSchema without running the full pipeline. Useful for
|
|
6
|
+
* pre-commit hooks and CI checks in external repos.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
9
|
* ailf validate-tasks .ailf/tasks/
|
|
@@ -11,97 +11,141 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @see packages/eval/src/adapters/task-sources/repo-schemas.ts
|
|
13
13
|
* @see packages/eval/src/adapters/task-sources/repo-validation.ts
|
|
14
|
+
* @see packages/eval/src/adapters/task-sources/task-file-loader.ts
|
|
14
15
|
*/
|
|
15
16
|
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
16
|
-
import { resolve, relative } from "path";
|
|
17
|
+
import { resolve, relative, basename } from "path";
|
|
17
18
|
import { Command } from "commander";
|
|
18
19
|
import { load } from "js-yaml";
|
|
19
20
|
import { detectLegacyFieldNames, parseCanonicalTaskFile, } from "../adapters/task-sources/repo-schemas.js";
|
|
20
21
|
import { validateCanonicalTasks, formatValidationResult, } from "../adapters/task-sources/repo-validation.js";
|
|
22
|
+
import { discoverTsTaskFiles, loadTsTaskFile, } from "../adapters/task-sources/task-file-loader.js";
|
|
21
23
|
export function createValidateTasksCommand() {
|
|
22
24
|
return new Command("validate-tasks")
|
|
23
|
-
.description("Validate task YAML
|
|
25
|
+
.description("Validate task files (YAML and TypeScript) in .ailf/tasks/ against the canonical schema")
|
|
24
26
|
.argument("[path]", "Path to tasks directory (default: .ailf/tasks/)", ".ailf/tasks")
|
|
25
27
|
.option("--strict", "Treat warnings as errors", false)
|
|
26
28
|
.action(async (tasksPath, opts) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
const exitCode = await runValidateTasks(tasksPath, opts);
|
|
30
|
+
process.exit(exitCode);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Execute the validate-tasks command logic. Returns the exit code (0 success,
|
|
35
|
+
* 1 failure) so callers can decide how to surface it — the CLI wrapper calls
|
|
36
|
+
* `process.exit`, tests can assert directly.
|
|
37
|
+
*/
|
|
38
|
+
export async function runValidateTasks(tasksPath, opts) {
|
|
39
|
+
// Resolve relative to the caller's working directory, not the
|
|
40
|
+
// eval package root (which differs when run via bin/ailf.js)
|
|
41
|
+
const callerCwd = opts.callerCwd ?? process.env.AILF_CALLER_CWD ?? process.cwd();
|
|
42
|
+
const resolvedPath = resolve(callerCwd, tasksPath);
|
|
43
|
+
if (!existsSync(resolvedPath)) {
|
|
44
|
+
console.error(`Directory not found: ${resolvedPath}`);
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
const yamlFiles = readdirSync(resolvedPath).filter((f) => (f.endsWith(".yaml") || f.endsWith(".yml")) && !f.startsWith("."));
|
|
48
|
+
const tsFiles = discoverTsTaskFiles(resolvedPath);
|
|
49
|
+
const fileCount = yamlFiles.length + tsFiles.length;
|
|
50
|
+
if (fileCount === 0) {
|
|
51
|
+
console.error(`No task files found in ${resolvedPath}\n` +
|
|
52
|
+
" Expected .yaml, .yml, .task.ts, or .task.js files");
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
console.log(`\nValidating ${fileCount} task file(s) in ${relative(process.cwd(), resolvedPath)}/\n`);
|
|
56
|
+
let totalTasks = 0;
|
|
57
|
+
let hasErrors = false;
|
|
58
|
+
const allTasks = [];
|
|
59
|
+
for (const file of yamlFiles) {
|
|
60
|
+
const filePath = resolve(resolvedPath, file);
|
|
61
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = load(raw);
|
|
34
65
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
console.error(`
|
|
38
|
-
|
|
66
|
+
catch (err) {
|
|
67
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
68
|
+
console.error(` ${file}: YAML parse error`);
|
|
69
|
+
console.error(` ${msg}\n`);
|
|
70
|
+
hasErrors = true;
|
|
71
|
+
continue;
|
|
39
72
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
for (const file of yamlFiles) {
|
|
45
|
-
const filePath = resolve(resolvedPath, file);
|
|
46
|
-
const raw = readFileSync(filePath, "utf-8");
|
|
47
|
-
let parsed;
|
|
48
|
-
try {
|
|
49
|
-
parsed = load(raw);
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
53
|
-
console.error(` ${file}: YAML parse error`);
|
|
54
|
-
console.error(` ${msg}\n`);
|
|
55
|
-
hasErrors = true;
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
if (!Array.isArray(parsed)) {
|
|
59
|
-
console.error(` ${file}: Expected a YAML array of task definitions`);
|
|
60
|
-
hasErrors = true;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
// Detect legacy field names before Zod validation
|
|
64
|
-
const legacyWarnings = detectLegacyFieldNames(parsed, file);
|
|
65
|
-
if (legacyWarnings.length > 0) {
|
|
66
|
-
console.error(` ${file}: Uses legacy field names`);
|
|
67
|
-
for (const w of legacyWarnings) {
|
|
68
|
-
console.error(` ${w}`);
|
|
69
|
-
}
|
|
70
|
-
console.error();
|
|
71
|
-
hasErrors = true;
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
try {
|
|
75
|
-
const tasks = parseCanonicalTaskFile(parsed, file);
|
|
76
|
-
console.log(` ${file}: ${tasks.length} task${tasks.length === 1 ? "" : "s"} valid`);
|
|
77
|
-
totalTasks += tasks.length;
|
|
78
|
-
allTasks.push(...tasks);
|
|
79
|
-
}
|
|
80
|
-
catch (err) {
|
|
81
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
82
|
-
console.error(` ${file}: Schema validation failed`);
|
|
83
|
-
console.error(`${msg
|
|
84
|
-
.split("\n")
|
|
85
|
-
.map((l) => ` ${l}`)
|
|
86
|
-
.join("\n")}\n`);
|
|
87
|
-
hasErrors = true;
|
|
88
|
-
}
|
|
73
|
+
if (!Array.isArray(parsed)) {
|
|
74
|
+
console.error(` ${file}: Expected a YAML array of task definitions`);
|
|
75
|
+
hasErrors = true;
|
|
76
|
+
continue;
|
|
89
77
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const semanticResult = validateCanonicalTasks(allTasks);
|
|
94
|
-
const formatted = formatValidationResult(semanticResult);
|
|
95
|
-
console.log(formatted);
|
|
96
|
-
if (!semanticResult.valid) {
|
|
97
|
-
hasErrors = true;
|
|
98
|
-
}
|
|
99
|
-
if (opts.strict && semanticResult.warnings.length > 0) {
|
|
100
|
-
hasErrors = true;
|
|
101
|
-
console.log("\n --strict mode: warnings treated as errors");
|
|
102
|
-
}
|
|
78
|
+
if (!validateTaskArray(parsed, file, allTasks)) {
|
|
79
|
+
hasErrors = true;
|
|
80
|
+
continue;
|
|
103
81
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
82
|
+
totalTasks += parsed.length;
|
|
83
|
+
}
|
|
84
|
+
for (const tsFilePath of tsFiles) {
|
|
85
|
+
const file = basename(tsFilePath);
|
|
86
|
+
let loaded;
|
|
87
|
+
try {
|
|
88
|
+
loaded = await loadTsTaskFile(tsFilePath);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
92
|
+
console.error(` ${file}: Failed to load TypeScript task file`);
|
|
93
|
+
console.error(` ${msg}\n`);
|
|
94
|
+
hasErrors = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!validateTaskArray(loaded.tasks, file, allTasks)) {
|
|
98
|
+
hasErrors = true;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
totalTasks += loaded.tasks.length;
|
|
102
|
+
}
|
|
103
|
+
if (allTasks.length > 0) {
|
|
104
|
+
console.log();
|
|
105
|
+
const semanticResult = validateCanonicalTasks(allTasks);
|
|
106
|
+
const formatted = formatValidationResult(semanticResult);
|
|
107
|
+
console.log(formatted);
|
|
108
|
+
if (!semanticResult.valid) {
|
|
109
|
+
hasErrors = true;
|
|
110
|
+
}
|
|
111
|
+
if (opts.strict && semanticResult.warnings.length > 0) {
|
|
112
|
+
hasErrors = true;
|
|
113
|
+
console.log("\n --strict mode: warnings treated as errors");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
console.log(`\n${hasErrors ? "FAIL" : "OK"} ${totalTasks} task${totalTasks === 1 ? "" : "s"} across ${fileCount} file${fileCount === 1 ? "" : "s"}\n`);
|
|
117
|
+
return hasErrors ? 1 : 0;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Validate an array of raw task entries — runs the legacy-field detector and
|
|
121
|
+
* the canonical Zod schema, appending valid tasks to `accumulator`.
|
|
122
|
+
*
|
|
123
|
+
* Returns `true` when the file is fully valid, `false` when any error was
|
|
124
|
+
* reported (the caller is responsible for flipping its own error flag).
|
|
125
|
+
*/
|
|
126
|
+
function validateTaskArray(entries, file, accumulator) {
|
|
127
|
+
const legacyWarnings = detectLegacyFieldNames(entries, file);
|
|
128
|
+
if (legacyWarnings.length > 0) {
|
|
129
|
+
console.error(` ${file}: Uses legacy field names`);
|
|
130
|
+
for (const w of legacyWarnings) {
|
|
131
|
+
console.error(` ${w}`);
|
|
132
|
+
}
|
|
133
|
+
console.error();
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const tasks = parseCanonicalTaskFile(entries, file);
|
|
138
|
+
console.log(` ${file}: ${tasks.length} task${tasks.length === 1 ? "" : "s"} valid`);
|
|
139
|
+
accumulator.push(...tasks);
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
144
|
+
console.error(` ${file}: Schema validation failed`);
|
|
145
|
+
console.error(`${msg
|
|
146
|
+
.split("\n")
|
|
147
|
+
.map((l) => ` ${l}`)
|
|
148
|
+
.join("\n")}\n`);
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
107
151
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -39,3 +39,5 @@ export { env } from "./_vendor/ailf-core/index.d.ts";
|
|
|
39
39
|
export type { AgentHarnessTaskDefinition, CustomTaskDefinition, GeneralizedAssertionDefinition, GeneralizedDocRef, GeneralizedTaskDefinition, GeneralizedTemplatedAssertion, GeneralizedValueAssertion, IdDocRef, KnowledgeProbeTaskDefinition, LiteracyTaskDefinition, MCPServerTaskDefinition, PathDocRef, PerspectiveDocRef, RubricRef, SlugDocRef, TaskCommonFields, TaskDifficulty, TaskOptions, TaskProviderConfig, TaskStatus, } from "./_vendor/ailf-core/index.d.ts";
|
|
40
40
|
export { CanonicalTaskFileSchema, CanonicalTaskSchema, CURATED_ASSERTION_TYPES, detectLegacyFieldNames, parseCanonicalTaskFile, RUBRIC_TEMPLATE_NAMES, type CanonicalTask, type CuratedAssertionType, type RubricTemplateName, } from "./adapters/task-sources/repo-schemas.js";
|
|
41
41
|
export { formatValidationResult, validateCanonicalTasks, type ValidationMessage, type ValidationResult, } from "./adapters/task-sources/repo-validation.js";
|
|
42
|
+
export { InMemoryPluginRegistry } from "./_vendor/ailf-core/index.d.ts";
|
|
43
|
+
export type { CompilationContext, ModeBase, ModeCompileResult, ModeHandler, PresetDefinition, } from "./_vendor/ailf-core/index.d.ts";
|
package/dist/index.js
CHANGED
|
@@ -46,3 +46,7 @@ export { env } from "./_vendor/ailf-core/index.js";
|
|
|
46
46
|
// ---------------------------------------------------------------------------
|
|
47
47
|
export { CanonicalTaskFileSchema, CanonicalTaskSchema, CURATED_ASSERTION_TYPES, detectLegacyFieldNames, parseCanonicalTaskFile, RUBRIC_TEMPLATE_NAMES, } from "./adapters/task-sources/repo-schemas.js";
|
|
48
48
|
export { formatValidationResult, validateCanonicalTasks, } from "./adapters/task-sources/repo-validation.js";
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Plugin extension points — for authoring custom presets, modes, and registries
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
export { InMemoryPluginRegistry } from "./_vendor/ailf-core/index.js";
|
|
@@ -25,6 +25,7 @@ import { createRequire } from "module";
|
|
|
25
25
|
import { existsSync, readFileSync } from "fs";
|
|
26
26
|
import { load } from "js-yaml";
|
|
27
27
|
import { resolve } from "path";
|
|
28
|
+
import { resolveAilfAlias } from "../../adapters/config-sources/ailf-resolver.js";
|
|
28
29
|
/**
|
|
29
30
|
* Load a config file by name, searching for TS/JS/YAML/JSON variants.
|
|
30
31
|
*
|
|
@@ -134,7 +135,11 @@ function loadTsFile(filePath, format) {
|
|
|
134
135
|
// jiti supports sync loading. Use createRequire for ESM compatibility.
|
|
135
136
|
const esmRequire = createRequire(import.meta.url);
|
|
136
137
|
const { createJiti } = esmRequire("jiti");
|
|
137
|
-
const
|
|
138
|
+
const alias = resolveAilfAlias(filePath);
|
|
139
|
+
const jiti = createJiti(filePath, {
|
|
140
|
+
interopDefault: true,
|
|
141
|
+
...(alias ? { alias } : {}),
|
|
142
|
+
});
|
|
138
143
|
const mod = jiti(filePath);
|
|
139
144
|
const data = (mod?.default ?? mod);
|
|
140
145
|
return { data, filePath, format };
|
|
@@ -14,6 +14,7 @@ import { existsSync } from "fs";
|
|
|
14
14
|
import { resolve } from "path";
|
|
15
15
|
import { pathToFileURL } from "url";
|
|
16
16
|
import { createJiti } from "jiti";
|
|
17
|
+
import { resolveAilfAlias } from "../../adapters/config-sources/ailf-resolver.js";
|
|
17
18
|
/** Thrown for preset-specific load errors (distinguishes from third-party errors) */
|
|
18
19
|
class PresetLoadError extends Error {
|
|
19
20
|
constructor(message) {
|
|
@@ -53,9 +54,11 @@ function loadSinglePreset(ref, rootDir) {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
try {
|
|
57
|
+
const alias = resolveAilfAlias(filePath);
|
|
56
58
|
const jiti = createJiti(pathToFileURL(rootDir).href, {
|
|
57
59
|
interopDefault: true,
|
|
58
60
|
requireCache: true,
|
|
61
|
+
...(alias ? { alias } : {}),
|
|
59
62
|
});
|
|
60
63
|
// jiti() is the synchronous loader
|
|
61
64
|
const mod = jiti(filePath);
|