agent-manifest 3.2.0 → 3.3.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/dist/cli.js CHANGED
@@ -59,18 +59,69 @@ program
59
59
  const files = await discovery.findRelevantFiles();
60
60
  console.log(`🔍 Found ${files.length} relevant files.`);
61
61
  const tsParser = new index_1.TSParser(projectPath);
62
- const expressParser = new index_1.ExpressParser(tsParser.getProject());
62
+ const sharedProject = tsParser.getProject();
63
+ const expressParser = new index_1.ExpressParser(sharedProject);
64
+ const socketParser = new index_1.SocketIOParser(sharedProject);
65
+ const trpcParser = new index_1.TRPCParser(sharedProject);
66
+ const sseParser = new index_1.SSEParser(sharedProject);
67
+ const remixParser = new index_1.RemixParser(sharedProject);
68
+ const wsParser = new index_1.WebSocketParser(sharedProject);
69
+ const openApiParser = new index_1.OpenAPIParser();
70
+ const prismaParser = new index_1.PrismaParser();
63
71
  const actions = [];
72
+ // Determine which files are OpenAPI / Prisma by extension/name
73
+ const openApiExts = new Set(['.json', '.yaml', '.yml']);
74
+ const prismaExt = '.prisma';
64
75
  for (const file of files) {
65
76
  console.log(`📄 Parsing: ${path.relative(projectPath, file)}`);
66
- const fileActions = await tsParser.parseFile(file);
67
- actions.push(...fileActions);
68
- if (file.endsWith('.ts') || file.endsWith('.js')) {
77
+ const ext = path.extname(file).toLowerCase();
78
+ const base = path.basename(file).toLowerCase();
79
+ // ── OpenAPI spec ──────────────────────────────────────────────────────
80
+ if (openApiExts.has(ext) && index_1.OPENAPI_PATTERNS.some(p => {
81
+ const pat = p.replace('**/', '').replace('*', '');
82
+ return base.includes(pat.split('.')[0]);
83
+ })) {
84
+ try {
85
+ actions.push(...openApiParser.parseFile(file, projectPath));
86
+ }
87
+ catch { /* ignore */ }
88
+ continue;
89
+ }
90
+ // ── Prisma schema ─────────────────────────────────────────────────────
91
+ if (ext === prismaExt || base === 'schema.prisma') {
92
+ try {
93
+ actions.push(...prismaParser.parseFile(file));
94
+ }
95
+ catch { /* ignore */ }
96
+ continue;
97
+ }
98
+ // ── TypeScript / JavaScript source files ──────────────────────────────
99
+ if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.jsx') {
100
+ // Standard TS server-action / contract / annotation parsing
101
+ try {
102
+ const fileActions = await tsParser.parseFile(file);
103
+ actions.push(...fileActions);
104
+ }
105
+ catch { /* ignore */ }
69
106
  try {
70
107
  const content = fs.readFileSync(file, 'utf8');
71
108
  if ((0, index_1.looksLikeRouteFile)(content)) {
72
- const expressActions = await expressParser.parseFile(file, projectPath);
73
- actions.push(...expressActions);
109
+ actions.push(...await expressParser.parseFile(file, projectPath));
110
+ }
111
+ if ((0, index_1.looksLikeSocketIOFile)(content)) {
112
+ actions.push(...await socketParser.parseFile(file, projectPath));
113
+ }
114
+ if ((0, index_1.looksLikeTRPCFile)(content)) {
115
+ actions.push(...await trpcParser.parseFile(file, projectPath));
116
+ }
117
+ if ((0, index_1.looksLikeSSEFile)(content)) {
118
+ actions.push(...await sseParser.parseFile(file, projectPath));
119
+ }
120
+ if ((0, index_1.looksLikeRemixRouteFile)(content)) {
121
+ actions.push(...await remixParser.parseFile(file, projectPath));
122
+ }
123
+ if ((0, index_1.looksLikeWebSocketFile)(content)) {
124
+ actions.push(...await wsParser.parseFile(file, projectPath));
74
125
  }
75
126
  }
76
127
  catch { /* ignore */ }
@@ -135,7 +186,7 @@ program
135
186
  });
