agent-manifest 3.2.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/LICENSE +21 -0
- package/README.md +204 -0
- package/dist/cli.js +174 -0
- package/dist/discovery/service.js +159 -0
- package/dist/generator/json.js +25 -0
- package/dist/index.js +36 -0
- package/dist/parser/auth-detector.js +131 -0
- package/dist/parser/capability-detector.js +115 -0
- package/dist/parser/contract-parser.js +347 -0
- package/dist/parser/express-parser.js +163 -0
- package/dist/parser/intent-classifier.js +157 -0
- package/dist/parser/ts-parser.js +419 -0
- package/dist/parser/zod-extractor.js +335 -0
- package/dist/types.js +2 -0
- package/package.json +61 -0
- package/schema/agent.schema.json +168 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZodExtractor = void 0;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
/**
|
|
6
|
+
* Extracts parameter schemas from Zod validators in a source file.
|
|
7
|
+
*
|
|
8
|
+
* Handles patterns like:
|
|
9
|
+
* const Schema = z.object({ field: z.string().min(1).default('x'), ... })
|
|
10
|
+
* Schema.safeParse(req.body) / Schema.parse(await request.json())
|
|
11
|
+
*
|
|
12
|
+
* Also resolves schemas imported from other files (e.g. src/lib/schemas.ts),
|
|
13
|
+
* so centralised schema files are picked up even when the route file only
|
|
14
|
+
* imports and uses the schema rather than defining it inline.
|
|
15
|
+
*/
|
|
16
|
+
class ZodExtractor {
|
|
17
|
+
extractSchemas(sourceFile) {
|
|
18
|
+
const schemas = new Map();
|
|
19
|
+
for (const varDecl of sourceFile.getVariableDeclarations()) {
|
|
20
|
+
const init = varDecl.getInitializer();
|
|
21
|
+
if (!init)
|
|
22
|
+
continue;
|
|
23
|
+
const shape = this.tryExtractObjectShape(init);
|
|
24
|
+
if (shape) {
|
|
25
|
+
schemas.set(varDecl.getName(), shape);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return schemas;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Walk .parse() / .safeParse() calls in the file. When the schema variable
|
|
32
|
+
* isn't defined locally, follow the import graph one level to find it in the
|
|
33
|
+
* source file that exported it.
|
|
34
|
+
*/
|
|
35
|
+
findUsedSchema(sourceFile, schemas) {
|
|
36
|
+
// Build a name→sourceFile map for every named import so we can resolve
|
|
37
|
+
// cross-file schemas on demand.
|
|
38
|
+
const importedFrom = this.buildImportMap(sourceFile);
|
|
39
|
+
let found = null;
|
|
40
|
+
sourceFile.forEachDescendant(node => {
|
|
41
|
+
if (found)
|
|
42
|
+
return;
|
|
43
|
+
if (!ts_morph_1.Node.isCallExpression(node))
|
|
44
|
+
return;
|
|
45
|
+
const expr = node.getExpression();
|
|
46
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(expr))
|
|
47
|
+
return;
|
|
48
|
+
const method = expr.getName();
|
|
49
|
+
if (method !== 'safeParse' && method !== 'parse')
|
|
50
|
+
return;
|
|
51
|
+
const schemaName = expr.getExpression().getText().trim();
|
|
52
|
+
// 1. Local schema defined in the same file
|
|
53
|
+
if (schemas.has(schemaName)) {
|
|
54
|
+
found = schemas.get(schemaName);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// 2. Schema imported from another file — resolve and extract
|
|
58
|
+
const importedFile = importedFrom.get(schemaName);
|
|
59
|
+
if (importedFile) {
|
|
60
|
+
const remoteSchemas = this.extractSchemas(importedFile);
|
|
61
|
+
if (remoteSchemas.has(schemaName)) {
|
|
62
|
+
found = remoteSchemas.get(schemaName);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return found;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build a map of { localName → SourceFile } for every named import in the
|
|
70
|
+
* file whose module specifier resolves within the project.
|
|
71
|
+
*/
|
|
72
|
+
buildImportMap(sourceFile) {
|
|
73
|
+
const map = new Map();
|
|
74
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
75
|
+
const resolvedFile = importDecl.getModuleSpecifierSourceFile();
|
|
76
|
+
if (!resolvedFile)
|
|
77
|
+
continue;
|
|
78
|
+
for (const named of importDecl.getNamedImports()) {
|
|
79
|
+
// alias: `import { BodySchema as Body }` → local name is "Body"
|
|
80
|
+
map.set(named.getAliasNode()?.getText() ?? named.getName(), resolvedFile);
|
|
81
|
+
}
|
|
82
|
+
// Default import: `import Schemas from './schemas'` — less common for Zod
|
|
83
|
+
const defaultImport = importDecl.getDefaultImport();
|
|
84
|
+
if (defaultImport)
|
|
85
|
+
map.set(defaultImport.getText(), resolvedFile);
|
|
86
|
+
}
|
|
87
|
+
return map;
|
|
88
|
+
}
|
|
89
|
+
tryExtractObjectShape(node) {
|
|
90
|
+
const base = this.getZodBase(node);
|
|
91
|
+
if (!base || base.method !== 'object')
|
|
92
|
+
return null;
|
|
93
|
+
const args = base.node.getArguments();
|
|
94
|
+
if (!args.length)
|
|
95
|
+
return null;
|
|
96
|
+
const shapeArg = args[0];
|
|
97
|
+
if (!ts_morph_1.Node.isObjectLiteralExpression(shapeArg))
|
|
98
|
+
return null;
|
|
99
|
+
const shape = {};
|
|
100
|
+
for (const prop of shapeArg.getProperties()) {
|
|
101
|
+
if (!ts_morph_1.Node.isPropertyAssignment(prop))
|
|
102
|
+
continue;
|
|
103
|
+
const init = prop.getInitializer();
|
|
104
|
+
if (init) {
|
|
105
|
+
shape[prop.getName()] = this.extractFieldType(init);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return shape;
|
|
109
|
+
}
|
|
110
|
+
extractFieldType(node) {
|
|
111
|
+
const isOptional = this.chainContains(node, 'optional') || this.chainContains(node, 'nullish');
|
|
112
|
+
const base = this.getZodBase(node);
|
|
113
|
+
if (!base)
|
|
114
|
+
return { type: 'any', required: !isOptional };
|
|
115
|
+
const result = this.mapZodMethod(base.method, base.node);
|
|
116
|
+
result.required = !isOptional;
|
|
117
|
+
// Walk the full chain to collect constraints
|
|
118
|
+
const constraints = this.extractConstraints(node, base.method);
|
|
119
|
+
Object.assign(result, constraints);
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Walk every method in the Zod chain to collect constraints:
|
|
124
|
+
* min, max, length, default, regex, email, url, uuid, datetime.
|
|
125
|
+
*/
|
|
126
|
+
extractConstraints(node, baseType) {
|
|
127
|
+
const constraints = {};
|
|
128
|
+
const isNumeric = baseType === 'number' || baseType === 'bigint';
|
|
129
|
+
this.walkChain(node, (method, callNode) => {
|
|
130
|
+
const args = callNode.getArguments();
|
|
131
|
+
switch (method) {
|
|
132
|
+
case 'min': {
|
|
133
|
+
const val = this.getNumericArg(args[0]);
|
|
134
|
+
if (val !== undefined) {
|
|
135
|
+
if (isNumeric)
|
|
136
|
+
constraints.minimum = val;
|
|
137
|
+
else
|
|
138
|
+
constraints.minLength = val;
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'max': {
|
|
143
|
+
const val = this.getNumericArg(args[0]);
|
|
144
|
+
if (val !== undefined) {
|
|
145
|
+
if (isNumeric)
|
|
146
|
+
constraints.maximum = val;
|
|
147
|
+
else
|
|
148
|
+
constraints.maxLength = val;
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case 'gte': {
|
|
153
|
+
const val = this.getNumericArg(args[0]);
|
|
154
|
+
if (val !== undefined && isNumeric)
|
|
155
|
+
constraints.minimum = val;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case 'lte': {
|
|
159
|
+
const val = this.getNumericArg(args[0]);
|
|
160
|
+
if (val !== undefined && isNumeric)
|
|
161
|
+
constraints.maximum = val;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'gt': {
|
|
165
|
+
const val = this.getNumericArg(args[0]);
|
|
166
|
+
if (val !== undefined && isNumeric)
|
|
167
|
+
constraints.minimum = val + Number.EPSILON;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case 'lt': {
|
|
171
|
+
const val = this.getNumericArg(args[0]);
|
|
172
|
+
if (val !== undefined && isNumeric)
|
|
173
|
+
constraints.maximum = val - Number.EPSILON;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case 'length': {
|
|
177
|
+
const val = this.getNumericArg(args[0]);
|
|
178
|
+
if (val !== undefined) {
|
|
179
|
+
constraints.minLength = val;
|
|
180
|
+
constraints.maxLength = val;
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case 'default': {
|
|
185
|
+
if (args[0]) {
|
|
186
|
+
const val = this.getLiteralValue(args[0]);
|
|
187
|
+
if (val !== undefined)
|
|
188
|
+
constraints.default = val;
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
case 'regex': {
|
|
193
|
+
if (args[0] && ts_morph_1.Node.isRegularExpressionLiteral(args[0])) {
|
|
194
|
+
const text = args[0].getText();
|
|
195
|
+
const lastSlash = text.lastIndexOf('/');
|
|
196
|
+
constraints.pattern = text.slice(1, lastSlash);
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
case 'email':
|
|
201
|
+
constraints.format = 'email';
|
|
202
|
+
break;
|
|
203
|
+
case 'url':
|
|
204
|
+
constraints.format = 'uri';
|
|
205
|
+
break;
|
|
206
|
+
case 'uuid':
|
|
207
|
+
constraints.format = 'uuid';
|
|
208
|
+
break;
|
|
209
|
+
case 'datetime':
|
|
210
|
+
constraints.format = 'date-time';
|
|
211
|
+
break;
|
|
212
|
+
case 'ip':
|
|
213
|
+
constraints.format = 'ip';
|
|
214
|
+
break;
|
|
215
|
+
case 'cuid':
|
|
216
|
+
case 'cuid2':
|
|
217
|
+
case 'ulid':
|
|
218
|
+
constraints.format = method;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
return constraints;
|
|
223
|
+
}
|
|
224
|
+
mapZodMethod(method, node) {
|
|
225
|
+
switch (method) {
|
|
226
|
+
case 'string': return { type: 'string', required: true };
|
|
227
|
+
case 'number': return { type: 'number', required: true };
|
|
228
|
+
case 'boolean': return { type: 'boolean', required: true };
|
|
229
|
+
case 'bigint': return { type: 'number', required: true };
|
|
230
|
+
case 'date': return { type: 'string', format: 'date-time', required: true };
|
|
231
|
+
case 'array': return { type: 'array', required: true };
|
|
232
|
+
case 'object': return { type: 'object', required: true };
|
|
233
|
+
case 'any':
|
|
234
|
+
case 'unknown': return { type: 'any', required: true };
|
|
235
|
+
case 'enum': {
|
|
236
|
+
const args = node.getArguments();
|
|
237
|
+
if (args.length > 0 && ts_morph_1.Node.isArrayLiteralExpression(args[0])) {
|
|
238
|
+
const values = args[0]
|
|
239
|
+
.getElements()
|
|
240
|
+
.filter(ts_morph_1.Node.isStringLiteral)
|
|
241
|
+
.map(el => el.getLiteralValue());
|
|
242
|
+
return { type: 'string', enum: values, required: true };
|
|
243
|
+
}
|
|
244
|
+
return { type: 'string', required: true };
|
|
245
|
+
}
|
|
246
|
+
case 'literal': {
|
|
247
|
+
const args = node.getArguments();
|
|
248
|
+
if (args.length > 0) {
|
|
249
|
+
const val = args[0];
|
|
250
|
+
if (ts_morph_1.Node.isStringLiteral(val))
|
|
251
|
+
return { type: 'string', enum: [val.getLiteralValue()], required: true };
|
|
252
|
+
if (ts_morph_1.Node.isNumericLiteral(val))
|
|
253
|
+
return { type: 'number', required: true };
|
|
254
|
+
}
|
|
255
|
+
return { type: 'any', required: true };
|
|
256
|
+
}
|
|
257
|
+
case 'union': {
|
|
258
|
+
const args = node.getArguments();
|
|
259
|
+
if (args.length > 0 && ts_morph_1.Node.isArrayLiteralExpression(args[0])) {
|
|
260
|
+
const types = args[0].getElements().map(el => this.extractFieldType(el).type);
|
|
261
|
+
const unique = [...new Set(types)];
|
|
262
|
+
return { type: unique.length === 1 ? unique[0] : 'string', required: true };
|
|
263
|
+
}
|
|
264
|
+
return { type: 'string', required: true };
|
|
265
|
+
}
|
|
266
|
+
default:
|
|
267
|
+
return { type: 'string', required: true };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/** Walk a Zod chain back to the root z.*() call. */
|
|
271
|
+
getZodBase(node) {
|
|
272
|
+
if (!ts_morph_1.Node.isCallExpression(node))
|
|
273
|
+
return null;
|
|
274
|
+
const expr = node.getExpression();
|
|
275
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(expr))
|
|
276
|
+
return null;
|
|
277
|
+
const method = expr.getName();
|
|
278
|
+
const obj = expr.getExpression();
|
|
279
|
+
if (obj.getText() === 'z')
|
|
280
|
+
return { method, node };
|
|
281
|
+
return this.getZodBase(obj);
|
|
282
|
+
}
|
|
283
|
+
/** Invoke callback for every method call in the chain (from outermost to innermost). */
|
|
284
|
+
walkChain(node, cb) {
|
|
285
|
+
if (!ts_morph_1.Node.isCallExpression(node))
|
|
286
|
+
return;
|
|
287
|
+
const expr = node.getExpression();
|
|
288
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(expr))
|
|
289
|
+
return;
|
|
290
|
+
cb(expr.getName(), node);
|
|
291
|
+
this.walkChain(expr.getExpression(), cb);
|
|
292
|
+
}
|
|
293
|
+
chainContains(node, target) {
|
|
294
|
+
if (!ts_morph_1.Node.isCallExpression(node))
|
|
295
|
+
return false;
|
|
296
|
+
const expr = node.getExpression();
|
|
297
|
+
if (!ts_morph_1.Node.isPropertyAccessExpression(expr))
|
|
298
|
+
return false;
|
|
299
|
+
if (expr.getName() === target)
|
|
300
|
+
return true;
|
|
301
|
+
return this.chainContains(expr.getExpression(), target);
|
|
302
|
+
}
|
|
303
|
+
getNumericArg(node) {
|
|
304
|
+
if (!node)
|
|
305
|
+
return undefined;
|
|
306
|
+
if (ts_morph_1.Node.isNumericLiteral(node))
|
|
307
|
+
return Number(node.getLiteralValue());
|
|
308
|
+
// Handle negative literals: -0.01 is PrefixUnaryExpression
|
|
309
|
+
if (ts_morph_1.Node.isPrefixUnaryExpression(node)) {
|
|
310
|
+
const operand = node.getOperand();
|
|
311
|
+
if (ts_morph_1.Node.isNumericLiteral(operand))
|
|
312
|
+
return -Number(operand.getLiteralValue());
|
|
313
|
+
}
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
getLiteralValue(node) {
|
|
317
|
+
// Never embed env var values — store the variable name as a sentinel instead
|
|
318
|
+
const text = node.getText().trim();
|
|
319
|
+
const envMatch = text.match(/process\.env\.([A-Z0-9_]+)/);
|
|
320
|
+
if (envMatch)
|
|
321
|
+
return { $env: envMatch[1] };
|
|
322
|
+
if (ts_morph_1.Node.isStringLiteral(node))
|
|
323
|
+
return node.getLiteralValue();
|
|
324
|
+
if (ts_morph_1.Node.isNumericLiteral(node))
|
|
325
|
+
return Number(node.getLiteralValue());
|
|
326
|
+
if (text === 'true')
|
|
327
|
+
return true;
|
|
328
|
+
if (text === 'false')
|
|
329
|
+
return false;
|
|
330
|
+
if (text === 'null')
|
|
331
|
+
return null;
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
exports.ZodExtractor = ZodExtractor;
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-manifest",
|
|
3
|
+
"version": "3.2.0",
|
|
4
|
+
"description": "Universal agent manifest compiler — generates agent.json for any web app so AI agents can discover and interact with it",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agentjson": "dist/cli.js",
|
|
8
|
+
"agent-json": "dist/cli.js",
|
|
9
|
+
"farcaster-agent-compiler": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"schema",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc && node -e \"const fs=require('fs');const f='dist/cli.js';const c=fs.readFileSync(f,'utf8');if(!c.startsWith('#!/'))fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c);\"",
|
|
19
|
+
"start": "node dist/cli.js",
|
|
20
|
+
"test": "jest"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/0xedev/farcaster-agent-compiler.git"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"agent",
|
|
28
|
+
"ai",
|
|
29
|
+
"manifest",
|
|
30
|
+
"agent.json",
|
|
31
|
+
"compiler",
|
|
32
|
+
"farcaster",
|
|
33
|
+
"next.js",
|
|
34
|
+
"express",
|
|
35
|
+
"hono",
|
|
36
|
+
"fastify",
|
|
37
|
+
"openapi",
|
|
38
|
+
"llm"
|
|
39
|
+
],
|
|
40
|
+
"author": "",
|
|
41
|
+
"license": "ISC",
|
|
42
|
+
"type": "commonjs",
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/jest": "^30.0.0",
|
|
45
|
+
"@types/node": "^25.5.0",
|
|
46
|
+
"jest": "^30.3.0",
|
|
47
|
+
"ts-jest": "^29.4.6",
|
|
48
|
+
"ts-node": "^10.9.2",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"zod": "^4.3.6",
|
|
51
|
+
"zod-to-json-schema": "^3.25.1"
|
|
52
|
+
},
|
|
53
|
+
"directories": {
|
|
54
|
+
"doc": "docs"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"commander": "^14.0.3",
|
|
58
|
+
"tinyglobby": "^0.2.15",
|
|
59
|
+
"ts-morph": "^27.0.2"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://farcaster.xyz/schemas/agent.json",
|
|
4
|
+
"title": "Farcaster Agent Manifest",
|
|
5
|
+
"description": "Describes all agent-callable actions in a Farcaster Mini App",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["name", "description", "version", "auth", "capabilities", "actions"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"name": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Mini app name"
|
|
13
|
+
},
|
|
14
|
+
"description": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Human-readable description of the mini app"
|
|
17
|
+
},
|
|
18
|
+
"version": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
21
|
+
"description": "Semantic version of the mini app"
|
|
22
|
+
},
|
|
23
|
+
"author": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Author or organization name"
|
|
26
|
+
},
|
|
27
|
+
"url": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"format": "uri",
|
|
30
|
+
"description": "Mini app homepage or Farcaster frame URL"
|
|
31
|
+
},
|
|
32
|
+
"auth": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"required": ["type"],
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"description": "How agents should authenticate with this app",
|
|
37
|
+
"properties": {
|
|
38
|
+
"type": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"enum": ["none", "bearer", "api-key", "oauth2", "basic", "farcaster-frame", "cookie"],
|
|
41
|
+
"description": "Authentication scheme"
|
|
42
|
+
},
|
|
43
|
+
"header": { "type": "string", "description": "Header name, e.g. Authorization or X-API-Key" },
|
|
44
|
+
"scheme": { "type": "string", "description": "Header value prefix, e.g. Bearer or Token" },
|
|
45
|
+
"queryParam": { "type": "string", "description": "Query param name for api-key auth" },
|
|
46
|
+
"docsUrl": { "type": "string", "format": "uri", "description": "Where to obtain credentials" }
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"metadata": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"properties": {
|
|
53
|
+
"iconUrl": { "type": "string", "format": "uri" },
|
|
54
|
+
"homeUrl": { "type": "string", "format": "uri" },
|
|
55
|
+
"imageUrl": { "type": "string", "format": "uri" },
|
|
56
|
+
"splashImageUrl": { "type": "string", "format": "uri" },
|
|
57
|
+
"splashBackgroundColor": { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" }
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"capabilities": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": { "type": "string" },
|
|
63
|
+
"description": "Farcaster SDK capabilities used by this app"
|
|
64
|
+
},
|
|
65
|
+
"actions": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"items": { "$ref": "#/definitions/AgentAction" }
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"definitions": {
|
|
71
|
+
"ParameterProperty": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["type"],
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"properties": {
|
|
76
|
+
"type": { "type": "string" },
|
|
77
|
+
"description": { "type": "string" },
|
|
78
|
+
"required": { "type": "boolean" },
|
|
79
|
+
"enum": { "type": "array", "items": { "type": "string" } },
|
|
80
|
+
"default": {},
|
|
81
|
+
"minimum": { "type": "number" },
|
|
82
|
+
"maximum": { "type": "number" },
|
|
83
|
+
"minLength": { "type": "integer", "minimum": 0 },
|
|
84
|
+
"maxLength": { "type": "integer", "minimum": 0 },
|
|
85
|
+
"pattern": { "type": "string" },
|
|
86
|
+
"format": { "type": "string" }
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"AgentAction": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"required": ["name", "description", "intent", "type", "location", "safety", "agentSafe", "inputs", "outputs"],
|
|
92
|
+
"additionalProperties": false,
|
|
93
|
+
"properties": {
|
|
94
|
+
"name": { "type": "string", "description": "Unique action identifier" },
|
|
95
|
+
"description": { "type": "string" },
|
|
96
|
+
"intent": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"pattern": "^[a-z][a-z0-9]*\\.[a-z][a-z0-9]*$",
|
|
99
|
+
"description": "Standardized semantic intent in domain.verb format"
|
|
100
|
+
},
|
|
101
|
+
"type": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"enum": ["api", "contract", "function"]
|
|
104
|
+
},
|
|
105
|
+
"location": { "type": "string" },
|
|
106
|
+
"method": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"enum": ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]
|
|
109
|
+
},
|
|
110
|
+
"abiFunction": { "type": "string" },
|
|
111
|
+
"isReadOnly": { "type": "boolean" },
|
|
112
|
+
"chainId": { "type": "integer" },
|
|
113
|
+
"contractAddress": {
|
|
114
|
+
"oneOf": [
|
|
115
|
+
{ "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$", "description": "Literal deployed address" },
|
|
116
|
+
{
|
|
117
|
+
"type": "object",
|
|
118
|
+
"required": ["$env"],
|
|
119
|
+
"additionalProperties": false,
|
|
120
|
+
"properties": {
|
|
121
|
+
"$env": { "type": "string", "description": "Environment variable name that holds the address" }
|
|
122
|
+
},
|
|
123
|
+
"description": "Address resolved from an environment variable at runtime"
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
"safety": {
|
|
128
|
+
"type": "string",
|
|
129
|
+
"enum": ["read", "write", "financial", "destructive", "confidential"],
|
|
130
|
+
"description": "Safety classification for agent policy enforcement. confidential = handles PII, credentials, or sensitive identity data requiring encrypted transport and human confirmation"
|
|
131
|
+
},
|
|
132
|
+
"agentSafe": {
|
|
133
|
+
"type": "boolean",
|
|
134
|
+
"description": "True when the action can be executed autonomously without human confirmation"
|
|
135
|
+
},
|
|
136
|
+
"requiredAuth": {
|
|
137
|
+
"type": "object",
|
|
138
|
+
"required": ["required"],
|
|
139
|
+
"additionalProperties": false,
|
|
140
|
+
"properties": {
|
|
141
|
+
"required": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"enum": ["public", "required", "farcaster-signed"],
|
|
144
|
+
"description": "Whether this action needs credentials"
|
|
145
|
+
},
|
|
146
|
+
"scope": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"description": "Optional OAuth/custom scope e.g. payments:write"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"inputs": {
|
|
153
|
+
"type": "object",
|
|
154
|
+
"additionalProperties": { "$ref": "#/definitions/ParameterProperty" }
|
|
155
|
+
},
|
|
156
|
+
"outputs": {
|
|
157
|
+
"type": "object",
|
|
158
|
+
"required": ["type"],
|
|
159
|
+
"additionalProperties": false,
|
|
160
|
+
"properties": {
|
|
161
|
+
"type": { "type": "string" },
|
|
162
|
+
"description": { "type": "string" }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|