lsh-framework 1.6.0 ā 1.7.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/README.md +3 -0
- package/dist/cli.js +2 -0
- package/dist/commands/doctor.js +35 -0
- package/dist/commands/ipfs.js +146 -0
- package/dist/lib/ipfs-client-manager.js +321 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -815,6 +815,9 @@ lsh daemon start
|
|
|
815
815
|
- **[SECRETS_QUICK_REFERENCE.md](docs/features/secrets/SECRETS_QUICK_REFERENCE.md)** - Quick reference for daily use
|
|
816
816
|
- **[SECRETS_CHEATSHEET.txt](SECRETS_CHEATSHEET.txt)** - Command cheatsheet
|
|
817
817
|
|
|
818
|
+
### IPFS Integration
|
|
819
|
+
- **[IPFS_CLIENT_GUIDE.md](docs/features/ipfs/IPFS_CLIENT_GUIDE.md)** - š IPFS client installation and management (v1.6.0+)
|
|
820
|
+
|
|
818
821
|
### Installation & Development
|
|
819
822
|
- **[INSTALL.md](docs/deployment/INSTALL.md)** - Detailed installation instructions
|
|
820
823
|
- **[CLAUDE.md](CLAUDE.md)** - Developer guide for contributors
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ import { registerDoctorCommands } from './commands/doctor.js';
|
|
|
10
10
|
import { registerCompletionCommands } from './commands/completion.js';
|
|
11
11
|
import { registerConfigCommands } from './commands/config.js';
|
|
12
12
|
import { registerSyncHistoryCommands } from './commands/sync-history.js';
|
|
13
|
+
import { registerIPFSCommands } from './commands/ipfs.js';
|
|
13
14
|
import { init_daemon } from './services/daemon/daemon.js';
|
|
14
15
|
import { init_supabase } from './services/supabase/supabase.js';
|
|
15
16
|
import { init_cron } from './services/cron/cron.js';
|
|
@@ -144,6 +145,7 @@ function findSimilarCommands(input, validCommands) {
|
|
|
144
145
|
registerDoctorCommands(program);
|
|
145
146
|
registerConfigCommands(program);
|
|
146
147
|
registerSyncHistoryCommands(program);
|
|
148
|
+
registerIPFSCommands(program);
|
|
147
149
|
// Secrets management (primary feature)
|
|
148
150
|
await init_secrets(program);
|
|
149
151
|
// Supporting services
|
package/dist/commands/doctor.js
CHANGED
|
@@ -7,6 +7,7 @@ import * as fs from 'fs/promises';
|
|
|
7
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
|
+
import { IPFSClientManager } from '../lib/ipfs-client-manager.js';
|
|
10
11
|
/**
|
|
11
12
|
* Register doctor commands
|
|
12
13
|
*/
|
|
@@ -48,6 +49,8 @@ async function runHealthCheck(options) {
|
|
|
48
49
|
checks.push(...storageChecks);
|
|
49
50
|
// Git repository check
|
|
50
51
|
checks.push(await checkGitRepository(options.verbose));
|
|
52
|
+
// IPFS client check
|
|
53
|
+
checks.push(await checkIPFSClient(options.verbose));
|
|
51
54
|
// Permissions check
|
|
52
55
|
checks.push(await checkPermissions(options.verbose));
|
|
53
56
|
// Display results
|
|
@@ -294,6 +297,38 @@ async function checkGitRepository(verbose) {
|
|
|
294
297
|
};
|
|
295
298
|
}
|
|
296
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* Check IPFS client installation
|
|
302
|
+
*/
|
|
303
|
+
async function checkIPFSClient(verbose) {
|
|
304
|
+
try {
|
|
305
|
+
const manager = new IPFSClientManager();
|
|
306
|
+
const info = await manager.detect();
|
|
307
|
+
if (info.installed) {
|
|
308
|
+
return {
|
|
309
|
+
name: 'IPFS Client',
|
|
310
|
+
status: 'pass',
|
|
311
|
+
message: `${info.type} v${info.version} installed`,
|
|
312
|
+
details: verbose ? `Path: ${info.path}` : undefined,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
name: 'IPFS Client',
|
|
317
|
+
status: 'warn',
|
|
318
|
+
message: 'Not installed (optional for local storage)',
|
|
319
|
+
details: 'Install with: lsh ipfs install',
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
const err = error;
|
|
324
|
+
return {
|
|
325
|
+
name: 'IPFS Client',
|
|
326
|
+
status: 'warn',
|
|
327
|
+
message: 'Could not check IPFS client',
|
|
328
|
+
details: err.message,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
297
332
|
/**
|
|
298
333
|
* Check file permissions
|
|
299
334
|
*/
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IPFS Commands
|
|
3
|
+
* Manage IPFS client installation and configuration
|
|
4
|
+
*/
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { IPFSClientManager } from '../lib/ipfs-client-manager.js';
|
|
8
|
+
/**
|
|
9
|
+
* Register IPFS commands
|
|
10
|
+
*/
|
|
11
|
+
export function registerIPFSCommands(program) {
|
|
12
|
+
const ipfsCommand = program
|
|
13
|
+
.command('ipfs')
|
|
14
|
+
.description('Manage IPFS client installation and configuration');
|
|
15
|
+
// lsh ipfs status
|
|
16
|
+
ipfsCommand
|
|
17
|
+
.command('status')
|
|
18
|
+
.description('Check IPFS client installation status')
|
|
19
|
+
.option('--json', 'Output as JSON')
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
try {
|
|
22
|
+
const manager = new IPFSClientManager();
|
|
23
|
+
const info = await manager.detect();
|
|
24
|
+
if (options.json) {
|
|
25
|
+
console.log(JSON.stringify(info, null, 2));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
console.log(chalk.bold.cyan('\nš¦ IPFS Client Status'));
|
|
29
|
+
console.log(chalk.gray('ā'.repeat(50)));
|
|
30
|
+
console.log('');
|
|
31
|
+
if (info.installed) {
|
|
32
|
+
console.log(chalk.green('ā
IPFS client installed'));
|
|
33
|
+
console.log(` Type: ${info.type}`);
|
|
34
|
+
console.log(` Version: ${info.version}`);
|
|
35
|
+
console.log(` Path: ${info.path}`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.log(chalk.yellow('ā ļø IPFS client not installed'));
|
|
39
|
+
console.log('');
|
|
40
|
+
console.log(chalk.gray(' Install with: lsh ipfs install'));
|
|
41
|
+
}
|
|
42
|
+
console.log('');
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
const err = error;
|
|
46
|
+
console.error(chalk.red('\nā Failed to check status:'), err.message);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// lsh ipfs install
|
|
51
|
+
ipfsCommand
|
|
52
|
+
.command('install')
|
|
53
|
+
.description('Install IPFS client (Kubo)')
|
|
54
|
+
.option('-f, --force', 'Force reinstall even if already installed')
|
|
55
|
+
.option('-v, --version <version>', 'Install specific version')
|
|
56
|
+
.action(async (options) => {
|
|
57
|
+
const spinner = ora('Installing IPFS client...').start();
|
|
58
|
+
try {
|
|
59
|
+
const manager = new IPFSClientManager();
|
|
60
|
+
await manager.install({
|
|
61
|
+
force: options.force,
|
|
62
|
+
version: options.version,
|
|
63
|
+
});
|
|
64
|
+
spinner.succeed(chalk.green('IPFS client installed successfully!'));
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(chalk.gray('Next steps:'));
|
|
67
|
+
console.log(chalk.cyan(' lsh ipfs init # Initialize IPFS repository'));
|
|
68
|
+
console.log(chalk.cyan(' lsh ipfs start # Start IPFS daemon'));
|
|
69
|
+
console.log('');
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const err = error;
|
|
73
|
+
spinner.fail(chalk.red('Installation failed'));
|
|
74
|
+
console.error(chalk.red(err.message));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
// lsh ipfs uninstall
|
|
79
|
+
ipfsCommand
|
|
80
|
+
.command('uninstall')
|
|
81
|
+
.description('Uninstall LSH-managed IPFS client')
|
|
82
|
+
.action(async () => {
|
|
83
|
+
try {
|
|
84
|
+
const manager = new IPFSClientManager();
|
|
85
|
+
await manager.uninstall();
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
const err = error;
|
|
89
|
+
console.error(chalk.red('\nā Uninstallation failed:'), err.message);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
// lsh ipfs init
|
|
94
|
+
ipfsCommand
|
|
95
|
+
.command('init')
|
|
96
|
+
.description('Initialize IPFS repository')
|
|
97
|
+
.action(async () => {
|
|
98
|
+
const spinner = ora('Initializing IPFS repository...').start();
|
|
99
|
+
try {
|
|
100
|
+
const manager = new IPFSClientManager();
|
|
101
|
+
await manager.init();
|
|
102
|
+
spinner.succeed(chalk.green('IPFS repository initialized!'));
|
|
103
|
+
console.log('');
|
|
104
|
+
console.log(chalk.gray('Next step:'));
|
|
105
|
+
console.log(chalk.cyan(' lsh ipfs start # Start IPFS daemon'));
|
|
106
|
+
console.log('');
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const err = error;
|
|
110
|
+
spinner.fail(chalk.red('Initialization failed'));
|
|
111
|
+
console.error(chalk.red(err.message));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
// lsh ipfs start
|
|
116
|
+
ipfsCommand
|
|
117
|
+
.command('start')
|
|
118
|
+
.description('Start IPFS daemon')
|
|
119
|
+
.action(async () => {
|
|
120
|
+
try {
|
|
121
|
+
const manager = new IPFSClientManager();
|
|
122
|
+
await manager.start();
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
const err = error;
|
|
126
|
+
console.error(chalk.red('\nā Failed to start daemon:'), err.message);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
// lsh ipfs stop
|
|
131
|
+
ipfsCommand
|
|
132
|
+
.command('stop')
|
|
133
|
+
.description('Stop IPFS daemon')
|
|
134
|
+
.action(async () => {
|
|
135
|
+
try {
|
|
136
|
+
const manager = new IPFSClientManager();
|
|
137
|
+
await manager.stop();
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const err = error;
|
|
141
|
+
console.error(chalk.red('\nā Failed to stop daemon:'), err.message);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
export default registerIPFSCommands;
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IPFS Client Manager
|
|
3
|
+
* Detects, installs, and manages IPFS clients across platforms
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import { exec } from 'child_process';
|
|
9
|
+
import { promisify } from 'util';
|
|
10
|
+
import { createLogger } from './logger.js';
|
|
11
|
+
import { getPlatformInfo } from './platform-utils.js';
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
const logger = createLogger('IPFSClientManager');
|
|
14
|
+
/**
|
|
15
|
+
* IPFS Client Manager
|
|
16
|
+
*
|
|
17
|
+
* Manages IPFS client installation and configuration:
|
|
18
|
+
* 1. Kubo (formerly go-ipfs) - Official Go implementation
|
|
19
|
+
* 2. Helia - Modern JavaScript implementation
|
|
20
|
+
* 3. js-ipfs - Legacy JavaScript implementation (deprecated)
|
|
21
|
+
*/
|
|
22
|
+
export class IPFSClientManager {
|
|
23
|
+
lshDir;
|
|
24
|
+
ipfsDir;
|
|
25
|
+
binDir;
|
|
26
|
+
constructor() {
|
|
27
|
+
this.lshDir = path.join(os.homedir(), '.lsh');
|
|
28
|
+
this.ipfsDir = path.join(this.lshDir, 'ipfs');
|
|
29
|
+
this.binDir = path.join(this.ipfsDir, 'bin');
|
|
30
|
+
// Ensure directories exist
|
|
31
|
+
this.ensureDirectories();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Detect if IPFS client is installed
|
|
35
|
+
*/
|
|
36
|
+
async detect() {
|
|
37
|
+
// Check for system-wide Kubo installation
|
|
38
|
+
try {
|
|
39
|
+
const { stdout: kuboVersion } = await execAsync('ipfs version');
|
|
40
|
+
const versionMatch = kuboVersion.match(/ipfs version ([0-9.]+)/);
|
|
41
|
+
if (versionMatch) {
|
|
42
|
+
const { stdout: kuboPath } = await execAsync('which ipfs');
|
|
43
|
+
return {
|
|
44
|
+
installed: true,
|
|
45
|
+
version: versionMatch[1],
|
|
46
|
+
path: kuboPath.trim(),
|
|
47
|
+
type: 'kubo',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Kubo not found in system PATH
|
|
53
|
+
}
|
|
54
|
+
// Check for local LSH-managed installation
|
|
55
|
+
const localIpfsPath = path.join(this.binDir, 'ipfs');
|
|
56
|
+
if (fs.existsSync(localIpfsPath)) {
|
|
57
|
+
try {
|
|
58
|
+
const { stdout: localVersion } = await execAsync(`${localIpfsPath} version`);
|
|
59
|
+
const versionMatch = localVersion.match(/ipfs version ([0-9.]+)/);
|
|
60
|
+
if (versionMatch) {
|
|
61
|
+
return {
|
|
62
|
+
installed: true,
|
|
63
|
+
version: versionMatch[1],
|
|
64
|
+
path: localIpfsPath,
|
|
65
|
+
type: 'kubo',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Local binary exists but not working
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
installed: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Install IPFS client
|
|
79
|
+
*/
|
|
80
|
+
async install(options = {}) {
|
|
81
|
+
const platformInfo = getPlatformInfo();
|
|
82
|
+
const clientInfo = await this.detect();
|
|
83
|
+
// Check if already installed
|
|
84
|
+
if (clientInfo.installed && !options.force) {
|
|
85
|
+
logger.info(`ā
IPFS already installed: ${clientInfo.type} v${clientInfo.version}`);
|
|
86
|
+
logger.info(` Path: ${clientInfo.path}`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
logger.info('š¦ Installing IPFS client (Kubo)...');
|
|
90
|
+
// Determine version to install
|
|
91
|
+
const version = options.version || await this.getLatestKuboVersion();
|
|
92
|
+
logger.info(` Version: ${version}`);
|
|
93
|
+
logger.info(` Platform: ${platformInfo.platformName} ${platformInfo.arch}`);
|
|
94
|
+
// Download and install based on platform
|
|
95
|
+
try {
|
|
96
|
+
if (platformInfo.platform === 'darwin') {
|
|
97
|
+
await this.installKuboMacOS(version);
|
|
98
|
+
}
|
|
99
|
+
else if (platformInfo.platform === 'linux') {
|
|
100
|
+
await this.installKuboLinux(version);
|
|
101
|
+
}
|
|
102
|
+
else if (platformInfo.platform === 'win32') {
|
|
103
|
+
await this.installKuboWindows(version);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
throw new Error(`Unsupported platform: ${platformInfo.platform}`);
|
|
107
|
+
}
|
|
108
|
+
logger.info('ā
IPFS client installed successfully!');
|
|
109
|
+
// Verify installation
|
|
110
|
+
const verifyInfo = await this.detect();
|
|
111
|
+
if (verifyInfo.installed) {
|
|
112
|
+
logger.info(` Version: ${verifyInfo.version}`);
|
|
113
|
+
logger.info(` Path: ${verifyInfo.path}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
const err = error;
|
|
118
|
+
logger.error(`ā Installation failed: ${err.message}`);
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Uninstall LSH-managed IPFS client
|
|
124
|
+
*/
|
|
125
|
+
async uninstall() {
|
|
126
|
+
const clientInfo = await this.detect();
|
|
127
|
+
if (!clientInfo.installed) {
|
|
128
|
+
logger.info('ā¹ļø No IPFS client installed');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// Only uninstall if it's LSH-managed
|
|
132
|
+
if (clientInfo.path?.startsWith(this.binDir)) {
|
|
133
|
+
logger.info('šļø Uninstalling LSH-managed IPFS client...');
|
|
134
|
+
if (fs.existsSync(this.ipfsDir)) {
|
|
135
|
+
fs.rmSync(this.ipfsDir, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
logger.info('ā
IPFS client uninstalled');
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
logger.info('ā¹ļø System-wide IPFS installation detected');
|
|
141
|
+
logger.info(' LSH cannot uninstall system packages');
|
|
142
|
+
logger.info(` Path: ${clientInfo.path}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Initialize IPFS repository
|
|
147
|
+
*/
|
|
148
|
+
async init() {
|
|
149
|
+
const clientInfo = await this.detect();
|
|
150
|
+
if (!clientInfo.installed) {
|
|
151
|
+
throw new Error('IPFS client not installed. Run: lsh ipfs install');
|
|
152
|
+
}
|
|
153
|
+
const ipfsRepoPath = path.join(this.ipfsDir, 'repo');
|
|
154
|
+
// Check if already initialized
|
|
155
|
+
if (fs.existsSync(path.join(ipfsRepoPath, 'config'))) {
|
|
156
|
+
logger.info('ā
IPFS repository already initialized');
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
logger.info('š§ Initializing IPFS repository...');
|
|
160
|
+
try {
|
|
161
|
+
const ipfsCmd = clientInfo.path || 'ipfs';
|
|
162
|
+
await execAsync(`${ipfsCmd} init`, {
|
|
163
|
+
env: { ...process.env, IPFS_PATH: ipfsRepoPath },
|
|
164
|
+
});
|
|
165
|
+
logger.info('ā
IPFS repository initialized');
|
|
166
|
+
logger.info(` Path: ${ipfsRepoPath}`);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
const err = error;
|
|
170
|
+
logger.error(`ā Initialization failed: ${err.message}`);
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Start IPFS daemon
|
|
176
|
+
*/
|
|
177
|
+
async start() {
|
|
178
|
+
const clientInfo = await this.detect();
|
|
179
|
+
if (!clientInfo.installed) {
|
|
180
|
+
throw new Error('IPFS client not installed. Run: lsh ipfs install');
|
|
181
|
+
}
|
|
182
|
+
logger.info('š Starting IPFS daemon...');
|
|
183
|
+
const ipfsRepoPath = path.join(this.ipfsDir, 'repo');
|
|
184
|
+
const ipfsCmd = clientInfo.path || 'ipfs';
|
|
185
|
+
try {
|
|
186
|
+
// Start daemon in background
|
|
187
|
+
const daemon = exec(`${ipfsCmd} daemon`, {
|
|
188
|
+
env: { ...process.env, IPFS_PATH: ipfsRepoPath },
|
|
189
|
+
});
|
|
190
|
+
// Log PID for management
|
|
191
|
+
const pidPath = path.join(this.ipfsDir, 'daemon.pid');
|
|
192
|
+
if (daemon.pid) {
|
|
193
|
+
fs.writeFileSync(pidPath, daemon.pid.toString(), 'utf8');
|
|
194
|
+
}
|
|
195
|
+
logger.info('ā
IPFS daemon started');
|
|
196
|
+
logger.info(` PID: ${daemon.pid}`);
|
|
197
|
+
logger.info(' API: http://localhost:5001');
|
|
198
|
+
logger.info(' Gateway: http://localhost:8080');
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
const err = error;
|
|
202
|
+
logger.error(`ā Failed to start daemon: ${err.message}`);
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Stop IPFS daemon
|
|
208
|
+
*/
|
|
209
|
+
async stop() {
|
|
210
|
+
const pidPath = path.join(this.ipfsDir, 'daemon.pid');
|
|
211
|
+
if (!fs.existsSync(pidPath)) {
|
|
212
|
+
logger.info('ā¹ļø IPFS daemon not running (no PID file)');
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const pid = parseInt(fs.readFileSync(pidPath, 'utf8'), 10);
|
|
216
|
+
logger.info(`š Stopping IPFS daemon (PID: ${pid})...`);
|
|
217
|
+
try {
|
|
218
|
+
process.kill(pid, 'SIGTERM');
|
|
219
|
+
fs.unlinkSync(pidPath);
|
|
220
|
+
logger.info('ā
IPFS daemon stopped');
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
const err = error;
|
|
224
|
+
logger.error(`ā Failed to stop daemon: ${err.message}`);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get latest Kubo version from GitHub releases
|
|
230
|
+
*/
|
|
231
|
+
async getLatestKuboVersion() {
|
|
232
|
+
try {
|
|
233
|
+
// Use GitHub API to get latest release
|
|
234
|
+
const response = await fetch('https://api.github.com/repos/ipfs/kubo/releases/latest');
|
|
235
|
+
const data = await response.json();
|
|
236
|
+
// Remove 'v' prefix if present
|
|
237
|
+
return data.tag_name.replace(/^v/, '');
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
// Fallback to known stable version
|
|
241
|
+
return '0.26.0';
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Install Kubo on macOS
|
|
246
|
+
*/
|
|
247
|
+
async installKuboMacOS(version) {
|
|
248
|
+
const arch = os.arch() === 'arm64' ? 'arm64' : 'amd64';
|
|
249
|
+
const downloadUrl = `https://dist.ipfs.tech/kubo/v${version}/kubo_v${version}_darwin-${arch}.tar.gz`;
|
|
250
|
+
const tarPath = path.join(this.ipfsDir, 'kubo.tar.gz');
|
|
251
|
+
logger.info(' Downloading Kubo...');
|
|
252
|
+
// Download
|
|
253
|
+
await execAsync(`curl -L -o ${tarPath} ${downloadUrl}`);
|
|
254
|
+
logger.info(' Extracting...');
|
|
255
|
+
// Extract
|
|
256
|
+
await execAsync(`tar -xzf ${tarPath} -C ${this.ipfsDir}`);
|
|
257
|
+
// Move binary
|
|
258
|
+
const extractedBinPath = path.join(this.ipfsDir, 'kubo', 'ipfs');
|
|
259
|
+
fs.mkdirSync(this.binDir, { recursive: true });
|
|
260
|
+
fs.renameSync(extractedBinPath, path.join(this.binDir, 'ipfs'));
|
|
261
|
+
// Make executable
|
|
262
|
+
fs.chmodSync(path.join(this.binDir, 'ipfs'), 0o755);
|
|
263
|
+
// Cleanup
|
|
264
|
+
fs.unlinkSync(tarPath);
|
|
265
|
+
fs.rmSync(path.join(this.ipfsDir, 'kubo'), { recursive: true });
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Install Kubo on Linux
|
|
269
|
+
*/
|
|
270
|
+
async installKuboLinux(version) {
|
|
271
|
+
const arch = os.arch() === 'arm64' ? 'arm64' : 'amd64';
|
|
272
|
+
const downloadUrl = `https://dist.ipfs.tech/kubo/v${version}/kubo_v${version}_linux-${arch}.tar.gz`;
|
|
273
|
+
const tarPath = path.join(this.ipfsDir, 'kubo.tar.gz');
|
|
274
|
+
logger.info(' Downloading Kubo...');
|
|
275
|
+
// Download
|
|
276
|
+
await execAsync(`curl -L -o ${tarPath} ${downloadUrl}`);
|
|
277
|
+
logger.info(' Extracting...');
|
|
278
|
+
// Extract
|
|
279
|
+
await execAsync(`tar -xzf ${tarPath} -C ${this.ipfsDir}`);
|
|
280
|
+
// Move binary
|
|
281
|
+
const extractedBinPath = path.join(this.ipfsDir, 'kubo', 'ipfs');
|
|
282
|
+
fs.mkdirSync(this.binDir, { recursive: true });
|
|
283
|
+
fs.renameSync(extractedBinPath, path.join(this.binDir, 'ipfs'));
|
|
284
|
+
// Make executable
|
|
285
|
+
fs.chmodSync(path.join(this.binDir, 'ipfs'), 0o755);
|
|
286
|
+
// Cleanup
|
|
287
|
+
fs.unlinkSync(tarPath);
|
|
288
|
+
fs.rmSync(path.join(this.ipfsDir, 'kubo'), { recursive: true });
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Install Kubo on Windows
|
|
292
|
+
*/
|
|
293
|
+
async installKuboWindows(version) {
|
|
294
|
+
const downloadUrl = `https://dist.ipfs.tech/kubo/v${version}/kubo_v${version}_windows-amd64.zip`;
|
|
295
|
+
const zipPath = path.join(this.ipfsDir, 'kubo.zip');
|
|
296
|
+
logger.info(' Downloading Kubo...');
|
|
297
|
+
// Download
|
|
298
|
+
await execAsync(`curl -L -o ${zipPath} ${downloadUrl}`);
|
|
299
|
+
logger.info(' Extracting...');
|
|
300
|
+
// Extract (Windows has built-in tar that supports zip)
|
|
301
|
+
await execAsync(`tar -xf ${zipPath} -C ${this.ipfsDir}`);
|
|
302
|
+
// Move binary
|
|
303
|
+
const extractedBinPath = path.join(this.ipfsDir, 'kubo', 'ipfs.exe');
|
|
304
|
+
fs.mkdirSync(this.binDir, { recursive: true });
|
|
305
|
+
fs.renameSync(extractedBinPath, path.join(this.binDir, 'ipfs.exe'));
|
|
306
|
+
// Cleanup
|
|
307
|
+
fs.unlinkSync(zipPath);
|
|
308
|
+
fs.rmSync(path.join(this.ipfsDir, 'kubo'), { recursive: true });
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Ensure required directories exist
|
|
312
|
+
*/
|
|
313
|
+
ensureDirectories() {
|
|
314
|
+
if (!fs.existsSync(this.lshDir)) {
|
|
315
|
+
fs.mkdirSync(this.lshDir, { recursive: true });
|
|
316
|
+
}
|
|
317
|
+
if (!fs.existsSync(this.ipfsDir)) {
|
|
318
|
+
fs.mkdirSync(this.ipfsDir, { recursive: true });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lsh-framework",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.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": {
|