@ceschiatti/redistail 0.0.1 → 0.0.3
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/redistail.exe +0 -0
- package/dist/cli/redistail.js +1 -1
- package/package.json +3 -2
- package/src/cli/config-service.ts +455 -0
- package/src/cli/display-service.ts +321 -0
- package/src/cli/layers.ts +492 -0
- package/src/cli/pubsub-monitor-service.ts +296 -0
- package/src/cli/redistail-launcher.js +65 -0
- package/src/cli/redistail.ts +417 -0
- package/src/cli/signal-handler-service.ts +210 -0
- package/src/cli/stream-monitor-service.ts +427 -0
- package/src/cli/types.ts +302 -0
- package/src/cli/version-generated.ts +27 -0
- package/src/cli/version.ts +27 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Configuration Service for redistail utility.
|
|
3
|
+
*
|
|
4
|
+
* This module provides services for parsing command-line arguments,
|
|
5
|
+
* loading environment configuration, and displaying help/version information.
|
|
6
|
+
* Built using Effect-TS patterns with proper error handling and dependency injection.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Context, Effect, Layer, Config, Console } from 'effect';
|
|
10
|
+
import { BUILD_COMMIT, BUILD_VERSION } from './version-generated.js';
|
|
11
|
+
import {
|
|
12
|
+
CLIConfig,
|
|
13
|
+
RedistailConfig,
|
|
14
|
+
isValidConnectionType,
|
|
15
|
+
isValidTopicName,
|
|
16
|
+
createInvalidArgumentError,
|
|
17
|
+
createMissingArgumentError,
|
|
18
|
+
createEnvironmentError,
|
|
19
|
+
createDefaultRedistailConfig,
|
|
20
|
+
DEFAULT_CLI_REDIS_CONFIG,
|
|
21
|
+
DEFAULT_DISPLAY_CONFIG,
|
|
22
|
+
DEFAULT_MONITORING_CONFIG,
|
|
23
|
+
} from './types.js';
|
|
24
|
+
import type { CLIError, ConfigError } from './types.js';
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Service Interface
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* CLI Configuration Service interface
|
|
32
|
+
*/
|
|
33
|
+
export interface CLIConfigService {
|
|
34
|
+
readonly parseArgs: (args: string[]) => Effect.Effect<CLIConfig, CLIError>;
|
|
35
|
+
readonly loadConfig: () => Effect.Effect<RedistailConfig, ConfigError>;
|
|
36
|
+
readonly showHelp: () => Effect.Effect<void, never>;
|
|
37
|
+
readonly showVersion: () => Effect.Effect<void, never>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Service tag for dependency injection
|
|
42
|
+
*/
|
|
43
|
+
export const CLIConfigService =
|
|
44
|
+
Context.GenericTag<CLIConfigService>('CLIConfigService');
|
|
45
|
+
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// Environment Configuration Schema
|
|
48
|
+
// ============================================================================
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Redis configuration from environment variables
|
|
52
|
+
*/
|
|
53
|
+
const redisHost = Config.withDefault(
|
|
54
|
+
Config.string('REDIS_HOST'),
|
|
55
|
+
DEFAULT_CLI_REDIS_CONFIG.host,
|
|
56
|
+
);
|
|
57
|
+
const redisPort = Config.withDefault(
|
|
58
|
+
Config.integer('REDIS_PORT'),
|
|
59
|
+
DEFAULT_CLI_REDIS_CONFIG.port,
|
|
60
|
+
);
|
|
61
|
+
const redisUrl = Config.option(Config.string('REDIS_URL'));
|
|
62
|
+
const redisTimeout = Config.withDefault(
|
|
63
|
+
Config.integer('REDIS_TIMEOUT'),
|
|
64
|
+
DEFAULT_CLI_REDIS_CONFIG.timeout,
|
|
65
|
+
);
|
|
66
|
+
const redisRetryAttempts = Config.withDefault(
|
|
67
|
+
Config.integer('REDIS_RETRY_ATTEMPTS'),
|
|
68
|
+
DEFAULT_CLI_REDIS_CONFIG.retryAttempts,
|
|
69
|
+
);
|
|
70
|
+
const redisRetryDelay = Config.withDefault(
|
|
71
|
+
Config.integer('REDIS_RETRY_DELAY'),
|
|
72
|
+
DEFAULT_CLI_REDIS_CONFIG.retryDelay,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Display configuration from environment variables
|
|
77
|
+
*/
|
|
78
|
+
const redistailColors = Config.withDefault(
|
|
79
|
+
Config.boolean('REDISTAIL_COLORS'),
|
|
80
|
+
DEFAULT_DISPLAY_CONFIG.colors,
|
|
81
|
+
);
|
|
82
|
+
const redistailTimestamps = Config.withDefault(
|
|
83
|
+
Config.boolean('REDISTAIL_TIMESTAMPS'),
|
|
84
|
+
DEFAULT_DISPLAY_CONFIG.timestamps,
|
|
85
|
+
);
|
|
86
|
+
const redistailPrettyJson = Config.withDefault(
|
|
87
|
+
Config.boolean('REDISTAIL_PRETTY_JSON'),
|
|
88
|
+
DEFAULT_DISPLAY_CONFIG.prettyJson,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Monitoring configuration from environment variables
|
|
93
|
+
*/
|
|
94
|
+
const redistailBlockTimeout = Config.withDefault(
|
|
95
|
+
Config.integer('REDISTAIL_BLOCK_TIMEOUT'),
|
|
96
|
+
DEFAULT_MONITORING_CONFIG.blockTimeout,
|
|
97
|
+
);
|
|
98
|
+
const redistailMaxReconnectAttempts = Config.withDefault(
|
|
99
|
+
Config.integer('REDISTAIL_MAX_RECONNECT_ATTEMPTS'),
|
|
100
|
+
DEFAULT_MONITORING_CONFIG.maxReconnectAttempts,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// Implementation Functions
|
|
105
|
+
// ============================================================================
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Parse command line arguments into CLIConfig
|
|
109
|
+
*/
|
|
110
|
+
const parseCommandLineArgs = (
|
|
111
|
+
args: string[],
|
|
112
|
+
): Effect.Effect<CLIConfig, CLIError> =>
|
|
113
|
+
Effect.gen(function* () {
|
|
114
|
+
// Handle help flags
|
|
115
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
116
|
+
return CLIConfig({
|
|
117
|
+
connectionType: 'pubsub',
|
|
118
|
+
topicName: '',
|
|
119
|
+
help: true,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Handle version flags
|
|
124
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
125
|
+
return CLIConfig({
|
|
126
|
+
connectionType: 'pubsub',
|
|
127
|
+
topicName: '',
|
|
128
|
+
version: true,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check for minimum required arguments
|
|
133
|
+
if (args.length < 2) {
|
|
134
|
+
return yield* Effect.fail(
|
|
135
|
+
createMissingArgumentError(
|
|
136
|
+
'Missing required arguments. Usage: redistail <pubsub|stream> <topic-name>',
|
|
137
|
+
'ARGUMENT_PARSING',
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const [connectionType, topicName, ...extraArgs] = args;
|
|
143
|
+
|
|
144
|
+
// Validate connection type
|
|
145
|
+
if (!isValidConnectionType(connectionType)) {
|
|
146
|
+
return yield* Effect.fail(
|
|
147
|
+
createInvalidArgumentError(
|
|
148
|
+
`Invalid connection type: ${connectionType}. Must be 'pubsub' or 'stream'`,
|
|
149
|
+
'ARGUMENT_PARSING',
|
|
150
|
+
),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Validate topic name
|
|
155
|
+
if (!isValidTopicName(topicName)) {
|
|
156
|
+
return yield* Effect.fail(
|
|
157
|
+
createInvalidArgumentError(
|
|
158
|
+
'Topic name cannot be empty',
|
|
159
|
+
'ARGUMENT_PARSING',
|
|
160
|
+
),
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check for unexpected extra arguments
|
|
165
|
+
if (extraArgs.length > 0) {
|
|
166
|
+
return yield* Effect.fail(
|
|
167
|
+
createInvalidArgumentError(
|
|
168
|
+
`Unexpected arguments: ${extraArgs.join(', ')}. Usage: redistail <pubsub|stream> <topic-name>`,
|
|
169
|
+
'ARGUMENT_PARSING',
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return CLIConfig({
|
|
175
|
+
connectionType,
|
|
176
|
+
topicName: topicName.trim(),
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Load configuration from environment variables
|
|
182
|
+
*/
|
|
183
|
+
const loadRedistailConfig = (): Effect.Effect<RedistailConfig, ConfigError> =>
|
|
184
|
+
Effect.gen(function* () {
|
|
185
|
+
const config = yield* Effect.all({
|
|
186
|
+
redisHost,
|
|
187
|
+
redisPort,
|
|
188
|
+
redisUrl,
|
|
189
|
+
redisTimeout,
|
|
190
|
+
redisRetryAttempts,
|
|
191
|
+
redisRetryDelay,
|
|
192
|
+
redistailColors,
|
|
193
|
+
redistailTimestamps,
|
|
194
|
+
redistailPrettyJson,
|
|
195
|
+
redistailBlockTimeout,
|
|
196
|
+
redistailMaxReconnectAttempts,
|
|
197
|
+
}).pipe(
|
|
198
|
+
Effect.mapError((configError) =>
|
|
199
|
+
createEnvironmentError(
|
|
200
|
+
'Failed to load configuration from environment',
|
|
201
|
+
configError,
|
|
202
|
+
'CONFIG_LOADING',
|
|
203
|
+
),
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// Validate configuration values
|
|
208
|
+
if (config.redisPort < 1 || config.redisPort > 65535) {
|
|
209
|
+
return yield* Effect.fail(
|
|
210
|
+
createEnvironmentError(
|
|
211
|
+
`Invalid REDIS_PORT: ${config.redisPort}. Must be between 1 and 65535`,
|
|
212
|
+
undefined,
|
|
213
|
+
'CONFIG_VALIDATION',
|
|
214
|
+
),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (config.redisTimeout < 0) {
|
|
219
|
+
return yield* Effect.fail(
|
|
220
|
+
createEnvironmentError(
|
|
221
|
+
`Invalid REDIS_TIMEOUT: ${config.redisTimeout}. Must be non-negative`,
|
|
222
|
+
undefined,
|
|
223
|
+
'CONFIG_VALIDATION',
|
|
224
|
+
),
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (config.redisRetryAttempts < 0) {
|
|
229
|
+
return yield* Effect.fail(
|
|
230
|
+
createEnvironmentError(
|
|
231
|
+
`Invalid REDIS_RETRY_ATTEMPTS: ${config.redisRetryAttempts}. Must be non-negative`,
|
|
232
|
+
undefined,
|
|
233
|
+
'CONFIG_VALIDATION',
|
|
234
|
+
),
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (config.redisRetryDelay < 0) {
|
|
239
|
+
return yield* Effect.fail(
|
|
240
|
+
createEnvironmentError(
|
|
241
|
+
`Invalid REDIS_RETRY_DELAY: ${config.redisRetryDelay}. Must be non-negative`,
|
|
242
|
+
undefined,
|
|
243
|
+
'CONFIG_VALIDATION',
|
|
244
|
+
),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (config.redistailBlockTimeout < 0) {
|
|
249
|
+
return yield* Effect.fail(
|
|
250
|
+
createEnvironmentError(
|
|
251
|
+
`Invalid REDISTAIL_BLOCK_TIMEOUT: ${config.redistailBlockTimeout}. Must be non-negative`,
|
|
252
|
+
undefined,
|
|
253
|
+
'CONFIG_VALIDATION',
|
|
254
|
+
),
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (config.redistailMaxReconnectAttempts < 0) {
|
|
259
|
+
return yield* Effect.fail(
|
|
260
|
+
createEnvironmentError(
|
|
261
|
+
`Invalid REDISTAIL_MAX_RECONNECT_ATTEMPTS: ${config.redistailMaxReconnectAttempts}. Must be non-negative`,
|
|
262
|
+
undefined,
|
|
263
|
+
'CONFIG_VALIDATION',
|
|
264
|
+
),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return RedistailConfig({
|
|
269
|
+
redis: {
|
|
270
|
+
host: config.redisHost,
|
|
271
|
+
port: config.redisPort,
|
|
272
|
+
url:
|
|
273
|
+
config.redisUrl._tag === 'Some' ? config.redisUrl.value : undefined,
|
|
274
|
+
timeout: config.redisTimeout,
|
|
275
|
+
retryAttempts: config.redisRetryAttempts,
|
|
276
|
+
retryDelay: config.redisRetryDelay,
|
|
277
|
+
},
|
|
278
|
+
display: {
|
|
279
|
+
colors: config.redistailColors,
|
|
280
|
+
timestamps: config.redistailTimestamps,
|
|
281
|
+
prettyJson: config.redistailPrettyJson,
|
|
282
|
+
},
|
|
283
|
+
monitoring: {
|
|
284
|
+
blockTimeout: config.redistailBlockTimeout,
|
|
285
|
+
maxReconnectAttempts: config.redistailMaxReconnectAttempts,
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Display help information
|
|
292
|
+
*/
|
|
293
|
+
const showHelp = (): Effect.Effect<void, never> =>
|
|
294
|
+
Console.log(`
|
|
295
|
+
redistail - Redis message monitoring utility
|
|
296
|
+
|
|
297
|
+
USAGE:
|
|
298
|
+
redistail <CONNECTION_TYPE> <TOPIC_NAME>
|
|
299
|
+
|
|
300
|
+
ARGUMENTS:
|
|
301
|
+
CONNECTION_TYPE Type of Redis connection: 'pubsub' or 'stream'
|
|
302
|
+
TOPIC_NAME Name of the channel (pubsub) or stream key (stream) to monitor
|
|
303
|
+
|
|
304
|
+
OPTIONS:
|
|
305
|
+
-h, --help Show this help message
|
|
306
|
+
-v, --version Show version information
|
|
307
|
+
|
|
308
|
+
ENVIRONMENT VARIABLES:
|
|
309
|
+
REDIS_HOST Redis hostname (default: ${DEFAULT_CLI_REDIS_CONFIG.host})
|
|
310
|
+
REDIS_PORT Redis port (default: ${DEFAULT_CLI_REDIS_CONFIG.port})
|
|
311
|
+
REDIS_URL Complete Redis connection URL (overrides host/port)
|
|
312
|
+
REDIS_TIMEOUT Connection timeout in ms (default: ${DEFAULT_CLI_REDIS_CONFIG.timeout})
|
|
313
|
+
REDIS_RETRY_ATTEMPTS Number of retry attempts (default: ${DEFAULT_CLI_REDIS_CONFIG.retryAttempts})
|
|
314
|
+
REDIS_RETRY_DELAY Delay between retries in ms (default: ${DEFAULT_CLI_REDIS_CONFIG.retryDelay})
|
|
315
|
+
REDISTAIL_COLORS Enable colored output (default: ${DEFAULT_DISPLAY_CONFIG.colors})
|
|
316
|
+
REDISTAIL_TIMESTAMPS Enable timestamps (default: ${DEFAULT_DISPLAY_CONFIG.timestamps})
|
|
317
|
+
REDISTAIL_PRETTY_JSON Pretty-print JSON content (default: ${DEFAULT_DISPLAY_CONFIG.prettyJson})
|
|
318
|
+
REDISTAIL_BLOCK_TIMEOUT Block timeout for streams in ms (default: ${DEFAULT_MONITORING_CONFIG.blockTimeout})
|
|
319
|
+
REDISTAIL_MAX_RECONNECT_ATTEMPTS Maximum reconnection attempts (default: ${DEFAULT_MONITORING_CONFIG.maxReconnectAttempts})
|
|
320
|
+
|
|
321
|
+
EXAMPLES:
|
|
322
|
+
redistail pubsub my-channel
|
|
323
|
+
redistail stream my-stream
|
|
324
|
+
REDIS_HOST=redis.example.com redistail pubsub notifications
|
|
325
|
+
REDIS_URL=redis://user:pass@redis.example.com:6380 redistail stream events
|
|
326
|
+
`);
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Display version information
|
|
330
|
+
*/
|
|
331
|
+
const showVersion = (): Effect.Effect<void, never> =>
|
|
332
|
+
Console.log(`redistail ${BUILD_VERSION}\nBuild: ${BUILD_COMMIT}`);
|
|
333
|
+
|
|
334
|
+
// ============================================================================
|
|
335
|
+
// Service Implementation
|
|
336
|
+
// ============================================================================
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* CLI Configuration Service implementation
|
|
340
|
+
*/
|
|
341
|
+
const cliConfigServiceImpl: CLIConfigService = {
|
|
342
|
+
parseArgs: parseCommandLineArgs,
|
|
343
|
+
loadConfig: loadRedistailConfig,
|
|
344
|
+
showHelp,
|
|
345
|
+
showVersion,
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Live layer for CLI Configuration Service
|
|
350
|
+
*/
|
|
351
|
+
export const CLIConfigServiceLive: Layer.Layer<CLIConfigService> =
|
|
352
|
+
Layer.succeed(CLIConfigService, cliConfigServiceImpl);
|
|
353
|
+
|
|
354
|
+
// ============================================================================
|
|
355
|
+
// Convenience Functions
|
|
356
|
+
// ============================================================================
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Parse arguments and load configuration in one operation
|
|
360
|
+
*/
|
|
361
|
+
export const parseArgsAndLoadConfig = (
|
|
362
|
+
args: string[],
|
|
363
|
+
): Effect.Effect<
|
|
364
|
+
{ cli: CLIConfig; config: RedistailConfig },
|
|
365
|
+
CLIError | ConfigError,
|
|
366
|
+
CLIConfigService
|
|
367
|
+
> =>
|
|
368
|
+
Effect.gen(function* () {
|
|
369
|
+
const cliConfigService = yield* CLIConfigService;
|
|
370
|
+
const cli = yield* cliConfigService.parseArgs(args);
|
|
371
|
+
const config = yield* cliConfigService.loadConfig();
|
|
372
|
+
return { cli, config };
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Handle help and version flags, returning true if handled
|
|
377
|
+
*/
|
|
378
|
+
export const handleHelpAndVersion = (
|
|
379
|
+
cliConfig: CLIConfig,
|
|
380
|
+
): Effect.Effect<boolean, never, CLIConfigService> =>
|
|
381
|
+
Effect.gen(function* () {
|
|
382
|
+
const cliConfigService = yield* CLIConfigService;
|
|
383
|
+
|
|
384
|
+
if (cliConfig.help) {
|
|
385
|
+
yield* cliConfigService.showHelp();
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (cliConfig.version) {
|
|
390
|
+
yield* cliConfigService.showVersion();
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return false;
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Validate CLI configuration for runtime use
|
|
399
|
+
*/
|
|
400
|
+
export const validateCLIConfig = (
|
|
401
|
+
cliConfig: CLIConfig,
|
|
402
|
+
): Effect.Effect<CLIConfig, CLIError> =>
|
|
403
|
+
Effect.gen(function* () {
|
|
404
|
+
if (cliConfig.help || cliConfig.version) {
|
|
405
|
+
return cliConfig;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (!isValidConnectionType(cliConfig.connectionType)) {
|
|
409
|
+
return yield* Effect.fail(
|
|
410
|
+
createInvalidArgumentError(
|
|
411
|
+
`Invalid connection type: ${cliConfig.connectionType}`,
|
|
412
|
+
'CONFIG_VALIDATION',
|
|
413
|
+
),
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (!isValidTopicName(cliConfig.topicName)) {
|
|
418
|
+
return yield* Effect.fail(
|
|
419
|
+
createInvalidArgumentError(
|
|
420
|
+
'Topic name cannot be empty',
|
|
421
|
+
'CONFIG_VALIDATION',
|
|
422
|
+
),
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return cliConfig;
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Create a complete configuration for the redistail application
|
|
431
|
+
*/
|
|
432
|
+
export const createCompleteConfig = (
|
|
433
|
+
args: string[],
|
|
434
|
+
): Effect.Effect<
|
|
435
|
+
{ cli: CLIConfig; config: RedistailConfig; shouldExit: boolean },
|
|
436
|
+
CLIError | ConfigError,
|
|
437
|
+
CLIConfigService
|
|
438
|
+
> =>
|
|
439
|
+
Effect.gen(function* () {
|
|
440
|
+
const cliConfigService = yield* CLIConfigService;
|
|
441
|
+
const cli = yield* cliConfigService.parseArgs(args);
|
|
442
|
+
|
|
443
|
+
// Handle help and version early
|
|
444
|
+
if (cli.help || cli.version) {
|
|
445
|
+
const shouldExit = yield* handleHelpAndVersion(cli);
|
|
446
|
+
const config = createDefaultRedistailConfig();
|
|
447
|
+
return { cli, config, shouldExit };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Load full configuration for normal operation
|
|
451
|
+
const config = yield* cliConfigService.loadConfig();
|
|
452
|
+
const validatedCli = yield* validateCLIConfig(cli);
|
|
453
|
+
|
|
454
|
+
return { cli: validatedCli, config, shouldExit: false };
|
|
455
|
+
});
|