@aifabrix/builder 2.3.6 → 2.4.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/lib/config.js CHANGED
@@ -13,14 +13,15 @@ const fs = require('fs').promises;
13
13
  const path = require('path');
14
14
  const yaml = require('js-yaml');
15
15
  const os = require('os');
16
- const paths = require('./utils/paths');
16
+ // Avoid importing paths here to prevent circular dependency.
17
+ // Config location is always under OS home at ~/.aifabrix/config.yaml
17
18
 
18
19
  // Default (for tests and constants): always reflects OS home
19
20
  const CONFIG_DIR = path.join(os.homedir(), '.aifabrix');
20
21
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.yaml');
21
22
 
22
- // Runtime (respects AIFABRIX_HOME override)
23
- const RUNTIME_CONFIG_DIR = paths.getAifabrixHome();
23
+ // Runtime config directory (always under OS home)
24
+ const RUNTIME_CONFIG_DIR = path.join(os.homedir(), '.aifabrix');
24
25
  const RUNTIME_CONFIG_FILE = path.join(RUNTIME_CONFIG_DIR, 'config.yaml');
25
26
 
26
27
  // Cache for developer ID - loaded when getConfig() is first called
@@ -368,7 +369,8 @@ async function setSecretsEncryptionKey(key) {
368
369
  */
369
370
  async function getSecretsPath() {
370
371
  const config = await getConfig();
371
- return config['secrets-path'] || null;
372
+ // Backward compatibility: prefer new key, fallback to legacy
373
+ return config['aifabrix-secrets'] || config['secrets-path'] || null;
372
374
  }
373
375
 
374
376
  /**
@@ -382,7 +384,54 @@ async function setSecretsPath(secretsPath) {
382
384
  }
383
385
 
384
386
  const config = await getConfig();
385
- config['secrets-path'] = secretsPath;
387
+ // Store under new canonical key
388
+ config['aifabrix-secrets'] = secretsPath;
389
+ await saveConfig(config);
390
+ }
391
+
392
+ /**
393
+ * Get aifabrix-home override from configuration
394
+ * @returns {Promise<string|null>} Home override path or null if not set
395
+ */
396
+ async function getAifabrixHomeOverride() {
397
+ const config = await getConfig();
398
+ return config['aifabrix-home'] || null;
399
+ }
400
+
401
+ /**
402
+ * Set aifabrix-home override in configuration
403
+ * @param {string} homePath - Base directory path for AI Fabrix files
404
+ * @returns {Promise<void>}
405
+ */
406
+ async function setAifabrixHomeOverride(homePath) {
407
+ if (!homePath || typeof homePath !== 'string') {
408
+ throw new Error('Home path is required and must be a string');
409
+ }
410
+ const config = await getConfig();
411
+ config['aifabrix-home'] = homePath;
412
+ await saveConfig(config);
413
+ }
414
+
415
+ /**
416
+ * Get aifabrix-secrets path from configuration (canonical)
417
+ * @returns {Promise<string|null>} Secrets path or null if not set
418
+ */
419
+ async function getAifabrixSecretsPath() {
420
+ const config = await getConfig();
421
+ return config['aifabrix-secrets'] || null;
422
+ }
423
+
424
+ /**
425
+ * Set aifabrix-secrets path in configuration (canonical)
426
+ * @param {string} secretsPath - Path to default secrets file
427
+ * @returns {Promise<void>}
428
+ */
429
+ async function setAifabrixSecretsPath(secretsPath) {
430
+ if (!secretsPath || typeof secretsPath !== 'string') {
431
+ throw new Error('Secrets path is required and must be a string');
432
+ }
433
+ const config = await getConfig();
434
+ config['aifabrix-secrets'] = secretsPath;
386
435
  await saveConfig(config);
387
436
  }
388
437
 
@@ -405,6 +454,10 @@ const exportsObj = {
405
454
  setSecretsEncryptionKey,
406
455
  getSecretsPath,
407
456
  setSecretsPath,
457
+ getAifabrixHomeOverride,
458
+ setAifabrixHomeOverride,
459
+ getAifabrixSecretsPath,
460
+ setAifabrixSecretsPath,
408
461
  CONFIG_DIR,
409
462
  CONFIG_FILE
410
463
  };
package/lib/secrets.js CHANGED
@@ -12,7 +12,6 @@
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
14
  const yaml = require('js-yaml');
15
- const os = require('os');
16
15
  const chalk = require('chalk');
17
16
  const logger = require('./utils/logger');
18
17
  const config = require('./config');
@@ -33,6 +32,7 @@ const {
33
32
  resolveUrlPort
34
33
  } = require('./utils/secrets-utils');
35
34
  const { decryptSecret, isEncrypted } = require('./utils/secrets-encryption');
35
+ const pathsUtil = require('./utils/paths');
36
36
 
37
37
  /**
38
38
  * Loads environment configuration for docker/local context
@@ -414,7 +414,7 @@ async function generateAdminSecretsEnv(secretsPath) {
414
414
  secrets = await loadSecrets(secretsPath);
415
415
  } catch (error) {
416
416
  // If secrets file doesn't exist, create default secrets
417
- const defaultSecretsPath = secretsPath || path.join(os.homedir(), '.aifabrix', 'secrets.yaml');
417
+ const defaultSecretsPath = secretsPath || path.join(pathsUtil.getAifabrixHome(), 'secrets.yaml');
418
418
 
419
419
  if (!fs.existsSync(defaultSecretsPath)) {
420
420
  logger.log('Creating default secrets file...');
@@ -425,7 +425,7 @@ async function generateAdminSecretsEnv(secretsPath) {
425
425
  }
426
426
  }
427
427
 
428
- const aifabrixDir = path.join(os.homedir(), '.aifabrix');
428
+ const aifabrixDir = pathsUtil.getAifabrixHome();
429
429
  const adminEnvPath = path.join(aifabrixDir, 'admin-secrets.env');
430
430
 
431
431
  if (!fs.existsSync(aifabrixDir)) {
@@ -13,25 +13,45 @@
13
13
 
14
14
  const path = require('path');
15
15
  const os = require('os');
16
+ const fs = require('fs');
17
+ const yaml = require('js-yaml');
18
+
19
+ function safeHomedir() {
20
+ try {
21
+ if (typeof os.homedir === 'function') {
22
+ const hd = os.homedir();
23
+ if (typeof hd === 'string' && hd.length > 0) {
24
+ return hd;
25
+ }
26
+ }
27
+ } catch {
28
+ // ignore
29
+ }
30
+ return process.env.HOME || process.env.USERPROFILE || '/';
31
+ }
16
32
 
17
33
  /**
18
34
  * Returns the base AI Fabrix directory.
19
- * Respects AIFABRIX_HOME environment variable, otherwise defaults to ~/.aifabrix
35
+ * Resolved from config.yaml `aifabrix-home` (stored under OS home).
36
+ * Falls back to ~/.aifabrix when not specified.
20
37
  *
21
38
  * @returns {string} Absolute path to the AI Fabrix home directory
22
39
  */
23
40
  function getAifabrixHome() {
24
- // In test environments, ignore AIFABRIX_HOME to keep deterministic paths for unit tests
25
- // Jest sets JEST_WORKER_ID; NODE_ENV may be 'test'
26
- const isTestEnv = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined;
27
- if (isTestEnv) {
28
- return path.join(os.homedir(), '.aifabrix');
29
- }
30
- const homeOverride = process.env.AIFABRIX_HOME && String(process.env.AIFABRIX_HOME).trim();
31
- if (homeOverride) {
32
- return path.resolve(homeOverride);
41
+ try {
42
+ const configPath = path.join(safeHomedir(), '.aifabrix', 'config.yaml');
43
+ if (fs.existsSync(configPath)) {
44
+ const content = fs.readFileSync(configPath, 'utf8');
45
+ const config = yaml.load(content) || {};
46
+ const homeOverride = config && typeof config['aifabrix-home'] === 'string' ? config['aifabrix-home'].trim() : '';
47
+ if (homeOverride) {
48
+ return path.resolve(homeOverride);
49
+ }
50
+ }
51
+ } catch {
52
+ // Ignore errors and fall back to default
33
53
  }
34
- return path.join(os.homedir(), '.aifabrix');
54
+ return path.join(safeHomedir(), '.aifabrix');
35
55
  }
36
56
 
37
57
  /**
@@ -11,54 +11,32 @@
11
11
 
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
- const os = require('os');
15
14
  const yaml = require('js-yaml');
16
15
  const config = require('../config');
17
16
  const paths = require('./paths');
18
17
 
19
18
  /**
20
- * Resolves secrets file path (backward compatibility)
21
- * Checks common locations if path is not provided
19
+ * Resolves secrets file path when an explicit path is provided.
20
+ * If not provided, returns default fallback under <home>/secrets.yaml.
22
21
  * @function resolveSecretsPath
23
22
  * @param {string} [secretsPath] - Path to secrets file (optional)
24
23
  * @returns {string} Resolved secrets file path
25
24
  */
26
25
  function resolveSecretsPath(secretsPath) {
27
- let resolvedPath = secretsPath;
28
-
29
- if (!resolvedPath) {
30
- // Check common locations for secrets.local.yaml
31
- const commonLocations = [
32
- path.join(process.cwd(), '..', 'aifabrix-setup', 'secrets.local.yaml'),
33
- path.join(process.cwd(), '..', '..', 'aifabrix-setup', 'secrets.local.yaml'),
34
- path.join(process.cwd(), 'secrets.local.yaml'),
35
- path.join(process.cwd(), '..', 'secrets.local.yaml'),
36
- path.join(paths.getAifabrixHome(), 'secrets.yaml')
37
- ];
38
-
39
- // Find first existing file
40
- for (const location of commonLocations) {
41
- if (fs.existsSync(location)) {
42
- resolvedPath = location;
43
- break;
44
- }
45
- }
46
-
47
- // If none found, use default location
48
- if (!resolvedPath) {
49
- resolvedPath = path.join(paths.getAifabrixHome(), 'secrets.yaml');
50
- }
51
- } else if (secretsPath.startsWith('..')) {
52
- resolvedPath = path.resolve(process.cwd(), secretsPath);
26
+ if (secretsPath && secretsPath.startsWith('..')) {
27
+ return path.resolve(process.cwd(), secretsPath);
53
28
  }
54
-
55
- return resolvedPath;
29
+ if (secretsPath) {
30
+ return secretsPath;
31
+ }
32
+ // Default fallback
33
+ return path.join(paths.getAifabrixHome(), 'secrets.yaml');
56
34
  }
57
35
 
58
36
  /**
59
37
  * Determines the actual secrets file paths that loadSecrets would use
60
38
  * Mirrors the cascading lookup logic from loadSecrets
61
- * Checks config.yaml for general secrets-path as fallback
39
+ * Uses config.yaml for default secrets path as fallback
62
40
  *
63
41
  * @async
64
42
  * @function getActualSecretsPath
@@ -78,8 +56,8 @@ async function getActualSecretsPath(secretsPath, appName) {
78
56
  };
79
57
  }
80
58
 
81
- // Cascading lookup: user's file first
82
- const userSecretsPath = path.join(os.homedir(), '.aifabrix', 'secrets.local.yaml');
59
+ // Cascading lookup: user's file first (under configured home)
60
+ const userSecretsPath = path.join(paths.getAifabrixHome(), 'secrets.local.yaml');
83
61
 
84
62
  // Check build.secrets from variables.yaml if appName provided
85
63
  let buildSecretsPath = null;
@@ -102,15 +80,14 @@ async function getActualSecretsPath(secretsPath, appName) {
102
80
  }
103
81
  }
104
82
 
105
- // If no build.secrets found in variables.yaml, check config.yaml for general secrets-path
83
+ // If no build.secrets found in variables.yaml, check config.yaml for canonical secrets path
106
84
  if (!buildSecretsPath) {
107
85
  try {
108
- const generalSecretsPath = await config.getSecretsPath();
109
- if (generalSecretsPath) {
110
- // Resolve relative paths from current working directory
111
- buildSecretsPath = path.isAbsolute(generalSecretsPath)
112
- ? generalSecretsPath
113
- : path.resolve(process.cwd(), generalSecretsPath);
86
+ const canonicalSecretsPath = await config.getAifabrixSecretsPath();
87
+ if (canonicalSecretsPath) {
88
+ buildSecretsPath = path.isAbsolute(canonicalSecretsPath)
89
+ ? canonicalSecretsPath
90
+ : path.resolve(process.cwd(), canonicalSecretsPath);
114
91
  }
115
92
  } catch (error) {
116
93
  // Ignore errors, continue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aifabrix/builder",
3
- "version": "2.3.6",
3
+ "version": "2.4.0",
4
4
  "description": "AI Fabrix Local Fabric & Deployment SDK",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -148,11 +148,13 @@ aifabrix login --method credentials --app {{appName}} --environment dev
148
148
  aifabrix login --method credentials --app {{appName}} --client-id $CLIENT_ID --client-secret $CLIENT_SECRET --environment dev
149
149
  ```
150
150
 
151
- ### Environment Variables
151
+ ### Configuration
152
152
 
153
- ```bash
154
- export AIFABRIX_HOME=/custom/path
155
- export AIFABRIX_SECRETS=/path/to/secrets.yaml
153
+ Set overrides in `~/.aifabrix/config.yaml`:
154
+
155
+ ```yaml
156
+ aifabrix-home: "/custom/path"
157
+ aifabrix-secrets: "/path/to/secrets.yaml"
156
158
  ```
157
159
 
158
160
  ---