@aloma.io/integration-sdk 3.8.50 → 3.8.52

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.
@@ -0,0 +1,216 @@
1
+ # OpenAPI to Connector Generator
2
+
3
+ A deterministic script that generates Aloma connector controllers from OpenAPI 3.x specifications.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Deterministic**: Same OpenAPI input always produces the same connector definition
8
+ - ✅ **No LLM involvement**: Pure parsing and code generation
9
+ - ✅ **OpenAPI 3.x support**: Validates against OpenAPI 3.x schema
10
+ - ✅ **JSON & YAML support**: Handles both JSON and YAML OpenAPI specifications
11
+ - ✅ **Comprehensive coverage**: Generates methods for all endpoints and operations
12
+ - ✅ **Rich documentation**: Uses OpenAPI descriptions and summaries for JSDoc comments
13
+ - ✅ **Parameter mapping**: Maps OpenAPI parameters to method arguments
14
+ - ✅ **Error handling**: Clear error messages for invalid specifications
15
+
16
+ ## Installation
17
+
18
+ The script is included in the integration-sdk package. Install dependencies:
19
+
20
+ ```bash
21
+ npm install
22
+ ```
23
+
24
+ Build the project:
25
+
26
+ ```bash
27
+ npm run build
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### CLI Usage (Recommended)
33
+
34
+ After deploying to npm, you can use the integrated CLI command:
35
+
36
+ ```bash
37
+ # Create a complete connector project from OpenAPI spec
38
+ npx @aloma.io/integration-sdk@latest from-openapi "My Connector" --connector-id "my-connector-123" --spec api.yaml
39
+
40
+ # With custom output path
41
+ npx @aloma.io/integration-sdk@latest from-openapi "My Connector" --connector-id "my-connector-123" --spec api.yaml --out src/controller/index.mts
42
+ ```
43
+
44
+ #### Options
45
+
46
+ - `<name>` (required): Name of the connector project
47
+ - `--connector-id <id>` (required): ID of the connector
48
+ - `--spec <file>` (required): OpenAPI specification file (JSON or YAML)
49
+ - `--out <file>` (optional): Output file path for the controller (default: `src/controller/index.mts`)
50
+
51
+ ### Standalone Script Usage
52
+
53
+ You can also use the standalone script directly:
54
+
55
+ ```bash
56
+ node ./build/openapi-to-connector.mjs generate --name "My Connector" --spec api.yaml --out controller.mts
57
+ ```
58
+
59
+ #### Options
60
+
61
+ - `--name <name>` (required): Human-readable connector name
62
+ - `--spec <file>` (required): OpenAPI specification file (JSON or YAML)
63
+ - `--out <file>` (optional): Output file path (default: `index.mts`)
64
+
65
+ ### Programmatic Usage
66
+
67
+ ```typescript
68
+ import { OpenAPIToConnector } from './build/openapi-to-connector.mjs';
69
+
70
+ // Parse OpenAPI spec
71
+ const spec = OpenAPIToConnector.parseSpec(specString);
72
+
73
+ // Generate controller
74
+ const generator = new OpenAPIToConnector(spec, 'My Connector');
75
+ const controllerCode = generator.generateController();
76
+ ```
77
+
78
+ ## Example
79
+
80
+ ### Input: OpenAPI Specification
81
+
82
+ ```yaml
83
+ openapi: 3.0.0
84
+ info:
85
+ title: User API
86
+ version: 1.0.0
87
+ paths:
88
+ /users:
89
+ get:
90
+ operationId: listUsers
91
+ summary: List all users
92
+ parameters:
93
+ - name: page
94
+ in: query
95
+ schema:
96
+ type: integer
97
+ responses:
98
+ '200':
99
+ description: Success
100
+ post:
101
+ operationId: createUser
102
+ summary: Create user
103
+ requestBody:
104
+ required: true
105
+ content:
106
+ application/json:
107
+ schema:
108
+ type: object
109
+ responses:
110
+ '201':
111
+ description: Created
112
+ ```
113
+
114
+ ### Output: Generated Controller
115
+
116
+ ```typescript
117
+ import {AbstractController} from '@aloma.io/integration-sdk';
118
+
119
+ export default class Controller extends AbstractController {
120
+
121
+ /**
122
+ * List all users
123
+ *
124
+ * @param args - Request arguments
125
+ * @param args.page - Page number for pagination
126
+ * @returns Response data
127
+ */
128
+ async listUsers(args: any) {
129
+ // TODO: Implement GET /users
130
+ throw new Error('Method not implemented');
131
+ }
132
+
133
+ /**
134
+ * Create user
135
+ *
136
+ * @param args.body - Request body
137
+ * @returns Response data
138
+ */
139
+ async createUser(args: any) {
140
+ // TODO: Implement POST /users
141
+ throw new Error('Method not implemented');
142
+ }
143
+ }
144
+ ```
145
+
146
+ ## Generated Method Names
147
+
148
+ The script generates method names using the following priority:
149
+
150
+ 1. **Operation ID**: If `operationId` is present, use it directly
151
+ 2. **Method + Path**: Combine HTTP method with path segments
152
+ - `GET /users` → `getUsers`
153
+ - `POST /users/{id}/posts` → `post_users_{id}_posts`
154
+
155
+ ## Parameter Mapping
156
+
157
+ - **Path parameters**: Mapped to `args.paramName`
158
+ - **Query parameters**: Mapped to `args.paramName`
159
+ - **Request body**: Mapped to `args.body`
160
+ - **Headers**: Mapped to `args.headerName`
161
+
162
+ ## JSDoc Generation
163
+
164
+ The script generates comprehensive JSDoc comments using:
165
+
166
+ - **Summary**: From `operation.summary`
167
+ - **Description**: From `operation.description`
168
+ - **Parameters**: From `operation.parameters` with descriptions
169
+ - **Request body**: When present
170
+ - **Return type**: Always includes `@returns Response data`
171
+
172
+ ## Error Handling
173
+
174
+ The script provides clear error messages for:
175
+
176
+ - Invalid JSON/YAML syntax
177
+ - Non-OpenAPI 3.x specifications
178
+ - Missing required fields (`openapi`, `info`, `paths`)
179
+ - Empty specifications (no operations)
180
+
181
+ ## Testing
182
+
183
+ Run the included tests:
184
+
185
+ ```bash
186
+ npm test
187
+ ```
188
+
189
+ Or test manually:
190
+
191
+ ```bash
192
+ # Test with sample API
193
+ node ./build/openapi-to-connector.mjs generate --name "Sample API" --spec ./examples/sample-api.yaml --out ./test-output.mts
194
+ ```
195
+
196
+ ## Requirements Met
197
+
198
+ ✅ **Runnable script**: CLI with proper argument parsing
199
+ ✅ **OpenAPI 3.x support**: Validates and parses OpenAPI 3.x specs
200
+ ✅ **JSON/YAML support**: Handles both formats
201
+ ✅ **Deterministic**: Same input always produces same output
202
+ ✅ **No inference**: Fails fast on missing information
203
+ ✅ **Full coverage**: Generates methods for all operations
204
+ ✅ **Rich documentation**: Uses all available descriptions
205
+ ✅ **Testing**: Includes unit tests for core functionality
206
+
207
+ ## Integration with Aloma SDK
208
+
209
+ The generated controller extends `AbstractController` and follows the Aloma connector pattern:
210
+
211
+ - Each OpenAPI operation becomes a public async method
212
+ - Methods receive `args` parameter with all request data
213
+ - Methods should return response data or throw errors
214
+ - JSDoc comments provide rich documentation for the UI
215
+
216
+ The generated controller can be used directly in an Aloma connector project after implementing the actual API calls.
package/README.md CHANGED
@@ -2,4 +2,18 @@
2
2
 
3
3
  ## Creating a new Connector
4
4
 
5
- With the aloma integration SDK cli you can simply create a new connector via `npx @aloma.io/integration-sdk@latest create connectorName --connector-id 1234`.
5
+ With the aloma integration SDK cli you can create a new connector in two ways:
6
+
7
+ ### 1. Create from scratch
8
+ ```bash
9
+ npx @aloma.io/integration-sdk@latest create connectorName --connector-id 1234
10
+ ```
11
+
12
+ ### 2. Generate from OpenAPI specification
13
+ ```bash
14
+ npx @aloma.io/integration-sdk@latest from-openapi connectorName --connector-id 1234 --spec api.yaml
15
+ ```
16
+
17
+ This will automatically generate a complete connector project with methods for all OpenAPI endpoints, ready for implementation.
18
+
19
+ For more details, see [OPENAPI_TO_CONNECTOR.md](./OPENAPI_TO_CONNECTOR.md).
package/build/cli.mjs CHANGED
@@ -9,6 +9,7 @@ import { TARGET_DIR } from './builder/index.mjs';
9
9
  import { notEmpty } from './internal/util/index.mjs';
10
10
  import JWE from './internal/util/jwe/index.mjs';
11
11
  import parseTypes from './transform/index.mjs';
12
+ import { OpenAPIToConnector } from './openapi-to-connector.mjs';
12
13
  const exec = util.promisify(ChildProcess.exec);
13
14
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
15
  const files = [
@@ -76,7 +77,7 @@ program
76
77
  console.log('Generating keys ...');
77
78
  await generateKeys({ target });
78
79
  console.log('Installing dependencies ...');
79
- await exec(`cd ${target}; yarn`);
80
+ await exec(`cd ${target}; yarn --ignore-engines`);
80
81
  console.log('Building ...');
81
82
  await exec(`cd ${target}; yarn build`);
82
83
  console.log(`
