lsh-framework 3.0.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/cli.js +1 -1
- package/dist/commands/doctor.js +32 -15
- package/dist/commands/init.js +31 -12
- package/dist/commands/self.js +0 -1
- package/dist/constants/validation.js +2 -0
- package/dist/daemon/lshd.js +21 -4
- package/dist/daemon/saas-api-routes.js +44 -37
- package/dist/daemon/saas-api-server.js +9 -5
- package/dist/lib/cron-job-manager.js +2 -0
- package/dist/lib/ipfs-secrets-storage.js +26 -7
- package/dist/lib/job-manager.js +0 -1
- package/dist/lib/lshrc-init.js +0 -1
- package/dist/lib/saas-audit.js +6 -3
- package/dist/lib/saas-auth.js +6 -3
- package/dist/lib/saas-billing.js +10 -2
- package/dist/lib/saas-encryption.js +2 -1
- package/dist/lib/saas-organizations.js +5 -0
- package/dist/lib/saas-secrets.js +4 -1
- package/dist/lib/saas-types.js +57 -0
- package/dist/lib/secrets-manager.js +63 -6
- package/dist/lib/supabase-client.js +1 -2
- package/dist/services/secrets/secrets.js +63 -24
- package/package.json +4 -3
|
@@ -14,13 +14,16 @@ export async function init_secrets(program) {
|
|
|
14
14
|
.description('Push local .env to encrypted cloud storage')
|
|
15
15
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
16
16
|
.option('-e, --env <name>', 'Environment name (dev/staging/prod)', 'dev')
|
|
17
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
17
18
|
.option('--force', 'Force push even if destructive changes detected')
|
|
18
19
|
.action(async (options) => {
|
|
19
|
-
const manager = new SecretsManager();
|
|
20
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
20
21
|
try {
|
|
22
|
+
// Resolve file path (handles global mode)
|
|
23
|
+
const filePath = manager.resolveFilePath(options.file);
|
|
21
24
|
// v2.0: Use context-aware default environment
|
|
22
25
|
const env = options.env === 'dev' ? manager.getDefaultEnvironment() : options.env;
|
|
23
|
-
await manager.push(
|
|
26
|
+
await manager.push(filePath, env, options.force);
|
|
24
27
|
}
|
|
25
28
|
catch (error) {
|
|
26
29
|
const err = error;
|
|
@@ -38,13 +41,16 @@ export async function init_secrets(program) {
|
|
|
38
41
|
.description('Pull .env from encrypted cloud storage')
|
|
39
42
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
40
43
|
.option('-e, --env <name>', 'Environment name (dev/staging/prod)', 'dev')
|
|
44
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
41
45
|
.option('--force', 'Overwrite without creating backup')
|
|
42
46
|
.action(async (options) => {
|
|
43
|
-
const manager = new SecretsManager();
|
|
47
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
44
48
|
try {
|
|
49
|
+
// Resolve file path (handles global mode)
|
|
50
|
+
const filePath = manager.resolveFilePath(options.file);
|
|
45
51
|
// v2.0: Use context-aware default environment
|
|
46
52
|
const env = options.env === 'dev' ? manager.getDefaultEnvironment() : options.env;
|
|
47
|
-
await manager.pull(
|
|
53
|
+
await manager.pull(filePath, env, options.force);
|
|
48
54
|
}
|
|
49
55
|
catch (error) {
|
|
50
56
|
const err = error;
|
|
@@ -62,12 +68,14 @@ export async function init_secrets(program) {
|
|
|
62
68
|
.alias('ls')
|
|
63
69
|
.description('List secrets in the current local .env file')
|
|
64
70
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
71
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
65
72
|
.option('--keys-only', 'Show only keys, not values')
|
|
66
73
|
.option('--format <type>', 'Output format: env, json, yaml, toml, export', 'env')
|
|
67
74
|
.option('--no-mask', 'Show full values (default: auto based on format)')
|
|
68
75
|
.action(async (options) => {
|
|
69
76
|
try {
|
|
70
|
-
const
|
|
77
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
78
|
+
const envPath = path.resolve(manager.resolveFilePath(options.file));
|
|
71
79
|
if (!fs.existsSync(envPath)) {
|
|
72
80
|
console.error(`ā File not found: ${envPath}`);
|
|
73
81
|
console.log('š” Tip: Pull from cloud with: lsh pull --env <environment>');
|
|
@@ -138,11 +146,12 @@ export async function init_secrets(program) {
|
|
|
138
146
|
program
|
|
139
147
|
.command('env [environment]')
|
|
140
148
|
.description('List all stored environments or show secrets for specific environment')
|
|
149
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
141
150
|
.option('--all-files', 'List all tracked .env files across environments')
|
|
142
151
|
.option('--format <type>', 'Output format: env, json, yaml, toml, export', 'env')
|
|
143
152
|
.action(async (environment, options) => {
|
|
144
153
|
try {
|
|
145
|
-
const manager = new SecretsManager();
|
|
154
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
146
155
|
// If --all-files flag is set, list all tracked files
|
|
147
156
|
if (options.allFiles) {
|
|
148
157
|
const files = await manager.listAllFiles();
|
|
@@ -213,10 +222,12 @@ export async function init_secrets(program) {
|
|
|
213
222
|
.command('create')
|
|
214
223
|
.description('Create a new .env file')
|
|
215
224
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
225
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
216
226
|
.option('-t, --template', 'Create with common template variables')
|
|
217
227
|
.action(async (options) => {
|
|
218
228
|
try {
|
|
219
|
-
const
|
|
229
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
230
|
+
const envPath = path.resolve(manager.resolveFilePath(options.file));
|
|
220
231
|
// Check if file already exists
|
|
221
232
|
if (fs.existsSync(envPath)) {
|
|
222
233
|
console.log(`ā File already exists: ${envPath}`);
|
|
@@ -269,23 +280,26 @@ API_KEY=
|
|
|
269
280
|
.description('Automatically set up and synchronize secrets (smart mode)')
|
|
270
281
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
271
282
|
.option('-e, --env <name>', 'Environment name', 'dev')
|
|
283
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
272
284
|
.option('--dry-run', 'Show what would be done without executing')
|
|
273
285
|
.option('--legacy', 'Use legacy sync mode (suggestions only)')
|
|
274
286
|
.option('--load', 'Output eval-able export commands for loading secrets')
|
|
275
287
|
.option('--force', 'Force sync even if destructive changes detected')
|
|
276
288
|
.option('--force-rekey', 'Re-encrypt cloud secrets with current local key (use when key mismatch)')
|
|
277
289
|
.action(async (options) => {
|
|
278
|
-
const manager = new SecretsManager();
|
|
290
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
279
291
|
try {
|
|
292
|
+
// Resolve file path (handles global mode)
|
|
293
|
+
const filePath = manager.resolveFilePath(options.file);
|
|
280
294
|
// v2.0: Use context-aware default environment
|
|
281
295
|
const env = options.env === 'dev' ? manager.getDefaultEnvironment() : options.env;
|
|
282
296
|
if (options.legacy) {
|
|
283
297
|
// Use legacy sync (suggestions only)
|
|
284
|
-
await manager.sync(
|
|
298
|
+
await manager.sync(filePath, env);
|
|
285
299
|
}
|
|
286
300
|
else {
|
|
287
301
|
// Use new smart sync (auto-execute)
|
|
288
|
-
await manager.smartSync(
|
|
302
|
+
await manager.smartSync(filePath, env, !options.dryRun, options.load, options.force, options.forceRekey);
|
|
289
303
|
}
|
|
290
304
|
}
|
|
291
305
|
catch (error) {
|
|
@@ -304,10 +318,12 @@ API_KEY=
|
|
|
304
318
|
.description('Get detailed secrets status (JSON output)')
|
|
305
319
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
306
320
|
.option('-e, --env <name>', 'Environment name', 'dev')
|
|
321
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
307
322
|
.action(async (options) => {
|
|
308
323
|
try {
|
|
309
|
-
const manager = new SecretsManager();
|
|
310
|
-
const
|
|
324
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
325
|
+
const filePath = manager.resolveFilePath(options.file);
|
|
326
|
+
const status = await manager.status(filePath, options.env);
|
|
311
327
|
console.log(JSON.stringify(status, null, 2));
|
|
312
328
|
}
|
|
313
329
|
catch (error) {
|
|
@@ -322,14 +338,20 @@ API_KEY=
|
|
|
322
338
|
.description('Show current directory context and tracked environment')
|
|
323
339
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
324
340
|
.option('-e, --env <name>', 'Environment name', 'dev')
|
|
341
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
325
342
|
.action(async (options) => {
|
|
326
343
|
try {
|
|
327
|
-
const gitInfo = getGitRepoInfo();
|
|
328
|
-
const manager = new SecretsManager();
|
|
329
|
-
const envPath = path.resolve(options.file);
|
|
344
|
+
const gitInfo = options.global ? null : getGitRepoInfo();
|
|
345
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
346
|
+
const envPath = path.resolve(manager.resolveFilePath(options.file));
|
|
330
347
|
console.log('\nš Current Directory Context\n');
|
|
331
|
-
//
|
|
332
|
-
if (
|
|
348
|
+
// Workspace Info
|
|
349
|
+
if (options.global) {
|
|
350
|
+
console.log('š Global Workspace:');
|
|
351
|
+
console.log(` Location: ${manager.getHomeDir()}`);
|
|
352
|
+
console.log(' Mode: Global (not repo-specific)');
|
|
353
|
+
}
|
|
354
|
+
else if (gitInfo?.isGitRepo) {
|
|
333
355
|
console.log('š Git Repository:');
|
|
334
356
|
console.log(` Root: ${gitInfo.rootPath || 'unknown'}`);
|
|
335
357
|
console.log(` Name: ${gitInfo.repoName || 'unknown'}`);
|
|
@@ -347,12 +369,22 @@ API_KEY=
|
|
|
347
369
|
// Environment Tracking
|
|
348
370
|
console.log('š Environment Tracking:');
|
|
349
371
|
// Show the effective environment name used for cloud storage
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
: options.env
|
|
372
|
+
let effectiveEnv;
|
|
373
|
+
if (options.global) {
|
|
374
|
+
effectiveEnv = options.env === 'dev' ? 'global' : `global_${options.env}`;
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
effectiveEnv = gitInfo?.repoName
|
|
378
|
+
? `${gitInfo.repoName}_${options.env}`
|
|
379
|
+
: options.env;
|
|
380
|
+
}
|
|
353
381
|
console.log(` Base environment: ${options.env}`);
|
|
354
382
|
console.log(` Cloud storage name: ${effectiveEnv}`);
|
|
355
|
-
if (
|
|
383
|
+
if (options.global) {
|
|
384
|
+
console.log(' Namespace: global');
|
|
385
|
+
console.log(' ā¹ļø Global workspace mode enabled');
|
|
386
|
+
}
|
|
387
|
+
else if (gitInfo?.repoName) {
|
|
356
388
|
console.log(` Namespace: ${gitInfo.repoName}`);
|
|
357
389
|
console.log(' ā¹ļø Repo-based isolation enabled');
|
|
358
390
|
}
|
|
@@ -420,13 +452,15 @@ API_KEY=
|
|
|
420
452
|
.command('get [key]')
|
|
421
453
|
.description('Get a specific secret value from .env file, or all secrets with --all')
|
|
422
454
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
455
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
423
456
|
.option('--all', 'Get all secrets from the file')
|
|
424
457
|
.option('--export', 'Output in export format for shell evaluation (alias for --format export)')
|
|
425
458
|
.option('--format <type>', 'Output format: env, json, yaml, toml, export', 'env')
|
|
426
459
|
.option('--exact', 'Require exact key match (disable fuzzy matching)')
|
|
427
460
|
.action(async (key, options) => {
|
|
428
461
|
try {
|
|
429
|
-
const
|
|
462
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
463
|
+
const envPath = path.resolve(manager.resolveFilePath(options.file));
|
|
430
464
|
if (!fs.existsSync(envPath)) {
|
|
431
465
|
console.error(`ā File not found: ${envPath}`);
|
|
432
466
|
process.exit(1);
|
|
@@ -535,10 +569,12 @@ API_KEY=
|
|
|
535
569
|
.command('set [key] [value]')
|
|
536
570
|
.description('Set a specific secret value in .env file, or batch upsert from stdin (KEY=VALUE format)')
|
|
537
571
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
572
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
538
573
|
.option('--stdin', 'Read KEY=VALUE pairs from stdin (one per line)')
|
|
539
574
|
.action(async (key, value, options) => {
|
|
540
575
|
try {
|
|
541
|
-
const
|
|
576
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
577
|
+
const envPath = path.resolve(manager.resolveFilePath(options.file));
|
|
542
578
|
// Check if we should read from stdin
|
|
543
579
|
const isStdin = options.stdin || (!key && !value);
|
|
544
580
|
if (isStdin) {
|
|
@@ -792,10 +828,12 @@ API_KEY=
|
|
|
792
828
|
.command('delete')
|
|
793
829
|
.description('Delete .env file (requires confirmation)')
|
|
794
830
|
.option('-f, --file <path>', 'Path to .env file', '.env')
|
|
831
|
+
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
795
832
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
796
833
|
.action(async (options) => {
|
|
797
834
|
try {
|
|
798
|
-
const
|
|
835
|
+
const manager = new SecretsManager({ globalMode: options.global });
|
|
836
|
+
const envPath = path.resolve(manager.resolveFilePath(options.file));
|
|
799
837
|
// Check if file exists
|
|
800
838
|
if (!fs.existsSync(envPath)) {
|
|
801
839
|
console.log(`ā File not found: ${envPath}`);
|
|
@@ -846,6 +884,7 @@ API_KEY=
|
|
|
846
884
|
program
|
|
847
885
|
.command('clear')
|
|
848
886
|
.description('Clear local metadata and cache to resolve stuck registries')
|
|
887
|
+
.option('-g, --global', 'Use global workspace ($HOME) - default behavior')
|
|
849
888
|
.option('--repo <name>', 'Clear metadata for specific repo only')
|
|
850
889
|
.option('--cache', 'Also clear local encrypted secrets cache')
|
|
851
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.
|
|
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,8 +17,9 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc",
|
|
19
19
|
"watch": "tsc --watch",
|
|
20
|
-
"test": "node --experimental-vm-modules ./node_modules/.bin/jest",
|
|
21
|
-
"test:
|
|
20
|
+
"test": "node --experimental-vm-modules ./node_modules/.bin/jest --runInBand",
|
|
21
|
+
"test:ci": "node --experimental-vm-modules ./node_modules/.bin/jest --runInBand",
|
|
22
|
+
"test:coverage": "node --experimental-vm-modules ./node_modules/.bin/jest --coverage --runInBand",
|
|
22
23
|
"clean": "rm -rf ./build; rm -rf ./bin; rm -rf ./dist",
|
|
23
24
|
"lint": "eslint src --ext .js,.ts,.tsx",
|
|
24
25
|
"lint:fix": "eslint src --ext .js,.ts,.tsx --fix",
|