@aaac/contracts 0.0.1 → 0.1.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/chunk-B7OY3RPU.js +1287 -0
- package/dist/chunk-B7OY3RPU.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +3975 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +341 -6
- package/dist/index.js +62 -2
- package/dist/index.js.map +1 -1
- package/package.json +17 -1
|
@@ -0,0 +1,1287 @@
|
|
|
1
|
+
// src/schema.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var SUPPORTED_SCHEMA_VERSIONS = ["aaac/0.1"];
|
|
4
|
+
var SCHEMA_VERSION_PATTERN = /^aaac\/\d+\.\d+$/;
|
|
5
|
+
var JSONSchemaRefSchema = z.union([
|
|
6
|
+
z.object({ schema: z.record(z.string(), z.unknown()) }),
|
|
7
|
+
z.object({ $ref: z.string() })
|
|
8
|
+
]);
|
|
9
|
+
var OptionSchema = z.object({
|
|
10
|
+
name: z.string().min(1),
|
|
11
|
+
schema: z.record(z.string(), z.unknown()),
|
|
12
|
+
required: z.boolean().optional(),
|
|
13
|
+
description: z.string().optional()
|
|
14
|
+
});
|
|
15
|
+
var ArtifactSlotValueSchema = z.union([
|
|
16
|
+
z.string(),
|
|
17
|
+
z.object({
|
|
18
|
+
artifact: z.string(),
|
|
19
|
+
direction: z.enum(["read", "write", "readwrite"])
|
|
20
|
+
})
|
|
21
|
+
]);
|
|
22
|
+
var OperationSchema = z.object({
|
|
23
|
+
description: z.string().min(1),
|
|
24
|
+
agentWorkflow: z.string().optional(),
|
|
25
|
+
agentTask: z.string().optional(),
|
|
26
|
+
handler: z.string().optional(),
|
|
27
|
+
options: z.array(OptionSchema).optional(),
|
|
28
|
+
input: JSONSchemaRefSchema.optional(),
|
|
29
|
+
output: z.object({
|
|
30
|
+
success: JSONSchemaRefSchema,
|
|
31
|
+
failed: JSONSchemaRefSchema.optional()
|
|
32
|
+
}),
|
|
33
|
+
artifactSlots: z.record(z.string(), ArtifactSlotValueSchema).optional(),
|
|
34
|
+
riskLevel: z.enum(["low", "medium", "high"]).optional(),
|
|
35
|
+
idempotent: z.boolean().optional(),
|
|
36
|
+
requiresConfirmation: z.boolean().optional(),
|
|
37
|
+
execution: z.object({
|
|
38
|
+
timeoutMs: z.number().optional(),
|
|
39
|
+
costCeilingUsd: z.number().optional()
|
|
40
|
+
}).optional(),
|
|
41
|
+
memoryRef: z.object({
|
|
42
|
+
input: z.boolean().optional(),
|
|
43
|
+
output: z.boolean().optional()
|
|
44
|
+
}).optional()
|
|
45
|
+
}).superRefine((op, ctx) => {
|
|
46
|
+
const dispatchCount = [op.agentWorkflow, op.agentTask, op.handler].filter(
|
|
47
|
+
(v) => v !== void 0
|
|
48
|
+
).length;
|
|
49
|
+
if (dispatchCount === 0) {
|
|
50
|
+
ctx.addIssue({
|
|
51
|
+
code: "custom",
|
|
52
|
+
message: "Exactly one of agentWorkflow, agentTask, or handler must be specified",
|
|
53
|
+
path: ["agentWorkflow"]
|
|
54
|
+
});
|
|
55
|
+
} else if (dispatchCount > 1) {
|
|
56
|
+
ctx.addIssue({
|
|
57
|
+
code: "custom",
|
|
58
|
+
message: "Only one of agentWorkflow, agentTask, or handler may be specified",
|
|
59
|
+
path: ["agentWorkflow"]
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
var ComponentSchema = z.object({
|
|
64
|
+
schema: z.string().regex(SCHEMA_VERSION_PATTERN, 'schema must match pattern "aaac/x.y"'),
|
|
65
|
+
info: z.object({
|
|
66
|
+
id: z.string().min(1),
|
|
67
|
+
version: z.string().min(1),
|
|
68
|
+
description: z.string().optional()
|
|
69
|
+
}),
|
|
70
|
+
implementation: z.string().optional(),
|
|
71
|
+
projections: z.array(
|
|
72
|
+
z.enum(["library", "cli", "mcp", "claude", "openai"])
|
|
73
|
+
),
|
|
74
|
+
operations: z.record(z.string(), OperationSchema)
|
|
75
|
+
}).superRefine((component, ctx) => {
|
|
76
|
+
const usesAgentic = Object.values(component.operations).some(
|
|
77
|
+
(op) => op.agentWorkflow !== void 0 || op.agentTask !== void 0
|
|
78
|
+
);
|
|
79
|
+
if (usesAgentic && !component.implementation) {
|
|
80
|
+
ctx.addIssue({
|
|
81
|
+
code: "custom",
|
|
82
|
+
message: "implementation is required when any operation uses agentWorkflow or agentTask",
|
|
83
|
+
path: ["implementation"]
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// src/parser.ts
|
|
89
|
+
import { readFile } from "fs/promises";
|
|
90
|
+
import { parse as parseYaml } from "yaml";
|
|
91
|
+
var ComponentParseError = class extends Error {
|
|
92
|
+
constructor(message, filePath, cause) {
|
|
93
|
+
super(filePath ? `${filePath}: ${message}` : message);
|
|
94
|
+
this.filePath = filePath;
|
|
95
|
+
this.cause = cause;
|
|
96
|
+
this.name = "ComponentParseError";
|
|
97
|
+
}
|
|
98
|
+
filePath;
|
|
99
|
+
cause;
|
|
100
|
+
};
|
|
101
|
+
function formatZodError(error, filePath) {
|
|
102
|
+
const prefix = filePath ? `${filePath}: ` : "";
|
|
103
|
+
const details = error.issues.map((issue) => {
|
|
104
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
105
|
+
return `${path}: ${issue.message}`;
|
|
106
|
+
}).join("; ");
|
|
107
|
+
return `${prefix}Validation failed: ${details}`;
|
|
108
|
+
}
|
|
109
|
+
function normalizeOperation(op) {
|
|
110
|
+
return {
|
|
111
|
+
description: op.description,
|
|
112
|
+
agentWorkflow: op.agentWorkflow ?? op.agent_workflow,
|
|
113
|
+
agentTask: op.agentTask ?? op.agent_task,
|
|
114
|
+
handler: op.handler,
|
|
115
|
+
options: op.options,
|
|
116
|
+
input: op.input,
|
|
117
|
+
output: op.output,
|
|
118
|
+
artifactSlots: op.artifactSlots ?? op.artifact_slots,
|
|
119
|
+
riskLevel: op.riskLevel ?? op.risk_level,
|
|
120
|
+
idempotent: op.idempotent,
|
|
121
|
+
requiresConfirmation: op.requiresConfirmation ?? op.requires_confirmation,
|
|
122
|
+
execution: op.execution,
|
|
123
|
+
memoryRef: op.memoryRef ?? op.memory_ref
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function normalizeComponent(raw) {
|
|
127
|
+
const operations = raw.operations;
|
|
128
|
+
const normalizedOps = {};
|
|
129
|
+
if (operations && typeof operations === "object" && !Array.isArray(operations)) {
|
|
130
|
+
for (const [name, op] of Object.entries(operations)) {
|
|
131
|
+
if (op && typeof op === "object" && !Array.isArray(op)) {
|
|
132
|
+
normalizedOps[name] = normalizeOperation(op);
|
|
133
|
+
} else {
|
|
134
|
+
normalizedOps[name] = op;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
schema: raw.schema,
|
|
140
|
+
info: raw.info,
|
|
141
|
+
implementation: raw.implementation,
|
|
142
|
+
projections: raw.projections,
|
|
143
|
+
operations: Object.keys(normalizedOps).length > 0 ? normalizedOps : raw.operations
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function validateComponent(raw, filePath) {
|
|
147
|
+
const normalized = raw && typeof raw === "object" && !Array.isArray(raw) ? normalizeComponent(raw) : raw;
|
|
148
|
+
const result = ComponentSchema.safeParse(normalized);
|
|
149
|
+
if (!result.success) {
|
|
150
|
+
throw new ComponentParseError(
|
|
151
|
+
formatZodError(result.error, filePath),
|
|
152
|
+
filePath,
|
|
153
|
+
result.error
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return result.data;
|
|
157
|
+
}
|
|
158
|
+
function parseComponentString(content, filePath) {
|
|
159
|
+
let raw;
|
|
160
|
+
try {
|
|
161
|
+
raw = parseYaml(content);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
throw new ComponentParseError(
|
|
164
|
+
`Invalid YAML: ${err.message}`,
|
|
165
|
+
filePath,
|
|
166
|
+
err
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
return validateComponent(raw, filePath);
|
|
170
|
+
}
|
|
171
|
+
async function parseComponentFile(filePath) {
|
|
172
|
+
let content;
|
|
173
|
+
try {
|
|
174
|
+
content = await readFile(filePath, "utf-8");
|
|
175
|
+
} catch (err) {
|
|
176
|
+
throw new ComponentParseError(
|
|
177
|
+
`Cannot read file: ${err.message}`,
|
|
178
|
+
filePath,
|
|
179
|
+
err
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
return parseComponentString(content, filePath);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/errors.ts
|
|
186
|
+
var ComponentCompileError = class extends Error {
|
|
187
|
+
constructor(message) {
|
|
188
|
+
super(message);
|
|
189
|
+
this.name = "ComponentCompileError";
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
var ComponentValidationError = class extends ComponentCompileError {
|
|
193
|
+
constructor(message) {
|
|
194
|
+
super(message);
|
|
195
|
+
this.name = "ComponentValidationError";
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/ref-resolver.ts
|
|
200
|
+
import { readFileSync } from "fs";
|
|
201
|
+
import { resolve, dirname, extname } from "path";
|
|
202
|
+
import { parse as parseYaml2 } from "yaml";
|
|
203
|
+
var RefResolutionError = class extends Error {
|
|
204
|
+
constructor(ref, message) {
|
|
205
|
+
super(`Cannot resolve $ref "${ref}": ${message}`);
|
|
206
|
+
this.ref = ref;
|
|
207
|
+
this.name = "RefResolutionError";
|
|
208
|
+
}
|
|
209
|
+
ref;
|
|
210
|
+
};
|
|
211
|
+
function loadFile(filePath, fileCache) {
|
|
212
|
+
const absPath = resolve(filePath);
|
|
213
|
+
const cached = fileCache.get(absPath);
|
|
214
|
+
if (cached !== void 0) {
|
|
215
|
+
return cached;
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const content = readFileSync(absPath, "utf-8");
|
|
219
|
+
const ext = extname(absPath).toLowerCase();
|
|
220
|
+
const parsed = ext === ".json" ? JSON.parse(content) : parseYaml2(content);
|
|
221
|
+
fileCache.set(absPath, parsed);
|
|
222
|
+
return parsed;
|
|
223
|
+
} catch (err) {
|
|
224
|
+
throw new RefResolutionError(
|
|
225
|
+
filePath,
|
|
226
|
+
`Cannot read external file: ${err.message}`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function resolveJsonPointer(root, pointer) {
|
|
231
|
+
const segments = pointer.split("/").filter((s) => s.length > 0).map((s) => s.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
232
|
+
let current = root;
|
|
233
|
+
for (const segment of segments) {
|
|
234
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
235
|
+
throw new RefResolutionError(
|
|
236
|
+
`#${pointer}`,
|
|
237
|
+
`Cannot traverse into non-object at "${segment}"`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
const obj = current;
|
|
241
|
+
if (!(segment in obj)) {
|
|
242
|
+
throw new RefResolutionError(`#${pointer}`, `Key "${segment}" not found`);
|
|
243
|
+
}
|
|
244
|
+
current = obj[segment];
|
|
245
|
+
}
|
|
246
|
+
return current;
|
|
247
|
+
}
|
|
248
|
+
function resolveExternalRef(ref, basePath, fileCache) {
|
|
249
|
+
const hashIndex = ref.indexOf("#/");
|
|
250
|
+
const filePart = hashIndex >= 0 ? ref.slice(0, hashIndex) : ref;
|
|
251
|
+
const pointer = hashIndex >= 0 ? ref.slice(hashIndex + 1) : null;
|
|
252
|
+
const absFilePath = resolve(basePath, filePart);
|
|
253
|
+
const externalDoc = loadFile(absFilePath, fileCache);
|
|
254
|
+
if (pointer) {
|
|
255
|
+
return resolveJsonPointer(externalDoc, pointer);
|
|
256
|
+
}
|
|
257
|
+
return externalDoc;
|
|
258
|
+
}
|
|
259
|
+
function resolveRefs(value, options = {}) {
|
|
260
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
261
|
+
const fileCache = /* @__PURE__ */ new Map();
|
|
262
|
+
const basePath = options.basePath ?? process.cwd();
|
|
263
|
+
return deepResolve(value, value, resolved, basePath, fileCache);
|
|
264
|
+
}
|
|
265
|
+
function deepResolve(value, root, resolved, basePath, fileCache) {
|
|
266
|
+
if (value === null || value === void 0) {
|
|
267
|
+
return value;
|
|
268
|
+
}
|
|
269
|
+
if (typeof value !== "object") {
|
|
270
|
+
return value;
|
|
271
|
+
}
|
|
272
|
+
if (Array.isArray(value)) {
|
|
273
|
+
return value.map(
|
|
274
|
+
(item) => deepResolve(item, root, resolved, basePath, fileCache)
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
const obj = value;
|
|
278
|
+
if ("$ref" in obj && typeof obj["$ref"] === "string") {
|
|
279
|
+
const ref = obj["$ref"];
|
|
280
|
+
if (ref.startsWith("#/")) {
|
|
281
|
+
if (resolved.has(ref)) {
|
|
282
|
+
throw new RefResolutionError(ref, "Circular reference detected");
|
|
283
|
+
}
|
|
284
|
+
resolved.add(ref);
|
|
285
|
+
const pointer = ref.slice(1);
|
|
286
|
+
const target2 = resolveJsonPointer(root, pointer);
|
|
287
|
+
const result3 = deepResolve(target2, root, resolved, basePath, fileCache);
|
|
288
|
+
resolved.delete(ref);
|
|
289
|
+
return result3;
|
|
290
|
+
}
|
|
291
|
+
if (resolved.has(ref)) {
|
|
292
|
+
throw new RefResolutionError(ref, "Circular reference detected");
|
|
293
|
+
}
|
|
294
|
+
resolved.add(ref);
|
|
295
|
+
const target = resolveExternalRef(ref, basePath, fileCache);
|
|
296
|
+
const hashIndex = ref.indexOf("#/");
|
|
297
|
+
const externalBasePath = hashIndex >= 0 ? dirname(resolve(basePath, ref.slice(0, hashIndex))) : dirname(resolve(basePath, ref));
|
|
298
|
+
const externalResolved = /* @__PURE__ */ new Set();
|
|
299
|
+
const result2 = deepResolve(
|
|
300
|
+
target,
|
|
301
|
+
target,
|
|
302
|
+
externalResolved,
|
|
303
|
+
externalBasePath,
|
|
304
|
+
fileCache
|
|
305
|
+
);
|
|
306
|
+
resolved.delete(ref);
|
|
307
|
+
return result2;
|
|
308
|
+
}
|
|
309
|
+
const result = {};
|
|
310
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
311
|
+
result[key] = deepResolve(val, root, resolved, basePath, fileCache);
|
|
312
|
+
}
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/validation.ts
|
|
317
|
+
import { access, readFile as readFile2 } from "fs/promises";
|
|
318
|
+
import { resolve as resolve2 } from "path";
|
|
319
|
+
import { parse as parseYaml3 } from "yaml";
|
|
320
|
+
function extractIdsFromSection(section) {
|
|
321
|
+
if (!section || typeof section !== "object" || Array.isArray(section)) {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
return Object.keys(section).filter(
|
|
325
|
+
(key) => key.length > 0
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
function extractImplementationIds(content) {
|
|
329
|
+
if (!content || typeof content !== "object" || Array.isArray(content)) {
|
|
330
|
+
return { workflowIds: [], taskIds: [] };
|
|
331
|
+
}
|
|
332
|
+
const doc = content;
|
|
333
|
+
return {
|
|
334
|
+
workflowIds: extractIdsFromSection(doc.workflows),
|
|
335
|
+
taskIds: extractIdsFromSection(doc.tasks)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
async function fileExists(absolutePath) {
|
|
339
|
+
try {
|
|
340
|
+
await access(absolutePath);
|
|
341
|
+
return true;
|
|
342
|
+
} catch {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async function loadImplementationIds(implementationPath, basePath) {
|
|
347
|
+
const absolutePath = resolve2(basePath, implementationPath);
|
|
348
|
+
if (!await fileExists(absolutePath)) {
|
|
349
|
+
throw new ComponentValidationError(
|
|
350
|
+
`Implementation file not found: ${absolutePath}`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
let content;
|
|
354
|
+
try {
|
|
355
|
+
content = await readFile2(absolutePath, "utf-8");
|
|
356
|
+
} catch (err) {
|
|
357
|
+
throw new ComponentValidationError(
|
|
358
|
+
`Cannot read implementation file ${absolutePath}: ${err.message}`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
let parsed;
|
|
362
|
+
try {
|
|
363
|
+
parsed = parseYaml3(content);
|
|
364
|
+
} catch (err) {
|
|
365
|
+
throw new ComponentValidationError(
|
|
366
|
+
`Invalid YAML in implementation file ${absolutePath}: ${err.message}`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
return extractImplementationIds(parsed);
|
|
370
|
+
}
|
|
371
|
+
function formatAvailableIds(label, ids) {
|
|
372
|
+
if (ids.length === 0) {
|
|
373
|
+
return `${label}: (none)`;
|
|
374
|
+
}
|
|
375
|
+
return `${label}: ${ids.join(", ")}`;
|
|
376
|
+
}
|
|
377
|
+
async function validateComponent2(ir, options) {
|
|
378
|
+
const usesAgentic = Object.values(ir.operations).some(
|
|
379
|
+
(op) => op.agentWorkflow !== void 0 || op.agentTask !== void 0
|
|
380
|
+
);
|
|
381
|
+
if (!ir.implementation) {
|
|
382
|
+
if (usesAgentic) {
|
|
383
|
+
throw new ComponentValidationError(
|
|
384
|
+
"implementation is required when any operation uses agentWorkflow or agentTask"
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const ids = await loadImplementationIds(ir.implementation, options.basePath);
|
|
390
|
+
for (const [operationName, op] of Object.entries(ir.operations)) {
|
|
391
|
+
if (op.agentWorkflow !== void 0) {
|
|
392
|
+
if (!ids.workflowIds.includes(op.agentWorkflow)) {
|
|
393
|
+
throw new ComponentValidationError(
|
|
394
|
+
`Operation "${operationName}": agentWorkflow "${op.agentWorkflow}" not found in implementation DSL. ${formatAvailableIds("Available workflows", ids.workflowIds)}`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (op.agentTask !== void 0) {
|
|
399
|
+
if (!ids.taskIds.includes(op.agentTask)) {
|
|
400
|
+
throw new ComponentValidationError(
|
|
401
|
+
`Operation "${operationName}": agentTask "${op.agentTask}" not found in implementation DSL. ${formatAvailableIds("Available tasks", ids.taskIds)}`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/compiler.ts
|
|
409
|
+
import { dirname as dirname2 } from "path";
|
|
410
|
+
function assertSupportedSchemaVersion(schema) {
|
|
411
|
+
if (!SUPPORTED_SCHEMA_VERSIONS.includes(schema)) {
|
|
412
|
+
throw new ComponentCompileError(
|
|
413
|
+
`Unsupported schema version "${schema}". Supported: ${SUPPORTED_SCHEMA_VERSIONS.join(", ")}`
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function toJSONSchemaRef(ref, basePath) {
|
|
418
|
+
let value = ref;
|
|
419
|
+
if (basePath) {
|
|
420
|
+
value = resolveRefs(ref, { basePath });
|
|
421
|
+
}
|
|
422
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
423
|
+
const obj = value;
|
|
424
|
+
if ("schema" in obj && obj.schema && typeof obj.schema === "object") {
|
|
425
|
+
return { schema: obj.schema };
|
|
426
|
+
}
|
|
427
|
+
if ("$ref" in obj && typeof obj["$ref"] === "string") {
|
|
428
|
+
return { $ref: obj["$ref"] };
|
|
429
|
+
}
|
|
430
|
+
return obj;
|
|
431
|
+
}
|
|
432
|
+
return ref;
|
|
433
|
+
}
|
|
434
|
+
function normalizeArtifactSlots(slots) {
|
|
435
|
+
if (!slots) {
|
|
436
|
+
return {};
|
|
437
|
+
}
|
|
438
|
+
const result = {};
|
|
439
|
+
for (const [slotName, value] of Object.entries(slots)) {
|
|
440
|
+
if (typeof value === "string") {
|
|
441
|
+
result[slotName] = { artifact: value, direction: "readwrite" };
|
|
442
|
+
} else {
|
|
443
|
+
result[slotName] = {
|
|
444
|
+
artifact: value.artifact,
|
|
445
|
+
direction: value.direction
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return result;
|
|
450
|
+
}
|
|
451
|
+
function asSchemaObject(ref) {
|
|
452
|
+
if (!ref) {
|
|
453
|
+
return void 0;
|
|
454
|
+
}
|
|
455
|
+
if ("schema" in ref && ref.schema && typeof ref.schema === "object") {
|
|
456
|
+
return ref.schema;
|
|
457
|
+
}
|
|
458
|
+
if (!("$ref" in ref)) {
|
|
459
|
+
return ref;
|
|
460
|
+
}
|
|
461
|
+
return void 0;
|
|
462
|
+
}
|
|
463
|
+
function extractFailureClasses(failedRef) {
|
|
464
|
+
const schema = asSchemaObject(failedRef);
|
|
465
|
+
if (!schema) {
|
|
466
|
+
return [];
|
|
467
|
+
}
|
|
468
|
+
const failureClasses = schema["failure_classes"];
|
|
469
|
+
if (Array.isArray(failureClasses)) {
|
|
470
|
+
return failureClasses.filter((v) => typeof v === "string");
|
|
471
|
+
}
|
|
472
|
+
const properties = schema["properties"];
|
|
473
|
+
if (properties && typeof properties === "object") {
|
|
474
|
+
const failureClass = properties["failure_class"];
|
|
475
|
+
if (failureClass && typeof failureClass === "object") {
|
|
476
|
+
const enumValues = failureClass["enum"];
|
|
477
|
+
if (Array.isArray(enumValues)) {
|
|
478
|
+
return enumValues.filter((v) => typeof v === "string");
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const oneOf = schema["oneOf"];
|
|
483
|
+
if (Array.isArray(oneOf)) {
|
|
484
|
+
const classes = [];
|
|
485
|
+
for (const variant of oneOf) {
|
|
486
|
+
if (!variant || typeof variant !== "object") {
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
const obj = variant;
|
|
490
|
+
if (typeof obj["const"] === "string") {
|
|
491
|
+
classes.push(obj["const"]);
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
const props = obj["properties"];
|
|
495
|
+
if (props && typeof props === "object") {
|
|
496
|
+
const fc = props["failure_class"];
|
|
497
|
+
if (fc && typeof fc === "object") {
|
|
498
|
+
const c = fc["const"];
|
|
499
|
+
if (typeof c === "string") {
|
|
500
|
+
classes.push(c);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (classes.length > 0) {
|
|
506
|
+
return classes;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return [];
|
|
510
|
+
}
|
|
511
|
+
function compileOption(opt) {
|
|
512
|
+
return {
|
|
513
|
+
name: opt.name,
|
|
514
|
+
schema: opt.schema,
|
|
515
|
+
required: opt.required ?? false,
|
|
516
|
+
description: opt.description
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function compileOperation(op, basePath) {
|
|
520
|
+
const output = {
|
|
521
|
+
success: toJSONSchemaRef(op.output.success, basePath),
|
|
522
|
+
failed: op.output.failed ? toJSONSchemaRef(op.output.failed, basePath) : void 0
|
|
523
|
+
};
|
|
524
|
+
return {
|
|
525
|
+
description: op.description,
|
|
526
|
+
agentWorkflow: op.agentWorkflow,
|
|
527
|
+
agentTask: op.agentTask,
|
|
528
|
+
handler: op.handler,
|
|
529
|
+
options: (op.options ?? []).map(compileOption),
|
|
530
|
+
input: op.input ? toJSONSchemaRef(op.input, basePath) : void 0,
|
|
531
|
+
output,
|
|
532
|
+
errorModel: {
|
|
533
|
+
failureClasses: extractFailureClasses(output.failed)
|
|
534
|
+
},
|
|
535
|
+
artifactSlots: normalizeArtifactSlots(op.artifactSlots),
|
|
536
|
+
riskLevel: op.riskLevel ?? "medium",
|
|
537
|
+
idempotent: op.idempotent ?? false,
|
|
538
|
+
requiresConfirmation: op.requiresConfirmation ?? false,
|
|
539
|
+
execution: {
|
|
540
|
+
timeoutMs: op.execution?.timeoutMs,
|
|
541
|
+
costCeilingUsd: op.execution?.costCeilingUsd
|
|
542
|
+
},
|
|
543
|
+
memoryRef: op.memoryRef
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function compileComponent(dsl, options = {}) {
|
|
547
|
+
assertSupportedSchemaVersion(dsl.schema);
|
|
548
|
+
const operations = {};
|
|
549
|
+
for (const [name, op] of Object.entries(dsl.operations)) {
|
|
550
|
+
operations[name] = compileOperation(op, options.basePath);
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
schema: dsl.schema,
|
|
554
|
+
info: {
|
|
555
|
+
id: dsl.info.id,
|
|
556
|
+
version: dsl.info.version,
|
|
557
|
+
description: dsl.info.description
|
|
558
|
+
},
|
|
559
|
+
implementation: dsl.implementation,
|
|
560
|
+
projections: [...dsl.projections],
|
|
561
|
+
operations
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
async function maybeValidateComponent(ir, options) {
|
|
565
|
+
if (options.validate === false) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (!options.basePath) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
await validateComponent2(ir, { basePath: options.basePath });
|
|
572
|
+
}
|
|
573
|
+
async function compileComponentAsync(dsl, options = {}) {
|
|
574
|
+
const ir = compileComponent(dsl, options);
|
|
575
|
+
await maybeValidateComponent(ir, options);
|
|
576
|
+
return ir;
|
|
577
|
+
}
|
|
578
|
+
async function compileComponentFile(filePath, options = {}) {
|
|
579
|
+
const basePath = dirname2(filePath);
|
|
580
|
+
const dsl = await parseComponentFile(filePath);
|
|
581
|
+
const ir = compileComponent(dsl, { ...options, basePath });
|
|
582
|
+
await maybeValidateComponent(ir, { ...options, basePath });
|
|
583
|
+
return ir;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/generators/cli-generator.ts
|
|
587
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
588
|
+
function unwrapSchema(ref) {
|
|
589
|
+
if (!ref) {
|
|
590
|
+
return void 0;
|
|
591
|
+
}
|
|
592
|
+
if ("schema" in ref && ref.schema && typeof ref.schema === "object") {
|
|
593
|
+
return ref.schema;
|
|
594
|
+
}
|
|
595
|
+
if ("$ref" in ref) {
|
|
596
|
+
return { $ref: ref.$ref };
|
|
597
|
+
}
|
|
598
|
+
return ref;
|
|
599
|
+
}
|
|
600
|
+
function buildEffects(artifactSlots) {
|
|
601
|
+
const reads = [];
|
|
602
|
+
const writes = [];
|
|
603
|
+
for (const [slotName, slot] of Object.entries(artifactSlots)) {
|
|
604
|
+
if (slot.direction === "read" || slot.direction === "readwrite") {
|
|
605
|
+
reads.push(slotName);
|
|
606
|
+
}
|
|
607
|
+
if (slot.direction === "write" || slot.direction === "readwrite") {
|
|
608
|
+
writes.push(slotName);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (reads.length === 0 && writes.length === 0) {
|
|
612
|
+
return void 0;
|
|
613
|
+
}
|
|
614
|
+
const effects = {};
|
|
615
|
+
if (reads.length > 0) {
|
|
616
|
+
effects.reads = reads;
|
|
617
|
+
}
|
|
618
|
+
if (writes.length > 0) {
|
|
619
|
+
effects.writes = writes;
|
|
620
|
+
}
|
|
621
|
+
return effects;
|
|
622
|
+
}
|
|
623
|
+
function buildExits(op) {
|
|
624
|
+
const exits = {
|
|
625
|
+
"0": {
|
|
626
|
+
description: "Success"
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
const successSchema = unwrapSchema(op.output.success);
|
|
630
|
+
if (successSchema) {
|
|
631
|
+
exits["0"].stdout = {
|
|
632
|
+
format: "json",
|
|
633
|
+
schema: successSchema
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
const failedSchema = unwrapSchema(op.output.failed);
|
|
637
|
+
if (failedSchema) {
|
|
638
|
+
const exitCode = op.errorModel.failureClasses.length > 0 ? "1" : "1";
|
|
639
|
+
exits[exitCode] = {
|
|
640
|
+
description: "Operation failed",
|
|
641
|
+
stderr: {
|
|
642
|
+
format: "json",
|
|
643
|
+
schema: failedSchema
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
for (let i = 0; i < op.errorModel.failureClasses.length; i++) {
|
|
648
|
+
const code = String(i + 1);
|
|
649
|
+
if (!exits[code]) {
|
|
650
|
+
exits[code] = {
|
|
651
|
+
description: `Failure: ${op.errorModel.failureClasses[i]}`
|
|
652
|
+
};
|
|
653
|
+
if (failedSchema) {
|
|
654
|
+
exits[code].stderr = {
|
|
655
|
+
format: "json",
|
|
656
|
+
schema: failedSchema
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return exits;
|
|
662
|
+
}
|
|
663
|
+
function buildCommand(operationName, op) {
|
|
664
|
+
const command = {
|
|
665
|
+
summary: op.description
|
|
666
|
+
};
|
|
667
|
+
if (Object.keys(op.artifactSlots).length > 0) {
|
|
668
|
+
const slots = {};
|
|
669
|
+
for (const [name, slot] of Object.entries(op.artifactSlots)) {
|
|
670
|
+
slots[name] = { direction: slot.direction };
|
|
671
|
+
}
|
|
672
|
+
command.artifact_slots = slots;
|
|
673
|
+
}
|
|
674
|
+
if (op.options.length > 0) {
|
|
675
|
+
command.options = op.options.map((opt) => {
|
|
676
|
+
const entry = {
|
|
677
|
+
name: opt.name,
|
|
678
|
+
schema: opt.schema
|
|
679
|
+
};
|
|
680
|
+
if (opt.description) {
|
|
681
|
+
entry.description = opt.description;
|
|
682
|
+
}
|
|
683
|
+
if (opt.required) {
|
|
684
|
+
entry.required = true;
|
|
685
|
+
}
|
|
686
|
+
return entry;
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
const inputSchema = unwrapSchema(op.input);
|
|
690
|
+
if (inputSchema) {
|
|
691
|
+
command.streams = {
|
|
692
|
+
stdin: { schema: inputSchema }
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
command.exits = buildExits(op);
|
|
696
|
+
const effects = buildEffects(op.artifactSlots);
|
|
697
|
+
if (effects) {
|
|
698
|
+
command.effects = effects;
|
|
699
|
+
}
|
|
700
|
+
const xAgent = {
|
|
701
|
+
risk_level: op.riskLevel,
|
|
702
|
+
requires_confirmation: op.requiresConfirmation,
|
|
703
|
+
idempotent: op.idempotent
|
|
704
|
+
};
|
|
705
|
+
if (op.agentWorkflow) {
|
|
706
|
+
xAgent.dsl_workflow = op.agentWorkflow;
|
|
707
|
+
}
|
|
708
|
+
if (op.agentTask) {
|
|
709
|
+
xAgent.dsl_task = op.agentTask;
|
|
710
|
+
}
|
|
711
|
+
if (op.execution.timeoutMs !== void 0) {
|
|
712
|
+
xAgent.expected_duration_ms = op.execution.timeoutMs;
|
|
713
|
+
}
|
|
714
|
+
command["x-agent"] = xAgent;
|
|
715
|
+
return command;
|
|
716
|
+
}
|
|
717
|
+
function buildCliContract(ir) {
|
|
718
|
+
const commands = {};
|
|
719
|
+
for (const [name, op] of Object.entries(ir.operations)) {
|
|
720
|
+
commands[name] = buildCommand(name, op);
|
|
721
|
+
}
|
|
722
|
+
return {
|
|
723
|
+
cli_contracts: "0.1.0",
|
|
724
|
+
info: {
|
|
725
|
+
title: ir.info.id,
|
|
726
|
+
version: ir.info.version,
|
|
727
|
+
description: ir.info.description ?? `${ir.info.id} component CLI`
|
|
728
|
+
},
|
|
729
|
+
command_sets: {
|
|
730
|
+
[ir.info.id]: {
|
|
731
|
+
summary: ir.info.description ?? ir.info.id,
|
|
732
|
+
global_options: [
|
|
733
|
+
{
|
|
734
|
+
name: "resume",
|
|
735
|
+
schema: { type: "string" },
|
|
736
|
+
description: "Resume from a previous memory_ref ID"
|
|
737
|
+
}
|
|
738
|
+
],
|
|
739
|
+
commands
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
var CLI_STUB_CONTENT = `extends: ./generated/cli-contract.generated.yaml
|
|
745
|
+
`;
|
|
746
|
+
var cliGenerator = {
|
|
747
|
+
generate(ir, _options) {
|
|
748
|
+
const contract = buildCliContract(ir);
|
|
749
|
+
const yamlContent = `# Auto-generated by @aaac/contracts. Do not edit.
|
|
750
|
+
${stringifyYaml(contract)}`;
|
|
751
|
+
const files = [
|
|
752
|
+
{
|
|
753
|
+
path: "generated/cli-contract.generated.yaml",
|
|
754
|
+
content: yamlContent,
|
|
755
|
+
overwrite: true
|
|
756
|
+
}
|
|
757
|
+
];
|
|
758
|
+
if (ir.projections.includes("cli")) {
|
|
759
|
+
files.push({
|
|
760
|
+
path: "cli-contract.yaml",
|
|
761
|
+
content: CLI_STUB_CONTENT,
|
|
762
|
+
overwrite: false
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
return files;
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
// src/generators/schema-to-ts.ts
|
|
770
|
+
function unwrapSchema2(ref) {
|
|
771
|
+
if (!ref) {
|
|
772
|
+
return void 0;
|
|
773
|
+
}
|
|
774
|
+
if ("schema" in ref && ref.schema && typeof ref.schema === "object") {
|
|
775
|
+
return ref.schema;
|
|
776
|
+
}
|
|
777
|
+
if ("$ref" in ref) {
|
|
778
|
+
return void 0;
|
|
779
|
+
}
|
|
780
|
+
return ref;
|
|
781
|
+
}
|
|
782
|
+
function isValidIdentifier(name) {
|
|
783
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
784
|
+
}
|
|
785
|
+
function quoteKey(name) {
|
|
786
|
+
return isValidIdentifier(name) ? name : JSON.stringify(name);
|
|
787
|
+
}
|
|
788
|
+
function toPascalCase(name) {
|
|
789
|
+
return name.split(/[-_]/).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
790
|
+
}
|
|
791
|
+
function jsonSchemaToTs(schema, indent, generated) {
|
|
792
|
+
if (!schema) {
|
|
793
|
+
return "unknown";
|
|
794
|
+
}
|
|
795
|
+
const enumValues = schema["enum"];
|
|
796
|
+
if (Array.isArray(enumValues) && enumValues.length > 0) {
|
|
797
|
+
return enumValues.map((v) => typeof v === "string" ? JSON.stringify(v) : String(v)).join(" | ");
|
|
798
|
+
}
|
|
799
|
+
const type = schema["type"];
|
|
800
|
+
if (type === "string") {
|
|
801
|
+
return "string";
|
|
802
|
+
}
|
|
803
|
+
if (type === "number" || type === "integer") {
|
|
804
|
+
return "number";
|
|
805
|
+
}
|
|
806
|
+
if (type === "boolean") {
|
|
807
|
+
return "boolean";
|
|
808
|
+
}
|
|
809
|
+
if (type === "array") {
|
|
810
|
+
const items = schema["items"];
|
|
811
|
+
if (items && typeof items === "object" && !Array.isArray(items)) {
|
|
812
|
+
return `${jsonSchemaToTs(items, indent, generated)}[]`;
|
|
813
|
+
}
|
|
814
|
+
return "unknown[]";
|
|
815
|
+
}
|
|
816
|
+
if (type === "object" || schema["properties"]) {
|
|
817
|
+
const properties = schema["properties"];
|
|
818
|
+
if (!properties || typeof properties !== "object") {
|
|
819
|
+
return "Record<string, unknown>";
|
|
820
|
+
}
|
|
821
|
+
const required = Array.isArray(schema["required"]) ? schema["required"] : [];
|
|
822
|
+
const lines = [];
|
|
823
|
+
for (const [propName, propSchema] of Object.entries(
|
|
824
|
+
properties
|
|
825
|
+
)) {
|
|
826
|
+
if (!propSchema || typeof propSchema !== "object") {
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
const optional = required.includes(propName) ? "" : "?";
|
|
830
|
+
const tsType = jsonSchemaToTs(
|
|
831
|
+
propSchema,
|
|
832
|
+
indent + " ",
|
|
833
|
+
generated
|
|
834
|
+
);
|
|
835
|
+
lines.push(`${indent} ${quoteKey(propName)}${optional}: ${tsType};`);
|
|
836
|
+
}
|
|
837
|
+
if (lines.length === 0) {
|
|
838
|
+
return "Record<string, unknown>";
|
|
839
|
+
}
|
|
840
|
+
return `{
|
|
841
|
+
${lines.join("\n")}
|
|
842
|
+
${indent}}`;
|
|
843
|
+
}
|
|
844
|
+
return "unknown";
|
|
845
|
+
}
|
|
846
|
+
function schemaRefToTsType(ref, fallback = "unknown") {
|
|
847
|
+
return jsonSchemaToTs(unwrapSchema2(ref), "", /* @__PURE__ */ new Set()) || fallback;
|
|
848
|
+
}
|
|
849
|
+
function operationTypeNames(operationName) {
|
|
850
|
+
const base = toPascalCase(operationName);
|
|
851
|
+
return {
|
|
852
|
+
input: `${base}Input`,
|
|
853
|
+
output: `${base}Output`,
|
|
854
|
+
options: `${base}Options`,
|
|
855
|
+
failure: `${base}Failure`,
|
|
856
|
+
failureClass: `${base}FailureClass`
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function interfaceFromSchema(name, ref, fallbackBody) {
|
|
860
|
+
const schema = unwrapSchema2(ref);
|
|
861
|
+
if (!schema) {
|
|
862
|
+
return void 0;
|
|
863
|
+
}
|
|
864
|
+
const body = jsonSchemaToTs(schema, "", /* @__PURE__ */ new Set());
|
|
865
|
+
if (body === "unknown" && !schema["properties"]) {
|
|
866
|
+
return `export type ${name} = ${fallbackBody};
|
|
867
|
+
`;
|
|
868
|
+
}
|
|
869
|
+
if (body.startsWith("{")) {
|
|
870
|
+
return `export interface ${name} ${body}
|
|
871
|
+
`;
|
|
872
|
+
}
|
|
873
|
+
return `export type ${name} = ${body};
|
|
874
|
+
`;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// src/generators/client-generator.ts
|
|
878
|
+
var clientGenerator = {
|
|
879
|
+
generate(ir, _options) {
|
|
880
|
+
const functions = Object.keys(ir.operations).map((operationName) => {
|
|
881
|
+
const names = operationTypeNames(operationName);
|
|
882
|
+
return `export async function ${operationName}(
|
|
883
|
+
options: ${names.options},
|
|
884
|
+
input: ${names.input},
|
|
885
|
+
ctx: AaacInvocationContext,
|
|
886
|
+
memoryRef?: string,
|
|
887
|
+
): Promise<${names.output}> {
|
|
888
|
+
return runtime.execute(${JSON.stringify(operationName)}, options, input, ctx, memoryRef);
|
|
889
|
+
}`;
|
|
890
|
+
});
|
|
891
|
+
const content = `/** Auto-generated by @aaac/contracts. Do not edit. */
|
|
892
|
+
/* eslint-disable */
|
|
893
|
+
|
|
894
|
+
import type {
|
|
895
|
+
${Object.keys(ir.operations).flatMap((name) => {
|
|
896
|
+
const n = operationTypeNames(name);
|
|
897
|
+
const types = [n.input, n.output, n.options];
|
|
898
|
+
return types.map((t) => ` ${t},`);
|
|
899
|
+
}).join("\n")}
|
|
900
|
+
} from "./component.types.js";
|
|
901
|
+
import { runtime, type AaacInvocationContext } from "./component.runtime.js";
|
|
902
|
+
|
|
903
|
+
${functions.join("\n\n")}
|
|
904
|
+
`;
|
|
905
|
+
return [
|
|
906
|
+
{
|
|
907
|
+
path: "generated/component.client.ts",
|
|
908
|
+
content,
|
|
909
|
+
overwrite: true
|
|
910
|
+
}
|
|
911
|
+
];
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
// src/generators/handler-generator.ts
|
|
916
|
+
import { existsSync } from "fs";
|
|
917
|
+
import { join, relative, dirname as dirname3 } from "path";
|
|
918
|
+
function typesImportPath(handlerPath) {
|
|
919
|
+
const handlerDir = dirname3(handlerPath);
|
|
920
|
+
const rel = relative(handlerDir, "generated/component.types");
|
|
921
|
+
const importPath = rel.startsWith(".") ? rel : `./${rel}`;
|
|
922
|
+
return `${importPath}.js`;
|
|
923
|
+
}
|
|
924
|
+
function buildHandlerSkeleton(operationName, handlerPath) {
|
|
925
|
+
const names = operationTypeNames(operationName);
|
|
926
|
+
const typesImport = typesImportPath(handlerPath);
|
|
927
|
+
return `/** Handler skeleton \u2014 implement the logic below. */
|
|
928
|
+
/* eslint-disable */
|
|
929
|
+
|
|
930
|
+
import type { ${names.input}, ${names.output} } from "${typesImport}";
|
|
931
|
+
|
|
932
|
+
export async function ${operationName}(
|
|
933
|
+
input: ${names.input},
|
|
934
|
+
): Promise<${names.output}> {
|
|
935
|
+
// TODO: implement
|
|
936
|
+
throw new Error("Not implemented: ${operationName}");
|
|
937
|
+
}
|
|
938
|
+
`;
|
|
939
|
+
}
|
|
940
|
+
var handlerGenerator = {
|
|
941
|
+
generate(ir, options) {
|
|
942
|
+
const files = [];
|
|
943
|
+
for (const [operationName, op] of Object.entries(ir.operations)) {
|
|
944
|
+
if (!op.handler) {
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
947
|
+
const absPath = join(options.outputDir, op.handler);
|
|
948
|
+
if (existsSync(absPath)) {
|
|
949
|
+
files.push({
|
|
950
|
+
path: op.handler,
|
|
951
|
+
content: "",
|
|
952
|
+
overwrite: false,
|
|
953
|
+
warning: `Handler file already exists, skipping: ${op.handler}`
|
|
954
|
+
});
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
files.push({
|
|
958
|
+
path: op.handler,
|
|
959
|
+
content: buildHandlerSkeleton(operationName, op.handler),
|
|
960
|
+
overwrite: false
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
return files;
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
// src/generators/runtime-generator.ts
|
|
968
|
+
import { relative as relative2, dirname as dirname4, join as join2 } from "path";
|
|
969
|
+
function handlerImportPath(handlerPath, outputDir) {
|
|
970
|
+
const fromGenerated = join2(outputDir, "generated", "component.runtime.ts");
|
|
971
|
+
const absHandler = join2(outputDir, handlerPath);
|
|
972
|
+
let importPath = relative2(dirname4(fromGenerated), absHandler);
|
|
973
|
+
if (!importPath.startsWith(".")) {
|
|
974
|
+
importPath = `./${importPath}`;
|
|
975
|
+
}
|
|
976
|
+
return importPath.replace(/\.ts$/, ".js");
|
|
977
|
+
}
|
|
978
|
+
function dispatchBody(operationName, op, outputDir) {
|
|
979
|
+
const names = operationTypeNames(operationName);
|
|
980
|
+
const imports = [];
|
|
981
|
+
if (op.agentWorkflow) {
|
|
982
|
+
return {
|
|
983
|
+
imports,
|
|
984
|
+
caseBody: ` ctx.policy?.assertAllowed(${JSON.stringify(operationName)});
|
|
985
|
+
return ctx.runtime.runWorkflow(${JSON.stringify(op.agentWorkflow)}, {
|
|
986
|
+
options,
|
|
987
|
+
input,
|
|
988
|
+
observer: ctx.observer,
|
|
989
|
+
abortSignal: ctx.abortSignal,
|
|
990
|
+
});`
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
if (op.agentTask) {
|
|
994
|
+
return {
|
|
995
|
+
imports,
|
|
996
|
+
caseBody: ` ctx.policy?.assertAllowed(${JSON.stringify(operationName)});
|
|
997
|
+
return ctx.runtime.runTask(${JSON.stringify(op.agentTask)}, {
|
|
998
|
+
options,
|
|
999
|
+
input,
|
|
1000
|
+
observer: ctx.observer,
|
|
1001
|
+
abortSignal: ctx.abortSignal,
|
|
1002
|
+
});`
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
if (op.handler) {
|
|
1006
|
+
const importPath = handlerImportPath(op.handler, outputDir);
|
|
1007
|
+
imports.push(
|
|
1008
|
+
`import { ${operationName} } from ${JSON.stringify(importPath)};`
|
|
1009
|
+
);
|
|
1010
|
+
return {
|
|
1011
|
+
imports,
|
|
1012
|
+
caseBody: ` ctx.policy?.assertAllowed(${JSON.stringify(operationName)});
|
|
1013
|
+
return ${operationName}(input);`
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
return {
|
|
1017
|
+
imports,
|
|
1018
|
+
caseBody: ` throw new Error("No dispatch target for operation ${operationName}");`
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
var runtimeGenerator = {
|
|
1022
|
+
generate(ir, options) {
|
|
1023
|
+
const componentConst = `${toPascalCase(ir.info.id)}Component`;
|
|
1024
|
+
const allImports = [];
|
|
1025
|
+
const cases = [];
|
|
1026
|
+
for (const [operationName, op] of Object.entries(ir.operations)) {
|
|
1027
|
+
const names = operationTypeNames(operationName);
|
|
1028
|
+
const { imports, caseBody } = dispatchBody(
|
|
1029
|
+
operationName,
|
|
1030
|
+
op,
|
|
1031
|
+
options.outputDir
|
|
1032
|
+
);
|
|
1033
|
+
allImports.push(...imports);
|
|
1034
|
+
cases.push(` case ${JSON.stringify(operationName)}:
|
|
1035
|
+
${caseBody}
|
|
1036
|
+
`);
|
|
1037
|
+
}
|
|
1038
|
+
const uniqueImports = [...new Set(allImports)];
|
|
1039
|
+
const content = `/** Auto-generated by @aaac/contracts. Do not edit. */
|
|
1040
|
+
/* eslint-disable */
|
|
1041
|
+
|
|
1042
|
+
import { createRuntimeBinding } from "@aaac/runtime";
|
|
1043
|
+
import type {
|
|
1044
|
+
${Object.keys(ir.operations).flatMap((name) => {
|
|
1045
|
+
const n = operationTypeNames(name);
|
|
1046
|
+
return [` ${n.input},`, ` ${n.output},`, ` ${n.options},`];
|
|
1047
|
+
}).join("\n")}
|
|
1048
|
+
} from "./component.types.js";
|
|
1049
|
+
${uniqueImports.length > 0 ? `${uniqueImports.join("\n")}
|
|
1050
|
+
` : ""}
|
|
1051
|
+
export interface AaacInvocationContext {
|
|
1052
|
+
runtime: {
|
|
1053
|
+
runWorkflow(workflowId: string, args: unknown): Promise<unknown>;
|
|
1054
|
+
runTask(taskId: string, args: unknown): Promise<unknown>;
|
|
1055
|
+
};
|
|
1056
|
+
policy?: {
|
|
1057
|
+
assertAllowed(operationName: string): void;
|
|
1058
|
+
};
|
|
1059
|
+
observer?: unknown;
|
|
1060
|
+
abortSignal?: AbortSignal;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const ${componentConst} = ${JSON.stringify(
|
|
1064
|
+
{ id: ir.info.id, version: ir.info.version },
|
|
1065
|
+
null,
|
|
1066
|
+
2
|
|
1067
|
+
)} as const;
|
|
1068
|
+
|
|
1069
|
+
function applyMemoryRef(
|
|
1070
|
+
ctx: AaacInvocationContext,
|
|
1071
|
+
memoryRef?: string,
|
|
1072
|
+
): AaacInvocationContext {
|
|
1073
|
+
if (!memoryRef) {
|
|
1074
|
+
return ctx;
|
|
1075
|
+
}
|
|
1076
|
+
return {
|
|
1077
|
+
...ctx,
|
|
1078
|
+
memoryRef: {
|
|
1079
|
+
id: memoryRef,
|
|
1080
|
+
provider: "aaac",
|
|
1081
|
+
compat: "v1",
|
|
1082
|
+
created_at: new Date().toISOString(),
|
|
1083
|
+
},
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
async function dispatchExecute(
|
|
1088
|
+
operationName: string,
|
|
1089
|
+
options: Record<string, unknown>,
|
|
1090
|
+
input: unknown,
|
|
1091
|
+
ctx: AaacInvocationContext,
|
|
1092
|
+
): Promise<unknown> {
|
|
1093
|
+
switch (operationName) {
|
|
1094
|
+
${cases.join("")}
|
|
1095
|
+
default:
|
|
1096
|
+
throw new Error(\`Unknown operation: \${operationName}\`);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
const _binding = createRuntimeBinding(${componentConst}, {
|
|
1101
|
+
execute: dispatchExecute,
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
export const runtime = {
|
|
1105
|
+
getOperationNames(): string[] {
|
|
1106
|
+
return _binding.getOperationNames();
|
|
1107
|
+
},
|
|
1108
|
+
getOperationIR(name: string) {
|
|
1109
|
+
return _binding.getOperationIR(name);
|
|
1110
|
+
},
|
|
1111
|
+
execute(
|
|
1112
|
+
operationName: string,
|
|
1113
|
+
options: Record<string, unknown>,
|
|
1114
|
+
input: unknown,
|
|
1115
|
+
ctx: AaacInvocationContext,
|
|
1116
|
+
memoryRef?: string,
|
|
1117
|
+
): Promise<unknown> {
|
|
1118
|
+
return _binding.execute(
|
|
1119
|
+
operationName,
|
|
1120
|
+
options,
|
|
1121
|
+
input,
|
|
1122
|
+
applyMemoryRef(ctx, memoryRef),
|
|
1123
|
+
);
|
|
1124
|
+
},
|
|
1125
|
+
};
|
|
1126
|
+
`;
|
|
1127
|
+
return [
|
|
1128
|
+
{
|
|
1129
|
+
path: "generated/component.runtime.ts",
|
|
1130
|
+
content,
|
|
1131
|
+
overwrite: true
|
|
1132
|
+
}
|
|
1133
|
+
];
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
// src/generators/types-generator.ts
|
|
1138
|
+
function generateOperationTypes(operationName, op) {
|
|
1139
|
+
const names = operationTypeNames(operationName);
|
|
1140
|
+
const parts = [];
|
|
1141
|
+
if (op.input) {
|
|
1142
|
+
const iface = interfaceFromSchema(
|
|
1143
|
+
names.input,
|
|
1144
|
+
op.input,
|
|
1145
|
+
"Record<string, unknown>"
|
|
1146
|
+
);
|
|
1147
|
+
parts.push(iface ?? `export type ${names.input} = Record<string, unknown>;
|
|
1148
|
+
`);
|
|
1149
|
+
} else {
|
|
1150
|
+
parts.push(`export type ${names.input} = Record<string, unknown>;
|
|
1151
|
+
`);
|
|
1152
|
+
}
|
|
1153
|
+
const outputIface = interfaceFromSchema(
|
|
1154
|
+
names.output,
|
|
1155
|
+
op.output.success,
|
|
1156
|
+
"unknown"
|
|
1157
|
+
);
|
|
1158
|
+
parts.push(outputIface ?? `export type ${names.output} = unknown;
|
|
1159
|
+
`);
|
|
1160
|
+
if (op.options.length > 0) {
|
|
1161
|
+
const fields = op.options.map((opt) => {
|
|
1162
|
+
const fieldName = opt.name.includes("-") || opt.name.includes("_") ? JSON.stringify(opt.name) : opt.name;
|
|
1163
|
+
const optional = opt.required ? "" : "?";
|
|
1164
|
+
const tsType = schemaRefToTsType({ schema: opt.schema });
|
|
1165
|
+
const desc = opt.description ? ` /** ${opt.description} */
|
|
1166
|
+
` : "";
|
|
1167
|
+
return `${desc} ${fieldName}${optional}: ${tsType};`;
|
|
1168
|
+
});
|
|
1169
|
+
parts.push(`export interface ${names.options} {
|
|
1170
|
+
${fields.join("\n")}
|
|
1171
|
+
}
|
|
1172
|
+
`);
|
|
1173
|
+
} else {
|
|
1174
|
+
parts.push(`export type ${names.options} = Record<string, never>;
|
|
1175
|
+
`);
|
|
1176
|
+
}
|
|
1177
|
+
if (op.output.failed) {
|
|
1178
|
+
const failureIface = interfaceFromSchema(
|
|
1179
|
+
names.failure,
|
|
1180
|
+
op.output.failed,
|
|
1181
|
+
"unknown"
|
|
1182
|
+
);
|
|
1183
|
+
parts.push(failureIface ?? `export type ${names.failure} = unknown;
|
|
1184
|
+
`);
|
|
1185
|
+
}
|
|
1186
|
+
if (op.errorModel.failureClasses.length > 0) {
|
|
1187
|
+
const union = op.errorModel.failureClasses.map((c) => JSON.stringify(c)).join(" | ");
|
|
1188
|
+
parts.push(`export type ${names.failureClass} = ${union};
|
|
1189
|
+
`);
|
|
1190
|
+
}
|
|
1191
|
+
return parts.join("\n");
|
|
1192
|
+
}
|
|
1193
|
+
var typesGenerator = {
|
|
1194
|
+
generate(ir, _options) {
|
|
1195
|
+
const operationBlocks = Object.entries(ir.operations).map(
|
|
1196
|
+
([name, op]) => generateOperationTypes(name, op)
|
|
1197
|
+
);
|
|
1198
|
+
const content = `/** Auto-generated by @aaac/contracts. Do not edit. */
|
|
1199
|
+
/* eslint-disable */
|
|
1200
|
+
|
|
1201
|
+
${operationBlocks.join("\n")}
|
|
1202
|
+
`;
|
|
1203
|
+
return [
|
|
1204
|
+
{
|
|
1205
|
+
path: "generated/component.types.ts",
|
|
1206
|
+
content,
|
|
1207
|
+
overwrite: true
|
|
1208
|
+
}
|
|
1209
|
+
];
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
|
|
1213
|
+
// src/generators/index.ts
|
|
1214
|
+
import { mkdir, writeFile, access as access2 } from "fs/promises";
|
|
1215
|
+
import { dirname as dirname5, join as join3 } from "path";
|
|
1216
|
+
var ALL_GENERATORS = [
|
|
1217
|
+
typesGenerator,
|
|
1218
|
+
clientGenerator,
|
|
1219
|
+
runtimeGenerator,
|
|
1220
|
+
cliGenerator,
|
|
1221
|
+
handlerGenerator
|
|
1222
|
+
];
|
|
1223
|
+
function generateAll(ir, options) {
|
|
1224
|
+
return ALL_GENERATORS.flatMap((gen) => gen.generate(ir, options));
|
|
1225
|
+
}
|
|
1226
|
+
async function fileExists2(path) {
|
|
1227
|
+
try {
|
|
1228
|
+
await access2(path);
|
|
1229
|
+
return true;
|
|
1230
|
+
} catch {
|
|
1231
|
+
return false;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
async function writeGeneratedFiles(files, outputDir) {
|
|
1235
|
+
const written = [];
|
|
1236
|
+
const skipped = [];
|
|
1237
|
+
const warnings = [];
|
|
1238
|
+
for (const file of files) {
|
|
1239
|
+
const targetPath = join3(outputDir, file.path);
|
|
1240
|
+
if (file.warning) {
|
|
1241
|
+
warnings.push(file.warning);
|
|
1242
|
+
}
|
|
1243
|
+
if (!file.overwrite && await fileExists2(targetPath)) {
|
|
1244
|
+
skipped.push(file.path);
|
|
1245
|
+
continue;
|
|
1246
|
+
}
|
|
1247
|
+
if (!file.content && !file.overwrite) {
|
|
1248
|
+
skipped.push(file.path);
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1251
|
+
await mkdir(dirname5(targetPath), { recursive: true });
|
|
1252
|
+
await writeFile(targetPath, file.content, "utf-8");
|
|
1253
|
+
written.push(file.path);
|
|
1254
|
+
}
|
|
1255
|
+
return { written, skipped, warnings };
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
export {
|
|
1259
|
+
SUPPORTED_SCHEMA_VERSIONS,
|
|
1260
|
+
SCHEMA_VERSION_PATTERN,
|
|
1261
|
+
JSONSchemaRefSchema,
|
|
1262
|
+
OptionSchema,
|
|
1263
|
+
ArtifactSlotValueSchema,
|
|
1264
|
+
OperationSchema,
|
|
1265
|
+
ComponentSchema,
|
|
1266
|
+
ComponentParseError,
|
|
1267
|
+
parseComponentString,
|
|
1268
|
+
parseComponentFile,
|
|
1269
|
+
ComponentCompileError,
|
|
1270
|
+
ComponentValidationError,
|
|
1271
|
+
RefResolutionError,
|
|
1272
|
+
resolveRefs,
|
|
1273
|
+
extractImplementationIds,
|
|
1274
|
+
validateComponent2 as validateComponent,
|
|
1275
|
+
extractFailureClasses,
|
|
1276
|
+
compileComponent,
|
|
1277
|
+
compileComponentAsync,
|
|
1278
|
+
compileComponentFile,
|
|
1279
|
+
cliGenerator,
|
|
1280
|
+
clientGenerator,
|
|
1281
|
+
handlerGenerator,
|
|
1282
|
+
runtimeGenerator,
|
|
1283
|
+
typesGenerator,
|
|
1284
|
+
generateAll,
|
|
1285
|
+
writeGeneratedFiles
|
|
1286
|
+
};
|
|
1287
|
+
//# sourceMappingURL=chunk-B7OY3RPU.js.map
|