lsh-framework 1.8.1 → 2.0.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/dist/cli.js CHANGED
@@ -11,6 +11,7 @@ import { registerCompletionCommands } from './commands/completion.js';
11
11
  import { registerConfigCommands } from './commands/config.js';
12
12
  import { registerSyncHistoryCommands } from './commands/sync-history.js';
13
13
  import { registerIPFSCommands } from './commands/ipfs.js';
14
+ import { registerMigrateCommand } from './commands/migrate.js';
14
15
  import { init_daemon } from './services/daemon/daemon.js';
15
16
  import { init_supabase } from './services/supabase/supabase.js';
16
17
  import { init_cron } from './services/cron/cron.js';
@@ -146,6 +147,7 @@ function findSimilarCommands(input, validCommands) {
146
147
  registerConfigCommands(program);
147
148
  registerSyncHistoryCommands(program);
148
149
  registerIPFSCommands(program);
150
+ registerMigrateCommand(program);
149
151
  // Secrets management (primary feature)
150
152
  await init_secrets(program);
151
153
  // Supporting services
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Migration Command - Help users migrate from v1.x to v2.0
3
+ */
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import { homedir } from 'os';
7
+ import { getGitRepoInfo } from '../lib/git-utils.js';
8
+ export function registerMigrateCommand(program) {
9
+ program
10
+ .command('migrate')
11
+ .description('Migrate from v1.x to v2.0 environment naming')
12
+ .option('--dry-run', 'Show what would be migrated without doing it')
13
+ .option('--v1-compat', 'Enable v1.x compatibility mode')
14
+ .action(async (options) => {
15
+ if (options.v1Compat) {
16
+ console.log('\nšŸ”„ Enabling v1.x Compatibility Mode\n');
17
+ console.log('Add this to your shell profile (~/.bashrc or ~/.zshrc):');
18
+ console.log('');
19
+ console.log(' export LSH_V1_COMPAT=true');
20
+ console.log('');
21
+ console.log('This will keep v1.x behavior (repo_dev naming).');
22
+ console.log('');
23
+ return;
24
+ }
25
+ console.log('\nšŸ”„ LSH v2.0 Migration Guide\n');
26
+ console.log('━'.repeat(60));
27
+ console.log('');
28
+ // Check if in git repo
29
+ const gitInfo = getGitRepoInfo(process.cwd());
30
+ if (!gitInfo?.isGitRepo) {
31
+ console.log('ā„¹ļø Not in a git repository');
32
+ console.log('');
33
+ console.log('v2.0 only affects git repositories.');
34
+ console.log('Outside git repos, behavior is unchanged (default: dev)');
35
+ console.log('');
36
+ return;
37
+ }
38
+ console.log('šŸ“ Current Repository:', gitInfo.repoName);
39
+ console.log('🌿 Branch:', gitInfo.currentBranch);
40
+ console.log('');
41
+ console.log('šŸ“Š Environment Naming Changes:\n');
42
+ console.log(' v1.x Behavior:');
43
+ console.log(` lsh push → ${gitInfo.repoName}_dev`);
44
+ console.log(` lsh push --env dev → ${gitInfo.repoName}_dev`);
45
+ console.log(` lsh push --env staging → ${gitInfo.repoName}_staging`);
46
+ console.log('');
47
+ console.log(' v2.0 Behavior:');
48
+ console.log(` lsh push → ${gitInfo.repoName} (simpler!)`);
49
+ console.log(` lsh push --env dev → ${gitInfo.repoName}_dev`);
50
+ console.log(` lsh push --env staging → ${gitInfo.repoName}_staging`);
51
+ console.log('');
52
+ // Check for existing v1.x cache
53
+ const cacheDir = path.join(homedir(), '.lsh', 'secrets-cache');
54
+ const repoName = gitInfo.repoName || 'unknown';
55
+ const v1EnvName = `${repoName}_dev`;
56
+ const v2EnvName = repoName;
57
+ let hasV1Data = false;
58
+ let hasV2Data = false;
59
+ if (fs.existsSync(cacheDir)) {
60
+ const files = fs.readdirSync(cacheDir);
61
+ hasV1Data = files.some(f => f.includes(v1EnvName));
62
+ hasV2Data = files.some(f => f.includes(v2EnvName) && !f.includes(`${v2EnvName}_`));
63
+ }
64
+ console.log('šŸ’¾ Your Current Data:\n');
65
+ if (hasV1Data) {
66
+ console.log(` āœ… v1.x data found: ${v1EnvName}`);
67
+ }
68
+ if (hasV2Data) {
69
+ console.log(` āœ… v2.0 data found: ${v2EnvName}`);
70
+ }
71
+ if (!hasV1Data && !hasV2Data) {
72
+ console.log(' ā„¹ļø No local cache found (fresh start)');
73
+ }
74
+ console.log('');
75
+ console.log('šŸ”§ Migration Steps:\n');
76
+ if (hasV1Data && !hasV2Data) {
77
+ console.log(' You have v1.x data. To migrate:\n');
78
+ console.log(' 1. Push with explicit environment name:');
79
+ console.log(` lsh push --env dev # Keeps v1.x format (${v1EnvName})`);
80
+ console.log('');
81
+ console.log(' 2. Or start using v2.0 format:');
82
+ console.log(` lsh push # New v2.0 format (${v2EnvName})`);
83
+ console.log('');
84
+ console.log(' šŸ’” Both formats can coexist. Use --env dev to access old data.');
85
+ }
86
+ else if (hasV2Data) {
87
+ console.log(' āœ… You\'re already using v2.0 format!');
88
+ console.log('');
89
+ console.log(` Continue using: lsh push # → ${v2EnvName}`);
90
+ }
91
+ else {
92
+ console.log(' āœ… Fresh start - you\'re ready for v2.0!');
93
+ console.log('');
94
+ console.log(` Start using: lsh push # → ${v2EnvName}`);
95
+ }
96
+ console.log('');
97
+ console.log('━'.repeat(60));
98
+ console.log('');
99
+ console.log('šŸ“š For more information:');
100
+ console.log(' https://github.com/gwicho38/lsh/blob/main/docs/releases/2.0.0.md');
101
+ console.log('');
102
+ console.log('šŸ’” Tip: Use --v1-compat flag to keep v1.x behavior');
103
+ console.log('');
104
+ });
105
+ }
@@ -234,11 +234,14 @@ export class SecretsManager {
234
234
  if (filename !== '.env' && !filename.startsWith('.env.')) {
235
235
  throw new Error(`Invalid filename: ${filename}. Must be '.env' or start with '.env.'`);
236
236
  }
237
- logger.info(`Pulling ${filename} (${environment}) from IPFS...`);
237
+ logger.info(`Pulling ${filename} (${this.getRepoAwareEnvironment(environment)}) from IPFS...`);
238
238
  // Get secrets from IPFS storage
239
239
  const secrets = await this.storage.pull(environment, this.encryptionKey, this.gitInfo?.repoName);
240
240
  if (secrets.length === 0) {
241
- throw new Error(`No secrets found for environment: ${environment}`);
241
+ const effectiveEnv = this.getRepoAwareEnvironment(environment);
242
+ throw new Error(`No secrets found for environment: ${effectiveEnv}\n\n` +
243
+ `šŸ’” Tip: Check available environments with: lsh env\n` +
244
+ ` Or push secrets first with: lsh push --env ${environment}`);
242
245
  }
243
246
  // Backup existing .env if it exists (unless force is true)
244
247
  if (fs.existsSync(envFilePath) && !force) {
@@ -360,15 +363,39 @@ export class SecretsManager {
360
363
  }
361
364
  return status;
362
365
  }
366
+ /**
367
+ * Get the default environment name based on context
368
+ * v2.0: In git repo, default is repo name; otherwise 'dev'
369
+ */
370
+ getDefaultEnvironment() {
371
+ // Check for v1 compatibility mode
372
+ if (process.env.LSH_V1_COMPAT === 'true') {
373
+ return 'dev'; // v1.x behavior
374
+ }
375
+ // v2.0 behavior: use repo name as default in git repos
376
+ if (this.gitInfo?.repoName) {
377
+ return ''; // Empty string signals "use repo name only"
378
+ }
379
+ return 'dev';
380
+ }
363
381
  /**
364
382
  * Get repo-aware environment namespace
365
- * Returns environment name with repo context if in a git repo
383
+ * v2.0: Returns environment name with repo context if in a git repo
384
+ *
385
+ * Behavior:
386
+ * - Empty env in repo: returns just repo name (v2.0 default)
387
+ * - Named env in repo: returns repo_env (e.g., repo_staging)
388
+ * - Any env outside repo: returns env as-is
366
389
  */
367
390
  getRepoAwareEnvironment(environment) {
368
391
  if (this.gitInfo?.repoName) {
392
+ // v2.0: Empty environment means "use repo name only"
393
+ if (environment === '' || environment === 'default') {
394
+ return this.gitInfo.repoName;
395
+ }
369
396
  return `${this.gitInfo.repoName}_${environment}`;
370
397
  }
371
- return environment;
398
+ return environment || 'dev'; // Default to 'dev' outside git repos
372
399
  }
373
400
  /**
374
401
  * Generate encryption key if not set
@@ -18,7 +18,9 @@ export async function init_secrets(program) {
18
18
  .action(async (options) => {
19
19
  const manager = new SecretsManager();
20
20
  try {
21
- await manager.push(options.file, options.env, options.force);
21
+ // v2.0: Use context-aware default environment
22
+ const env = options.env === 'dev' ? manager.getDefaultEnvironment() : options.env;
23
+ await manager.push(options.file, env, options.force);
22
24
  }
23
25
  catch (error) {
24
26
  const err = error;
@@ -40,7 +42,9 @@ export async function init_secrets(program) {
40
42
  .action(async (options) => {
41
43
  const manager = new SecretsManager();
42
44
  try {
43
- await manager.pull(options.file, options.env, options.force);
45
+ // v2.0: Use context-aware default environment
46
+ const env = options.env === 'dev' ? manager.getDefaultEnvironment() : options.env;
47
+ await manager.pull(options.file, env, options.force);
44
48
  }
45
49
  catch (error) {
46
50
  const err = error;
@@ -273,13 +277,15 @@ API_KEY=
273
277
  .action(async (options) => {
274
278
  const manager = new SecretsManager();
275
279
  try {
280
+ // v2.0: Use context-aware default environment
281
+ const env = options.env === 'dev' ? manager.getDefaultEnvironment() : options.env;
276
282
  if (options.legacy) {
277
283
  // Use legacy sync (suggestions only)
278
- await manager.sync(options.file, options.env);
284
+ await manager.sync(options.file, env);
279
285
  }
280
286
  else {
281
287
  // Use new smart sync (auto-execute)
282
- await manager.smartSync(options.file, options.env, !options.dryRun, options.load, options.force, options.forceRekey);
288
+ await manager.smartSync(options.file, env, !options.dryRun, options.load, options.force, options.forceRekey);
283
289
  }
284
290
  }
285
291
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lsh-framework",
3
- "version": "1.8.1",
3
+ "version": "2.0.0",
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": {