@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 +58 -5
- package/lib/secrets.js +3 -3
- package/lib/utils/paths.js +31 -11
- package/lib/utils/secrets-path.js +18 -41
- package/package.json +1 -1
- package/templates/applications/README.md.hbs +6 -4
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
|
-
|
|
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 (
|
|
23
|
-
const RUNTIME_CONFIG_DIR =
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
428
|
+
const aifabrixDir = pathsUtil.getAifabrixHome();
|
|
429
429
|
const adminEnvPath = path.join(aifabrixDir, 'admin-secrets.env');
|
|
430
430
|
|
|
431
431
|
if (!fs.existsSync(aifabrixDir)) {
|
package/lib/utils/paths.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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(
|
|
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
|
|
21
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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(
|
|
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
|
|
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
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
@@ -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
|
-
###
|
|
151
|
+
### Configuration
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
---
|