lsh-framework 2.1.2 → 2.2.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.
@@ -10,6 +10,7 @@ import * as crypto from 'crypto';
10
10
  import { createClient } from '@supabase/supabase-js';
11
11
  import ora from 'ora';
12
12
  import { getPlatformPaths } from '../lib/platform-utils.js';
13
+ import { getGitRepoInfo } from '../lib/git-utils.js';
13
14
  /**
14
15
  * Register init commands
15
16
  */
@@ -101,9 +102,56 @@ async function runSetupWizard(options) {
101
102
  ]);
102
103
  storageType = storage;
103
104
  }
105
+ // Check if secrets already exist for this repo in the cloud
106
+ const cloudCheck = await checkCloudSecretsExist();
107
+ let encryptionKey;
108
+ if (cloudCheck.exists && cloudCheck.repoName) {
109
+ // Secrets found! This is an existing project
110
+ console.log(chalk.cyan(`\n✨ Found existing secrets for "${cloudCheck.repoName}" in cloud!`));
111
+ console.log(chalk.gray('This appears to be an existing project.'));
112
+ console.log('');
113
+ const { useExisting } = await inquirer.prompt([
114
+ {
115
+ type: 'confirm',
116
+ name: 'useExisting',
117
+ message: 'Pull existing secrets from another machine?',
118
+ default: true,
119
+ },
120
+ ]);
121
+ if (useExisting) {
122
+ // Prompt for existing encryption key
123
+ const { existingKey } = await inquirer.prompt([
124
+ {
125
+ type: 'password',
126
+ name: 'existingKey',
127
+ message: 'Enter the encryption key from your other machine:',
128
+ mask: '*',
129
+ validate: (input) => {
130
+ if (!input.trim())
131
+ return 'Encryption key is required';
132
+ if (input.length !== 64)
133
+ return 'Key should be 64 characters (32 bytes hex)';
134
+ if (!/^[0-9a-f]+$/i.test(input))
135
+ return 'Key should be hexadecimal';
136
+ return true;
137
+ },
138
+ },
139
+ ]);
140
+ encryptionKey = existingKey.trim();
141
+ }
142
+ else {
143
+ // Generate new key (will overwrite existing)
144
+ console.log(chalk.yellow('\n⚠️ Generating new key will overwrite existing secrets!'));
145
+ encryptionKey = generateEncryptionKey();
146
+ }
147
+ }
148
+ else {
149
+ // No existing secrets - generate new key
150
+ encryptionKey = generateEncryptionKey();
151
+ }
104
152
  const config = {
105
153
  storageType,
106
- encryptionKey: generateEncryptionKey(),
154
+ encryptionKey,
107
155
  };
108
156
  // Configure based on storage type
109
157
  if (storageType === 'storacha') {
@@ -118,6 +166,20 @@ async function runSetupWizard(options) {
118
166
  else {
119
167
  await configureLocal(config);
120
168
  }
169
+ // If using existing key and secrets exist, offer to pull them now
170
+ if (cloudCheck.exists && config.encryptionKey && cloudCheck.repoName) {
171
+ const { pullNow } = await inquirer.prompt([
172
+ {
173
+ type: 'confirm',
174
+ name: 'pullNow',
175
+ message: 'Pull secrets now?',
176
+ default: true,
177
+ },
178
+ ]);
179
+ if (pullNow) {
180
+ await pullSecretsAfterInit(config.encryptionKey, cloudCheck.repoName);
181
+ }
182
+ }
121
183
  // Save configuration
122
184
  await saveConfiguration(config);
123
185
  // Show success message
@@ -139,6 +201,59 @@ async function checkExistingConfig() {
139
201
  return false;
140
202
  }
141
203
  }
204
+ /**
205
+ * Pull secrets after init is complete
206
+ */
207
+ async function pullSecretsAfterInit(encryptionKey, repoName) {
208
+ const spinner = ora('Pulling secrets from cloud...').start();
209
+ try {
210
+ // Dynamically import SecretsManager to avoid circular dependencies
211
+ const { SecretsManager } = await import('../lib/secrets-manager.js');
212
+ const secretsManager = new SecretsManager();
213
+ // Pull secrets for this repo
214
+ await secretsManager.pull('.env', '', false);
215
+ spinner.succeed(chalk.green('✅ Secrets pulled successfully!'));
216
+ }
217
+ catch (error) {
218
+ spinner.fail(chalk.red('❌ Failed to pull secrets'));
219
+ const err = error;
220
+ console.log(chalk.yellow(`\n⚠️ ${err.message}`));
221
+ console.log(chalk.gray('\nYou can try pulling manually later with:'));
222
+ console.log(chalk.cyan(` lsh pull`));
223
+ }
224
+ }
225
+ /**
226
+ * Check if secrets already exist in cloud for current repo
227
+ */
228
+ async function checkCloudSecretsExist() {
229
+ try {
230
+ const gitInfo = getGitRepoInfo();
231
+ if (!gitInfo?.repoName) {
232
+ return { exists: false };
233
+ }
234
+ const repoName = gitInfo.repoName;
235
+ const environment = repoName; // Default environment for repo
236
+ // Check if metadata file exists with this repo's secrets
237
+ const paths = getPlatformPaths();
238
+ const metadataPath = path.join(paths.dataDir, 'secrets-metadata.json');
239
+ try {
240
+ const metadataContent = await fs.readFile(metadataPath, 'utf-8');
241
+ const metadata = JSON.parse(metadataContent);
242
+ // Check if any environment matches this repo name
243
+ const hasSecrets = Object.keys(metadata).some(env => env === repoName || env.startsWith(`${repoName}_`));
244
+ if (hasSecrets) {
245
+ return { exists: true, repoName, environment };
246
+ }
247
+ }
248
+ catch {
249
+ // Metadata file doesn't exist or can't be read
250
+ }
251
+ return { exists: false, repoName, environment };
252
+ }
253
+ catch {
254
+ return { exists: false };
255
+ }
256
+ }
142
257
  /**
143
258
  * Configure Supabase
144
259
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lsh-framework",
3
- "version": "2.1.2",
3
+ "version": "2.2.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": {