@aifabrix/builder 2.36.1 → 2.36.2
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.
|
@@ -728,7 +728,7 @@ async function runCommandWithErrorHandling(command, args, options, appName) {
|
|
|
728
728
|
const errorOutput = `${result.stdout}\n${result.stderr}`;
|
|
729
729
|
if (appName && (errorOutput.includes('not found') ||
|
|
730
730
|
(errorOutput.includes('External system') && errorOutput.includes('not found')))) {
|
|
731
|
-
|
|
731
|
+
logInfo(`System ${appName} not deployed on dataplane; download step omitted (optional).`);
|
|
732
732
|
return { skipped: true };
|
|
733
733
|
}
|
|
734
734
|
throw new Error(`${command} failed: ${result.stderr || result.stdout}`);
|
package/lib/api/pipeline.api.js
CHANGED
|
@@ -81,9 +81,33 @@ async function getPipelineHealth(controllerUrl, envKey) {
|
|
|
81
81
|
return await client.get(`/api/v1/pipeline/${envKey}/health`);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Publish one external system via dataplane pipeline endpoint
|
|
86
|
+
* POST /api/v1/pipeline/publish
|
|
87
|
+
* Request body: external system JSON (external-system.schema.json). Optional field in body:
|
|
88
|
+
* generateMcpContract (boolean, default true). Optional: generateOpenApiContract (boolean).
|
|
89
|
+
* Do not use query parameters for MCP; use the field in the body only.
|
|
90
|
+
*
|
|
91
|
+
* @async
|
|
92
|
+
* @function publishSystemViaPipeline
|
|
93
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
94
|
+
* @param {Object} authConfig - Authentication configuration
|
|
95
|
+
* @param {Object} systemConfig - External system configuration (conforms to external-system.schema.json)
|
|
96
|
+
* @returns {Promise<Object>} Published external system response
|
|
97
|
+
* @throws {Error} If publish fails
|
|
98
|
+
*/
|
|
99
|
+
async function publishSystemViaPipeline(dataplaneUrl, authConfig, systemConfig) {
|
|
100
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
101
|
+
return await client.post('/api/v1/pipeline/publish', {
|
|
102
|
+
body: systemConfig
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
84
106
|
/**
|
|
85
107
|
* Publish datasource via dataplane pipeline endpoint
|
|
86
108
|
* POST /api/v1/pipeline/{systemKey}/publish
|
|
109
|
+
* No generateMcpContract for this endpoint; dataplane always uses default (MCP generated).
|
|
110
|
+
*
|
|
87
111
|
* @async
|
|
88
112
|
* @function publishDatasourceViaPipeline
|
|
89
113
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -169,11 +193,15 @@ async function deployDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig,
|
|
|
169
193
|
/**
|
|
170
194
|
* Upload application configuration via dataplane pipeline endpoint
|
|
171
195
|
* POST /api/v1/pipeline/upload
|
|
196
|
+
* Body: { version, application, dataSources }. Include application.generateMcpContract
|
|
197
|
+
* and/or application.generateOpenApiContract to control contract generation when
|
|
198
|
+
* publishing this upload (publish reads from stored config; no query param on publish).
|
|
199
|
+
*
|
|
172
200
|
* @async
|
|
173
201
|
* @function uploadApplicationViaPipeline
|
|
174
202
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
175
203
|
* @param {Object} authConfig - Authentication configuration
|
|
176
|
-
* @param {Object} applicationSchema -
|
|
204
|
+
* @param {Object} applicationSchema - { version, application, dataSources }; application may include generateMcpContract, generateOpenApiContract
|
|
177
205
|
* @returns {Promise<Object>} Upload response with uploadId
|
|
178
206
|
* @throws {Error} If upload fails
|
|
179
207
|
*/
|
|
@@ -203,20 +231,22 @@ async function validateUploadViaPipeline(dataplaneUrl, uploadId, authConfig) {
|
|
|
203
231
|
/**
|
|
204
232
|
* Publish upload via dataplane pipeline endpoint
|
|
205
233
|
* POST /api/v1/pipeline/upload/{uploadId}/publish
|
|
234
|
+
* No body or query parameters. MCP/OpenAPI generation is taken from the application config
|
|
235
|
+
* that was uploaded (application.generateMcpContract, application.generateOpenApiContract).
|
|
236
|
+
* To control MCP/OpenAPI, include those fields in the application object when calling
|
|
237
|
+
* uploadApplicationViaPipeline.
|
|
238
|
+
*
|
|
206
239
|
* @async
|
|
207
240
|
* @function publishUploadViaPipeline
|
|
208
241
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
209
242
|
* @param {string} uploadId - Upload ID
|
|
210
243
|
* @param {Object} authConfig - Authentication configuration
|
|
211
|
-
* @param {Object} [options] - Publish options
|
|
212
|
-
* @param {boolean} [options.generateMcpContract] - Generate MCP contract (default: true)
|
|
213
244
|
* @returns {Promise<Object>} Publish response
|
|
214
245
|
* @throws {Error} If publish fails
|
|
215
246
|
*/
|
|
216
|
-
async function publishUploadViaPipeline(dataplaneUrl, uploadId, authConfig
|
|
247
|
+
async function publishUploadViaPipeline(dataplaneUrl, uploadId, authConfig) {
|
|
217
248
|
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
218
|
-
|
|
219
|
-
return await client.post(`/api/v1/pipeline/upload/${uploadId}/publish?generateMcpContract=${generateMcpContract}`);
|
|
249
|
+
return await client.post(`/api/v1/pipeline/upload/${uploadId}/publish`);
|
|
220
250
|
}
|
|
221
251
|
|
|
222
252
|
module.exports = {
|
|
@@ -224,6 +254,7 @@ module.exports = {
|
|
|
224
254
|
deployPipeline,
|
|
225
255
|
getPipelineDeployment,
|
|
226
256
|
getPipelineHealth,
|
|
257
|
+
publishSystemViaPipeline,
|
|
227
258
|
publishDatasourceViaPipeline,
|
|
228
259
|
testDatasourceViaPipeline,
|
|
229
260
|
deployExternalSystemViaPipeline,
|
package/lib/app/list.js
CHANGED
|
@@ -168,7 +168,7 @@ function displayApplications(applications, environment, controllerUrl) {
|
|
|
168
168
|
const urlAndPort = formatUrlAndPort(app);
|
|
169
169
|
logger.log(`${hasPipeline} ${chalk.cyan(app.key)} - ${app.displayName} (${app.status || 'unknown'})${urlAndPort}`);
|
|
170
170
|
});
|
|
171
|
-
logger.log('');
|
|
171
|
+
logger.log(chalk.gray(' To show details for an app: aifabrix app show <appKey>\n'));
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
/**
|
|
@@ -197,15 +197,15 @@ function displayTokenInfo(tokenInfo) {
|
|
|
197
197
|
const statusIcon = tokenInfo.authenticated ? chalk.green('✓') : chalk.red('✗');
|
|
198
198
|
const statusText = tokenInfo.authenticated ? 'Authenticated' : 'Not authenticated';
|
|
199
199
|
|
|
200
|
-
logger.log(`Status: ${statusIcon} ${statusText}`);
|
|
201
|
-
logger.log(`Token Type: ${chalk.cyan(tokenInfo.type)}`);
|
|
200
|
+
logger.log(` Status: ${statusIcon} ${statusText}`);
|
|
201
|
+
logger.log(` Token Type: ${chalk.cyan(tokenInfo.type)}`);
|
|
202
202
|
|
|
203
203
|
if (tokenInfo.appName) {
|
|
204
|
-
logger.log(`Application: ${chalk.cyan(tokenInfo.appName)}`);
|
|
204
|
+
logger.log(` Application: ${chalk.cyan(tokenInfo.appName)}`);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
if (tokenInfo.expiresAt) {
|
|
208
|
-
logger.log(`Expires: ${chalk.gray(formatExpiration(tokenInfo.expiresAt))}`);
|
|
208
|
+
logger.log(` Expires: ${chalk.gray(formatExpiration(tokenInfo.expiresAt))}`);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
if (tokenInfo.error) {
|
|
@@ -241,14 +241,43 @@ async function resolveDataplaneUrlSilent(controllerUrl, environment, authConfig)
|
|
|
241
241
|
* @param {boolean} dataplaneConnected - Whether dataplane health check passed
|
|
242
242
|
*/
|
|
243
243
|
function displayDataplaneSection(dataplaneUrl, dataplaneConnected) {
|
|
244
|
+
logger.log('');
|
|
244
245
|
if (dataplaneUrl) {
|
|
245
246
|
logger.log(`Dataplane: ${chalk.cyan(dataplaneUrl)}`);
|
|
246
247
|
const statusIcon = dataplaneConnected ? chalk.green('✓') : chalk.red('✗');
|
|
247
248
|
const statusText = dataplaneConnected ? 'Connected' : 'Not reachable';
|
|
248
|
-
|
|
249
|
+
displayOpenApiDocs(null, dataplaneUrl);
|
|
250
|
+
logger.log('');
|
|
251
|
+
logger.log(` Status: ${statusIcon} ${statusText}`);
|
|
249
252
|
} else {
|
|
250
253
|
logger.log(`Dataplane: ${chalk.gray('—')}`);
|
|
251
|
-
logger.log(
|
|
254
|
+
logger.log('');
|
|
255
|
+
logger.log(` Status: ${chalk.gray('Not discovered')}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Normalize base URL (no trailing slash) for docs path
|
|
261
|
+
* @param {string} url - Base URL
|
|
262
|
+
* @returns {string} URL without trailing slash
|
|
263
|
+
*/
|
|
264
|
+
function normalizeBaseUrl(url) {
|
|
265
|
+
return (url || '').replace(/\/$/, '');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Display Open API documentation links (Controller and Dataplane)
|
|
270
|
+
* @param {string} controllerUrl - Controller URL
|
|
271
|
+
* @param {string|null} dataplaneUrl - Dataplane URL or null
|
|
272
|
+
*/
|
|
273
|
+
function displayOpenApiDocs(controllerUrl, dataplaneUrl) {
|
|
274
|
+
const controllerBase = normalizeBaseUrl(controllerUrl);
|
|
275
|
+
if (controllerBase) {
|
|
276
|
+
logger.log(` Open API docs: ${chalk.cyan(controllerBase + '/api/docs')}`);
|
|
277
|
+
}
|
|
278
|
+
if (dataplaneUrl) {
|
|
279
|
+
const dataplaneBase = normalizeBaseUrl(dataplaneUrl);
|
|
280
|
+
logger.log(` Open API docs: ${chalk.cyan(dataplaneBase + '/api/docs')}`);
|
|
252
281
|
}
|
|
253
282
|
}
|
|
254
283
|
|
|
@@ -264,11 +293,12 @@ function displayDataplaneSection(dataplaneUrl, dataplaneConnected) {
|
|
|
264
293
|
function displayStatus(controllerUrl, environment, tokenInfo, dataplaneInfo) {
|
|
265
294
|
logger.log(chalk.bold('\n🔐 Authentication Status\n'));
|
|
266
295
|
logger.log(`Controller: ${chalk.cyan(controllerUrl)}`);
|
|
267
|
-
|
|
296
|
+
displayOpenApiDocs(controllerUrl, null);
|
|
297
|
+
logger.log(` Environment: ${chalk.cyan(environment || 'Not specified')}\n`);
|
|
268
298
|
|
|
269
299
|
if (!tokenInfo) {
|
|
270
|
-
logger.log(`Status: ${chalk.red('✗ Not authenticated')}`);
|
|
271
|
-
logger.log(`Token Type: ${chalk.gray('None')}\n`);
|
|
300
|
+
logger.log(` Status: ${chalk.red('✗ Not authenticated')}`);
|
|
301
|
+
logger.log(` Token Type: ${chalk.gray('None')}\n`);
|
|
272
302
|
logger.log(chalk.yellow('💡 Run "aifabrix login" to authenticate\n'));
|
|
273
303
|
return;
|
|
274
304
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"key": "external-system-schema",
|
|
8
8
|
"name": "External System Configuration Schema",
|
|
9
9
|
"description": "JSON schema for validating ExternalSystem configuration files",
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.2.0",
|
|
11
11
|
"type": "schema",
|
|
12
12
|
"category": "integration",
|
|
13
13
|
"author": "AI Fabrix Team",
|
|
@@ -27,6 +27,15 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": [],
|
|
29
29
|
"changelog": [
|
|
30
|
+
{
|
|
31
|
+
"version": "1.2.0",
|
|
32
|
+
"date": "2025-02-01T00:00:00Z",
|
|
33
|
+
"changes": [
|
|
34
|
+
"Added generateMcpContract (boolean, default true): config-only control for MCP contract generation on publish",
|
|
35
|
+
"Added generateOpenApiContract (boolean, default true): reserved for future use"
|
|
36
|
+
],
|
|
37
|
+
"breaking": false
|
|
38
|
+
},
|
|
30
39
|
{
|
|
31
40
|
"version": "1.1.0",
|
|
32
41
|
"date": "2025-12-01T00:00:00Z",
|
|
@@ -407,6 +416,20 @@
|
|
|
407
416
|
"type": "boolean",
|
|
408
417
|
"description": "Master switch for all endpoints in this system. If false, no endpoints are registered regardless of individual endpoint active flags.",
|
|
409
418
|
"default": true
|
|
419
|
+
},
|
|
420
|
+
"credentialIdOrKey": {
|
|
421
|
+
"type": "string",
|
|
422
|
+
"description": "Credential identifier (ID or key) to use for authenticating with this external system."
|
|
423
|
+
},
|
|
424
|
+
"generateMcpContract": {
|
|
425
|
+
"type": "boolean",
|
|
426
|
+
"description": "Whether to generate MCP contract on publish. Config only (no query parameter); default true when absent.",
|
|
427
|
+
"default": true
|
|
428
|
+
},
|
|
429
|
+
"generateOpenApiContract": {
|
|
430
|
+
"type": "boolean",
|
|
431
|
+
"description": "Reserved: whether to generate or expose OpenAPI contract on publish. Not yet implemented.",
|
|
432
|
+
"default": true
|
|
410
433
|
}
|
|
411
434
|
},
|
|
412
435
|
"additionalProperties": false
|
|
@@ -251,9 +251,8 @@ async function tryClientTokenAuth(environment, appName, controllerUrl) {
|
|
|
251
251
|
controller: clientToken.controller
|
|
252
252
|
};
|
|
253
253
|
}
|
|
254
|
-
} catch
|
|
255
|
-
// Client token unavailable
|
|
256
|
-
logger.warn(`Client token unavailable: ${error.message}`);
|
|
254
|
+
} catch {
|
|
255
|
+
// Client token unavailable; getDeploymentAuth will try client credentials next (no warning here to avoid misleading output when env credentials succeed)
|
|
257
256
|
}
|
|
258
257
|
return null;
|
|
259
258
|
}
|