lsh-framework 3.1.8 → 3.1.14
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 +4 -2
- package/dist/commands/context.js +578 -0
- package/dist/commands/init.js +40 -273
- package/dist/commands/ipfs.js +47 -10
- package/dist/commands/sync.js +365 -0
- package/dist/constants/config.js +0 -1
- package/dist/lib/ipfs-secrets-storage.js +70 -92
- package/dist/lib/ipfs-sync-logger.js +5 -7
- package/dist/lib/ipfs-sync.js +349 -0
- package/dist/services/secrets/secrets.js +52 -111
- package/package.json +1 -2
- package/dist/commands/storacha.js +0 -208
- package/dist/lib/storacha-client.js +0 -516
package/dist/commands/init.js
CHANGED
|
@@ -7,7 +7,6 @@ import chalk from 'chalk';
|
|
|
7
7
|
import * as fs from 'fs/promises';
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
import * as crypto from 'crypto';
|
|
10
|
-
import { createClient } from '@supabase/supabase-js';
|
|
11
10
|
import ora from 'ora';
|
|
12
11
|
import { getPlatformPaths } from '../lib/platform-utils.js';
|
|
13
12
|
import { getGitRepoInfo } from '../lib/git-utils.js';
|
|
@@ -20,11 +19,6 @@ export function registerInitCommands(program) {
|
|
|
20
19
|
.command('init')
|
|
21
20
|
.description('Interactive setup wizard (first-time configuration)')
|
|
22
21
|
.option('-g, --global', 'Use global workspace ($HOME)')
|
|
23
|
-
.option('--local', 'Use local-only encryption (no cloud sync)')
|
|
24
|
-
.option('--storacha', 'Use Storacha IPFS network sync (recommended)')
|
|
25
|
-
.option('--supabase', 'Use Supabase cloud storage')
|
|
26
|
-
.option('--postgres', 'Use self-hosted PostgreSQL')
|
|
27
|
-
.option('--skip-test', 'Skip connection testing')
|
|
28
22
|
.action(async (options) => {
|
|
29
23
|
try {
|
|
30
24
|
await runSetupWizard(options);
|
|
@@ -75,51 +69,22 @@ async function runSetupWizard(options) {
|
|
|
75
69
|
return;
|
|
76
70
|
}
|
|
77
71
|
}
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
else if (options.supabase) {
|
|
90
|
-
storageType = 'supabase';
|
|
72
|
+
// Check IPFS daemon status
|
|
73
|
+
const { getIPFSSync } = await import('../lib/ipfs-sync.js');
|
|
74
|
+
const ipfsSync = getIPFSSync();
|
|
75
|
+
const daemonRunning = await ipfsSync.checkDaemon();
|
|
76
|
+
if (!daemonRunning) {
|
|
77
|
+
console.log(chalk.yellow('\n⚠️ IPFS daemon not running'));
|
|
78
|
+
console.log(chalk.gray(' For network sync, start IPFS:'));
|
|
79
|
+
console.log(chalk.cyan(' lsh ipfs install && lsh ipfs init && lsh ipfs start'));
|
|
80
|
+
console.log(chalk.gray(' Setup will continue with local-only mode.'));
|
|
81
|
+
console.log('');
|
|
91
82
|
}
|
|
92
83
|
else {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{
|
|
96
|
-
type: 'list',
|
|
97
|
-
name: 'storage',
|
|
98
|
-
message: 'Choose storage backend:',
|
|
99
|
-
choices: [
|
|
100
|
-
{
|
|
101
|
-
name: '🌐 Storacha (IPFS network, zero-config, recommended)',
|
|
102
|
-
value: 'storacha',
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
name: '☁️ Supabase (cloud-hosted, team collaboration)',
|
|
106
|
-
value: 'supabase',
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
name: '💾 Local encryption (file-based, no cloud sync)',
|
|
110
|
-
value: 'local',
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
name: '🐘 Self-hosted PostgreSQL',
|
|
114
|
-
value: 'postgres',
|
|
115
|
-
},
|
|
116
|
-
],
|
|
117
|
-
default: 'storacha',
|
|
118
|
-
},
|
|
119
|
-
]);
|
|
120
|
-
storageType = storage;
|
|
84
|
+
console.log(chalk.green('\n✅ IPFS daemon running - network sync enabled'));
|
|
85
|
+
console.log('');
|
|
121
86
|
}
|
|
122
|
-
// Check if secrets already exist
|
|
87
|
+
// Check if secrets already exist locally
|
|
123
88
|
const cloudCheck = await checkCloudSecretsExist();
|
|
124
89
|
let encryptionKey;
|
|
125
90
|
if (cloudCheck.exists && cloudCheck.repoName) {
|
|
@@ -167,22 +132,8 @@ async function runSetupWizard(options) {
|
|
|
167
132
|
encryptionKey = generateEncryptionKey();
|
|
168
133
|
}
|
|
169
134
|
const config = {
|
|
170
|
-
storageType,
|
|
171
135
|
encryptionKey,
|
|
172
136
|
};
|
|
173
|
-
// Configure based on storage type
|
|
174
|
-
if (storageType === 'storacha') {
|
|
175
|
-
await configureStoracha(config);
|
|
176
|
-
}
|
|
177
|
-
else if (storageType === 'supabase') {
|
|
178
|
-
await configureSupabase(config, options.skipTest);
|
|
179
|
-
}
|
|
180
|
-
else if (storageType === 'postgres') {
|
|
181
|
-
await configurePostgres(config, options.skipTest);
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
await configureLocal(config);
|
|
185
|
-
}
|
|
186
137
|
// If using existing key and secrets exist, offer to pull them now
|
|
187
138
|
if (cloudCheck.exists && config.encryptionKey && cloudCheck.repoName) {
|
|
188
139
|
const { pullNow } = await inquirer.prompt([
|
|
@@ -240,7 +191,7 @@ async function pullSecretsAfterInit(_encryptionKey, _repoName) {
|
|
|
240
191
|
}
|
|
241
192
|
}
|
|
242
193
|
/**
|
|
243
|
-
* Check if secrets already exist
|
|
194
|
+
* Check if secrets already exist locally for current repo
|
|
244
195
|
*/
|
|
245
196
|
async function checkCloudSecretsExist() {
|
|
246
197
|
try {
|
|
@@ -250,7 +201,7 @@ async function checkCloudSecretsExist() {
|
|
|
250
201
|
}
|
|
251
202
|
const repoName = gitInfo.repoName;
|
|
252
203
|
const environment = repoName; // Default environment for repo
|
|
253
|
-
//
|
|
204
|
+
// Check local metadata (fast path)
|
|
254
205
|
const paths = getPlatformPaths();
|
|
255
206
|
const metadataPath = path.join(paths.dataDir, 'secrets-metadata.json');
|
|
256
207
|
try {
|
|
@@ -263,26 +214,19 @@ async function checkCloudSecretsExist() {
|
|
|
263
214
|
}
|
|
264
215
|
}
|
|
265
216
|
catch {
|
|
266
|
-
// Metadata file doesn't exist or can't be read
|
|
217
|
+
// Metadata file doesn't exist or can't be read
|
|
267
218
|
}
|
|
268
|
-
// Check
|
|
219
|
+
// Check IPFS sync history for this repo
|
|
269
220
|
try {
|
|
270
|
-
const {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
const registryExists = await storacha.checkRegistry(repoName);
|
|
276
|
-
spinner.stop();
|
|
277
|
-
if (registryExists) {
|
|
278
|
-
return { exists: true, repoName, environment };
|
|
279
|
-
}
|
|
221
|
+
const { getIPFSSync } = await import('../lib/ipfs-sync.js');
|
|
222
|
+
const ipfsSync = getIPFSSync();
|
|
223
|
+
const latestCid = await ipfsSync.getLatestCid(repoName);
|
|
224
|
+
if (latestCid) {
|
|
225
|
+
return { exists: true, repoName, environment };
|
|
280
226
|
}
|
|
281
227
|
}
|
|
282
|
-
catch
|
|
283
|
-
//
|
|
284
|
-
const err = error;
|
|
285
|
-
console.log(chalk.gray(` (Network check skipped: ${err.message})`));
|
|
228
|
+
catch {
|
|
229
|
+
// IPFS sync history check failed
|
|
286
230
|
}
|
|
287
231
|
return { exists: false, repoName, environment };
|
|
288
232
|
}
|
|
@@ -290,141 +234,6 @@ async function checkCloudSecretsExist() {
|
|
|
290
234
|
return { exists: false };
|
|
291
235
|
}
|
|
292
236
|
}
|
|
293
|
-
/**
|
|
294
|
-
* Configure Supabase
|
|
295
|
-
*/
|
|
296
|
-
async function configureSupabase(config, skipTest) {
|
|
297
|
-
console.log(chalk.cyan('\n📦 Supabase Configuration'));
|
|
298
|
-
console.log(chalk.gray('Need credentials? Visit: https://supabase.com/dashboard'));
|
|
299
|
-
console.log('');
|
|
300
|
-
const answers = await inquirer.prompt([
|
|
301
|
-
{
|
|
302
|
-
type: 'input',
|
|
303
|
-
name: 'url',
|
|
304
|
-
message: 'Enter your Supabase URL:',
|
|
305
|
-
validate: (input) => {
|
|
306
|
-
if (!input.trim())
|
|
307
|
-
return 'URL is required';
|
|
308
|
-
if (!input.startsWith('https://'))
|
|
309
|
-
return 'URL must start with https://';
|
|
310
|
-
if (!input.includes('.supabase.co'))
|
|
311
|
-
return 'Must be a valid Supabase URL';
|
|
312
|
-
return true;
|
|
313
|
-
},
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
type: 'password',
|
|
317
|
-
name: 'key',
|
|
318
|
-
message: 'Enter your Supabase anon key:',
|
|
319
|
-
mask: '*',
|
|
320
|
-
validate: (input) => {
|
|
321
|
-
if (!input.trim())
|
|
322
|
-
return 'Anon key is required';
|
|
323
|
-
if (input.length < 100)
|
|
324
|
-
return 'Anon key seems too short';
|
|
325
|
-
return true;
|
|
326
|
-
},
|
|
327
|
-
},
|
|
328
|
-
]);
|
|
329
|
-
config.supabaseUrl = answers.url.trim();
|
|
330
|
-
config.supabaseKey = answers.key.trim();
|
|
331
|
-
// Test connection
|
|
332
|
-
if (!skipTest && config.supabaseUrl && config.supabaseKey) {
|
|
333
|
-
await testSupabaseConnection(config.supabaseUrl, config.supabaseKey);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Test Supabase connection
|
|
338
|
-
*/
|
|
339
|
-
async function testSupabaseConnection(url, key) {
|
|
340
|
-
const spinner = ora('Testing Supabase connection...').start();
|
|
341
|
-
try {
|
|
342
|
-
const supabase = createClient(url, key);
|
|
343
|
-
// Try to query the database (even if table doesn't exist, connection will work)
|
|
344
|
-
const { error } = await supabase.from('lsh_secrets').select('count').limit(0);
|
|
345
|
-
// Connection successful (404 table not found is fine - means connection works)
|
|
346
|
-
if (!error || error.code === 'PGRST116' || error.message.includes('relation')) {
|
|
347
|
-
spinner.succeed(chalk.green('✅ Connection successful!'));
|
|
348
|
-
}
|
|
349
|
-
else {
|
|
350
|
-
spinner.fail(chalk.red('❌ Connection failed'));
|
|
351
|
-
throw new Error(`Supabase error: ${error.message}`);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
catch (error) {
|
|
355
|
-
spinner.fail(chalk.red('❌ Connection failed'));
|
|
356
|
-
const err = error;
|
|
357
|
-
throw new Error(`Could not connect to Supabase: ${err.message}`);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Configure Storacha IPFS network sync
|
|
362
|
-
*/
|
|
363
|
-
async function configureStoracha(config) {
|
|
364
|
-
console.log(chalk.cyan('\n🌐 Storacha IPFS Network Sync'));
|
|
365
|
-
console.log(chalk.gray('Zero-config multi-host secrets sync via IPFS network'));
|
|
366
|
-
console.log('');
|
|
367
|
-
const answers = await inquirer.prompt([
|
|
368
|
-
{
|
|
369
|
-
type: 'input',
|
|
370
|
-
name: 'email',
|
|
371
|
-
message: 'Enter your email for Storacha authentication:',
|
|
372
|
-
validate: (input) => {
|
|
373
|
-
if (!input.trim())
|
|
374
|
-
return 'Email is required';
|
|
375
|
-
if (!input.includes('@'))
|
|
376
|
-
return 'Must be a valid email address';
|
|
377
|
-
return true;
|
|
378
|
-
},
|
|
379
|
-
},
|
|
380
|
-
]);
|
|
381
|
-
config.storachaEmail = answers.email.trim();
|
|
382
|
-
console.log('');
|
|
383
|
-
console.log(chalk.yellow('📧 Please check your email to complete authentication.'));
|
|
384
|
-
console.log(chalk.gray(' After setup completes, run:'));
|
|
385
|
-
console.log(chalk.cyan(' lsh storacha login ' + config.storachaEmail));
|
|
386
|
-
console.log('');
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Configure self-hosted PostgreSQL
|
|
390
|
-
*/
|
|
391
|
-
async function configurePostgres(config, skipTest) {
|
|
392
|
-
console.log(chalk.cyan('\n🐘 PostgreSQL Configuration'));
|
|
393
|
-
console.log('');
|
|
394
|
-
const { url } = await inquirer.prompt([
|
|
395
|
-
{
|
|
396
|
-
type: 'input',
|
|
397
|
-
name: 'url',
|
|
398
|
-
message: 'Enter PostgreSQL connection URL:',
|
|
399
|
-
default: 'postgresql://user:password@localhost:5432/lsh',
|
|
400
|
-
validate: (input) => {
|
|
401
|
-
if (!input.trim())
|
|
402
|
-
return 'Connection URL is required';
|
|
403
|
-
if (!input.startsWith('postgres'))
|
|
404
|
-
return 'Must be a valid PostgreSQL URL';
|
|
405
|
-
return true;
|
|
406
|
-
},
|
|
407
|
-
},
|
|
408
|
-
]);
|
|
409
|
-
config.postgresUrl = url.trim();
|
|
410
|
-
if (!skipTest) {
|
|
411
|
-
const spinner = ora('Testing PostgreSQL connection...').start();
|
|
412
|
-
// Note: We'll skip actual testing for now as we don't have pg client imported
|
|
413
|
-
spinner.info(chalk.yellow('⚠️ Connection test skipped. Run "lsh doctor" after setup to verify.'));
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Configure local-only mode
|
|
418
|
-
*/
|
|
419
|
-
async function configureLocal(_config) {
|
|
420
|
-
console.log(chalk.cyan('\n💾 Local Encryption Mode'));
|
|
421
|
-
console.log(chalk.gray('Secrets will be encrypted locally. No cloud sync available.'));
|
|
422
|
-
console.log('');
|
|
423
|
-
const paths = getPlatformPaths();
|
|
424
|
-
console.log(chalk.gray('Encrypted secrets will be stored in:'));
|
|
425
|
-
console.log(chalk.cyan(` ${path.join(paths.dataDir, 'encrypted')}`));
|
|
426
|
-
console.log('');
|
|
427
|
-
}
|
|
428
237
|
/**
|
|
429
238
|
* Generate a secure encryption key
|
|
430
239
|
*/
|
|
@@ -450,19 +259,6 @@ async function saveConfiguration(config, baseDir, globalMode) {
|
|
|
450
259
|
const updates = {
|
|
451
260
|
LSH_SECRETS_KEY: config.encryptionKey,
|
|
452
261
|
};
|
|
453
|
-
if (config.storageType === 'supabase' && config.supabaseUrl && config.supabaseKey) {
|
|
454
|
-
updates.SUPABASE_URL = config.supabaseUrl;
|
|
455
|
-
updates.SUPABASE_ANON_KEY = config.supabaseKey;
|
|
456
|
-
}
|
|
457
|
-
if (config.storageType === 'postgres' && config.postgresUrl) {
|
|
458
|
-
updates.DATABASE_URL = config.postgresUrl;
|
|
459
|
-
}
|
|
460
|
-
if (config.storageType === 'local') {
|
|
461
|
-
updates.LSH_STORAGE_MODE = 'local';
|
|
462
|
-
}
|
|
463
|
-
if (config.storageType === 'storacha') {
|
|
464
|
-
updates.LSH_STORACHA_ENABLED = 'true';
|
|
465
|
-
}
|
|
466
262
|
// Update .env content
|
|
467
263
|
for (const [key, value] of Object.entries(updates)) {
|
|
468
264
|
const regex = new RegExp(`^${key}=.*$`, 'm');
|
|
@@ -529,55 +325,26 @@ function showSuccessMessage(config) {
|
|
|
529
325
|
console.log(chalk.gray(' This key is saved in your .env file.'));
|
|
530
326
|
console.log(chalk.gray(' Share it with your team to sync secrets.'));
|
|
531
327
|
console.log('');
|
|
532
|
-
|
|
533
|
-
if (config.storageType === 'storacha') {
|
|
534
|
-
console.log(chalk.cyan('🌐 Using Storacha IPFS network sync'));
|
|
535
|
-
}
|
|
536
|
-
else if (config.storageType === 'supabase') {
|
|
537
|
-
console.log(chalk.cyan('☁️ Using Supabase cloud storage'));
|
|
538
|
-
}
|
|
539
|
-
else if (config.storageType === 'postgres') {
|
|
540
|
-
console.log(chalk.cyan('🐘 Using PostgreSQL storage'));
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
console.log(chalk.cyan('💾 Using local encryption'));
|
|
544
|
-
}
|
|
328
|
+
console.log(chalk.cyan('🌐 Using native IPFS for secrets sync'));
|
|
545
329
|
console.log('');
|
|
546
330
|
// Next steps
|
|
547
331
|
console.log(chalk.bold('🚀 Next steps:'));
|
|
548
332
|
console.log('');
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
console.log(chalk.gray(' 1. Verify your setup:'));
|
|
565
|
-
console.log(chalk.cyan(' lsh doctor'));
|
|
566
|
-
console.log('');
|
|
567
|
-
if (config.storageType !== 'local') {
|
|
568
|
-
console.log(chalk.gray(' 2. Push your secrets:'));
|
|
569
|
-
console.log(chalk.cyan(' lsh push --env dev'));
|
|
570
|
-
console.log('');
|
|
571
|
-
console.log(chalk.gray(' 3. On another machine:'));
|
|
572
|
-
console.log(chalk.cyan(' lsh init ') + chalk.gray('# Use the same credentials'));
|
|
573
|
-
console.log(chalk.cyan(' lsh pull --env dev'));
|
|
574
|
-
}
|
|
575
|
-
else {
|
|
576
|
-
console.log(chalk.gray(' 2. Start managing secrets:'));
|
|
577
|
-
console.log(chalk.cyan(' lsh set API_KEY myvalue'));
|
|
578
|
-
console.log(chalk.cyan(' lsh list'));
|
|
579
|
-
}
|
|
580
|
-
}
|
|
333
|
+
console.log(chalk.gray(' 1. (Optional) Start IPFS daemon for network sync:'));
|
|
334
|
+
console.log(chalk.cyan(' lsh ipfs install && lsh ipfs init && lsh ipfs start'));
|
|
335
|
+
console.log('');
|
|
336
|
+
console.log(chalk.gray(' 2. Push your secrets to IPFS:'));
|
|
337
|
+
console.log(chalk.cyan(' lsh sync push'));
|
|
338
|
+
console.log(chalk.gray(' (Returns a CID - share this with teammates)'));
|
|
339
|
+
console.log('');
|
|
340
|
+
console.log(chalk.gray(' 3. On another machine:'));
|
|
341
|
+
console.log(chalk.cyan(' lsh init'));
|
|
342
|
+
console.log(chalk.cyan(' export LSH_SECRETS_KEY=<key-from-teammate>'));
|
|
343
|
+
console.log(chalk.cyan(' lsh sync pull <cid>'));
|
|
344
|
+
console.log('');
|
|
345
|
+
console.log(chalk.gray(' Alternatively, use the classic push/pull commands:'));
|
|
346
|
+
console.log(chalk.cyan(' lsh push --env dev'));
|
|
347
|
+
console.log(chalk.cyan(' lsh pull --env dev'));
|
|
581
348
|
console.log('');
|
|
582
349
|
console.log(chalk.gray('📖 Documentation: https://github.com/gwicho38/lsh'));
|
|
583
350
|
console.log('');
|
package/dist/commands/ipfs.js
CHANGED
|
@@ -5,39 +5,76 @@
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import ora from 'ora';
|
|
7
7
|
import { IPFSClientManager } from '../lib/ipfs-client-manager.js';
|
|
8
|
+
import { getIPFSSync } from '../lib/ipfs-sync.js';
|
|
8
9
|
/**
|
|
9
10
|
* Register IPFS commands
|
|
10
11
|
*/
|
|
11
12
|
export function registerIPFSCommands(program) {
|
|
12
13
|
const ipfsCommand = program
|
|
13
14
|
.command('ipfs')
|
|
14
|
-
.description('Manage IPFS client installation and
|
|
15
|
+
.description('Manage IPFS client installation, configuration, and sync');
|
|
15
16
|
// lsh ipfs status
|
|
16
17
|
ipfsCommand
|
|
17
18
|
.command('status')
|
|
18
|
-
.description('Check IPFS client installation status')
|
|
19
|
+
.description('Check IPFS client installation and daemon status')
|
|
19
20
|
.option('--json', 'Output as JSON')
|
|
20
21
|
.action(async (options) => {
|
|
21
22
|
try {
|
|
22
23
|
const manager = new IPFSClientManager();
|
|
23
24
|
const info = await manager.detect();
|
|
25
|
+
const ipfsSync = getIPFSSync();
|
|
26
|
+
const daemonInfo = await ipfsSync.getDaemonInfo();
|
|
24
27
|
if (options.json) {
|
|
25
|
-
console.log(JSON.stringify(
|
|
28
|
+
console.log(JSON.stringify({
|
|
29
|
+
...info,
|
|
30
|
+
daemonRunning: !!daemonInfo,
|
|
31
|
+
daemonInfo,
|
|
32
|
+
}, null, 2));
|
|
26
33
|
return;
|
|
27
34
|
}
|
|
28
|
-
console.log(chalk.bold.cyan('\n📦 IPFS
|
|
35
|
+
console.log(chalk.bold.cyan('\n📦 IPFS Status'));
|
|
29
36
|
console.log(chalk.gray('━'.repeat(50)));
|
|
30
37
|
console.log('');
|
|
38
|
+
// Client installation status
|
|
39
|
+
console.log(chalk.bold('Client:'));
|
|
31
40
|
if (info.installed) {
|
|
32
|
-
console.log(chalk.green('✅ IPFS client installed'));
|
|
33
|
-
console.log(`
|
|
34
|
-
console.log(`
|
|
35
|
-
console.log(`
|
|
41
|
+
console.log(chalk.green(' ✅ IPFS client installed'));
|
|
42
|
+
console.log(` Type: ${info.type}`);
|
|
43
|
+
console.log(` Version: ${info.version}`);
|
|
44
|
+
console.log(` Path: ${info.path}`);
|
|
36
45
|
}
|
|
37
46
|
else {
|
|
38
|
-
console.log(chalk.yellow('⚠️ IPFS client not installed'));
|
|
47
|
+
console.log(chalk.yellow(' ⚠️ IPFS client not installed'));
|
|
39
48
|
console.log('');
|
|
40
|
-
console.log(chalk.gray('
|
|
49
|
+
console.log(chalk.gray(' Install with: lsh ipfs install'));
|
|
50
|
+
}
|
|
51
|
+
console.log('');
|
|
52
|
+
// Daemon status
|
|
53
|
+
console.log(chalk.bold('Daemon:'));
|
|
54
|
+
if (daemonInfo) {
|
|
55
|
+
console.log(chalk.green(' ✅ Daemon running'));
|
|
56
|
+
console.log(` Peer ID: ${daemonInfo.peerId.substring(0, 16)}...`);
|
|
57
|
+
console.log(` Version: ${daemonInfo.version}`);
|
|
58
|
+
console.log(' API: http://127.0.0.1:5001');
|
|
59
|
+
console.log(' Gateway: http://127.0.0.1:8080');
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(chalk.yellow(' ⚠️ Daemon not running'));
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(chalk.gray(' Start with: lsh ipfs start'));
|
|
65
|
+
}
|
|
66
|
+
console.log('');
|
|
67
|
+
// Quick actions
|
|
68
|
+
console.log(chalk.bold('Quick Actions:'));
|
|
69
|
+
if (!info.installed) {
|
|
70
|
+
console.log(chalk.cyan(' lsh ipfs install # Install IPFS'));
|
|
71
|
+
}
|
|
72
|
+
else if (!daemonInfo) {
|
|
73
|
+
console.log(chalk.cyan(' lsh ipfs start # Start daemon'));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log(chalk.cyan(' lsh ipfs sync push # Upload encrypted secrets'));
|
|
77
|
+
console.log(chalk.cyan(' lsh ipfs sync pull <cid> # Download by CID'));
|
|
41
78
|
}
|
|
42
79
|
console.log('');
|
|
43
80
|
}
|