lsh-framework 3.1.19 → 3.1.20

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.
Files changed (40) hide show
  1. package/README.md +9 -7
  2. package/dist/cli.js +90 -89
  3. package/dist/commands/completion.js +2 -0
  4. package/dist/commands/storacha.js +268 -0
  5. package/dist/constants/config.js +15 -0
  6. package/dist/constants/errors.js +11 -0
  7. package/dist/constants/ui.js +140 -1
  8. package/dist/constants/validation.js +45 -26
  9. package/dist/daemon/job-registry.js +2 -1
  10. package/dist/daemon/lshd.js +156 -15
  11. package/dist/daemon/saas-api-routes.js +91 -277
  12. package/dist/lib/api-response.js +226 -0
  13. package/dist/lib/command-validator.js +17 -76
  14. package/dist/lib/constant-time.js +126 -0
  15. package/dist/lib/cron-job-manager.js +1 -1
  16. package/dist/lib/floating-point-arithmetic.js +4 -2
  17. package/dist/lib/input-validator.js +318 -0
  18. package/dist/lib/job-storage-database.js +13 -5
  19. package/dist/lib/logger.js +21 -21
  20. package/dist/lib/metrics/index.js +34 -0
  21. package/dist/lib/metrics/metrics-collector.js +484 -0
  22. package/dist/lib/metrics/metrics-exporter.js +275 -0
  23. package/dist/lib/metrics/performance-profiler.js +333 -0
  24. package/dist/lib/metrics/types.js +70 -0
  25. package/dist/lib/min-heap.js +201 -0
  26. package/dist/lib/optimized-job-scheduler.js +355 -0
  27. package/dist/lib/saas-audit.js +0 -1
  28. package/dist/lib/saas-auth.js +1 -4
  29. package/dist/lib/saas-billing.js +3 -9
  30. package/dist/lib/saas-encryption.js +0 -1
  31. package/dist/lib/saas-organizations.js +0 -5
  32. package/dist/lib/saas-secrets.js +0 -2
  33. package/dist/lib/safe-eval.js +124 -0
  34. package/dist/lib/storacha-client.js +692 -0
  35. package/dist/lib/supabase-utils.js +396 -0
  36. package/dist/lib/validation-framework.js +415 -0
  37. package/dist/lib/validation-rules.js +699 -0
  38. package/dist/services/cron/cron-registrar.js +1 -1
  39. package/dist/services/secrets/secrets.js +10 -6
  40. package/package.json +1 -1
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # LSH v3.0.0 - Encrypted Secrets Manager
1
+ # LSH v3.1.19 - Encrypted Secrets Manager
2
2
 
3
3
  **The simplest way to sync `.env` files across all your machines.**
4
4
 
