@aifabrix/builder 2.11.0 → 2.20.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.
@@ -12,7 +12,8 @@
12
12
  const fs = require('fs');
13
13
  const chalk = require('chalk');
14
14
  const { getDeploymentAuth } = require('./utils/token-manager');
15
- const { authenticatedApiCall } = require('./utils/api');
15
+ const { getEnvironmentApplication } = require('./api/environments.api');
16
+ const { publishDatasourceViaPipeline } = require('./api/pipeline.api');
16
17
  const { formatApiError } = require('./utils/api-error-handler');
17
18
  const logger = require('./utils/logger');
18
19
  const { validateDatasourceFile } = require('./datasource-validate');
@@ -30,18 +31,8 @@ const { validateDatasourceFile } = require('./datasource-validate');
30
31
  * @throws {Error} If dataplane URL cannot be retrieved
31
32
  */
32
33
  async function getDataplaneUrl(controllerUrl, appKey, environment, authConfig) {
33
- // Call controller API to get application details
34
- // Expected: GET /api/v1/environments/{env}/applications/{appKey}
35
- const endpoint = `${controllerUrl}/api/v1/environments/${environment}/applications/${appKey}`;
36
-
37
- let response;
38
- if (authConfig.type === 'bearer' && authConfig.token) {
39
- response = await authenticatedApiCall(endpoint, {}, authConfig.token);
40
- } else {
41
- // For credentials, we'd need to use a different API call method
42
- // For now, use bearer token approach
43
- throw new Error('Bearer token authentication required for getting dataplane URL');
44
- }
34
+ // Call controller API to get application details using centralized API client
35
+ const response = await getEnvironmentApplication(controllerUrl, environment, appKey, authConfig);
45
36
 
46
37
  if (!response.success || !response.data) {
47
38
  const formattedError = response.formattedError || formatApiError(response);
@@ -129,24 +120,10 @@ async function deployDatasource(appKey, filePath, options) {
129
120
  const dataplaneUrl = await getDataplaneUrl(options.controller, appKey, options.environment, authConfig);
130
121
  logger.log(chalk.green(`āœ“ Dataplane URL: ${dataplaneUrl}`));
131
122
 
132
- // Publish to dataplane (using publish endpoint)
123
+ // Publish to dataplane using pipeline workflow endpoint
133
124
  logger.log(chalk.blue('\nšŸš€ Publishing datasource to dataplane...'));
134
- const publishEndpoint = `${dataplaneUrl}/api/v1/pipeline/${systemKey}/publish`;
135
-
136
- // Prepare publish request - send datasource configuration directly
137
- let publishResponse;
138
- if (authConfig.type === 'bearer' && authConfig.token) {
139
- publishResponse = await authenticatedApiCall(
140
- publishEndpoint,
141
- {
142
- method: 'POST',
143
- body: JSON.stringify(datasourceConfig)
144
- },
145
- authConfig.token
146
- );
147
- } else {
148
- throw new Error('Bearer token authentication required for dataplane publish');
149
- }
125
+
126
+ const publishResponse = await publishDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig, datasourceConfig);
150
127
 
151
128
  if (!publishResponse.success) {
152
129
  const formattedError = publishResponse.formattedError || formatApiError(publishResponse);
@@ -11,7 +11,7 @@
11
11
  const chalk = require('chalk');
12
12
  const { getConfig } = require('./config');
13
13
  const { getOrRefreshDeviceToken } = require('./utils/token-manager');
14
- const { authenticatedApiCall } = require('./utils/api');
14
+ const { listEnvironmentDatasources } = require('./api/environments.api');
15
15
  const { formatApiError } = require('./utils/api-error-handler');
16
16
  const logger = require('./utils/logger');
17
17
 
@@ -20,11 +20,15 @@ const logger = require('./utils/logger');
20
20
  * Handles multiple response formats similar to applications list
21
21
  *
22
22
  * @function extractDatasources
23
- * @param {Object} response - API response from authenticatedApiCall
23
+ * @param {Object} response - API response from centralized API client
24
24
  * @returns {Array} Array of datasources
25
25
  * @throws {Error} If response format is invalid
26
26
  */
27
27
  function extractDatasources(response) {
28
+ if (!response.success || !response.data) {
29
+ throw new Error('Invalid API response: missing success or data');
30
+ }
31
+
28
32
  const apiResponse = response.data;
29
33
  let datasources;
30
34
 
@@ -112,10 +116,9 @@ async function listDatasources(options) {
112
116
  process.exit(1);
113
117
  }
114
118
 
115
- // Call controller API - using placeholder endpoint until full specs available
116
- // Expected: GET /api/v1/environments/{env}/datasources
117
- const endpoint = `${controllerUrl}/api/v1/environments/${options.environment}/datasources`;
118
- const response = await authenticatedApiCall(endpoint, {}, token);
119
+ // Call controller API using centralized API client
120
+ const authConfig = { type: 'bearer', token };
121
+ const response = await listEnvironmentDatasources(controllerUrl, options.environment, authConfig);
119
122
 
120
123
  if (!response.success || !response.data) {
121
124
  const formattedError = response.formattedError || formatApiError(response);
package/lib/deployer.js CHANGED
@@ -9,84 +9,12 @@
9
9
  * @version 2.0.0
10
10
  */
11
11
 
12
- const axios = require('axios');
13
12
  const chalk = require('chalk');
14
13
  const auditLogger = require('./audit-logger');
15
14
  const logger = require('./utils/logger');
16
- const { createAuthHeaders } = require('./utils/auth-headers');
17
15
  const { validateControllerUrl, validateEnvironmentKey } = require('./utils/deployment-validation');
18
16
  const { handleDeploymentError, handleDeploymentErrors } = require('./utils/deployment-errors');
19
-
20
- /**
21
- * Handles deployment request retry logic
22
- * @async
23
- * @function handleDeploymentRetry
24
- * @param {string} endpoint - API endpoint
25
- * @param {Object} manifest - Deployment manifest
26
- * @param {Object} requestConfig - Request configuration
27
- * @param {number} maxRetries - Maximum retry attempts
28
- * @returns {Promise<Object>} Deployment result
29
- * @throws {Error} If deployment fails after retries
30
- */
31
- async function handleDeploymentRetry(endpoint, manifest, requestConfig, maxRetries) {
32
- let lastError;
33
-
34
- // Validate manifest before sending
35
- if (!manifest || typeof manifest !== 'object') {
36
- throw new Error('Deployment manifest is required and must be an object');
37
- }
38
-
39
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
40
- try {
41
- // Axios automatically serializes objects to JSON when Content-Type is application/json
42
- const response = await axios.post(endpoint, manifest, requestConfig);
43
-
44
- if (response.status >= 400) {
45
- const error = new Error(`Controller returned error: ${response.status} ${response.statusText}`);
46
- error.status = response.status;
47
- error.response = {
48
- status: response.status,
49
- statusText: response.statusText,
50
- data: response.data
51
- };
52
- error.data = response.data;
53
- throw error;
54
- }
55
-
56
- // OpenAPI spec: Response 202 structure { success: boolean, deploymentId: string, status: string, deploymentUrl?: string, healthCheckUrl?: string, message?: string }
57
- // Handle both OpenAPI format and legacy format for backward compatibility
58
- const responseData = response.data;
59
- if (responseData && typeof responseData === 'object') {
60
- // OpenAPI format: { success, deploymentId, status, deploymentUrl, healthCheckUrl, message }
61
- return responseData;
62
- }
63
-
64
- return response.data;
65
- } catch (error) {
66
- lastError = error;
67
-
68
- if (attempt < maxRetries) {
69
- const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
70
- logger.log(chalk.yellow(`āš ļø Deployment attempt ${attempt} failed, retrying in ${delay}ms...`));
71
- await new Promise(resolve => setTimeout(resolve, delay));
72
- }
73
- }
74
- }
75
-
76
- // Preserve formatted error if available
77
- const errorMessage = lastError.formatted || lastError.message;
78
- const error = new Error(`Deployment failed after ${maxRetries} attempts: ${errorMessage}`);
79
- if (lastError.formatted) {
80
- error.formatted = lastError.formatted;
81
- }
82
- if (lastError.status) {
83
- error.status = lastError.status;
84
- }
85
- if (lastError.data) {
86
- error.data = lastError.data;
87
- }
88
- throw error;
89
- }
17
+ const { validatePipeline, deployPipeline, getPipelineDeployment } = require('./api/pipeline.api');
90
18
 
91
19
  /**
92
20
  * Validates deployment configuration via validate endpoint
@@ -103,8 +31,6 @@ async function handleDeploymentRetry(endpoint, manifest, requestConfig, maxRetri
103
31
  */
104
32
  async function validateDeployment(url, envKey, manifest, authConfig, options = {}) {
105
33
  const validatedEnvKey = validateEnvironmentKey(envKey);
106
- const endpoint = `${url}/api/v1/pipeline/${validatedEnvKey}/validate`;
107
- const timeout = options.timeout || 30000;
108
34
  const maxRetries = options.maxRetries || 3;
109
35
 
110
36
  // Extract clientId and clientSecret
@@ -118,58 +44,68 @@ async function validateDeployment(url, envKey, manifest, authConfig, options = {
118
44
 
119
45
  // Build validation request
120
46
  const repositoryUrl = options.repositoryUrl || `https://github.com/aifabrix/${manifest.key}`;
121
- const validationRequest = {
47
+ const validationData = {
122
48
  clientId: clientId,
123
49
  clientSecret: clientSecret,
124
50
  repositoryUrl: repositoryUrl,
125
51
  applicationConfig: manifest
126
52
  };
127
53
 
128
- // Create request config with client credentials headers
129
- const requestConfig = {
130
- headers: {
131
- 'Content-Type': 'application/json',
132
- 'User-Agent': 'aifabrix-builder/2.0.0',
133
- 'x-client-id': clientId,
134
- 'x-client-secret': clientSecret
135
- },
136
- timeout,
137
- validateStatus: (status) => status < 600 // Don't throw on any status
54
+ // Use centralized API client with retry logic
55
+ const pipelineAuthConfig = {
56
+ type: 'client-credentials',
57
+ clientId: clientId,
58
+ clientSecret: clientSecret
138
59
  };
139
60
 
140
61
  let lastError;
141
62
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
142
63
  try {
143
- const response = await axios.post(endpoint, validationRequest, requestConfig);
64
+ const response = await validatePipeline(url, validatedEnvKey, pipelineAuthConfig, validationData);
144
65
 
145
66
  // Handle successful validation (200 OK with valid: true)
146
- if (response.status === 200 && response.data.valid === true) {
147
- return {
148
- success: true,
149
- validateToken: response.data.validateToken,
150
- draftDeploymentId: response.data.draftDeploymentId,
151
- imageServer: response.data.imageServer,
152
- imageUsername: response.data.imageUsername,
153
- imagePassword: response.data.imagePassword,
154
- expiresAt: response.data.expiresAt
155
- };
67
+ if (response.success && response.data) {
68
+ const responseData = response.data.data || response.data;
69
+ if (responseData.valid === true) {
70
+ return {
71
+ success: true,
72
+ validateToken: responseData.validateToken,
73
+ draftDeploymentId: responseData.draftDeploymentId,
74
+ imageServer: responseData.imageServer,
75
+ imageUsername: responseData.imageUsername,
76
+ imagePassword: responseData.imagePassword,
77
+ expiresAt: responseData.expiresAt
78
+ };
79
+ }
80
+ // Handle validation failure (valid: false)
81
+ if (responseData.valid === false) {
82
+ const errorMessage = responseData.errors && responseData.errors.length > 0
83
+ ? `Validation failed: ${responseData.errors.join(', ')}`
84
+ : 'Validation failed: Invalid configuration';
85
+ const error = new Error(errorMessage);
86
+ error.status = 400;
87
+ error.data = responseData;
88
+ throw error;
89
+ }
156
90
  }
157
91
 
158
- // Handle validation errors
159
- if (response.status >= 400) {
160
- const error = new Error(`Validation request failed: ${response.status} ${response.statusText}`);
161
- error.status = response.status;
162
- error.response = {
163
- status: response.status,
164
- statusText: response.statusText,
165
- data: response.data
166
- };
92
+ // Handle validation errors (non-success responses)
93
+ if (!response.success) {
94
+ const error = new Error(`Validation request failed: ${response.formattedError || response.error || 'Unknown error'}`);
95
+ error.status = response.status || 400;
167
96
  error.data = response.data;
168
97
  throw error;
169
98
  }
99
+
100
+ // If we get here, response.success is true but valid is not true and not false
101
+ // This is an unexpected state, throw an error
102
+ const error = new Error('Validation response is in an unexpected state');
103
+ error.status = 400;
104
+ error.data = response.data;
105
+ throw error;
170
106
  } catch (error) {
171
107
  lastError = error;
172
- const shouldRetry = attempt < maxRetries && error.response && error.response.status >= 500;
108
+ const shouldRetry = attempt < maxRetries && error.status && error.status >= 500;
173
109
  if (shouldRetry) {
174
110
  const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
175
111
  logger.log(chalk.yellow(`āš ļø Validation attempt ${attempt} failed, retrying in ${delay}ms...`));
@@ -198,8 +134,6 @@ async function validateDeployment(url, envKey, manifest, authConfig, options = {
198
134
  */
199
135
  async function sendDeploymentRequest(url, envKey, validateToken, authConfig, options = {}) {
200
136
  const validatedEnvKey = validateEnvironmentKey(envKey);
201
- const endpoint = `${url}/api/v1/pipeline/${validatedEnvKey}/deploy`;
202
- const timeout = options.timeout || 30000;
203
137
  const maxRetries = options.maxRetries || 3;
204
138
 
205
139
  // Extract clientId and clientSecret for deploy endpoint
@@ -212,24 +146,58 @@ async function sendDeploymentRequest(url, envKey, validateToken, authConfig, opt
212
146
 
213
147
  // Build deployment request
214
148
  const imageTag = options.imageTag || 'latest';
215
- const deployRequest = {
149
+ const deployData = {
216
150
  validateToken: validateToken,
217
151
  imageTag: imageTag
218
152
  };
219
153
 
220
- // Create request config with client credentials headers
221
- const requestConfig = {
222
- headers: {
223
- 'Content-Type': 'application/json',
224
- 'User-Agent': 'aifabrix-builder/2.0.0',
225
- 'x-client-id': clientId,
226
- 'x-client-secret': clientSecret
227
- },
228
- timeout,
229
- validateStatus: (status) => status < 500
154
+ // Use centralized API client with retry logic
155
+ const pipelineAuthConfig = {
156
+ type: 'client-credentials',
157
+ clientId: clientId,
158
+ clientSecret: clientSecret
230
159
  };
231
160
 
232
- return handleDeploymentRetry(endpoint, deployRequest, requestConfig, maxRetries);
161
+ // Wrap API call with retry logic
162
+ let lastError;
163
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
164
+ try {
165
+ const response = await deployPipeline(url, validatedEnvKey, pipelineAuthConfig, deployData);
166
+
167
+ if (response.success) {
168
+ return response.data.data || response.data;
169
+ }
170
+
171
+ // Handle deployment errors
172
+ if (response.status >= 400) {
173
+ const error = new Error(`Deployment request failed: ${response.formattedError || response.error || 'Unknown error'}`);
174
+ error.status = response.status;
175
+ error.data = response.data;
176
+ throw error;
177
+ }
178
+ } catch (error) {
179
+ lastError = error;
180
+ if (attempt < maxRetries) {
181
+ const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
182
+ logger.log(chalk.yellow(`āš ļø Deployment attempt ${attempt} failed, retrying in ${delay}ms...`));
183
+ await new Promise(resolve => setTimeout(resolve, delay));
184
+ }
185
+ }
186
+ }
187
+
188
+ // Preserve formatted error if available
189
+ const errorMessage = lastError.formatted || lastError.message;
190
+ const error = new Error(`Deployment failed after ${maxRetries} attempts: ${errorMessage}`);
191
+ if (lastError.formatted) {
192
+ error.formatted = lastError.formatted;
193
+ }
194
+ if (lastError.status) {
195
+ error.status = lastError.status;
196
+ }
197
+ if (lastError.data) {
198
+ error.data = lastError.data;
199
+ }
200
+ throw error;
233
201
  }
234
202
 
235
203
  /**
@@ -259,20 +227,17 @@ async function pollDeploymentStatus(deploymentId, controllerUrl, envKey, authCon
259
227
  const maxAttempts = options.maxAttempts || 60;
260
228
 
261
229
  const validatedEnvKey = validateEnvironmentKey(envKey);
262
- const statusEndpoint = `${controllerUrl}/api/v1/pipeline/${validatedEnvKey}/deployments/${deploymentId}`;
230
+
231
+ // Convert authConfig to format expected by API client
232
+ const pipelineAuthConfig = authConfig.type === 'bearer'
233
+ ? { type: 'bearer', token: authConfig.token }
234
+ : { type: 'client-credentials', clientId: authConfig.clientId, clientSecret: authConfig.clientSecret };
263
235
 
264
236
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
265
237
  try {
266
- const response = await axios.get(statusEndpoint, {
267
- headers: {
268
- 'Content-Type': 'application/json',
269
- ...createAuthHeaders(authConfig)
270
- },
271
- timeout: 10000,
272
- validateStatus: (status) => status < 500
273
- });
274
-
275
- if (response.status === 200) {
238
+ const response = await getPipelineDeployment(controllerUrl, validatedEnvKey, deploymentId, pipelineAuthConfig);
239
+
240
+ if (response.success && response.data) {
276
241
  // OpenAPI spec: Response structure { success: boolean, data: { status, progress, ... }, timestamp: string }
277
242
  const responseData = response.data;
278
243
  const deploymentData = responseData.data || responseData;
@@ -289,11 +254,14 @@ async function pollDeploymentStatus(deploymentId, controllerUrl, envKey, authCon
289
254
  await new Promise(resolve => setTimeout(resolve, interval));
290
255
  }
291
256
  } else {
292
- throw new Error(`Status check failed: ${response.status}`);
257
+ if (response.status === 404) {
258
+ throw new Error(`Deployment ${deploymentId} not found`);
259
+ }
260
+ throw new Error(`Status check failed: ${response.formattedError || response.error || 'Unknown error'}`);
293
261
  }
294
262
  } catch (error) {
295
- if (error.response && error.response.status === 404) {
296
- throw new Error(`Deployment ${deploymentId} not found`);
263
+ if (error.message && error.message.includes('not found')) {
264
+ throw error;
297
265
  }
298
266
  throw error;
299
267
  }
@@ -14,7 +14,8 @@ const logger = require('./utils/logger');
14
14
  const config = require('./config');
15
15
  const { validateControllerUrl, validateEnvironmentKey } = require('./utils/deployment-validation');
16
16
  const { getOrRefreshDeviceToken } = require('./utils/token-manager');
17
- const { authenticatedApiCall } = require('./utils/api');
17
+ const { getEnvironmentStatus } = require('./api/environments.api');
18
+ const { deployEnvironment: deployEnvironmentInfra } = require('./api/deployments.api');
18
19
  const { handleDeploymentErrors } = require('./utils/deployment-errors');
19
20
  const auditLogger = require('./audit-logger');
20
21
 
@@ -93,32 +94,23 @@ async function sendEnvironmentDeployment(controllerUrl, envKey, authConfig, opti
93
94
  deploymentRequest.description += ` (config: ${options.config})`;
94
95
  }
95
96
 
96
- // API endpoint: POST /api/v1/environments/{env}/deploy
97
- // Alternative: POST /api/v1/environments/deploy with environment in body
98
- const endpoint = `${validatedUrl}/api/v1/environments/${validatedEnvKey}/deploy`;
99
-
100
97
  // Log deployment attempt for audit
101
98
  await auditLogger.logDeploymentAttempt(validatedEnvKey, validatedUrl, options);
102
99
 
103
100
  try {
104
- const response = await authenticatedApiCall(
105
- endpoint,
106
- {
107
- method: 'POST',
108
- body: JSON.stringify(deploymentRequest)
109
- },
110
- authConfig.token
111
- );
101
+ // Use centralized API client
102
+ const apiAuthConfig = { type: 'bearer', token: authConfig.token };
103
+ const response = await deployEnvironmentInfra(validatedUrl, validatedEnvKey, apiAuthConfig, deploymentRequest);
112
104
 
113
105
  if (!response.success) {
114
106
  const error = new Error(response.formattedError || response.error || 'Environment deployment failed');
115
107
  error.status = response.status;
116
- error.data = response.errorData;
108
+ error.data = response.errorData || response.data;
117
109
  throw error;
118
110
  }
119
111
 
120
112
  // Handle response structure
121
- const responseData = response.data || {};
113
+ const responseData = response.data.data || response.data || {};
122
114
  return {
123
115
  success: true,
124
116
  environment: validatedEnvKey,
@@ -150,25 +142,22 @@ async function pollEnvironmentStatus(deploymentId, controllerUrl, envKey, authCo
150
142
 
151
143
  const pollInterval = options.pollInterval || 5000;
152
144
  const maxAttempts = options.maxAttempts || 60;
153
- const statusEndpoint = `${validatedUrl}/api/v1/environments/${validatedEnvKey}/status`;
154
145
 
155
146
  logger.log(chalk.blue(`ā³ Polling environment status (${pollInterval}ms intervals)...`));
156
147
 
148
+ // Use centralized API client
149
+ const apiAuthConfig = { type: 'bearer', token: authConfig.token };
150
+
157
151
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
158
152
  try {
159
153
  await new Promise(resolve => setTimeout(resolve, pollInterval));
160
154
 
161
- const response = await authenticatedApiCall(
162
- statusEndpoint,
163
- {
164
- method: 'GET'
165
- },
166
- authConfig.token
167
- );
155
+ const response = await getEnvironmentStatus(validatedUrl, validatedEnvKey, apiAuthConfig);
168
156
 
169
157
  if (response.success && response.data) {
170
- const status = response.data.status || response.data.ready;
171
- const isReady = status === 'ready' || status === 'completed' || response.data.ready === true;
158
+ const responseData = response.data.data || response.data;
159
+ const status = responseData.status || responseData.ready;
160
+ const isReady = status === 'ready' || status === 'completed' || responseData.ready === true;
172
161
 
173
162
  if (isReady) {
174
163
  return {
@@ -181,7 +170,7 @@ async function pollEnvironmentStatus(deploymentId, controllerUrl, envKey, authCo
181
170
 
182
171
  // Check for terminal failure states
183
172
  if (status === 'failed' || status === 'error') {
184
- throw new Error(`Environment deployment failed: ${response.data.message || 'Unknown error'}`);
173
+ throw new Error(`Environment deployment failed: ${responseData.message || 'Unknown error'}`);
185
174
  }
186
175
  }
187
176
  } catch (error) {
@@ -14,7 +14,13 @@ const fsSync = require('fs');
14
14
  const path = require('path');
15
15
  const yaml = require('js-yaml');
16
16
  const chalk = require('chalk');
17
- const { authenticatedApiCall } = require('./utils/api');
17
+ const {
18
+ deployExternalSystemViaPipeline,
19
+ deployDatasourceViaPipeline,
20
+ uploadApplicationViaPipeline,
21
+ validateUploadViaPipeline,
22
+ publishUploadViaPipeline
23
+ } = require('./api/pipeline.api');
18
24
  const { getDeploymentAuth } = require('./utils/token-manager');
19
25
  const { getConfig } = require('./config');
20
26
  const logger = require('./utils/logger');
@@ -155,14 +161,7 @@ async function buildExternalSystem(appName, options = {}) {
155
161
  const systemContent = await fs.readFile(systemFiles[0], 'utf8');
156
162
  const systemJson = JSON.parse(systemContent);
157
163
 
158
- const systemResponse = await authenticatedApiCall(
159
- `${dataplaneUrl}/api/v1/pipeline/deploy`,
160
- {
161
- method: 'POST',
162
- body: JSON.stringify(systemJson)
163
- },
164
- authConfig.token
165
- );
164
+ const systemResponse = await deployExternalSystemViaPipeline(dataplaneUrl, authConfig, systemJson);
166
165
 
167
166
  if (!systemResponse.success) {
168
167
  throw new Error(`Failed to deploy external system: ${systemResponse.error || systemResponse.formattedError}`);
@@ -178,14 +177,7 @@ async function buildExternalSystem(appName, options = {}) {
178
177
  const datasourceContent = await fs.readFile(datasourceFile, 'utf8');
179
178
  const datasourceJson = JSON.parse(datasourceContent);
180
179
 
181
- const datasourceResponse = await authenticatedApiCall(
182
- `${dataplaneUrl}/api/v1/pipeline/${systemKey}/deploy`,
183
- {
184
- method: 'POST',
185
- body: JSON.stringify(datasourceJson)
186
- },
187
- authConfig.token
188
- );
180
+ const datasourceResponse = await deployDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig, datasourceJson);
189
181
 
190
182
  if (!datasourceResponse.success) {
191
183
  throw new Error(`Failed to deploy datasource ${datasourceName}: ${datasourceResponse.error || datasourceResponse.formattedError}`);
@@ -245,14 +237,7 @@ async function deployExternalSystem(appName, options = {}) {
245
237
 
246
238
  // Step 1: Upload application
247
239
  logger.log(chalk.blue('šŸ“¤ Uploading application configuration...'));
248
- const uploadResponse = await authenticatedApiCall(
249
- `${dataplaneUrl}/api/v1/pipeline/upload`,
250
- {
251
- method: 'POST',
252
- body: JSON.stringify(applicationSchema)
253
- },
254
- authConfig.token
255
- );
240
+ const uploadResponse = await uploadApplicationViaPipeline(dataplaneUrl, authConfig, applicationSchema);
256
241
 
257
242
  if (!uploadResponse.success || !uploadResponse.data) {
258
243
  throw new Error(`Failed to upload application: ${uploadResponse.error || uploadResponse.formattedError || 'Unknown error'}`);
@@ -270,13 +255,7 @@ async function deployExternalSystem(appName, options = {}) {
270
255
  // Step 2: Validate upload (optional, can be skipped)
271
256
  if (!options.skipValidation) {
272
257
  logger.log(chalk.blue('šŸ” Validating upload...'));
273
- const validateResponse = await authenticatedApiCall(
274
- `${dataplaneUrl}/api/v1/pipeline/upload/${uploadId}/validate`,
275
- {
276
- method: 'POST'
277
- },
278
- authConfig.token
279
- );
258
+ const validateResponse = await validateUploadViaPipeline(dataplaneUrl, uploadId, authConfig);
280
259
 
281
260
  if (!validateResponse.success || !validateResponse.data) {
282
261
  throw new Error(`Validation failed: ${validateResponse.error || validateResponse.formattedError || 'Unknown error'}`);
@@ -308,13 +287,7 @@ async function deployExternalSystem(appName, options = {}) {
308
287
  const generateMcpContract = options.generateMcpContract !== false; // Default to true
309
288
  logger.log(chalk.blue(`šŸ“¢ Publishing application (MCP contract: ${generateMcpContract ? 'enabled' : 'disabled'})...`));
310
289
 
311
- const publishResponse = await authenticatedApiCall(
312
- `${dataplaneUrl}/api/v1/pipeline/upload/${uploadId}/publish?generateMcpContract=${generateMcpContract}`,
313
- {
314
- method: 'POST'
315
- },
316
- authConfig.token
317
- );
290
+ const publishResponse = await publishUploadViaPipeline(dataplaneUrl, uploadId, authConfig, { generateMcpContract });
318
291
 
319
292
  if (!publishResponse.success || !publishResponse.data) {
320
293
  throw new Error(`Failed to publish application: ${publishResponse.error || publishResponse.formattedError || 'Unknown error'}`);
@@ -14,7 +14,7 @@ const path = require('path');
14
14
  const os = require('os');
15
15
  const yaml = require('js-yaml');
16
16
  const chalk = require('chalk');
17
- const { authenticatedApiCall } = require('./utils/api');
17
+ const { getExternalSystemConfig } = require('./api/external-systems.api');
18
18
  const { getDeploymentAuth } = require('./utils/token-manager');
19
19
  const { getDataplaneUrl } = require('./datasource-deploy');
20
20
  const { getConfig } = require('./config');
@@ -284,14 +284,12 @@ async function downloadExternalSystem(systemKey, options = {}) {
284
284
  const dataplaneUrl = await getDataplaneUrl(controllerUrl, systemKey, environment, authConfig);
285
285
  logger.log(chalk.green(`āœ“ Dataplane URL: ${dataplaneUrl}`));
286
286
 
287
- // Download system configuration
288
- // Note: Verify this endpoint exists. Alternative: GET /api/v1/pipeline/{systemIdOrKey}
289
- const downloadEndpoint = `${dataplaneUrl}/api/v1/external/systems/${systemKey}/config`;
290
- logger.log(chalk.blue(`šŸ“” Downloading from: ${downloadEndpoint}`));
287
+ // Download system configuration using centralized API client
288
+ logger.log(chalk.blue(`šŸ“” Downloading system configuration: ${systemKey}`));
291
289
 
292
290
  if (options.dryRun) {
293
291
  logger.log(chalk.yellow('šŸ” Dry run mode - would download from:'));
294
- logger.log(chalk.gray(` ${downloadEndpoint}`));
292
+ logger.log(chalk.gray(` ${dataplaneUrl}/api/v1/external/systems/${systemKey}/config`));
295
293
  logger.log(chalk.yellow('\nWould create:'));
296
294
  logger.log(chalk.gray(` integration/${systemKey}/`));
297
295
  logger.log(chalk.gray(` integration/${systemKey}/variables.yaml`));
@@ -301,13 +299,7 @@ async function downloadExternalSystem(systemKey, options = {}) {
301
299
  return;
302
300
  }
303
301
 
304
- const response = await authenticatedApiCall(
305
- downloadEndpoint,
306
- {
307
- method: 'GET'
308
- },
309
- authConfig.token
310
- );
302
+ const response = await getExternalSystemConfig(dataplaneUrl, systemKey, authConfig);
311
303
 
312
304
  if (!response.success || !response.data) {
313
305
  throw new Error(`Failed to download system configuration: ${response.error || response.formattedError || 'Unknown error'}`);