@fentz26/envcp 1.2.0-exp.1 → 1.2.0-exp.8d5b91b
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 +8 -1
- package/VERSION +1 -1
- package/dist/adapters/base.d.ts +18 -3
- package/dist/adapters/base.d.ts.map +1 -1
- package/dist/adapters/base.js +86 -21
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/gemini.d.ts +1 -1
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +5 -3
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +5 -3
- package/dist/adapters/openai.js.map +1 -1
- package/dist/adapters/rest.d.ts +1 -1
- package/dist/adapters/rest.d.ts.map +1 -1
- package/dist/adapters/rest.js +29 -13
- package/dist/adapters/rest.js.map +1 -1
- package/dist/cli/index.js +170 -46
- package/dist/cli/index.js.map +1 -1
- package/dist/config/manager.d.ts +6 -2
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +31 -13
- package/dist/config/manager.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +6 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/server/unified.d.ts.map +1 -1
- package/dist/server/unified.js +9 -9
- package/dist/server/unified.js.map +1 -1
- package/dist/storage/index.d.ts +13 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +32 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/fs.d.ts +1 -0
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +12 -0
- package/dist/utils/fs.js.map +1 -1
- package/dist/vault/index.d.ts +2 -0
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +12 -2
- package/dist/vault/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { promptPassword, promptInput, promptConfirm, promptList } from '../utils
|
|
|
7
7
|
import { ensureDir, pathExists } from '../utils/fs.js';
|
|
8
8
|
import { loadConfig, initConfig, saveConfig, parseEnvFile, registerMcpConfig, isBlacklisted, canAccess } from '../config/manager.js';
|
|
9
9
|
import { ConfigGuard } from '../config/config-guard.js';
|
|
10
|
-
import { StorageManager, LogManager } from '../storage/index.js';
|
|
10
|
+
import { StorageManager, LogManager, resolveLogPath } from '../storage/index.js';
|
|
11
11
|
import { VERSION } from '../version.js';
|
|
12
12
|
import { SessionManager } from '../utils/session.js';
|
|
13
13
|
import { maskValue, validatePassword, encrypt, decrypt, generateRecoveryKey, createRecoveryData, recoverPassword } from '../utils/crypto.js';
|
|
@@ -16,11 +16,25 @@ import { HsmManager } from '../utils/hsm.js';
|
|
|
16
16
|
import { checkForUpdate, formatUpdateMessage, logUpdateCheck } from '../utils/update-checker.js';
|
|
17
17
|
import { LockoutManager } from '../utils/lockout.js';
|
|
18
18
|
import { initMemoryProtection } from '../utils/secure-memory.js';
|
|
19
|
-
import { getGlobalVaultPath, getProjectVaultPath, resolveVaultPath, setActiveVault, listVaults, initNamedVault, } from '../vault/index.js';
|
|
19
|
+
import { getGlobalVaultPath, getProjectVaultPath, resolveVaultPath, resolveSessionPath, setActiveVault, listVaults, initNamedVault, } from '../vault/index.js';
|
|
20
|
+
import { findProjectRoot } from '../utils/fs.js';
|
|
20
21
|
initMemoryProtection();
|
|
21
|
-
async function
|
|
22
|
+
async function resolveCliContext(vaultOverride) {
|
|
23
|
+
if (vaultOverride === 'global') {
|
|
24
|
+
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
25
|
+
const config = await loadConfig(home);
|
|
26
|
+
config.vault = { ...config.vault, mode: 'global' };
|
|
27
|
+
return { projectPath: home, config };
|
|
28
|
+
}
|
|
22
29
|
const projectPath = process.cwd();
|
|
23
30
|
const config = await loadConfig(projectPath);
|
|
31
|
+
if (vaultOverride === 'project') {
|
|
32
|
+
config.vault = { ...config.vault, mode: 'project' };
|
|
33
|
+
}
|
|
34
|
+
return { projectPath, config };
|
|
35
|
+
}
|
|
36
|
+
async function withSession(fn, vaultOverride) {
|
|
37
|
+
const { projectPath, config } = await resolveCliContext(vaultOverride);
|
|
24
38
|
const vaultPath = vaultOverride
|
|
25
39
|
? vaultOverride === 'global'
|
|
26
40
|
? getGlobalVaultPath(config)
|
|
@@ -28,15 +42,15 @@ async function withSession(fn, vaultOverride) {
|
|
|
28
42
|
: await resolveVaultPath(projectPath, config);
|
|
29
43
|
if (config.encryption?.enabled === false) {
|
|
30
44
|
const storage = new StorageManager(vaultPath, false);
|
|
31
|
-
const logManager = new LogManager(
|
|
45
|
+
const logManager = new LogManager(resolveLogPath(config.audit, projectPath), config.audit);
|
|
32
46
|
await logManager.init();
|
|
33
47
|
await fn(storage, '', config, projectPath, logManager);
|
|
34
48
|
return;
|
|
35
49
|
}
|
|
36
|
-
const sessionManager = new SessionManager(
|
|
50
|
+
const sessionManager = new SessionManager(resolveSessionPath(projectPath, config), config.session?.timeout_minutes || 30, config.session?.max_extensions || 5);
|
|
37
51
|
await sessionManager.init();
|
|
38
52
|
// Initialize audit logging
|
|
39
|
-
const logManager = new LogManager(
|
|
53
|
+
const logManager = new LogManager(resolveLogPath(config.audit, projectPath), config.audit);
|
|
40
54
|
await logManager.init();
|
|
41
55
|
let session = await sessionManager.load();
|
|
42
56
|
let password = '';
|
|
@@ -136,12 +150,32 @@ program
|
|
|
136
150
|
.option('--hsm-type <type>', 'HSM type for --auth-method hsm|multi: yubikey | gpg | pkcs11')
|
|
137
151
|
.option('--key-id <id>', 'GPG key ID or PKCS#11 key label for HSM auth')
|
|
138
152
|
.option('--pkcs11-lib <path>', 'Path to PKCS#11 shared library for --hsm-type pkcs11')
|
|
153
|
+
.option('--global', 'Initialize a global vault at ~/.envcp/ (config + store live in $HOME)')
|
|
154
|
+
.option('--force', 'Overwrite existing envcp.yaml (DESTRUCTIVE: existing vault may become inaccessible)')
|
|
139
155
|
.action(async (options) => {
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
|
|
156
|
+
const useGlobal = !!options.global;
|
|
157
|
+
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
158
|
+
const projectPath = useGlobal ? home : process.cwd();
|
|
159
|
+
const projectName = options.project || (useGlobal ? 'global' : path.basename(projectPath));
|
|
160
|
+
const configPath = path.join(projectPath, 'envcp.yaml');
|
|
161
|
+
if (await pathExists(configPath) && !options.force) {
|
|
162
|
+
const vaultRelPath = path.join('.envcp', 'store.enc');
|
|
163
|
+
console.log(chalk.yellow('EnvCP is already initialized here.'));
|
|
164
|
+
console.log(chalk.gray(` Config: ${configPath}`));
|
|
165
|
+
if (await pathExists(path.join(projectPath, vaultRelPath))) {
|
|
166
|
+
console.log(chalk.gray(` Vault: ${path.join(projectPath, vaultRelPath)}`));
|
|
167
|
+
}
|
|
168
|
+
console.log('');
|
|
169
|
+
console.log(chalk.gray('Running init again would overwrite your config and may make'));
|
|
170
|
+
console.log(chalk.gray('existing encrypted variables permanently inaccessible.'));
|
|
171
|
+
console.log('');
|
|
172
|
+
console.log(chalk.gray('To reconfigure: edit envcp.yaml directly'));
|
|
173
|
+
console.log(chalk.gray('To force re-init (DESTRUCTIVE): envcp init --force'));
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
console.log(chalk.blue(useGlobal ? 'Initializing EnvCP (global vault)...' : 'Initializing EnvCP...'));
|
|
143
177
|
console.log('');
|
|
144
|
-
const config = await initConfig(projectPath, projectName);
|
|
178
|
+
const config = await initConfig(projectPath, projectName, { global: useGlobal });
|
|
145
179
|
// Single security question (or skip if --no-encrypt)
|
|
146
180
|
let securityChoice;
|
|
147
181
|
if (options.encrypt === false) {
|
|
@@ -203,7 +237,7 @@ program
|
|
|
203
237
|
}
|
|
204
238
|
pwd = password;
|
|
205
239
|
}
|
|
206
|
-
await saveConfig(config, projectPath);
|
|
240
|
+
await saveConfig(config, projectPath, { global: useGlobal });
|
|
207
241
|
const modeLabel = securityChoice === 'none' ? 'no encryption' : securityChoice;
|
|
208
242
|
console.log(chalk.green('EnvCP initialized!'));
|
|
209
243
|
console.log(chalk.gray(` Project: ${config.project}`));
|
|
@@ -236,7 +270,7 @@ program
|
|
|
236
270
|
await storage.save(existing);
|
|
237
271
|
// Create session for encrypted mode
|
|
238
272
|
if (pwd) {
|
|
239
|
-
const sessionManager = new SessionManager(
|
|
273
|
+
const sessionManager = new SessionManager(resolveSessionPath(projectPath, config), config.session?.timeout_minutes || 30, config.session?.max_extensions || 5);
|
|
240
274
|
await sessionManager.init();
|
|
241
275
|
await sessionManager.create(pwd);
|
|
242
276
|
}
|
|
@@ -303,7 +337,7 @@ program
|
|
|
303
337
|
if (await hsm.isAvailable()) {
|
|
304
338
|
try {
|
|
305
339
|
await hsm.protectVaultPassword(pwd);
|
|
306
|
-
await saveConfig(config, projectPath);
|
|
340
|
+
await saveConfig(config, projectPath, { global: useGlobal });
|
|
307
341
|
console.log('');
|
|
308
342
|
console.log(chalk.green(` Vault password protected by ${hsm.backendName}`));
|
|
309
343
|
console.log(chalk.gray(` Future sessions will authenticate via hardware`));
|
|
@@ -317,7 +351,7 @@ program
|
|
|
317
351
|
console.log(chalk.yellow(` HSM device (${hsm.backendName}) not available at init time.`));
|
|
318
352
|
console.log(chalk.gray(' Run "envcp unlock --setup-hsm" later to enable hardware authentication.'));
|
|
319
353
|
config.auth = { method: authMethod, multi_factors: ['password', 'hsm'], fallback: 'password' };
|
|
320
|
-
await saveConfig(config, projectPath);
|
|
354
|
+
await saveConfig(config, projectPath, { global: useGlobal });
|
|
321
355
|
}
|
|
322
356
|
}
|
|
323
357
|
console.log('');
|
|
@@ -333,9 +367,9 @@ program
|
|
|
333
367
|
.option('--hsm-type <type>', 'HSM type: yubikey | gpg | pkcs11 (default: yubikey)')
|
|
334
368
|
.option('--key-id <id>', 'GPG key ID or PKCS#11 key label')
|
|
335
369
|
.option('--pkcs11-lib <path>', 'Path to PKCS#11 shared library (.so / .dll)')
|
|
370
|
+
.option('--global', 'Unlock the global vault at ~/.envcp')
|
|
336
371
|
.action(async (options) => {
|
|
337
|
-
const projectPath =
|
|
338
|
-
const config = await loadConfig(projectPath);
|
|
372
|
+
const { projectPath, config } = await resolveCliContext(options.global ? 'global' : undefined);
|
|
339
373
|
let password = options.password;
|
|
340
374
|
if (!password) {
|
|
341
375
|
password = await promptPassword('Enter password:');
|
|
@@ -349,7 +383,7 @@ program
|
|
|
349
383
|
if (passwordValid && passwordWarning) {
|
|
350
384
|
console.log(chalk.yellow('⚠ Weak password detected'));
|
|
351
385
|
}
|
|
352
|
-
const sessionDir = path.
|
|
386
|
+
const sessionDir = path.dirname(resolveSessionPath(projectPath, config));
|
|
353
387
|
let lockoutManager = new LockoutManager(path.join(sessionDir, '.lockout'));
|
|
354
388
|
// Handle recovery key if provided
|
|
355
389
|
if (options.recoveryKey) {
|
|
@@ -365,7 +399,7 @@ program
|
|
|
365
399
|
await lockoutManager.clearPermanentLockout();
|
|
366
400
|
console.log(chalk.green('Permanent lockout cleared.'));
|
|
367
401
|
// Log recovery event
|
|
368
|
-
const logManager = new LogManager(
|
|
402
|
+
const logManager = new LogManager(resolveLogPath(config.audit, projectPath), config.audit);
|
|
369
403
|
await logManager.init();
|
|
370
404
|
await logManager.log({
|
|
371
405
|
timestamp: new Date().toISOString(),
|
|
@@ -394,12 +428,13 @@ program
|
|
|
394
428
|
const progressiveDelay = bfpConfig?.progressive_delay ?? true;
|
|
395
429
|
const maxDelay = bfpConfig?.max_delay ?? 60;
|
|
396
430
|
const permanentThreshold = bfpConfig?.permanent_lockout_threshold ?? 0;
|
|
397
|
-
const sessionManager = new SessionManager(
|
|
431
|
+
const sessionManager = new SessionManager(resolveSessionPath(projectPath, config), config.session?.timeout_minutes || 30, config.session?.max_extensions || 5);
|
|
398
432
|
await sessionManager.init();
|
|
399
433
|
// Initialize audit logging
|
|
400
|
-
const logManager = new LogManager(
|
|
434
|
+
const logManager = new LogManager(resolveLogPath(config.audit, projectPath), config.audit);
|
|
401
435
|
await logManager.init();
|
|
402
|
-
const
|
|
436
|
+
const vaultPathForUnlock = await resolveVaultPath(projectPath, config);
|
|
437
|
+
const storage = new StorageManager(vaultPathForUnlock, config.storage.encrypted);
|
|
403
438
|
storage.setPassword(password);
|
|
404
439
|
const storeExists = await storage.exists();
|
|
405
440
|
// Check lockout before attempting password verification on existing stores
|
|
@@ -618,10 +653,10 @@ program
|
|
|
618
653
|
program
|
|
619
654
|
.command('lock')
|
|
620
655
|
.description('Lock EnvCP session')
|
|
621
|
-
.
|
|
622
|
-
|
|
623
|
-
const config = await
|
|
624
|
-
const sessionManager = new SessionManager(
|
|
656
|
+
.option('--global', 'Lock the global vault session at ~/.envcp/.session')
|
|
657
|
+
.action(async (options) => {
|
|
658
|
+
const { projectPath, config } = await resolveCliContext(options.global ? 'global' : undefined);
|
|
659
|
+
const sessionManager = new SessionManager(resolveSessionPath(projectPath, config), config.session?.timeout_minutes || 30, config.session?.max_extensions || 5);
|
|
625
660
|
await sessionManager.init();
|
|
626
661
|
await sessionManager.destroy();
|
|
627
662
|
console.log(chalk.green('Session locked'));
|
|
@@ -629,10 +664,10 @@ program
|
|
|
629
664
|
program
|
|
630
665
|
.command('status')
|
|
631
666
|
.description('Check session status')
|
|
632
|
-
.
|
|
633
|
-
|
|
634
|
-
const config = await
|
|
635
|
-
const sessionManager = new SessionManager(
|
|
667
|
+
.option('--global', 'Check status of the global vault session at ~/.envcp/.session')
|
|
668
|
+
.action(async (options) => {
|
|
669
|
+
const { projectPath, config } = await resolveCliContext(options.global ? 'global' : undefined);
|
|
670
|
+
const sessionManager = new SessionManager(resolveSessionPath(projectPath, config), config.session?.timeout_minutes || 30, config.session?.max_extensions || 5);
|
|
636
671
|
await sessionManager.init();
|
|
637
672
|
const password = await promptPassword('Enter password:');
|
|
638
673
|
const session = await sessionManager.load(password);
|
|
@@ -669,10 +704,10 @@ program
|
|
|
669
704
|
program
|
|
670
705
|
.command('extend')
|
|
671
706
|
.description('Extend session timeout')
|
|
672
|
-
.
|
|
673
|
-
|
|
674
|
-
const config = await
|
|
675
|
-
const sessionManager = new SessionManager(
|
|
707
|
+
.option('--global', 'Extend the global vault session at ~/.envcp/.session')
|
|
708
|
+
.action(async (options) => {
|
|
709
|
+
const { projectPath, config } = await resolveCliContext(options.global ? 'global' : undefined);
|
|
710
|
+
const sessionManager = new SessionManager(resolveSessionPath(projectPath, config), config.session?.timeout_minutes || 30, config.session?.max_extensions || 5);
|
|
676
711
|
await sessionManager.init();
|
|
677
712
|
const password = await promptPassword('Enter password:');
|
|
678
713
|
const loaded = await sessionManager.load(password);
|
|
@@ -749,7 +784,7 @@ program
|
|
|
749
784
|
console.log(chalk.yellow.bold(` ${newRecoveryKey}`));
|
|
750
785
|
console.log(chalk.gray('Your old recovery key no longer works.'));
|
|
751
786
|
// Create a session with new password
|
|
752
|
-
const sessionManager = new SessionManager(
|
|
787
|
+
const sessionManager = new SessionManager(resolveSessionPath(projectPath, config), config.session?.timeout_minutes || 30, config.session?.max_extensions || 5);
|
|
753
788
|
await sessionManager.init();
|
|
754
789
|
await sessionManager.create(newPassword);
|
|
755
790
|
console.log(chalk.green('Session unlocked with new password.'));
|
|
@@ -786,15 +821,70 @@ program
|
|
|
786
821
|
program
|
|
787
822
|
.command('add <name>')
|
|
788
823
|
.description('Add a new environment variable')
|
|
789
|
-
.option('-v, --value <value>', 'Variable value')
|
|
824
|
+
.option('-v, --value <value>', 'Variable value (WARNING: leaks in shell history; prefer --from-env/--from-file/--stdin)')
|
|
825
|
+
.option('--from-env <envVar>', 'Read value from the named environment variable')
|
|
826
|
+
.option('--from-file <path>', 'Read value from a file (trailing newline trimmed)')
|
|
827
|
+
.option('--stdin', 'Read value from piped stdin')
|
|
790
828
|
.option('-t, --tags <tags>', 'Tags (comma-separated)')
|
|
791
829
|
.option('-d, --description <desc>', 'Description')
|
|
792
830
|
.action(async (name, options) => {
|
|
831
|
+
const sourceFlags = [
|
|
832
|
+
options.value !== undefined ? '--value' : null,
|
|
833
|
+
options.fromEnv !== undefined ? '--from-env' : null,
|
|
834
|
+
options.fromFile !== undefined ? '--from-file' : null,
|
|
835
|
+
options.stdin ? '--stdin' : null,
|
|
836
|
+
].filter(Boolean);
|
|
837
|
+
if (sourceFlags.length > 1) {
|
|
838
|
+
console.error(chalk.red(`Error: ${sourceFlags.join(', ')} are mutually exclusive — pick one`));
|
|
839
|
+
process.exit(1);
|
|
840
|
+
}
|
|
841
|
+
let value;
|
|
842
|
+
let sourced = false;
|
|
843
|
+
if (options.value !== undefined) {
|
|
844
|
+
value = options.value;
|
|
845
|
+
sourced = true;
|
|
846
|
+
if (process.stdout.isTTY) {
|
|
847
|
+
console.warn(chalk.yellow('⚠ --value is visible in shell history and process list. Use --from-env, --from-file, or --stdin for secrets.'));
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
else if (options.fromEnv) {
|
|
851
|
+
const envValue = process.env[options.fromEnv];
|
|
852
|
+
if (envValue === undefined) {
|
|
853
|
+
console.error(chalk.red(`Error: environment variable '${options.fromEnv}' is not set`));
|
|
854
|
+
process.exit(1);
|
|
855
|
+
}
|
|
856
|
+
value = envValue;
|
|
857
|
+
sourced = true;
|
|
858
|
+
}
|
|
859
|
+
else if (options.fromFile) {
|
|
860
|
+
try {
|
|
861
|
+
const raw = await fs.readFile(options.fromFile, 'utf-8');
|
|
862
|
+
value = raw.replace(/\r?\n$/, '');
|
|
863
|
+
sourced = true;
|
|
864
|
+
}
|
|
865
|
+
catch (err) {
|
|
866
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
867
|
+
console.error(chalk.red(`Error: cannot read '${options.fromFile}': ${msg}`));
|
|
868
|
+
process.exit(1);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
else if (options.stdin) {
|
|
872
|
+
if (process.stdin.isTTY) {
|
|
873
|
+
console.error(chalk.red('Error: --stdin requires piped input (e.g., `echo "$SECRET" | envcp add NAME --stdin`)'));
|
|
874
|
+
process.exit(1);
|
|
875
|
+
}
|
|
876
|
+
value = await new Promise((resolve, reject) => {
|
|
877
|
+
const chunks = [];
|
|
878
|
+
process.stdin.on('data', (c) => chunks.push(typeof c === 'string' ? Buffer.from(c) : c));
|
|
879
|
+
process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8').replace(/\r?\n$/, '')));
|
|
880
|
+
process.stdin.on('error', reject);
|
|
881
|
+
});
|
|
882
|
+
sourced = true;
|
|
883
|
+
}
|
|
793
884
|
await withSession(async (storage, _password, config) => {
|
|
794
|
-
let value = options.value;
|
|
795
885
|
let tags = [];
|
|
796
886
|
let description = options.description;
|
|
797
|
-
if (!
|
|
887
|
+
if (!sourced) {
|
|
798
888
|
value = await promptPassword('Enter value:');
|
|
799
889
|
const tagsInput = await promptInput('Tags (comma-separated):');
|
|
800
890
|
tags = tagsInput.split(',').map((t) => t.trim()).filter(Boolean);
|
|
@@ -803,6 +893,10 @@ program
|
|
|
803
893
|
else if (options.tags) {
|
|
804
894
|
tags = options.tags.split(',').map((t) => t.trim()).filter(Boolean);
|
|
805
895
|
}
|
|
896
|
+
if (value === undefined) {
|
|
897
|
+
console.error(chalk.red('Error: no value provided'));
|
|
898
|
+
process.exit(1);
|
|
899
|
+
}
|
|
806
900
|
const now = new Date().toISOString();
|
|
807
901
|
const variable = {
|
|
808
902
|
name,
|
|
@@ -984,10 +1078,40 @@ program
|
|
|
984
1078
|
.option('--port <port>', 'HTTP port (for non-MCP modes)', '3456')
|
|
985
1079
|
.option('--host <host>', 'HTTP host', '127.0.0.1')
|
|
986
1080
|
.option('-k, --api-key <key>', 'API key for HTTP authentication')
|
|
1081
|
+
.option('--global', 'Force the global vault at ~/.envcp (skip project lookup)')
|
|
987
1082
|
.action(async (options) => {
|
|
988
|
-
const
|
|
1083
|
+
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
1084
|
+
const globalConfigPath = path.join(home, '.envcp', 'config.yaml');
|
|
1085
|
+
// Resolve where the vault lives: --global forces home; otherwise walk up
|
|
1086
|
+
// from cwd looking for envcp.yaml; otherwise fall back to a global install.
|
|
1087
|
+
let projectPath;
|
|
1088
|
+
let forceGlobalMode = false;
|
|
1089
|
+
if (options.global) {
|
|
1090
|
+
projectPath = home;
|
|
1091
|
+
forceGlobalMode = true;
|
|
1092
|
+
}
|
|
1093
|
+
else {
|
|
1094
|
+
const found = await findProjectRoot(process.cwd());
|
|
1095
|
+
if (found) {
|
|
1096
|
+
projectPath = found;
|
|
1097
|
+
}
|
|
1098
|
+
else if (await pathExists(globalConfigPath)) {
|
|
1099
|
+
projectPath = home;
|
|
1100
|
+
forceGlobalMode = true;
|
|
1101
|
+
}
|
|
1102
|
+
else {
|
|
1103
|
+
process.stderr.write('Error: No envcp.yaml found in cwd or any ancestor, and no global config at ~/.envcp/config.yaml.\n' +
|
|
1104
|
+
'Run `envcp init` (project) or `envcp init --global` first.\n');
|
|
1105
|
+
process.exit(1);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
989
1108
|
const configGuard = new ConfigGuard(projectPath);
|
|
990
1109
|
const config = await configGuard.loadAndLock();
|
|
1110
|
+
if (forceGlobalMode) {
|
|
1111
|
+
config.vault = { ...config.vault, mode: 'global' };
|
|
1112
|
+
}
|
|
1113
|
+
const vaultPath = await resolveVaultPath(projectPath, config);
|
|
1114
|
+
const sessionPath = resolveSessionPath(projectPath, config);
|
|
991
1115
|
const mode = options.mode;
|
|
992
1116
|
const port = parseInt(options.port, 10);
|
|
993
1117
|
const host = options.host;
|
|
@@ -997,20 +1121,20 @@ program
|
|
|
997
1121
|
if (config.encryption?.enabled === false) {
|
|
998
1122
|
if (mode === 'mcp') {
|
|
999
1123
|
const { EnvCPServer } = await import('../mcp/server.js');
|
|
1000
|
-
const server = new EnvCPServer(config, projectPath);
|
|
1124
|
+
const server = new EnvCPServer(config, projectPath, undefined, vaultPath, sessionPath);
|
|
1001
1125
|
await server.start();
|
|
1002
1126
|
return;
|
|
1003
1127
|
}
|
|
1004
1128
|
}
|
|
1005
1129
|
else {
|
|
1006
1130
|
// Encrypted mode: need password
|
|
1007
|
-
const sessionManager = new SessionManager(
|
|
1131
|
+
const sessionManager = new SessionManager(sessionPath, config.session?.timeout_minutes || 30, config.session?.max_extensions || 5);
|
|
1008
1132
|
await sessionManager.init();
|
|
1009
1133
|
let session = await sessionManager.load();
|
|
1010
1134
|
if (!session && !password) {
|
|
1011
1135
|
// MCP mode uses stdio — can't prompt interactively
|
|
1012
1136
|
if (mode === 'mcp') {
|
|
1013
|
-
process.stderr.write(
|
|
1137
|
+
process.stderr.write(`Error: No active session at ${sessionPath}. Run \`envcp unlock${forceGlobalMode ? ' --global' : ''}\` first, or use --password flag.\n`);
|
|
1014
1138
|
process.exit(1);
|
|
1015
1139
|
}
|
|
1016
1140
|
password = await promptPassword('Enter password:');
|
|
@@ -1029,7 +1153,7 @@ program
|
|
|
1029
1153
|
// MCP mode uses stdio
|
|
1030
1154
|
if (mode === 'mcp') {
|
|
1031
1155
|
const { EnvCPServer } = await import('../mcp/server.js');
|
|
1032
|
-
const server = new EnvCPServer(config, projectPath, password);
|
|
1156
|
+
const server = new EnvCPServer(config, projectPath, password, vaultPath, sessionPath);
|
|
1033
1157
|
await server.start();
|
|
1034
1158
|
return;
|
|
1035
1159
|
}
|
|
@@ -1340,7 +1464,7 @@ program
|
|
|
1340
1464
|
}
|
|
1341
1465
|
// 5. Session status
|
|
1342
1466
|
if (encrypted) {
|
|
1343
|
-
const sessionManager = new SessionManager(
|
|
1467
|
+
const sessionManager = new SessionManager(resolveSessionPath(projectPath, config), config.session?.timeout_minutes || 30, config.session?.max_extensions || 5);
|
|
1344
1468
|
await sessionManager.init();
|
|
1345
1469
|
const session = await sessionManager.load();
|
|
1346
1470
|
if (session) {
|
|
@@ -1727,7 +1851,7 @@ program
|
|
|
1727
1851
|
.action(async (options) => {
|
|
1728
1852
|
const projectPath = process.cwd();
|
|
1729
1853
|
const config = await loadConfig(projectPath);
|
|
1730
|
-
const logDir =
|
|
1854
|
+
const logDir = resolveLogPath(config.audit, projectPath);
|
|
1731
1855
|
const logs = new LogManager(logDir, config.audit);
|
|
1732
1856
|
await logs.init();
|
|
1733
1857
|
if (options.dates) {
|
|
@@ -1794,7 +1918,7 @@ program
|
|
|
1794
1918
|
.action(async (options) => {
|
|
1795
1919
|
const projectPath = process.cwd();
|
|
1796
1920
|
const config = await loadConfig(projectPath);
|
|
1797
|
-
const logDir =
|
|
1921
|
+
const logDir = resolveLogPath(config.audit, projectPath);
|
|
1798
1922
|
const logs = new LogManager(logDir, config.audit);
|
|
1799
1923
|
await logs.init();
|
|
1800
1924
|
if (!config.audit.hmac_chain) {
|
|
@@ -1817,7 +1941,7 @@ program
|
|
|
1817
1941
|
.action(async () => {
|
|
1818
1942
|
const projectPath = process.cwd();
|
|
1819
1943
|
const config = await loadConfig(projectPath);
|
|
1820
|
-
const logDir =
|
|
1944
|
+
const logDir = resolveLogPath(config.audit, projectPath);
|
|
1821
1945
|
const logs = new LogManager(logDir, config.audit);
|
|
1822
1946
|
await logs.init();
|
|
1823
1947
|
if (config.audit.protection === 'none') {
|