@@ -8,13 +8,15 @@
8
8
  [![Node.js CI](https://github.com/gwicho38/lsh/actions/workflows/node.js.yml/badge.svg)](https://github.com/gwicho38/lsh/actions/workflows/node.js.yml)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
10
 
11
- ## What's New in v3.0.0
11
+ ## What's New in v3.1.19
12
12
 
13
- - **Registry Fallback** - Pull now automatically checks the Storacha registry when local metadata is missing
14
- - **Improved Error Messages** - Clear, actionable error messages with environment context
15
- - **One-Command Setup** - `lsh init` handles everything with interactive prompts
16
- - **Zero-Config IPFS Sync** - Storacha network enabled by default after email authentication
17
- - **Smart Environment Detection** - Automatic repository-based namespacing
13
+ - **Type Safety Milestone** - All `@typescript-eslint/no-explicit-any` warnings eliminated (51+ → 0)
14
+ - **Constants Centralization** - Hardcoded strings moved to constants modules
15
+ - **API Response Builder** - Standardized API responses with `sendSuccess`, `sendError`, `ApiErrors`
16
+ - **Test Coverage** - 59 new tests for constants modules (142 total constants tests)
17
+ - **Code Quality** - Lint warnings reduced from 744 to 550 (26.1% reduction)
18
+
19
+ See [Release Notes](docs/releases/3.1.19.md) for full details.
18
20
 
19
21
  ## Quick Start
20
22
 
package/dist/cli.js CHANGED
@@ -15,6 +15,7 @@ import { registerMigrateCommand } from './commands/migrate.js';
15
15
  import { registerContextCommand } from './commands/context.js';
16
16
  import { init_secrets } from './services/secrets/secrets.js';
17
17
  import { loadGlobalConfigSync } from './lib/config-manager.js';
18
+ import { CLI_TEXT, CLI_HELP } from './constants/ui.js';
18
19
  import * as fs from 'fs';
19
20
  import * as path from 'path';
20
21
  import { fileURLToPath } from 'url';
@@ -33,59 +34,59 @@ function getVersion() {
33
34
  }
34
35
  const program = new Command();
35
36
  program
36
- .name('lsh')
37
- .description('LSH - Simple, cross-platform encrypted secrets manager')
37
+ .name(CLI_TEXT.NAME)
38
+ .description(CLI_TEXT.DESCRIPTION)
38
39
  .version(getVersion())
39
40
  .showSuggestionAfterError(true)
40
- .showHelpAfterError('(add --help for additional information)')
41
+ .showHelpAfterError(CLI_TEXT.HELP_AFTER_ERROR)
41
42
  .allowUnknownOption(false)
42
43
  .enablePositionalOptions();
43
44
  // Main action - show help by default
44
45
  program
45
- .option('-v, --verbose', 'Verbose output')
46
- .option('-d, --debug', 'Debug mode')
46
+ .option(CLI_TEXT.OPTION_VERBOSE, CLI_TEXT.OPTION_VERBOSE_DESC)
47
+ .option(CLI_TEXT.OPTION_DEBUG, CLI_TEXT.OPTION_DEBUG_DESC)
47
48
  .action(async (_options) => {
48
49
  // No arguments - show secrets-focused help
49
- console.log('LSH - Encrypted Secrets Manager');
50
+ console.log(CLI_HELP.TITLE);
50
51
  console.log('');
51
- console.log('šŸ” Secrets Management Commands:');
52
- console.log(' init Interactive setup wizard (first-time setup)');
53
- console.log(' doctor Check configuration and connectivity');
54
- console.log(' sync Check sync status & get recommendations');
55
- console.log(' push Upload .env to encrypted cloud storage');
56
- console.log(' pull Download .env from cloud storage');
57
- console.log(' list List secrets in current local .env file');
58
- console.log(' env [name] List/view cloud environments');
59
- console.log(' key Generate encryption key');
60
- console.log(' create Create new .env file');
61
- console.log(' get <key> Get a specific secret value (--all for all)');
62
- console.log(' set <key> <value> Set a specific secret value');
63
- console.log(' delete Delete .env file');
64
- console.log(' status Get detailed secrets status');
52
+ console.log(CLI_HELP.SECTION_SECRETS);
53
+ console.log(CLI_HELP.CMD_INIT);
54
+ console.log(CLI_HELP.CMD_DOCTOR);
55
+ console.log(CLI_HELP.CMD_SYNC);
56
+ console.log(CLI_HELP.CMD_PUSH);
57
+ console.log(CLI_HELP.CMD_PULL);
58
+ console.log(CLI_HELP.CMD_LIST);
59
+ console.log(CLI_HELP.CMD_ENV);
60
+ console.log(CLI_HELP.CMD_KEY);
61
+ console.log(CLI_HELP.CMD_CREATE);
62
+ console.log(CLI_HELP.CMD_GET);
63
+ console.log(CLI_HELP.CMD_SET);
64
+ console.log(CLI_HELP.CMD_DELETE);
65
+ console.log(CLI_HELP.CMD_STATUS);
65
66
  console.log('');
66
- console.log('šŸ”„ IPFS Sync:');
67
- console.log(' sync init Full IPFS setup (install, init, start)');
68
- console.log(' sync push Push secrets to IPFS → get CID');
69
- console.log(' sync pull <cid> Pull secrets by CID');
70
- console.log(' sync status Check IPFS and sync status');
71
- console.log(' sync start/stop Control IPFS daemon');
67
+ console.log(CLI_HELP.SECTION_IPFS);
68
+ console.log(CLI_HELP.CMD_SYNC_INIT);
69
+ console.log(CLI_HELP.CMD_SYNC_PUSH);
70
+ console.log(CLI_HELP.CMD_SYNC_PULL);
71
+ console.log(CLI_HELP.CMD_SYNC_STATUS);
72
+ console.log(CLI_HELP.CMD_SYNC_START_STOP);
72
73
  console.log('');
73
- console.log('šŸš€ Quick Start:');
74
- console.log(' lsh sync init # One-time IPFS setup');
75
- console.log(' lsh sync push # Push secrets to IPFS');
76
- console.log(' lsh sync pull <cid> # Pull on another machine');
74
+ console.log(CLI_HELP.SECTION_QUICK_START);
75
+ console.log(CLI_HELP.QUICK_SYNC_INIT);
76
+ console.log(CLI_HELP.QUICK_SYNC_PUSH);
77
+ console.log(CLI_HELP.QUICK_SYNC_PULL);
77
78
  console.log('');
78
- console.log('šŸ“š More Commands:');
79
- console.log(' config Manage LSH configuration (~/.config/lsh/lshrc)');
80
- console.log(' self Self-management commands');
81
- console.log(' --help Show all options');
79
+ console.log(CLI_HELP.SECTION_MORE);
80
+ console.log(CLI_HELP.CMD_CONFIG);
81
+ console.log(CLI_HELP.CMD_SELF);
82
+ console.log(CLI_HELP.CMD_HELP_OPT);
82
83
  console.log('');
83
- console.log('šŸ“– Documentation: https://github.com/gwicho38/lsh');
84
+ console.log(CLI_HELP.DOCS_LINK);
84
85
  });
85
86
  // Help subcommand
86
87
  program
87
88
  .command('help')
88
- .description('Show detailed help')
89
+ .description(CLI_TEXT.HELP_DESCRIPTION)
89
90
  .action(() => {
90
91
  showDetailedHelp();
91
92
  });
@@ -172,12 +173,12 @@ function findSimilarCommands(input, validCommands) {
172
173
  // For suggestions, only use primary command names (not aliases)
173
174
  const primaryCommands = program.commands.map(cmd => cmd.name());
174
175
  const suggestions = findSimilarCommands(firstArg, primaryCommands);
175
- console.error(`error: unknown command '${firstArg}'`);
176
+ console.error(`${CLI_TEXT.ERROR_UNKNOWN_COMMAND} '${firstArg}'`);
176
177
  if (suggestions.length > 0) {
177
- console.error(`\nDid you mean one of these?`);
178
+ console.error(CLI_TEXT.DID_YOU_MEAN);
178
179
  suggestions.forEach(cmd => console.error(` ${cmd}`));
179
180
  }
180
- console.error(`\nRun 'lsh --help' to see available commands.`);
181
+ console.error(CLI_TEXT.RUN_HELP);
181
182
  process.exit(1);
182
183
  }
183
184
  }
@@ -185,7 +186,7 @@ function findSimilarCommands(input, validCommands) {
185
186
  program.configureOutput({
186
187
  writeErr: (str) => {
187
188
  // Intercept error messages to add suggestions
188
- if (str.includes('error: unknown command')) {
189
+ if (str.includes(CLI_TEXT.ERROR_UNKNOWN_COMMAND)) {
189
190
  const match = str.match(/unknown command '([^']+)'/);
190
191
  if (match) {
191
192
  const unknownCommand = match[1];
@@ -193,10 +194,10 @@ function findSimilarCommands(input, validCommands) {
193
194
  const suggestions = findSimilarCommands(unknownCommand, validCommands);
194
195
  process.stderr.write(str);
195
196
  if (suggestions.length > 0) {
196
- process.stderr.write(`\nDid you mean one of these?\n`);
197
+ process.stderr.write(`${CLI_TEXT.DID_YOU_MEAN}\n`);
197
198
  suggestions.forEach(cmd => process.stderr.write(` ${cmd}\n`));
198
199
  }
199
- process.stderr.write(`\nRun 'lsh --help' to see available commands.\n`);
200
+ process.stderr.write(`${CLI_TEXT.RUN_HELP}\n`);
200
201
  return;
201
202
  }
202
203
  }
@@ -204,16 +205,16 @@ function findSimilarCommands(input, validCommands) {
204
205
  }
205
206
  });
206
207
  // Add custom error handler for unknown commands
207
- program.on('command:*', (operands) => {
208
+ program.on(CLI_TEXT.EVENT_UNKNOWN_COMMAND, (operands) => {
208
209
  const unknownCommand = operands[0];
209
210
  const validCommands = program.commands.map(cmd => cmd.name());
210
211
  const suggestions = findSimilarCommands(unknownCommand, validCommands);
211
- console.error(`error: unknown command '${unknownCommand}'`);
212
+ console.error(`${CLI_TEXT.ERROR_UNKNOWN_COMMAND} '${unknownCommand}'`);
212
213
  if (suggestions.length > 0) {
213
- console.error(`\nDid you mean one of these?`);
214
+ console.error(CLI_TEXT.DID_YOU_MEAN);
214
215
  suggestions.forEach(cmd => console.error(` ${cmd}`));
215
216
  }
216
- console.error(`\nRun 'lsh --help' to see available commands.`);
217
+ console.error(CLI_TEXT.RUN_HELP);
217
218
  process.exit(1);
218
219
  });
219
220
  // Parse command line arguments after all commands are registered
@@ -223,56 +224,56 @@ function findSimilarCommands(input, validCommands) {
223
224
  * Show detailed help
224
225
  */
225
226
  function showDetailedHelp() {
226
- console.log('LSH - Encrypted Secrets Manager');
227
- console.log('================================');
227
+ console.log(CLI_HELP.TITLE);
228
+ console.log(CLI_HELP.SEPARATOR);
228
229
  console.log('');
229
- console.log('Usage:');
230
- console.log(' lsh Show help (default)');
231
- console.log(' lsh init Interactive setup wizard');
232
- console.log(' lsh push Push secrets to cloud');
233
- console.log(' lsh pull Pull secrets from cloud');
230
+ console.log(CLI_HELP.SECTION_USAGE);
231
+ console.log(CLI_HELP.USAGE_DEFAULT);
232
+ console.log(CLI_HELP.USAGE_INIT);
233
+ console.log(CLI_HELP.USAGE_PUSH);
234
+ console.log(CLI_HELP.USAGE_PULL);
234
235
  console.log('');
235
- console.log('Main Commands:');
236
- console.log(' init Interactive setup wizard (first-time)');
237
- console.log(' doctor Health check & troubleshooting');
238
- console.log(' env Show local .env file contents');
239
- console.log(' key Generate encryption key');
240
- console.log(' status Detailed status report');
236
+ console.log(CLI_HELP.SECTION_MAIN_COMMANDS);
237
+ console.log(CLI_HELP.MAIN_INIT);
238
+ console.log(CLI_HELP.MAIN_DOCTOR);
239
+ console.log(CLI_HELP.MAIN_ENV);
240
+ console.log(CLI_HELP.MAIN_KEY);
241
+ console.log(CLI_HELP.MAIN_STATUS);
241
242
  console.log('');
242
- console.log('IPFS Sync:');
243
- console.log(' sync init Full IPFS setup (install, init, start)');
244
- console.log(' sync push Push secrets to IPFS → get CID');
245
- console.log(' sync pull <cid> Pull secrets by CID');
246
- console.log(' sync status Check IPFS client, daemon, and sync status');
247
- console.log(' sync start Start IPFS daemon');
248
- console.log(' sync stop Stop IPFS daemon');
249
- console.log(' sync history View sync history');
243
+ console.log(CLI_HELP.SECTION_IPFS);
244
+ console.log(CLI_HELP.DETAIL_SYNC_INIT);
245
+ console.log(CLI_HELP.DETAIL_SYNC_PUSH);
246
+ console.log(CLI_HELP.DETAIL_SYNC_PULL);
247
+ console.log(CLI_HELP.DETAIL_SYNC_STATUS);
248
+ console.log(CLI_HELP.DETAIL_SYNC_START);
249
+ console.log(CLI_HELP.DETAIL_SYNC_STOP);
250
+ console.log(CLI_HELP.DETAIL_SYNC_HISTORY);
250
251
  console.log('');
251
- console.log('Self-Management:');
252
- console.log(' self update Update to latest version');
253
- console.log(' self version Show version information');
254
- console.log(' self uninstall Uninstall from system');
252
+ console.log(CLI_HELP.SECTION_SELF_MANAGEMENT);
253
+ console.log(CLI_HELP.SELF_UPDATE);
254
+ console.log(CLI_HELP.SELF_VERSION);
255
+ console.log(CLI_HELP.SELF_UNINSTALL);
255
256
  console.log('');
256
- console.log('Examples:');
257
+ console.log(CLI_HELP.SECTION_EXAMPLES);
257
258
  console.log('');
258
- console.log(' First-Time Setup:');
259
- console.log(' lsh sync init # One-time IPFS setup');
260
- console.log(' lsh doctor # Verify setup');
259
+ console.log(` ${CLI_HELP.SECTION_FIRST_TIME}`);
260
+ console.log(CLI_HELP.EX_SYNC_INIT);
261
+ console.log(CLI_HELP.EX_DOCTOR);
261
262
  console.log('');
262
- console.log(' Daily Usage:');
263
- console.log(' lsh sync push # Push to IPFS → get CID');
264
- console.log(' lsh sync pull <cid> # Pull by CID');
265
- console.log(' lsh env --masked # View local secrets');
266
- console.log(' lsh get API_KEY # Get specific secret');
267
- console.log(' lsh set API_KEY newvalue # Update secret');
263
+ console.log(` ${CLI_HELP.SECTION_DAILY_USAGE}`);
264
+ console.log(CLI_HELP.EX_SYNC_PUSH);
265
+ console.log(CLI_HELP.EX_SYNC_PULL);
266
+ console.log(CLI_HELP.EX_ENV_MASKED);
267
+ console.log(CLI_HELP.EX_GET);
268
+ console.log(CLI_HELP.EX_SET);
268
269
  console.log('');
269
- console.log('Features:');
270
- console.log(' āœ… Cross-platform (Windows, macOS, Linux)');
271
- console.log(' āœ… AES-256 encryption');
272
- console.log(' āœ… Multi-environment support');
273
- console.log(' āœ… Team collaboration');
274
- console.log(' āœ… Automatic secret rotation');
275
- console.log(' āœ… Git-aware namespacing');
270
+ console.log(CLI_HELP.SECTION_FEATURES);
271
+ console.log(CLI_HELP.FEATURE_CROSS_PLATFORM);
272
+ console.log(CLI_HELP.FEATURE_ENCRYPTION);
273
+ console.log(CLI_HELP.FEATURE_MULTI_ENV);
274
+ console.log(CLI_HELP.FEATURE_TEAM);
275
+ console.log(CLI_HELP.FEATURE_ROTATION);
276
+ console.log(CLI_HELP.FEATURE_GIT_AWARE);
276
277
  console.log('');
277
- console.log('Need help? Visit https://github.com/gwicho38/lsh');
278
+ console.log(CLI_HELP.NEED_HELP);
278
279
  }
@@ -12,6 +12,7 @@ function generateBashCompletion() {
12
12
  # Or save to completion directory:
13
13
  # lsh completion bash > /etc/bash_completion.d/lsh
14
14
 
15
+
15
16
  _lsh_completion() {
16
17
  local cur prev words cword
17
18
  _init_completion || return
@@ -215,6 +216,7 @@ function generateZshCompletion() {
215
216
  # fpath=(~/.zsh/completions $fpath)
216
217
  # autoload -Uz compinit && compinit
217
218
 
219
+
218
220
  _lsh() {
219
221
  local -a commands
220
222
  local -a global_opts
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Storacha Commands
3
+ * Manage Storacha (IPFS network) authentication and configuration
4
+ */
5
+ import { getStorachaClient } from '../lib/storacha-client.js';
6
+ export function registerStorachaCommands(program) {
7
+ const storacha = program
8
+ .command('storacha')
9
+ .description('Manage Storacha (IPFS network) sync');
10
+ // Login command
11
+ storacha
12
+ .command('login <email>')
13
+ .description('Authenticate with Storacha via email')
14
+ .action(async (email) => {
15
+ try {
16
+ const client = getStorachaClient();
17
+ console.log('\nšŸ” Storacha Authentication\n');
18
+ console.log('━'.repeat(60));
19
+ console.log('');
20
+ await client.login(email);
21
+ }
22
+ catch (error) {
23
+ const err = error;
24
+ console.error('\nāŒ Authentication failed:', err.message);
25
+ console.error('');
26
+ console.error('šŸ’” Troubleshooting:');
27
+ console.error(' - Check your email for the verification link');
28
+ console.error(' - Complete signup at: https://console.storacha.network/');
29
+ console.error(' - Ensure you have an active internet connection');
30
+ console.error('');
31
+ process.exit(1);
32
+ }
33
+ });
34
+ // Status command
35
+ storacha
36
+ .command('status')
37
+ .description('Show Storacha authentication and configuration status')
38
+ .action(async () => {
39
+ try {
40
+ const client = getStorachaClient();
41
+ const status = await client.getStatus();
42
+ console.log('\nā˜ļø Storacha Status\n');
43
+ console.log('━'.repeat(60));
44
+ console.log('');
45
+ // Authentication status
46
+ console.log('šŸ” Authentication:');
47
+ if (status.authenticated) {
48
+ console.log(` Status: āœ… Authenticated`);
49
+ if (status.email) {
50
+ console.log(` Email: ${status.email}`);
51
+ }
52
+ }
53
+ else {
54
+ console.log(` Status: āŒ Not authenticated`);
55
+ console.log('');
56
+ console.log('šŸ’” To authenticate:');
57
+ console.log(' lsh storacha login [email protected]');
58
+ }
59
+ console.log('');
60
+ // Network sync status
61
+ console.log('🌐 Network Sync:');
62
+ console.log(` Enabled: ${status.enabled ? 'āœ… Yes' : 'āŒ No'}`);
63
+ if (!status.enabled) {
64
+ console.log('');
65
+ console.log('šŸ’” To enable:');
66
+ console.log(' export LSH_STORACHA_ENABLED=true');
67
+ console.log(' # Add to ~/.bashrc or ~/.zshrc for persistence');
68
+ }
69
+ console.log('');
70
+ // Spaces status
71
+ if (status.authenticated) {
72
+ console.log('šŸ“¦ Spaces:');
73
+ if (status.spaces.length === 0) {
74
+ console.log(' No spaces found');
75
+ console.log('');
76
+ console.log('šŸ’” Create a space:');
77
+ console.log(' lsh storacha space create my-space');
78
+ }
79
+ else {
80
+ console.log(` Total: ${status.spaces.length}`);
81
+ if (status.currentSpace) {
82
+ console.log(` Current: ${status.currentSpace}`);
83
+ }
84
+ console.log('');
85
+ console.log(' Available spaces:');
86
+ status.spaces.forEach(space => {
87
+ const marker = space.name === status.currentSpace ? '→' : ' ';
88
+ console.log(` ${marker} ${space.name}`);
89
+ });
90
+ }
91
+ console.log('');
92
+ }
93
+ // Quick actions
94
+ console.log('šŸ’” Quick Actions:');
95
+ if (!status.authenticated) {
96
+ console.log(' lsh storacha login [email protected]');
97
+ }
98
+ else if (!status.enabled) {
99
+ console.log(' export LSH_STORACHA_ENABLED=true');
100
+ }
101
+ else {
102
+ console.log(' lsh push # Will sync to Storacha network');
103
+ console.log(' lsh pull # Will download from Storacha if needed');
104
+ }
105
+ console.log('');
106
+ console.log('━'.repeat(60));
107
+ console.log('');
108
+ }
109
+ catch (error) {
110
+ const err = error;
111
+ console.error('āŒ Failed to get status:', err.message);
112
+ process.exit(1);
113
+ }
114
+ });
115
+ // Space commands
116
+ const space = storacha
117
+ .command('space')
118
+ .description('Manage Storacha spaces');
119
+ space
120
+ .command('create <name>')
121
+ .description('Create a new space')
122
+ .action(async (name) => {
123
+ try {
124
+ const client = getStorachaClient();
125
+ const status = await client.getStatus();
126
+ if (!status.authenticated) {
127
+ console.error('āŒ Not authenticated');
128
+ console.error('');
129
+ console.error('šŸ’” First, authenticate:');
130
+ console.error(' lsh storacha login [email protected]');
131
+ process.exit(1);
132
+ }
133
+ console.log(`\nšŸ†• Creating space: ${name}...\n`);
134
+ await client.createSpace(name);
135
+ console.log('');
136
+ }
137
+ catch (error) {
138
+ const err = error;
139
+ console.error('\nāŒ Failed to create space:', err.message);
140
+ process.exit(1);
141
+ }
142
+ });
143
+ space
144
+ .command('auto')
145
+ .description('Auto-select space based on current project (git repo or directory name)')
146
+ .action(async () => {
147
+ try {
148
+ const client = getStorachaClient();
149
+ const status = await client.getStatus();
150
+ if (!status.authenticated) {
151
+ console.error('āŒ Not authenticated');
152
+ console.error('');
153
+ console.error('šŸ’” First, authenticate:');
154
+ console.error(' lsh storacha login [email protected]');
155
+ process.exit(1);
156
+ }
157
+ const projectName = client.getProjectName();
158
+ console.log(`\nšŸ” Detected project: ${projectName}\n`);
159
+ const spaceName = await client.ensureProjectSpace();
160
+ console.log(`āœ… Active space: ${spaceName}\n`);
161
+ }
162
+ catch (error) {
163
+ const err = error;
164
+ console.error('\nāŒ Failed to auto-select space:', err.message);
165
+ process.exit(1);
166
+ }
167
+ });
168
+ space
169
+ .command('use <name>')
170
+ .description('Switch to a specific space')
171
+ .action(async (name) => {
172
+ try {
173
+ const client = getStorachaClient();
174
+ const status = await client.getStatus();
175
+ if (!status.authenticated) {
176
+ console.error('āŒ Not authenticated');
177
+ console.error('');
178
+ console.error('šŸ’” First, authenticate:');
179
+ console.error(' lsh storacha login [email protected]');
180
+ process.exit(1);
181
+ }
182
+ const found = await client.selectSpace(name);
183
+ if (found) {
184
+ console.log(`\nāœ… Switched to space: ${name}\n`);
185
+ }
186
+ else {
187
+ console.error(`\nāŒ Space not found: ${name}`);
188
+ console.error('');
189
+ console.error('šŸ’” To list available spaces:');
190
+ console.error(' lsh storacha space list');
191
+ console.error('');
192
+ console.error('šŸ’” To create a new space:');
193
+ console.error(` lsh storacha space create ${name}`);
194
+ process.exit(1);
195
+ }
196
+ }
197
+ catch (error) {
198
+ const err = error;
199
+ console.error('\nāŒ Failed to switch space:', err.message);
200
+ process.exit(1);
201
+ }
202
+ });
203
+ space
204
+ .command('list')
205
+ .description('List all spaces')
206
+ .action(async () => {
207
+ try {
208
+ const client = getStorachaClient();
209
+ const status = await client.getStatus();
210
+ if (!status.authenticated) {
211
+ console.error('āŒ Not authenticated');
212
+ console.error('');
213
+ console.error('šŸ’” First, authenticate:');
214
+ console.error(' lsh storacha login [email protected]');
215
+ process.exit(1);
216
+ }
217
+ console.log('\nšŸ“¦ Storacha Spaces\n');
218
+ console.log('━'.repeat(60));
219
+ console.log('');
220
+ if (status.spaces.length === 0) {
221
+ console.log('No spaces found');
222
+ console.log('');
223
+ console.log('šŸ’” Create a space:');
224
+ console.log(' lsh storacha space create my-space');
225
+ }
226
+ else {
227
+ status.spaces.forEach((space, index) => {
228
+ const marker = space.name === status.currentSpace ? '→' : ' ';
229
+ console.log(`${marker} ${index + 1}. ${space.name}`);
230
+ console.log(` DID: ${space.did}`);
231
+ console.log(` Registered: ${space.registered}`);
232
+ console.log('');
233
+ });
234
+ if (status.currentSpace) {
235
+ console.log(`Current space: ${status.currentSpace}`);
236
+ }
237
+ }
238
+ console.log('━'.repeat(60));
239
+ console.log('');
240
+ }
241
+ catch (error) {
242
+ const err = error;
243
+ console.error('āŒ Failed to list spaces:', err.message);
244
+ process.exit(1);
245
+ }
246
+ });
247
+ // Enable/disable commands
248
+ storacha
249
+ .command('enable')
250
+ .description('Enable Storacha network sync')
251
+ .action(() => {
252
+ const client = getStorachaClient();
253
+ client.enable();
254
+ console.log('');
255
+ console.log('šŸ’” For persistence, add to your shell profile:');
256
+ console.log(' echo "export LSH_STORACHA_ENABLED=true" >> ~/.bashrc');
257
+ console.log(' # or ~/.zshrc for zsh');
258
+ console.log('');
259
+ });
260
+ storacha
261
+ .command('disable')
262
+ .description('Disable Storacha network sync (local cache only)')
263
+ .action(() => {
264
+ const client = getStorachaClient();
265
+ client.disable();
266
+ console.log('');
267
+ });
268
+ }
@@ -34,6 +34,7 @@ export const ENV_VARS = {
34
34
  LSH_LOCAL_STORAGE_QUIET: 'LSH_LOCAL_STORAGE_QUIET',
35
35
  LSH_V1_COMPAT: 'LSH_V1_COMPAT',
36
36
  DISABLE_IPFS_SYNC: 'DISABLE_IPFS_SYNC',
37
+ LSH_USE_OPTIMIZED_SCHEDULER: 'LSH_USE_OPTIMIZED_SCHEDULER',
37
38
  // Logging
38
39
  LSH_LOG_LEVEL: 'LSH_LOG_LEVEL',
39
40
  LSH_LOG_FORMAT: 'LSH_LOG_FORMAT',
@@ -51,6 +52,13 @@ export const ENV_VARS = {
51
52
  REDIS_URL: 'REDIS_URL',
52
53
  // Monitoring
53
54
  MONITORING_API_PORT: 'MONITORING_API_PORT',
55
+ // Metrics collection
56
+ LSH_METRICS_ENABLED: 'LSH_METRICS_ENABLED',
57
+ LSH_METRICS_INTERVAL: 'LSH_METRICS_INTERVAL',
58
+ LSH_METRICS_EXPORT_FORMAT: 'LSH_METRICS_EXPORT_FORMAT',
59
+ LSH_METRICS_RETENTION: 'LSH_METRICS_RETENTION',
60
+ LSH_PROFILING_ENABLED: 'LSH_PROFILING_ENABLED',
61
+ LSH_PROFILING_SAMPLE_RATE: 'LSH_PROFILING_SAMPLE_RATE',
54
62
  // Stripe billing
55
63
  STRIPE_SECRET_KEY: 'STRIPE_SECRET_KEY',
56
64
  STRIPE_WEBHOOK_SECRET: 'STRIPE_WEBHOOK_SECRET',
@@ -87,6 +95,9 @@ export const DEFAULTS = {
87
95
  DEFAULT_EMAIL_FROM: 'noreply@lsh.dev',
88
96
  // Timeouts and intervals (in milliseconds)
89
97
  CHECK_INTERVAL_MS: 2000,
98
+ SCHEDULER_MIN_CHECK_INTERVAL_MS: 100,
99
+ SCHEDULER_MAX_CHECK_INTERVAL_MS: 60000,
100
+ SCHEDULER_DUE_BUFFER_MS: 50,
90
101
  REQUEST_TIMEOUT_MS: 10000,
91
102
  DAEMON_RESTART_DELAY_MS: 1000,
92
103
  JOB_TIMEOUT_1H_MS: 3600000,
@@ -115,6 +126,10 @@ export const DEFAULTS = {
115
126
  MAX_RECORDS_PER_JOB: 1000,
116
127
  MAX_TOTAL_RECORDS: 50000,
117
128
  METRICS_RETENTION_DAYS: 90,
129
+ // Metrics collection defaults
130
+ METRICS_COLLECTION_INTERVAL_MS: 60000, // 1 minute
131
+ METRICS_EXPORT_FORMAT: 'prometheus',
132
+ PROFILING_SAMPLE_RATE: 0.1, // 10% sampling by default
118
133
  // Shell defaults
119
134
  DEFAULT_SHELL_UNIX: '/bin/sh',
120
135
  DEFAULT_SHELL_WIN: 'cmd.exe',
@@ -70,6 +70,17 @@ To fix this:
70
70
  BASE64_COMMAND: 'Base64 encoded command detected',
71
71
  DYNAMIC_EVAL: 'Dynamic command evaluation detected',
72
72
  NULL_BYTE: 'Null byte injection detected',
73
+ // Warning-level security messages (command validator)
74
+ RECURSIVE_DELETE: 'Recursive deletion command',
75
+ SUDO_ELEVATED: 'Elevated privileges requested',
76
+ CHMOD_777: 'Overly permissive file permissions',
77
+ DISK_WRITE: 'Writing to disk device',
78
+ INSECURE_SSL: 'Insecure SSL certificate validation disabled',
79
+ FORK_BOMB: 'Fork bomb pattern detected',
80
+ EXCESSIVE_CHAINING: 'Excessive command chaining detected',
81
+ EXCESSIVE_PIPES: 'Excessive pipe usage detected',
82
+ NESTED_SUBSTITUTION: 'Nested command substitution detected',
83
+ CONTROL_CHARS: 'Control characters detected in command',
73
84
  };
74
85
  export const RISK_LEVELS = {
75
86
  CRITICAL: 'critical',