136
187
  // ─── Structural validator ────────────────────────────────────────────────────
137
188
  const SAFETY_LEVELS = new Set(['read', 'write', 'financial', 'destructive', 'confidential']);
138
- const ACTION_TYPES = new Set(['api', 'contract', 'function']);
189
+ const ACTION_TYPES = new Set(['api', 'contract', 'function', 'socket']);
139
190
  const AUTH_TYPES = new Set(['none', 'bearer', 'api-key', 'oauth2', 'basic', 'farcaster-frame', 'cookie']);
140
191
  const INTENT_RE = /^[a-z][a-z0-9]*\.[a-z][a-z0-9]*$/;
141
192
  function validateManifest(m) {
@@ -39,6 +39,13 @@ const path = __importStar(require("path"));
39
39
  const crypto = __importStar(require("crypto"));
40
40
  const tinyglobby_1 = require("tinyglobby");
41
41
  const express_parser_1 = require("../parser/express-parser");
42
+ const socketio_parser_1 = require("../parser/socketio-parser");
43
+ const trpc_parser_1 = require("../parser/trpc-parser");
44
+ const sse_parser_1 = require("../parser/sse-parser");
45
+ const remix_parser_1 = require("../parser/remix-parser");
46
+ const websocket_parser_1 = require("../parser/websocket-parser");
47
+ const openapi_parser_1 = require("../parser/openapi-parser");
48
+ const prisma_parser_1 = require("../parser/prisma-parser");
42
49
  /** SHA-1 of a file's content — used for change detection caching. */
43
50
  function fileHash(filePath) {
44
51
  try {
@@ -60,7 +67,6 @@ const ALWAYS_EXCLUDE = [
60
67
  '!**/out/**',
61
68
  '!**/build/**',
62
69
  '!**/.vercel/**',
63
- // Never scan env files — they may contain secrets
64
70
  '!**/.env',
65
71
  '!**/.env.*',
66
72
  '!**/secrets/**',
@@ -94,14 +100,14 @@ class DiscoveryService {
94
100
  }
95
101
  async findRelevantFiles() {
96
102
  const relevantFiles = [];
97
- // 0. Farcaster manifest (app identity / metadata)
103
+ // 0. Farcaster manifest
98
104
  const manifests = await (0, tinyglobby_1.glob)([
99
105
  '.well-known/farcaster.json',
100
106
  'public/.well-known/farcaster.json',
101
107
  '**/public/.well-known/farcaster.json',
102
108
  ], { cwd: this.projectPath, absolute: true });
103
109
  relevantFiles.push(...manifests);
104
- // 0.5. ABI JSON files (smart contract definitions)
110
+ // 0.5. ABI JSON files
105
111
  const abis = await (0, tinyglobby_1.glob)([
106
112
  '**/*ABI.json',
107
113
  '**/abi/*.json',
@@ -111,20 +117,36 @@ class DiscoveryService {
111
117
  ...ALWAYS_EXCLUDE,
112
118
  ], { cwd: this.projectPath, absolute: true });
113
119
  relevantFiles.push(...abis);
114
- // 1. API routes — support monorepo layouts (apps/*/src/app/api, apps/*/pages/api, etc.)
120
+ // 0.6. OpenAPI / Swagger specs
121
+ const openApiFiles = await (0, tinyglobby_1.glob)([
122
+ ...openapi_parser_1.OPENAPI_PATTERNS,
123
+ ...ALWAYS_EXCLUDE,
124
+ ], { cwd: this.projectPath, absolute: true });
125
+ relevantFiles.push(...openApiFiles);
126
+ // 0.7. Prisma schema files
127
+ const prismaFiles = await (0, tinyglobby_1.glob)([
128
+ ...prisma_parser_1.PRISMA_PATTERNS,
129
+ ...ALWAYS_EXCLUDE,
130
+ ], { cwd: this.projectPath, absolute: true });
131
+ relevantFiles.push(...prismaFiles);
132
+ // 1. Next.js API routes
115
133
  const apiRoutes = await (0, tinyglobby_1.glob)([
116
- // Next.js App Router
117
134
  '**/app/api/**/*.{ts,js,tsx,jsx}',
118
- // Next.js Pages Router
119
135
  '**/pages/api/**/*.{ts,js,tsx,jsx}',
120
- // Generic api/ folder
121
136
  '**/api/**/*.{ts,js,tsx,jsx}',
122
137
  ...ALWAYS_EXCLUDE,
123
138
  ], { cwd: this.projectPath, absolute: true });
124
139
  relevantFiles.push(...apiRoutes);
125
- // 2. Scan all TS/TSX files for signal keywords
140
+ // 2. Remix route files
141
+ const remixRoutes = await (0, tinyglobby_1.glob)([
142
+ '**/app/routes/**/*.{ts,js,tsx,jsx}',
143
+ '**/routes/**/*.{ts,js,tsx,jsx}',
144
+ ...ALWAYS_EXCLUDE,
145
+ ], { cwd: this.projectPath, absolute: true });
146
+ relevantFiles.push(...remixRoutes);
147
+ // 3. Scan all TS/TSX/JS files for signal keywords
126
148
  const allTsFiles = await (0, tinyglobby_1.glob)([
127
- '**/*.{ts,tsx}',
149
+ '**/*.{ts,tsx,js,jsx}',
128
150
  ...ALWAYS_EXCLUDE,
129
151
  ], { cwd: this.projectPath, absolute: true });
130
152
  for (const file of allTsFiles) {
@@ -132,13 +154,11 @@ class DiscoveryService {
132
154
  continue;
133
155
  const hash = fileHash(file);
134
156
  const cached = this.cache[file];
135
- // Cache hit: file unchanged, reuse previous relevance decision
136
157
  if (cached && cached.hash === hash) {
137
158
  if (cached.relevant)
138
159
  relevantFiles.push(file);
139
160
  continue;
140
161
  }
141
- // Cache miss: read and classify
142
162
  const content = fs.readFileSync(file, 'utf8');
143
163
  const relevant = content.includes('@agent-action') ||
144
164
  content.includes('useWriteContract') ||
@@ -146,7 +166,12 @@ class DiscoveryService {
146
166
  content.includes('writeContract') ||
147
167
  content.includes("'use server'") ||
148
168
  content.includes('"use server"') ||
149
- (0, express_parser_1.looksLikeRouteFile)(content);
169
+ (0, express_parser_1.looksLikeRouteFile)(content) ||
170
+ (0, socketio_parser_1.looksLikeSocketIOFile)(content) ||
171
+ (0, trpc_parser_1.looksLikeTRPCFile)(content) ||
172
+ (0, sse_parser_1.looksLikeSSEFile)(content) ||
173
+ (0, remix_parser_1.looksLikeRemixRouteFile)(content) ||
174
+ (0, websocket_parser_1.looksLikeWebSocketFile)(content);
150
175
  this.cache[file] = { hash, relevant };
151
176
  this.cacheModified = true;
152
177
  if (relevant)
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.CapabilityDetector = exports.AuthDetector = exports.ManifestGenerator = exports.ZodExtractor = exports.ContractParser = exports.looksLikeRouteFile = exports.ExpressParser = exports.TSParser = exports.DiscoveryService = void 0;
17
+ exports.CapabilityDetector = exports.AuthDetector = exports.ManifestGenerator = exports.ZodExtractor = exports.ContractParser = exports.PRISMA_PATTERNS = exports.PrismaParser = exports.OPENAPI_PATTERNS = exports.OpenAPIParser = exports.looksLikeWebSocketFile = exports.WebSocketParser = exports.looksLikeRemixRouteFile = exports.RemixParser = exports.looksLikeSSEFile = exports.SSEParser = exports.looksLikeTRPCFile = exports.TRPCParser = exports.looksLikeSocketIOFile = exports.SocketIOParser = exports.looksLikeRouteFile = exports.ExpressParser = exports.TSParser = exports.DiscoveryService = void 0;
18
18
  __exportStar(require("./types"), exports);
19
19
  var service_1 = require("./discovery/service");
20
20
  Object.defineProperty(exports, "DiscoveryService", { enumerable: true, get: function () { return service_1.DiscoveryService; } });
@@ -23,6 +23,27 @@ Object.defineProperty(exports, "TSParser", { enumerable: true, get: function ()
23
23
  var express_parser_1 = require("./parser/express-parser");
24
24
  Object.defineProperty(exports, "ExpressParser", { enumerable: true, get: function () { return express_parser_1.ExpressParser; } });
25
25
  Object.defineProperty(exports, "looksLikeRouteFile", { enumerable: true, get: function () { return express_parser_1.looksLikeRouteFile; } });
26
+ var socketio_parser_1 = require("./parser/socketio-parser");
27
+ Object.defineProperty(exports, "SocketIOParser", { enumerable: true, get: function () { return socketio_parser_1.SocketIOParser; } });
28
+ Object.defineProperty(exports, "looksLikeSocketIOFile", { enumerable: true, get: function () { return socketio_parser_1.looksLikeSocketIOFile; } });
29
+ var trpc_parser_1 = require("./parser/trpc-parser");
30
+ Object.defineProperty(exports, "TRPCParser", { enumerable: true, get: function () { return trpc_parser_1.TRPCParser; } });
31
+ Object.defineProperty(exports, "looksLikeTRPCFile", { enumerable: true, get: function () { return trpc_parser_1.looksLikeTRPCFile; } });
32
+ var sse_parser_1 = require("./parser/sse-parser");
33
+ Object.defineProperty(exports, "SSEParser", { enumerable: true, get: function () { return sse_parser_1.SSEParser; } });
34
+ Object.defineProperty(exports, "looksLikeSSEFile", { enumerable: true, get: function () { return sse_parser_1.looksLikeSSEFile; } });
35
+ var remix_parser_1 = require("./parser/remix-parser");
36
+ Object.defineProperty(exports, "RemixParser", { enumerable: true, get: function () { return remix_parser_1.RemixParser; } });
37
+ Object.defineProperty(exports, "looksLikeRemixRouteFile", { enumerable: true, get: function () { return remix_parser_1.looksLikeRemixRouteFile; } });
38
+ var websocket_parser_1 = require("./parser/websocket-parser");
39
+ Object.defineProperty(exports, "WebSocketParser", { enumerable: true, get: function () { return websocket_parser_1.WebSocketParser; } });
40
+ Object.defineProperty(exports, "looksLikeWebSocketFile", { enumerable: true, get: function () { return websocket_parser_1.looksLikeWebSocketFile; } });
41
+ var openapi_parser_1 = require("./parser/openapi-parser");
42
+ Object.defineProperty(exports, "OpenAPIParser", { enumerable: true, get: function () { return openapi_parser_1.OpenAPIParser; } });
43
+ Object.defineProperty(exports, "OPENAPI_PATTERNS", { enumerable: true, get: function () { return openapi_parser_1.OPENAPI_PATTERNS; } });
44
+ var prisma_parser_1 = require("./parser/prisma-parser");
45
+ Object.defineProperty(exports, "PrismaParser", { enumerable: true, get: function () { return prisma_parser_1.PrismaParser; } });
46
+ Object.defineProperty(exports, "PRISMA_PATTERNS", { enumerable: true, get: function () { return prisma_parser_1.PRISMA_PATTERNS; } });
26
47
  var contract_parser_1 = require("./parser/contract-parser");
27
48
  Object.defineProperty(exports, "ContractParser", { enumerable: true, get: function () { return contract_parser_1.ContractParser; } });
28
49
  var zod_extractor_1 = require("./parser/zod-extractor");
@@ -9,7 +9,7 @@ const INTENT_RULES = [
9
9
  // getUsers don't have a word boundary after the verb segment. Leading \b is kept
10
10
  // to avoid matching mid-word (e.g. "undo" matching "do").
11
11
  // Game
12
- { pattern: /\b(play|flip|roll|spin|bet|guess|draw|move|attack|defend|claim(?:Prize|Reward|Win))/i, intent: 'game.play' },
12
+ { pattern: /\b(play|flip|roll|spin|bet|guess|draw|move|make(?:Move)?|drop(?:Piece)?|attack|defend|claim(?:Prize|Reward|Win))/i, intent: 'game.play' },
13
13
  { pattern: /\b(score|leaderboard|rank|highscore)/i, intent: 'game.score' },
14
14
  { pattern: /\b(join(?:Game|Room|Lobby)|create(?:Game|Room)|start(?:Game|Round))/i, intent: 'game.join' },
15
15
  // Finance / DeFi
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.OPENAPI_PATTERNS = exports.OpenAPIParser = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const intent_classifier_1 = require("./intent-classifier");
39
+ /**
40
+ * Ingests existing OpenAPI 3.x / Swagger 2.x spec files and converts
41
+ * each operation into an agent action.
42
+ *
43
+ * Supports JSON natively. YAML is supported if `js-yaml` is installed
44
+ * in the target project (optional peer dep — we require() it at runtime
45
+ * so the compiler doesn't hard-depend on it).
46
+ */
47
+ class OpenAPIParser {
48
+ parseFile(filePath, projectPath) {
49
+ const content = fs.readFileSync(filePath, 'utf8');
50
+ const spec = this.parseSpec(filePath, content);
51
+ if (!spec)
52
+ return [];
53
+ // Support both OpenAPI 3.x (paths) and Swagger 2.x (basePath + paths)
54
+ const paths = spec.paths ?? {};
55
+ const actions = [];
56
+ for (const [routePath, pathItem] of Object.entries(paths)) {
57
+ for (const method of ['get', 'post', 'put', 'patch', 'delete', 'head']) {
58
+ const operation = pathItem[method];
59
+ if (!operation)
60
+ continue;
61
+ const operationId = operation.operationId ?? this.pathToActionName(routePath, method);
62
+ const description = operation.summary ?? operation.description ?? `${method.toUpperCase()} ${routePath}`;
63
+ const httpMethod = method.toUpperCase();
64
+ const safety = (0, intent_classifier_1.classifySafety)({ name: operationId, httpMethod, type: 'api' });
65
+ // Extract parameters (path + query + header)
66
+ const inputs = {};
67
+ for (const param of operation.parameters ?? []) {
68
+ inputs[param.name] = {
69
+ type: param.schema?.type ?? 'string',
70
+ description: param.description,
71
+ required: param.required ?? false,
72
+ };
73
+ }
74
+ // Extract requestBody schema fields (OpenAPI 3.x)
75
+ const bodySchema = operation.requestBody?.content?.['application/json']?.schema;
76
+ if (bodySchema?.properties) {
77
+ const required = bodySchema.required ?? [];
78
+ for (const [field, schema] of Object.entries(bodySchema.properties)) {
79
+ inputs[field] = {
80
+ type: schema.type ?? 'any',
81
+ description: schema.description,
82
+ required: required.includes(field),
83
+ };
84
+ }
85
+ }
86
+ // Derive auth requirement from OpenAPI security field
87
+ const hasSecurity = (operation.security && operation.security.length > 0) ||
88
+ (spec.security && spec.security.length > 0);
89
+ actions.push({
90
+ name: operationId,
91
+ description,
92
+ intent: (0, intent_classifier_1.inferIntent)(operationId),
93
+ type: 'api',
94
+ location: routePath,
95
+ method: httpMethod,
96
+ safety,
97
+ agentSafe: (0, intent_classifier_1.deriveAgentSafe)(safety),
98
+ requiredAuth: hasSecurity
99
+ ? { required: 'required' }
100
+ : (0, intent_classifier_1.inferActionAuth)({ safety, httpMethod, type: 'api' }),
101
+ inputs,
102
+ outputs: this.extractOutputSchema(operation),
103
+ });
104
+ }
105
+ }
106
+ return actions;
107
+ }
108
+ parseSpec(filePath, content) {
109
+ // JSON
110
+ if (filePath.endsWith('.json')) {
111
+ try {
112
+ return JSON.parse(content);
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }
118
+ // YAML — optional peer dep
119
+ try {
120
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
121
+ const yaml = require('js-yaml');
122
+ return yaml.load(content);
123
+ }
124
+ catch {
125
+ // js-yaml not installed or parse error
126
+ return null;
127
+ }
128
+ }
129
+ pathToActionName(routePath, method) {
130
+ const slug = routePath
131
+ .replace(/^\//, '')
132
+ .replace(/\{([^}]+)\}/g, '_$1')
133
+ .replace(/\//g, '_')
134
+ .replace(/[^a-zA-Z0-9_]/g, '');
135
+ return slug ? `${slug}_${method.toUpperCase()}` : `root_${method.toUpperCase()}`;
136
+ }
137
+ extractOutputSchema(operation) {
138
+ const response200 = operation.responses?.['200'] ?? operation.responses?.['201'];
139
+ if (!response200)
140
+ return { type: 'any' };
141
+ const schema = response200.content?.['application/json']?.schema;
142
+ return { type: schema?.type ?? 'object', description: response200.description };
143
+ }
144
+ }
145
+ exports.OpenAPIParser = OpenAPIParser;
146
+ /** File patterns that contain OpenAPI specs */
147
+ exports.OPENAPI_PATTERNS = [
148
+ '**/openapi.json',
149
+ '**/openapi.yaml',
150
+ '**/openapi.yml',
151
+ '**/swagger.json',
152
+ '**/swagger.yaml',
153
+ '**/swagger.yml',
154
+ '**/api-spec.json',
155
+ '**/api.json',
156
+ ];
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PRISMA_PATTERNS = exports.PrismaParser = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const intent_classifier_1 = require("./intent-classifier");
39
+ /**
40
+ * Reads a Prisma schema file and infers CRUD agent actions for every model.
41
+ *
42
+ * For a model named `User` it emits:
43
+ * listUsers → data.read (GET-like)
44
+ * getUser → data.read (GET-like, by id)
45
+ * createUser → data.create (POST-like)
46
+ * updateUser → data.update (PATCH-like)
47
+ * deleteUser → data.delete (DELETE-like)
48
+ *
49
+ * Fields declared on the model are mapped to action inputs where relevant.
50
+ */
51
+ class PrismaParser {
52
+ parseFile(filePath) {
53
+ const content = fs.readFileSync(filePath, 'utf8');
54
+ const models = this.extractModels(content);
55
+ const actions = [];
56
+ for (const model of models) {
57
+ const name = model.name;
58
+ const lower = name.charAt(0).toLowerCase() + name.slice(1);
59
+ const location = filePath;
60
+ // Writeable fields (non-id, non-auto) used as create/update inputs
61
+ const writeableFields = model.fields.filter(f => !f.isId && !f.isAuto && !f.isList);
62
+ const idField = model.fields.find(f => f.isId) ?? { name: 'id', type: 'String' };
63
+ const idInput = {
64
+ [idField.name]: { type: this.prismaTypeToJson(idField.type), required: true },
65
+ };
66
+ const writeInputs = {};
67
+ for (const f of writeableFields) {
68
+ writeInputs[f.name] = {
69
+ type: this.prismaTypeToJson(f.type),
70
+ required: !f.isOptional,
71
+ };
72
+ }
73
+ // list
74
+ actions.push({
75
+ name: `list${name}s`,
76
+ description: `List all ${name} records`,
77
+ intent: 'data.read',
78
+ type: 'function',
79
+ location,
80
+ safety: 'read',
81
+ agentSafe: true,
82
+ requiredAuth: (0, intent_classifier_1.inferActionAuth)({ safety: 'read', httpMethod: 'GET', type: 'function' }),
83
+ inputs: {},
84
+ outputs: { type: 'array', description: `Array of ${name}` },
85
+ });
86
+ // get
87
+ actions.push({
88
+ name: `get${name}`,
89
+ description: `Get a single ${name} by ${idField.name}`,
90
+ intent: 'data.read',
91
+ type: 'function',
92
+ location,
93
+ safety: 'read',
94
+ agentSafe: true,
95
+ requiredAuth: (0, intent_classifier_1.inferActionAuth)({ safety: 'read', httpMethod: 'GET', type: 'function' }),
96
+ inputs: idInput,
97
+ outputs: { type: 'object', description: name },
98
+ });
99
+ // create
100
+ actions.push({
101
+ name: `create${name}`,
102
+ description: `Create a new ${name} record`,
103
+ intent: 'data.create',
104
+ type: 'function',
105
+ location,
106
+ safety: 'write',
107
+ agentSafe: (0, intent_classifier_1.deriveAgentSafe)('write'),
108
+ requiredAuth: (0, intent_classifier_1.inferActionAuth)({ safety: 'write', type: 'function' }),
109
+ inputs: writeInputs,
110
+ outputs: { type: 'object', description: `Created ${name}` },
111
+ });
112
+ // update
113
+ actions.push({
114
+ name: `update${name}`,
115
+ description: `Update an existing ${name} record`,
116
+ intent: 'data.update',
117
+ type: 'function',
118
+ location,
119
+ safety: 'write',
120
+ agentSafe: (0, intent_classifier_1.deriveAgentSafe)('write'),
121
+ requiredAuth: (0, intent_classifier_1.inferActionAuth)({ safety: 'write', type: 'function' }),
122
+ inputs: { ...idInput, ...writeInputs },
123
+ outputs: { type: 'object', description: `Updated ${name}` },
124
+ });
125
+ // delete
126
+ actions.push({
127
+ name: `delete${name}`,
128
+ description: `Delete a ${name} record`,
129
+ intent: 'data.delete',
130
+ type: 'function',
131
+ location,
132
+ safety: 'destructive',
133
+ agentSafe: false,
134
+ requiredAuth: (0, intent_classifier_1.inferActionAuth)({ safety: 'destructive', type: 'function' }),
135
+ inputs: idInput,
136
+ outputs: { type: 'object', description: `Deleted ${name}` },
137
+ });
138
+ void lower; // suppress unused-variable warning
139
+ }
140
+ return actions;
141
+ }
142
+ // ─── Schema parsing helpers ───────────────────────────────────────────────
143
+ extractModels(content) {
144
+ const models = [];
145
+ // Match: model ModelName { ... }
146
+ const modelRe = /^model\s+(\w+)\s*\{([^}]+)\}/gm;
147
+ let modelMatch;
148
+ while ((modelMatch = modelRe.exec(content)) !== null) {
149
+ const modelName = modelMatch[1];
150
+ const body = modelMatch[2];
151
+ const fields = this.parseFields(body);
152
+ models.push({ name: modelName, fields });
153
+ }
154
+ return models;
155
+ }
156
+ parseFields(body) {
157
+ const fields = [];
158
+ const lines = body.split('\n');
159
+ for (const raw of lines) {
160
+ const line = raw.trim();
161
+ if (!line || line.startsWith('//') || line.startsWith('@@'))
162
+ continue;
163
+ // field Type @attr @attr
164
+ const match = line.match(/^(\w+)\s+(\w+)(\[\])?(\?)?(.*)$/);
165
+ if (!match)
166
+ continue;
167
+ const [, name, type, isList, isOptional, attrs] = match;
168
+ fields.push({
169
+ name,
170
+ type,
171
+ isList: !!isList,
172
+ isOptional: !!isOptional,
173
+ isId: attrs.includes('@id'),
174
+ isAuto: attrs.includes('@default(autoincrement())') || attrs.includes('@default(uuid())') || attrs.includes('@default(cuid())') || attrs.includes('@updatedAt'),
175
+ });
176
+ }
177
+ return fields;
178
+ }
179
+ prismaTypeToJson(prismaType) {
180
+ switch (prismaType) {
181
+ case 'String': return 'string';
182
+ case 'Int':
183
+ case 'Float':
184
+ case 'Decimal': return 'number';
185
+ case 'Boolean': return 'boolean';
186
+ case 'DateTime': return 'string';
187
+ case 'Json': return 'object';
188
+ case 'Bytes': return 'string';
189
+ default: return 'object'; // relations / enums
190
+ }
191
+ }
192
+ }
193
+ exports.PrismaParser = PrismaParser;
194
+ /** Prisma schema file patterns */
195
+ exports.PRISMA_PATTERNS = [
196
+ '**/schema.prisma',
197
+ '**/prisma/schema.prisma',
198
+ ];