@@ -101,6 +102,61 @@ program
101
102
  console.log(stdout);
102
103
  new Extractor().extract('./src/controller/index.mts', './build/.controller.json');
103
104
  });
105
+ program
106
+ .command('from-openapi')
107
+ .description('Generate a connector controller from an OpenAPI specification')
108
+ .argument('<name>', 'name of the connector project')
109
+ .requiredOption('--connector-id <id>', 'id of the connector')
110
+ .requiredOption('--spec <file>', 'OpenAPI specification file (JSON or YAML)')
111
+ .option('--out <file>', 'output file path for the controller', 'src/controller/index.mts')
112
+ .action(async (name, options) => {
113
+ name = name.replace(/[\/\.]/gi, '');
114
+ if (!name)
115
+ throw new Error('name is empty');
116
+ const target = `${process.cwd()}/${name}`;
117
+ try {
118
+ // Read and parse the OpenAPI spec
119
+ const specContent = fs.readFileSync(options.spec, 'utf-8');
120
+ const spec = OpenAPIToConnector.parseSpec(specContent);
121
+ // Create the connector project structure
122
+ fs.mkdirSync(target);
123
+ console.log('Creating connector project...');
124
+ extract({ ...options, target, name });
125
+ // Generate the controller from OpenAPI spec
126
+ console.log('Generating controller from OpenAPI specification...');
127
+ const generator = new OpenAPIToConnector(spec, name);
128
+ const controllerCode = generator.generateController();
129
+ // Write the generated controller
130
+ const controllerPath = `${target}/${options.out}`;
131
+ fs.mkdirSync(path.dirname(controllerPath), { recursive: true });
132
+ fs.writeFileSync(controllerPath, controllerCode);
133
+ console.log('Generating keys...');
134
+ await generateKeys({ target });
135
+ console.log('Installing dependencies...');
136
+ await exec(`cd ${target}; yarn --ignore-engines`);
137
+ console.log('Building...');
138
+ await exec(`cd ${target}; yarn build`);
139
+ console.log(`
140
+ ✅ Success! Generated connector from OpenAPI specification
141
+ 📝 Connector name: ${name}
142
+ 📊 Found ${generator.getOperationsCount()} operations
143
+ 📄 Controller generated: ${options.out}
144
+
145
+ Next steps:
146
+ 1.) Add the connector to a workspace
147
+ 2.) Edit ./${name}/.env and insert the registration token
148
+ 3.) Implement the actual API calls in each method in ${options.out}
149
+ 4.) Start the connector with cd ./${name}/; yarn start`);
150
+ }
151
+ catch (error) {
152
+ console.error('❌ Error:', error instanceof Error ? error.message : 'Unknown error');
153
+ // Clean up on error
154
+ if (fs.existsSync(target)) {
155
+ fs.rmSync(target, { recursive: true, force: true });
156
+ }
157
+ process.exit(1);
158
+ }
159
+ });
104
160
  class Extractor {
105
161
  async extract(source, target) {
106
162
  notEmpty(source, 'source');
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" resolution-mode="require"/>
1
2
  /**
2
3
  * http request fetcher
3
4
  */
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { OpenAPIV3 } from 'openapi-types';
3
+ export declare class OpenAPIToConnector {
4
+ private spec;
5
+ private connectorName;
6
+ constructor(spec: OpenAPIV3.Document, connectorName: string);
7
+ /**
8
+ * Parse OpenAPI spec from JSON or YAML string
9
+ */
10
+ static parseSpec(specString: string): OpenAPIV3.Document;
11
+ /**
12
+ * Extract all operations from the OpenAPI spec
13
+ */
14
+ private extractOperations;
15
+ /**
16
+ * Generate a valid JavaScript identifier from a string
17
+ */
18
+ private toValidIdentifier;
19
+ /**
20
+ * Generate method name from operation info
21
+ */
22
+ private generateMethodName;
23
+ /**
24
+ * Generate JSDoc comment for an operation
25
+ */
26
+ private generateJSDoc;
27
+ /**
28
+ * Get the number of operations in the OpenAPI spec
29
+ */
30
+ getOperationsCount(): number;
31
+ /**
32
+ * Generate the connector controller code
33
+ */
34
+ generateController(): string;
35
+ }
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import fs from 'node:fs';
4
+ import yaml from 'js-yaml';
5
+ import { z } from 'zod';
6
+ // OpenAPI 3.x validation schema
7
+ const OpenAPISchema = z.object({
8
+ openapi: z.string().regex(/^3\.\d+\.\d+$/),
9
+ info: z.object({
10
+ title: z.string(),
11
+ version: z.string(),
12
+ description: z.string().optional(),
13
+ }),
14
+ paths: z.record(z.string(), z.any()),
15
+ servers: z
16
+ .array(z.object({
17
+ url: z.string(),
18
+ description: z.string().optional(),
19
+ }))
20
+ .optional(),
21
+ components: z.any().optional(),
22
+ });
23
+ export class OpenAPIToConnector {
24
+ spec;
25
+ connectorName;
26
+ constructor(spec, connectorName) {
27
+ this.spec = spec;
28
+ this.connectorName = connectorName;
29
+ }
30
+ /**
31
+ * Parse OpenAPI spec from JSON or YAML string
32
+ */
33
+ static parseSpec(specString) {
34
+ let parsed;
35
+ try {
36
+ // Try parsing as JSON first
37
+ parsed = JSON.parse(specString);
38
+ }
39
+ catch {
40
+ try {
41
+ // If JSON fails, try YAML
42
+ parsed = yaml.load(specString);
43
+ }
44
+ catch (error) {
45
+ throw new Error(`Failed to parse OpenAPI spec: ${error instanceof Error ? error.message : 'Unknown error'}`);
46
+ }
47
+ }
48
+ // Validate against OpenAPI 3.x schema
49
+ const validationResult = OpenAPISchema.safeParse(parsed);
50
+ if (!validationResult.success) {
51
+ const errors = validationResult.error.errors.map((err) => `${err.path.join('.')}: ${err.message}`).join(', ');
52
+ throw new Error(`Invalid OpenAPI 3.x specification: ${errors}`);
53
+ }
54
+ return parsed;
55
+ }
56
+ /**
57
+ * Extract all operations from the OpenAPI spec
58
+ */
59
+ extractOperations() {
60
+ const operations = [];
61
+ for (const [path, pathItem] of Object.entries(this.spec.paths)) {
62
+ if (!pathItem)
63
+ continue;
64
+ const methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'];
65
+ for (const method of methods) {
66
+ const operation = pathItem[method];
67
+ if (!operation)
68
+ continue;
69
+ operations.push({
70
+ method: method.toUpperCase(),
71
+ path,
72
+ operationId: operation.operationId,
73
+ summary: operation.summary,
74
+ description: operation.description,
75
+ parameters: operation.parameters,
76
+ requestBody: operation.requestBody,
77
+ responses: operation.responses,
78
+ });
79
+ }
80
+ }
81
+ return operations;
82
+ }
83
+ /**
84
+ * Generate a valid JavaScript identifier from a string
85
+ */
86
+ toValidIdentifier(str) {
87
+ return str
88
+ .replace(/[^a-zA-Z0-9_$]/g, '_')
89
+ .replace(/^[0-9]/, '_$&')
90
+ .replace(/_+/g, '_')
91
+ .replace(/^_|_$/g, '');
92
+ }
93
+ /**
94
+ * Generate method name from operation info
95
+ */
96
+ generateMethodName(operation) {
97
+ if (operation.operationId) {
98
+ // Clean up HubSpot-style operationIds like "get-/crm/v3/objects/companies_getPage"
99
+ let cleaned = operation.operationId;
100
+ // Extract the last part after underscore if it exists
101
+ const parts = cleaned.split('_');
102
+ if (parts.length > 1) {
103
+ const lastPart = parts[parts.length - 1];
104
+ // If the last part looks like a method name (camelCase), use it
105
+ if (lastPart && /^[a-z][a-zA-Z0-9]*$/.test(lastPart)) {
106
+ cleaned = lastPart;
107
+ }
108
+ }
109
+ // Remove any remaining special characters and clean up
110
+ cleaned = cleaned
111
+ .replace(/^(get|post|put|patch|delete|head|options)-/i, '') // Remove HTTP method prefix
112
+ .replace(/[^a-zA-Z0-9_]/g, '_')
113
+ .replace(/_+/g, '_')
114
+ .replace(/^_|_$/g, '');
115
+ // If we still have a valid identifier, use it
116
+ if (cleaned && /^[a-zA-Z]/.test(cleaned)) {
117
+ return cleaned;
118
+ }
119
+ }
120
+ // Generate from method + path
121
+ const pathParts = operation.path
122
+ .replace(/[{}]/g, '') // Remove path parameters
123
+ .split('/')
124
+ .filter((part) => part.length > 0)
125
+ .map((part) => this.toValidIdentifier(part));
126
+ const methodPrefix = operation.method.toLowerCase();
127
+ const pathSuffix = pathParts.join('_') || 'root';
128
+ return `${methodPrefix}_${pathSuffix}`;
129
+ }
130
+ /**
131
+ * Generate JSDoc comment for an operation
132
+ */
133
+ generateJSDoc(operation) {
134
+ const lines = [];
135
+ if (operation.summary) {
136
+ lines.push(` * ${operation.summary}`);
137
+ }
138
+ if (operation.description) {
139
+ lines.push(` *`);
140
+ // Split long descriptions into multiple lines
141
+ const descLines = operation.description.split('\n');
142
+ descLines.forEach(line => {
143
+ if (line.trim()) {
144
+ lines.push(` * ${line.trim()}`);
145
+ }
146
+ });
147
+ }
148
+ // Document parameters with full details
149
+ if (operation.parameters && operation.parameters.length > 0) {
150
+ lines.push(' *');
151
+ lines.push(' * @param {Object} args - Request arguments');
152
+ for (const param of operation.parameters) {
153
+ if (typeof param === 'object' && 'name' in param) {
154
+ const paramName = param.name;
155
+ const paramDesc = param.description || '';
156
+ const paramRequired = param.required ? '(required)' : '(optional)';
157
+ const paramType = param.schema?.type || 'any';
158
+ const paramIn = param.in || '';
159
+ let paramDoc = ` * @param {${paramType}} args.${paramName} ${paramRequired}`;
160
+ if (paramDesc) {
161
+ paramDoc += ` - ${paramDesc}`;
162
+ }
163
+ if (paramIn) {
164
+ paramDoc += ` [${paramIn}]`;
165
+ }
166
+ lines.push(paramDoc);
167
+ }
168
+ }
169
+ }
170
+ // Document request body
171
+ if (operation.requestBody) {
172
+ lines.push(' *');
173
+ const bodyDesc = operation.requestBody.description || 'Request body';
174
+ const required = operation.requestBody.required ? '(required)' : '(optional)';
175
+ lines.push(` * @param {Object} args.body ${required} - ${bodyDesc}`);
176
+ }
177
+ // Document response
178
+ lines.push(' *');
179
+ lines.push(` * @returns {Promise<Object>} ${operation.method} ${operation.path} response`);
180
+ return lines.join('\n');
181
+ }
182
+ /**
183
+ * Get the number of operations in the OpenAPI spec
184
+ */
185
+ getOperationsCount() {
186
+ return this.extractOperations().length;
187
+ }
188
+ /**
189
+ * Generate the connector controller code
190
+ */
191
+ generateController() {
192
+ const operations = this.extractOperations();
193
+ if (operations.length === 0) {
194
+ throw new Error('No operations found in OpenAPI specification');
195
+ }
196
+ const methods = operations
197
+ .map((operation) => {
198
+ const methodName = this.generateMethodName(operation);
199
+ const jsdoc = this.generateJSDoc(operation);
200
+ return ` /**\n${jsdoc}\n */\n async ${methodName}(args: any) {\n // TODO: Implement ${operation.method} ${operation.path}\n throw new Error('Method not implemented');\n }`;
201
+ })
202
+ .join('\n\n');
203
+ return `import {AbstractController} from '@aloma.io/integration-sdk';
204
+
205
+ export default class Controller extends AbstractController {
206
+
207
+ ${methods}
208
+ }`;
209
+ }
210
+ }
211
+ // CLI setup
212
+ const program = new Command();
213
+ program
214
+ .name('openapi-to-connector')
215
+ .description('Generate a connector controller from an OpenAPI specification')
216
+ .version('1.0.0')
217
+ .showHelpAfterError();
218
+ program
219
+ .command('generate')
220
+ .description('Generate connector controller from OpenAPI spec')
221
+ .requiredOption('--name <name>', 'Human-readable connector name')
222
+ .requiredOption('--spec <file>', 'OpenAPI specification file (JSON or YAML)')
223
+ .option('--out <file>', 'Output file path', 'index.mts')
224
+ .action(async (options) => {
225
+ try {
226
+ // Read and parse the OpenAPI spec
227
+ const specContent = fs.readFileSync(options.spec, 'utf-8');
228
+ const spec = OpenAPIToConnector.parseSpec(specContent);
229
+ // Generate the connector controller
230
+ const generator = new OpenAPIToConnector(spec, options.name);
231
+ const controllerCode = generator.generateController();
232
+ // Write the output file
233
+ fs.writeFileSync(options.out, controllerCode);
234
+ console.log(`✅ Successfully generated connector controller: ${options.out}`);
235
+ console.log(`📝 Connector name: ${options.name}`);
236
+ console.log(`📊 Found ${generator['extractOperations']().length} operations`);
237
+ }
238
+ catch (error) {
239
+ console.error('❌ Error:', error instanceof Error ? error.message : 'Unknown error');
240
+ process.exit(1);
241
+ }
242
+ });
243
+ // Only run CLI if this file is executed directly
244
+ if (import.meta.url === `file://${process.argv[1]}`) {
245
+ // If no command is provided, show help
246
+ if (process.argv.length <= 2) {
247
+ program.help();
248
+ }
249
+ else {
250
+ program.parse();
251
+ }
252
+ }
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+
3
+ # Example script to generate a connector from OpenAPI specification
4
+ # Usage: ./generate-connector.sh <connector-name> <openapi-file> [output-file]
5
+
6
+ set -e
7
+
8
+ CONNECTOR_NAME=${1:-"My API Connector"}
9
+ OPENAPI_FILE=${2:-"./sample-api.yaml"}
10
+ OUTPUT_FILE=${3:-"./generated-controller.mts"}
11
+
12
+ echo "🚀 Generating connector: $CONNECTOR_NAME"
13
+ echo "📄 OpenAPI spec: $OPENAPI_FILE"
14
+ echo "📝 Output file: $OUTPUT_FILE"
15
+ echo ""
16
+
17
+ # Check if OpenAPI file exists
18
+ if [ ! -f "$OPENAPI_FILE" ]; then
19
+ echo "❌ Error: OpenAPI file '$OPENAPI_FILE' not found"
20
+ exit 1
21
+ fi
22
+
23
+ # Generate the connector
24
+ node ../build/openapi-to-connector.mjs generate \
25
+ --name "$CONNECTOR_NAME" \
26
+ --spec "$OPENAPI_FILE" \
27
+ --out "$OUTPUT_FILE"
28
+
29
+ echo ""
30
+ echo "✅ Success! Generated connector controller: $OUTPUT_FILE"
31
+ echo ""
32
+ echo "Next steps:"
33
+ echo "1. Review the generated controller"
34
+ echo "2. Implement the actual API calls in each method"
35
+ echo "3. Add the controller to your Aloma connector project"