lsh-framework 3.1.0 ā 3.1.1
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/dist/commands/doctor.js +31 -14
- package/dist/commands/init.js +29 -10
- package/dist/services/secrets/secrets.js +4 -1
- package/package.json +2 -2
package/dist/commands/doctor.js
CHANGED
|
@@ -8,6 +8,7 @@ import * as path from 'path';
|
|
|
8
8
|
import { createClient } from '@supabase/supabase-js';
|
|
9
9
|
import { getPlatformPaths, getPlatformInfo } from '../lib/platform-utils.js';
|
|
10
10
|
import { IPFSClientManager } from '../lib/ipfs-client-manager.js';
|
|
11
|
+
import * as os from 'os';
|
|
11
12
|
/**
|
|
12
13
|
* Register doctor commands
|
|
13
14
|
*/
|
|
@@ -15,6 +16,7 @@ export function registerDoctorCommands(program) {
|
|
|
15
16
|
program
|
|
16
17
|
.command('doctor')
|
|
17
18
|
.description('Health check and troubleshooting')
|
|
19
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
18
20
|
.option('-v, --verbose', 'Show detailed information')
|
|
19
21
|
.option('--json', 'Output results as JSON')
|
|
20
22
|
.action(async (options) => {
|
|
@@ -28,12 +30,25 @@ export function registerDoctorCommands(program) {
|
|
|
28
30
|
}
|
|
29
31
|
});
|
|
30
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Get the base directory for .env files
|
|
35
|
+
*/
|
|
36
|
+
function getBaseDir(globalMode) {
|
|
37
|
+
return globalMode ? os.homedir() : process.cwd();
|
|
38
|
+
}
|
|
31
39
|
/**
|
|
32
40
|
* Run comprehensive health check
|
|
33
41
|
*/
|
|
34
42
|
async function runHealthCheck(options) {
|
|
43
|
+
const baseDir = getBaseDir(options.global);
|
|
35
44
|
if (!options.json) {
|
|
36
|
-
|
|
45
|
+
if (options.global) {
|
|
46
|
+
console.log(chalk.bold.cyan('\nš„ LSH Health Check (Global Workspace)'));
|
|
47
|
+
console.log(chalk.yellow(` Location: ${baseDir}`));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
console.log(chalk.bold.cyan('\nš„ LSH Health Check'));
|
|
51
|
+
}
|
|
37
52
|
console.log(chalk.gray('ā'.repeat(50)));
|
|
38
53
|
console.log('');
|
|
39
54
|
}
|
|
@@ -41,18 +56,20 @@ async function runHealthCheck(options) {
|
|
|
41
56
|
// Platform check
|
|
42
57
|
checks.push(await checkPlatform(options.verbose));
|
|
43
58
|
// .env file check
|
|
44
|
-
checks.push(await checkEnvFile(options.verbose));
|
|
59
|
+
checks.push(await checkEnvFile(options.verbose, baseDir));
|
|
45
60
|
// Encryption key check
|
|
46
|
-
checks.push(await checkEncryptionKey(options.verbose));
|
|
61
|
+
checks.push(await checkEncryptionKey(options.verbose, baseDir));
|
|
47
62
|
// Storage backend check
|
|
48
|
-
const storageChecks = await checkStorageBackend(options.verbose);
|
|
63
|
+
const storageChecks = await checkStorageBackend(options.verbose, baseDir);
|
|
49
64
|
checks.push(...storageChecks);
|
|
50
|
-
// Git repository check
|
|
51
|
-
|
|
65
|
+
// Git repository check (skip for global mode)
|
|
66
|
+
if (!options.global) {
|
|
67
|
+
checks.push(await checkGitRepository(options.verbose));
|
|
68
|
+
}
|
|
52
69
|
// IPFS client check
|
|
53
70
|
checks.push(await checkIPFSClient(options.verbose));
|
|
54
71
|
// Permissions check
|
|
55
|
-
checks.push(await checkPermissions(options.verbose));
|
|
72
|
+
checks.push(await checkPermissions(options.verbose, baseDir));
|
|
56
73
|
// Display results
|
|
57
74
|
if (options.json) {
|
|
58
75
|
console.log(JSON.stringify({ checks, summary: getSummary(checks) }, null, 2));
|
|
@@ -86,9 +103,9 @@ async function checkPlatform(verbose) {
|
|
|
86
103
|
/**
|
|
87
104
|
* Check .env file
|
|
88
105
|
*/
|
|
89
|
-
async function checkEnvFile(verbose) {
|
|
106
|
+
async function checkEnvFile(verbose, baseDir) {
|
|
90
107
|
try {
|
|
91
|
-
const envPath = path.join(process.cwd(), '.env');
|
|
108
|
+
const envPath = path.join(baseDir || process.cwd(), '.env');
|
|
92
109
|
// Read file directly without access check to avoid TOCTOU race condition
|
|
93
110
|
const content = await fs.readFile(envPath, 'utf-8');
|
|
94
111
|
const lines = content.split('\n').filter(l => l.trim() && !l.startsWith('#'));
|
|
@@ -111,9 +128,9 @@ async function checkEnvFile(verbose) {
|
|
|
111
128
|
/**
|
|
112
129
|
* Check encryption key
|
|
113
130
|
*/
|
|
114
|
-
async function checkEncryptionKey(verbose) {
|
|
131
|
+
async function checkEncryptionKey(verbose, baseDir) {
|
|
115
132
|
try {
|
|
116
|
-
const envPath = path.join(process.cwd(), '.env');
|
|
133
|
+
const envPath = path.join(baseDir || process.cwd(), '.env');
|
|
117
134
|
const content = await fs.readFile(envPath, 'utf-8');
|
|
118
135
|
const match = content.match(/^LSH_SECRETS_KEY=(.+)$/m);
|
|
119
136
|
if (!match) {
|
|
@@ -163,10 +180,10 @@ async function checkEncryptionKey(verbose) {
|
|
|
163
180
|
/**
|
|
164
181
|
* Check storage backend configuration
|
|
165
182
|
*/
|
|
166
|
-
async function checkStorageBackend(verbose) {
|
|
183
|
+
async function checkStorageBackend(verbose, baseDir) {
|
|
167
184
|
const checks = [];
|
|
168
185
|
try {
|
|
169
|
-
const envPath = path.join(process.cwd(), '.env');
|
|
186
|
+
const envPath = path.join(baseDir || process.cwd(), '.env');
|
|
170
187
|
const content = await fs.readFile(envPath, 'utf-8');
|
|
171
188
|
const supabaseUrl = content.match(/^SUPABASE_URL=(.+)$/m)?.[1]?.trim();
|
|
172
189
|
const supabaseKey = content.match(/^SUPABASE_ANON_KEY=(.+)$/m)?.[1]?.trim();
|
|
@@ -332,7 +349,7 @@ async function checkIPFSClient(verbose) {
|
|
|
332
349
|
/**
|
|
333
350
|
* Check file permissions
|
|
334
351
|
*/
|
|
335
|
-
async function checkPermissions(verbose) {
|
|
352
|
+
async function checkPermissions(verbose, _baseDir) {
|
|
336
353
|
try {
|
|
337
354
|
const paths = getPlatformPaths();
|
|
338
355
|
// Check if we can write to temp directory with secure permissions
|
package/dist/commands/init.js
CHANGED
|
@@ -11,6 +11,7 @@ import { createClient } from '@supabase/supabase-js';
|
|
|
11
11
|
import ora from 'ora';
|
|
12
12
|
import { getPlatformPaths } from '../lib/platform-utils.js';
|
|
13
13
|
import { getGitRepoInfo } from '../lib/git-utils.js';
|
|
14
|
+
import * as os from 'os';
|
|
14
15
|
/**
|
|
15
16
|
* Register init commands
|
|
16
17
|
*/
|
|
@@ -18,6 +19,7 @@ export function registerInitCommands(program) {
|
|
|
18
19
|
program
|
|
19
20
|
.command('init')
|
|
20
21
|
.description('Interactive setup wizard (first-time configuration)')
|
|
22
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
21
23
|
.option('--local', 'Use local-only encryption (no cloud sync)')
|
|
22
24
|
.option('--storacha', 'Use Storacha IPFS network sync (recommended)')
|
|
23
25
|
.option('--supabase', 'Use Supabase cloud storage')
|
|
@@ -34,15 +36,30 @@ export function registerInitCommands(program) {
|
|
|
34
36
|
}
|
|
35
37
|
});
|
|
36
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Get the base directory for .env files
|
|
41
|
+
*/
|
|
42
|
+
function getBaseDir(globalMode) {
|
|
43
|
+
return globalMode ? os.homedir() : process.cwd();
|
|
44
|
+
}
|
|
37
45
|
/**
|
|
38
46
|
* Run the interactive setup wizard
|
|
39
47
|
*/
|
|
40
48
|
async function runSetupWizard(options) {
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
const globalMode = options.global ?? false;
|
|
50
|
+
const baseDir = getBaseDir(globalMode);
|
|
51
|
+
if (globalMode) {
|
|
52
|
+
console.log(chalk.bold.cyan('\nš LSH Secrets Manager - Global Setup Wizard'));
|
|
53
|
+
console.log(chalk.gray('ā'.repeat(50)));
|
|
54
|
+
console.log(chalk.yellow(`\nš Global Mode: Using $HOME (${baseDir})`));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log(chalk.bold.cyan('\nš LSH Secrets Manager - Setup Wizard'));
|
|
58
|
+
console.log(chalk.gray('ā'.repeat(50)));
|
|
59
|
+
}
|
|
43
60
|
console.log('');
|
|
44
61
|
// Check if already configured
|
|
45
|
-
const existingConfig = await checkExistingConfig();
|
|
62
|
+
const existingConfig = await checkExistingConfig(baseDir);
|
|
46
63
|
if (existingConfig) {
|
|
47
64
|
const { overwrite } = await inquirer.prompt([
|
|
48
65
|
{
|
|
@@ -181,16 +198,16 @@ async function runSetupWizard(options) {
|
|
|
181
198
|
}
|
|
182
199
|
}
|
|
183
200
|
// Save configuration
|
|
184
|
-
await saveConfiguration(config);
|
|
201
|
+
await saveConfiguration(config, baseDir, globalMode);
|
|
185
202
|
// Show success message
|
|
186
203
|
showSuccessMessage(config);
|
|
187
204
|
}
|
|
188
205
|
/**
|
|
189
206
|
* Check if LSH is already configured
|
|
190
207
|
*/
|
|
191
|
-
async function checkExistingConfig() {
|
|
208
|
+
async function checkExistingConfig(baseDir) {
|
|
192
209
|
try {
|
|
193
|
-
const envPath = path.join(
|
|
210
|
+
const envPath = path.join(baseDir, '.env');
|
|
194
211
|
// Read file directly without access check to avoid TOCTOU race condition
|
|
195
212
|
const content = await fs.readFile(envPath, 'utf-8');
|
|
196
213
|
return content.includes('LSH_SECRETS_KEY') ||
|
|
@@ -417,10 +434,10 @@ function generateEncryptionKey() {
|
|
|
417
434
|
/**
|
|
418
435
|
* Save configuration to .env file
|
|
419
436
|
*/
|
|
420
|
-
async function saveConfiguration(config) {
|
|
437
|
+
async function saveConfiguration(config, baseDir, globalMode) {
|
|
421
438
|
const spinner = ora('Saving configuration...').start();
|
|
422
439
|
try {
|
|
423
|
-
const envPath = path.join(
|
|
440
|
+
const envPath = path.join(baseDir, '.env');
|
|
424
441
|
let envContent = '';
|
|
425
442
|
// Try to read existing .env
|
|
426
443
|
try {
|
|
@@ -461,8 +478,10 @@ async function saveConfiguration(config) {
|
|
|
461
478
|
}
|
|
462
479
|
// Write .env file
|
|
463
480
|
await fs.writeFile(envPath, envContent, 'utf-8');
|
|
464
|
-
// Update .gitignore
|
|
465
|
-
|
|
481
|
+
// Update .gitignore (skip for global mode since it's in $HOME)
|
|
482
|
+
if (!globalMode) {
|
|
483
|
+
await updateGitignore();
|
|
484
|
+
}
|
|
466
485
|
spinner.succeed(chalk.green('ā
Configuration saved'));
|
|
467
486
|
}
|
|
468
487
|
catch (error) {
|
|
@@ -222,10 +222,12 @@ export async function init_secrets(program) {
|
|
|
222
222
|
.command('create')
|
|
223
223
|
.description('Create a new .env file')
|
|
224
224
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
225
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
225
226
|
.option('-t, --template', 'Create with common template variables')
|
|
226
227
|
.action(async (options) => {
|
|
227
228
|
try {
|
|
228
|
-
const
|
|
229
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
230
|
+
const envPath = path.resolve(manager.resolveFilePath(options.file));
|
|
229
231
|
// Check if file already exists
|
|
230
232
|
if (fs.existsSync(envPath)) {
|
|
231
233
|
console.log(`ā File already exists: ${envPath}`);
|
|
@@ -882,6 +884,7 @@ API_KEY=
|
|
|
882
884
|
program
|
|
883
885
|
.command('clear')
|
|
884
886
|
.description('Clear local metadata and cache to resolve stuck registries')
|
|
887
|
+
.option('-g, --global', 'Use global workspace ($HOME) - default behavior')
|
|
885
888
|
.option('--repo <name>', 'Clear metadata for specific repo only')
|
|
886
889
|
.option('--cache', 'Also clear local encrypted secrets cache')
|
|
887
890
|
.option('--storacha', 'Also delete old Storacha uploads (registries and secrets)')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lsh-framework",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Simple, cross-platform encrypted secrets manager with automatic sync, IPFS audit logs, and multi-environment support. Just run lsh sync and start managing your secrets.",
|
|
5
5
|
"main": "dist/app.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc",
|
|
19
19
|
"watch": "tsc --watch",
|
|
20
|
-
"test": "node --experimental-vm-modules ./node_modules/.bin/jest",
|
|
20
|
+
"test": "node --experimental-vm-modules ./node_modules/.bin/jest --runInBand",
|
|
21
21
|
"test:ci": "node --experimental-vm-modules ./node_modules/.bin/jest --runInBand",
|
|
22
22
|
"test:coverage": "node --experimental-vm-modules ./node_modules/.bin/jest --coverage --runInBand",
|
|
23
23
|
"clean": "rm -rf ./build; rm -rf ./bin; rm -rf ./dist",
|