@aifabrix/builder 2.40.0 → 2.40.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.
package/README.md CHANGED
@@ -16,7 +16,7 @@ Install the AI Fabrix platform and test it locally. Then add external integratio
16
16
  - **Full lifecycle in your version control:** Configuration, apps, and integrations live in your own VCS (GitHub, GitLab, Azure DevOps).
17
17
  - **One tool from day one:** Single CLI for local infra, app and integration creation, build, run, and deploy—same workflow for apps and integrations.
18
18
  - **Consistency and production readiness:** Schema-driven; deploy apps and integrations to the same controller/dataplane; production-ready secrets with `kv://` and Azure Key Vault.
19
- - **Application development:** Use **[miso-client](https://github.com/esystemsdev/aifabrix-miso-client)** for TypeScript and Python to talk to the dataplane and controller (see [templates/applications/dataplane/README.md](templates/applications/dataplane/README.md) and the repo for usage).
19
+ - **Application development:** Use **[miso-client](https://github.com/esystemsdev/aifabrix-miso-client)** (TypeScript and Python) to talk to the dataplane and controller. The repo includes both TypeScript and Python SDKs; see [templates/applications/dataplane/README.md](templates/applications/dataplane/README.md) and the repo for usage.
20
20
 
21
21
  ---
22
22
 
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Jest Configuration for Manual Tests (real API calls)
3
+ * Runs only tests/manual; requires user to be logged in (validated before run).
4
+ *
5
+ * @fileoverview Jest config for manual tests that call real Controller/Dataplane APIs
6
+ * @author AI Fabrix Team
7
+ * @version 2.0.0
8
+ */
9
+
10
+ const baseProject = require('./jest.config').projects[0];
11
+
12
+ module.exports = {
13
+ projects: [
14
+ {
15
+ ...baseProject,
16
+ displayName: 'manual',
17
+ testMatch: [
18
+ '**/tests/manual/**/*.test.js'
19
+ ],
20
+ testPathIgnorePatterns: [
21
+ '/node_modules/',
22
+ '\\\\node_modules\\\\'
23
+ ],
24
+ setupFilesAfterEnv: ['<rootDir>/tests/manual/setup.js'],
25
+ testTimeout: 60000,
26
+ maxWorkers: 1
27
+ }
28
+ ]
29
+ };
@@ -63,6 +63,7 @@ function setupAuthSubcommands(program) {
63
63
  const auth = program.command('auth').description('Authentication commands');
64
64
  auth.command('status')
65
65
  .description('Display authentication status for current controller and environment')
66
+ .option('--validate', 'Exit with code 1 when not authenticated (for scripted use, e.g. manual test setup)')
66
67
  .action(authStatusHandler);
67
68
  auth.command('config')
68
69
  .description('Configure authentication settings (controller, environment)')
@@ -183,11 +183,17 @@ function setupSplitJsonConvertShowCommands(program) {
183
183
  function setupValidateDiffCommands(program) {
184
184
  program.command('validate <appOrFile>')
185
185
  .description('Validate application or external integration file')
186
+ .option('--format <format>', 'Output format: json | default (human-readable)')
186
187
  .action(async(appOrFile, options) => {
187
188
  try {
188
189
  const validate = require('../validation/validate');
189
190
  const result = await validate.validateAppOrFile(appOrFile, options);
190
- validate.displayValidationResults(result);
191
+ const outFormat = (options.format || 'default').toLowerCase();
192
+ if (outFormat === 'json') {
193
+ logger.log(JSON.stringify(result, null, 2));
194
+ } else {
195
+ validate.displayValidationResults(result);
196
+ }
191
197
  if (!result.valid) process.exit(1);
192
198
  } catch (error) {
193
199
  handleCommandError(error, 'validate');
@@ -317,10 +317,11 @@ function displayStatus(controllerUrl, environment, tokenInfo, dataplaneInfo) {
317
317
  * Controller and environment come from config.yaml (set via aifabrix login or aifabrix auth config).
318
318
  * @async
319
319
  * @function handleAuthStatus
320
- * @param {Object} _options - Command options (unused; controller/environment from config only)
320
+ * @param {Object} options - Command options
321
+ * @param {boolean} [options.validate] - If true, exit with code 1 when not authenticated
321
322
  * @returns {Promise<void>} Resolves when status is displayed
322
323
  */
323
- async function handleAuthStatus(_options) {
324
+ async function handleAuthStatus(options = {}) {
324
325
  const { resolveEnvironment } = require('../core/config');
325
326
  const controllerUrl = await resolveControllerUrl();
326
327
  const environment = await resolveEnvironment();
@@ -342,6 +343,38 @@ async function handleAuthStatus(_options) {
342
343
  }
343
344
 
344
345
  displayStatus(controllerUrl, environment, tokenInfo, dataplaneInfo);
346
+
347
+ if (options.validate && (!tokenInfo || !tokenInfo.authenticated)) {
348
+ process.exit(1);
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Get resolved auth context (controller URL, environment, authConfig) for use after validation.
354
+ * Call only when auth status --validate has already passed (e.g. in manual tests).
355
+ * @async
356
+ * @function getValidatedAuthContext
357
+ * @returns {Promise<{controllerUrl: string, environment: string, authConfig: Object, dataplaneUrl: string|null}>}
358
+ * @throws {Error} If not authenticated
359
+ */
360
+ async function getValidatedAuthContext() {
361
+ const { resolveEnvironment } = require('../core/config');
362
+ const controllerUrl = await resolveControllerUrl();
363
+ const environment = await resolveEnvironment();
364
+
365
+ let tokenInfo = await checkDeviceToken(controllerUrl);
366
+ if (!tokenInfo) {
367
+ tokenInfo = await checkClientToken(controllerUrl, environment);
368
+ }
369
+
370
+ if (!tokenInfo || !tokenInfo.authenticated || !tokenInfo.token) {
371
+ throw new Error('Not authenticated. Run "aifabrix login" or configure client credentials.');
372
+ }
373
+
374
+ const authConfig = { type: 'bearer', token: tokenInfo.token };
375
+ const dataplaneUrl = await resolveDataplaneUrlSilent(controllerUrl, environment, authConfig);
376
+
377
+ return { controllerUrl, environment, authConfig, dataplaneUrl };
345
378
  }
346
379
 
347
- module.exports = { handleAuthStatus };
380
+ module.exports = { handleAuthStatus, getValidatedAuthContext };
@@ -2,6 +2,10 @@
2
2
  * @fileoverview Wizard file generator - saves dataplane-generated configurations to files
3
3
  * @author AI Fabrix Team
4
4
  * @version 2.0.0
5
+ *
6
+ * Standard credential-backed variable names (supplied at runtime from the selected credential;
7
+ * do not list in configuration array): CLIENTID, CLIENTSECRET, TOKENURL, APIKEY, USERNAME,
8
+ * PASSWORD, BASEURL. See docs/external-systems.md and docs/wizard.md.
5
9
  */
6
10
 
7
11
  const fs = require('fs').promises;
@@ -322,7 +326,7 @@ function addAuthenticationLines(lines, auth, authType) {
322
326
  function addBaseUrlLines(lines, systemConfig) {
323
327
  if (systemConfig.baseUrl || systemConfig.baseURL) {
324
328
  lines.push('# API Base URL');
325
- lines.push(`BASE_URL=${systemConfig.baseUrl || systemConfig.baseURL}`);
329
+ lines.push(`BASEURL=${systemConfig.baseUrl || systemConfig.baseURL}`);
326
330
  lines.push('');
327
331
  }
328
332
  }
@@ -178,6 +178,12 @@ function checkGlobalProjectRoot() {
178
178
  return null;
179
179
  }
180
180
 
181
+ // In test environment, allow temp dir as project root (Jest sets NODE_ENV=test / JEST_WORKER_ID)
182
+ const isTestEnv = process.env.NODE_ENV === 'test' || typeof process.env.JEST_WORKER_ID !== 'undefined';
183
+ if (isTestEnv) {
184
+ return globalRoot;
185
+ }
186
+
181
187
  // Verify that __dirname is actually within globalRoot
182
188
  const dirnameNormalized = path.resolve(__dirname);
183
189
  const globalRootNormalized = path.resolve(globalRoot);
@@ -452,7 +452,7 @@ async function validateAppOrFile(appOrFile, options = {}) {
452
452
  logOfflinePathWhenType(appPath);
453
453
 
454
454
  if (isExternal) {
455
- return await validateExternalSystemComplete(appName);
455
+ return await validateExternalSystemComplete(appName, options);
456
456
  }
457
457
 
458
458
  const appValidation = await validator.validateApplication(appName, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aifabrix/builder",
3
- "version": "2.40.0",
3
+ "version": "2.40.2",
4
4
  "description": "AI Fabrix Local Fabric & Deployment SDK",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -16,11 +16,13 @@
16
16
  "test:integration": "jest --config jest.config.integration.js --runInBand",
17
17
  "test:integration:python": "cross-env TEST_LANGUAGE=python jest --config jest.config.integration.js --runInBand",
18
18
  "test:integration:typescript": "cross-env TEST_LANGUAGE=typescript jest --config jest.config.integration.js --runInBand",
19
+ "test:manual": "jest --config jest.config.manual.js --runInBand",
19
20
  "lint": "eslint . --ext .js",
20
21
  "lint:fix": "eslint . --ext .js --fix",
21
22
  "lint:ci": "eslint . --ext .js --format json --output-file eslint-report.json",
22
23
  "dev": "node bin/aifabrix.js",
23
24
  "build": "npm run lint && npm run test",
25
+ "build:ci": "npm run lint && npm run test:ci",
24
26
  "pack": "npm run build && npm pack",
25
27
  "validate": "npm run build",
26
28
  "prepublishOnly": "npm run validate",
@@ -30,22 +30,7 @@
30
30
  "toolPrefix": "{{systemKey}}"
31
31
  }{{/if}},
32
32
  "tags": [],
33
- "configuration": [
34
- {
35
- "name": "BASE_URL",
36
- "value": "{{#if baseUrl}}{{baseUrl}}{{else}}https://api.example.com{{/if}}",
37
- "location": "variable",
38
- "required": true,
39
- "portalInput": {
40
- "field": "text",
41
- "label": "Base URL",
42
- "placeholder": "https://api.example.com",
43
- "validation": {
44
- "required": true
45
- }
46
- }
47
- }
48
- ]{{#if roles}},
33
+ "configuration": []{{#if roles}},
49
34
  "roles": [
50
35
  {{#each roles}}
51
36
  {