@qodalis/cli-core 2.0.0-beta.12 → 2.0.0-beta.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/public-api.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ITerminalOptions, ITerminalInitOnlyOptions, Terminal, ITheme } from '@xterm/xterm';
2
- import { Subject, Observable, Subscription } from 'rxjs';
2
+ import { Observable, Subscription, Subject } from 'rxjs';
3
3
 
4
4
  type CliProcessCommand = {
5
5
  /**
@@ -170,559 +170,428 @@ declare enum CliIcon {
170
170
  Gift = "\uD83C\uDF81"
171
171
  }
172
172
 
173
- /**
174
- * Configuration for a remote CLI server
175
- */
176
- type CliServerConfig = {
177
- /** Unique identifier, used for namespacing commands */
178
- name: string;
179
- /** Base URL of the server, e.g. "https://api.example.com" */
180
- url: string;
181
- /** Whether this server is enabled. @default true */
182
- enabled?: boolean;
183
- /** Custom headers sent with every request (e.g. auth tokens) */
184
- headers?: Record<string, string>;
185
- /** Request timeout in milliseconds. @default 30000 */
186
- timeout?: number;
187
- };
188
- /**
189
- * A single output item in a server response
190
- */
191
- type CliServerOutput = {
192
- type: 'text';
193
- value: string;
194
- style?: 'success' | 'error' | 'info' | 'warning';
195
- } | {
196
- type: 'table';
197
- headers: string[];
198
- rows: string[][];
199
- } | {
200
- type: 'list';
201
- items: string[];
202
- ordered?: boolean;
203
- } | {
204
- type: 'json';
205
- value: any;
206
- } | {
207
- type: 'key-value';
208
- entries: {
209
- key: string;
210
- value: string;
211
- }[];
212
- };
213
- /**
214
- * Structured response from a server command execution
215
- */
216
- type CliServerResponse = {
217
- exitCode: number;
218
- outputs: CliServerOutput[];
219
- };
220
- /**
221
- * Metadata about a remote command processor, returned by GET /api/qcli/commands
222
- */
223
- type CliServerCommandDescriptor = {
224
- command: string;
225
- description?: string;
226
- version?: string;
227
- parameters?: {
228
- name: string;
229
- aliases?: string[];
230
- description: string;
231
- required: boolean;
232
- type: string;
233
- defaultValue?: any;
234
- }[];
235
- processors?: CliServerCommandDescriptor[];
236
- };
237
- /**
238
- * Server capabilities returned by GET /api/qcli/capabilities
239
- */
240
- type CliServerCapabilities = {
241
- /** Whether this server supports remote shell access */
242
- shell: boolean;
243
- /** Server operating system (e.g. "linux", "win32", "darwin") */
244
- os?: string;
245
- /** Path to the shell binary on the server */
246
- shellPath?: string;
247
- /** Server version string */
248
- version?: string;
249
- /** Whether this server supports SSE streaming execution */
250
- streaming?: boolean;
251
- };
252
-
253
- /**
254
- * Represents a package source for the CLI package manager
255
- */
256
- interface CliPackageSource {
173
+ interface CliTableOptions {
174
+ /** Expand columns to fill the full terminal width. Default: false */
175
+ fullWidth?: boolean;
176
+ }
177
+ interface ICliTerminalWriter {
257
178
  /**
258
- * The name of the source, e.g. 'local', 'unpkg', 'jsdelivr'
179
+ * Write text to the terminal
180
+ * @param text The text to write
259
181
  */
260
- name: string;
182
+ write(text: string): void;
261
183
  /**
262
- * The base URL for fetching package files, e.g. 'http://localhost:3000/'
184
+ * Write text to the terminal followed by a newline
185
+ * @param text The text to write
263
186
  */
264
- url: string;
187
+ writeln(text?: string): void;
265
188
  /**
266
- * The kind of source.
267
- * - 'registry': npm-compatible registry with search API (e.g. npmjs.org, Verdaccio)
268
- * - 'file': static file server, packages are discovered by probing known paths
269
- * @default 'file'
189
+ * Write a success message to the terminal
190
+ * @param message The message to write
270
191
  */
271
- kind?: 'registry' | 'file';
272
- }
273
- /**
274
- * Represents a log level for the CLI
275
- */
276
- declare enum CliLogLevel {
277
- None = 0,
278
- DEBUG = 1,
279
- LOG = 2,
280
- INFO = 3,
281
- WARN = 4,
282
- ERROR = 5
283
- }
284
- /**
285
- * Options for the CLI
286
- */
287
- type CliOptions = Record<string, any> & {
192
+ writeSuccess(message: string): void;
288
193
  /**
289
- * Custom terminal options
194
+ * Write an info message to the terminal
195
+ * @param message The message to write
290
196
  */
291
- terminalOptions?: ITerminalOptions & ITerminalInitOnlyOptions;
197
+ writeInfo(message: string): void;
292
198
  /**
293
- * The minimum log level to display
199
+ * Write an error message to the terminal
200
+ * @param message The message to write
294
201
  */
295
- logLevel?: CliLogLevel;
202
+ writeError(message: string): void;
296
203
  /**
297
- * Custom package sources for the package manager.
298
- * Built-in sources (unpkg, jsdelivr) are always available.
204
+ * Write a warning message to the terminal
205
+ * @param message The message to write
299
206
  */
300
- packageSources?: {
301
- /**
302
- * Name of the primary source. Defaults to 'unpkg'.
303
- */
304
- primary?: string;
305
- /**
306
- * Additional custom sources
307
- */
308
- sources?: CliPackageSource[];
309
- };
207
+ writeWarning(message: string): void;
310
208
  /**
311
- * Remote CLI servers to connect to.
312
- * Commands from each server are discovered and registered as proxy processors.
209
+ * Wrap text in the specified foreground color.
210
+ * @param text The text to wrap
211
+ * @param color The foreground color to apply
212
+ * @returns The text wrapped in ANSI color codes
313
213
  */
314
- servers?: CliServerConfig[];
214
+ wrapInColor(text: string, color: CliForegroundColor): string;
315
215
  /**
316
- * Whether the `eval` / `js` / `calc` command is enabled.
317
- * Disabled by default because it runs arbitrary JavaScript via `eval()`
318
- * in the host page context — a security risk in multi-tenant or
319
- * user-facing deployments.
320
- * @default false
216
+ * Wrap text in the specified background color.
217
+ * @param text The text to wrap
218
+ * @param color The background color to apply
219
+ * @returns The text wrapped in ANSI background color codes
321
220
  */
322
- allowEval?: boolean;
323
- };
324
- /**
325
- * Represents a package that can be installed
326
- */
327
- interface Package {
221
+ wrapInBackgroundColor(text: string, color: CliBackgroundColor): string;
328
222
  /**
329
- * The name of the package
223
+ * Write a JSON object to the terminal
224
+ * @param json The JSON object to write
330
225
  */
331
- name: string;
226
+ writeJson(json: any): void;
332
227
  /**
333
- * The global name used to access the package
228
+ * Write content to a file (triggers a browser download)
229
+ * @param fileName The name of the file to download
230
+ * @param content The content to write to the file
334
231
  */
335
- globalName?: string;
232
+ writeToFile(fileName: string, content: string): void;
336
233
  /**
337
- * The version of the package
234
+ * Write an object array as a table to the terminal
235
+ * @param objects The objects to write to the table
338
236
  */
339
- version: string;
237
+ writeObjectsAsTable(objects: any[]): void;
340
238
  /**
341
- * The unpkg url to the package
239
+ * Write a table to the terminal
240
+ * @param headers The headers of the table
241
+ * @param rows The rows of the table
242
+ * @param options Optional table rendering options
342
243
  */
343
- url: string;
244
+ writeTable(headers: string[], rows: string[][], options?: CliTableOptions): void;
344
245
  /**
345
- * The dependencies for the module
246
+ * Write a divider to the terminal
247
+ * @param options Optional: color, length, character
346
248
  */
347
- dependencies?: Package[];
348
- }
349
- /**
350
- * Represents command processor metadata
351
- */
352
- type CliProcessorMetadata = Record<string, any> & {
249
+ writeDivider(options?: {
250
+ color?: CliForegroundColor;
251
+ length?: number;
252
+ char?: string;
253
+ }): void;
353
254
  /**
354
- * If true, the processor is sealed and cannot be replaced or removed.
355
- * Sealed processors can still be extended (wrapped) by processors with `extendsProcessor = true`.
255
+ * Write a list of items to the terminal with bullet, numbered, or custom prefix.
256
+ * @param items The list items to display
257
+ * @param options Optional: ordered (numbered), prefix (custom marker), color
356
258
  */
357
- sealed?: boolean;
259
+ writeList(items: string[], options?: {
260
+ ordered?: boolean;
261
+ prefix?: string;
262
+ color?: CliForegroundColor;
263
+ }): void;
358
264
  /**
359
- * If true, the processor requires the server to be running
265
+ * Write aligned key-value pairs to the terminal.
266
+ * @param entries Key-value pairs as a Record or array of tuples
267
+ * @param options Optional: separator string, key color
360
268
  */
361
- requireServer?: boolean;
269
+ writeKeyValue(entries: Record<string, string> | [string, string][], options?: {
270
+ separator?: string;
271
+ keyColor?: CliForegroundColor;
272
+ }): void;
362
273
  /**
363
- * The module the processor belongs to
274
+ * Write items in a multi-column layout.
275
+ * @param items The items to arrange in columns
276
+ * @param options Optional: number of columns, padding between columns
364
277
  */
365
- module?: string;
278
+ writeColumns(items: string[], options?: {
279
+ columns?: number;
280
+ padding?: number;
281
+ }): void;
366
282
  /**
367
- * An icon to display for the processor
283
+ * Write a clickable hyperlink using OSC 8 escape sequences.
284
+ * In terminals that support OSC 8, the text is clickable. In others,
285
+ * it falls back to displaying "text (url)".
286
+ * @param text The visible link text
287
+ * @param url The URL to link to
368
288
  */
369
- icon?: CliIcon | string;
289
+ writeLink(text: string, url: string): void;
370
290
  /**
371
- * The minimum required version of @qodalis/cli-core for this processor.
372
- * If the installed core version is lower, the processor will be skipped during boot.
291
+ * Write text inside a bordered box.
292
+ * Useful for callouts, notices, and highlighted information.
293
+ * @param content The content lines to display inside the box
294
+ * @param options Optional: title, border color, padding
373
295
  */
374
- requiredCoreVersion?: string;
296
+ writeBox(content: string | string[], options?: {
297
+ title?: string;
298
+ borderColor?: CliForegroundColor;
299
+ padding?: number;
300
+ }): void;
375
301
  /**
376
- * The minimum required version of @qodalis/cli for this processor.
377
- * If the installed CLI version is lower, the processor will be skipped during boot.
378
- */
379
- requiredCliVersion?: string;
380
- /**
381
- * If true, the processor is hidden from the help command listing.
382
- * The command still works when typed directly and `help <command>` still shows its details.
302
+ * Write text with a specified indentation level.
303
+ * Each level adds 2 spaces of indentation.
304
+ * @param text The text to write
305
+ * @param level The indentation level (default: 1)
383
306
  */
384
- hidden?: boolean;
385
- };
307
+ writeIndented(text: string, level?: number): void;
308
+ }
309
+
386
310
  /**
387
- * Represents a state configuration for the CLI processor
311
+ * Represents a clipboard for the CLI
388
312
  */
389
- type CliStateConfiguration = {
313
+ interface ICliClipboard {
390
314
  /**
391
- * The initial state for the processor
315
+ * Write text to the clipboard
316
+ * @param text The text to write to the clipboard
317
+ * @returns void
392
318
  */
393
- initialState: Record<string, any>;
319
+ write: (text: string) => Promise<void>;
394
320
  /**
395
- * The store identifier for the processor, if any
396
- * @remarks If the store identifier is not set, the processor command name is used
321
+ * Read text from the clipboard
322
+ * @returns The text read from the clipboard
397
323
  */
398
- storeName?: string;
399
- };
400
- type CliState = Record<string, any>;
401
- /**
402
- * Position of the CLI panel relative to the viewport edge.
403
- * - `'bottom'` — anchored to the bottom (default)
404
- * - `'top'` — anchored to the top
405
- * - `'left'` — anchored to the left (vertical layout)
406
- * - `'right'` — anchored to the right (vertical layout)
407
- */
408
- type CliPanelPosition = 'bottom' | 'top' | 'left' | 'right';
324
+ read: () => Promise<string>;
325
+ }
326
+
409
327
  /**
410
- * Alignment of the hidden-mode tab along its viewport edge.
411
- * - `'start'` — left for horizontal panels, top for vertical panels
412
- * - `'center'` — centered along the edge (default)
413
- * - `'end'` — right for horizontal panels, bottom for vertical panels
328
+ * Describes a global parameter (e.g. --help, --version) together with
329
+ * the handler that executes when the parameter is present.
330
+ *
331
+ * Global parameters are checked for every command before the command
332
+ * processor's own `processCommand` runs. If the handler returns `true`,
333
+ * command execution is halted (the parameter "consumed" the invocation).
414
334
  */
415
- type CliPanelHideAlignment = 'start' | 'center' | 'end';
335
+ interface ICliGlobalParameterHandler {
336
+ /**
337
+ * The parameter descriptor shown in help output and used for tab-completion.
338
+ */
339
+ parameter: ICliCommandParameterDescriptor;
340
+ /**
341
+ * Execution priority — lower values run first.
342
+ * @default 0
343
+ */
344
+ priority?: number;
345
+ /**
346
+ * Evaluate and optionally handle the parameter.
347
+ * @returns `true` to halt command execution, `false` to continue.
348
+ */
349
+ handle(args: Record<string, any>, processor: ICliCommandProcessor, command: CliProcessCommand, context: ICliExecutionContext): boolean | Promise<boolean>;
350
+ }
351
+
416
352
  /**
417
- * Configuration for the CLI panel component.
353
+ * Represents a service that executes commands
418
354
  */
419
- interface CliPanelConfig {
355
+ interface ICliCommandExecutorService {
420
356
  /**
421
- * Whether the CLI should be collapsed by default.
422
- * @default true
357
+ * Display the help output for a command, including its description,
358
+ * sub-commands, and parameters.
359
+ * @param command The parsed command to show help for
360
+ * @param context The execution context
423
361
  */
424
- isCollapsed?: boolean;
362
+ showHelp(command: CliProcessCommand, context: ICliExecutionContext): Promise<void>;
425
363
  /**
426
- * Whether the panel starts hidden (showing only the small tab/arrow).
427
- * @default false
364
+ * Execute a command
365
+ * @param command The command to execute
366
+ * @param context The context in which the command is executed
428
367
  */
429
- isHidden?: boolean;
368
+ executeCommand(command: string, context: ICliExecutionContext): Promise<void>;
430
369
  /**
431
- * Position of the panel relative to the viewport.
432
- * @default 'bottom'
370
+ * Register a global parameter handler.
371
+ * Global parameters (e.g. --help, --version) are evaluated for every
372
+ * command before the processor's own processCommand runs.
373
+ * @param handler The global parameter handler to register
433
374
  */
434
- position?: CliPanelPosition;
375
+ registerGlobalParameter(handler: ICliGlobalParameterHandler): void;
435
376
  /**
436
- * Whether the close button is shown.
437
- * @default true
377
+ * Returns the descriptors of all registered global parameters.
378
+ * Used by help output and tab-completion.
438
379
  */
439
- closable?: boolean;
380
+ getGlobalParameters(): ICliCommandParameterDescriptor[];
381
+ }
382
+
383
+ /**
384
+ * Represents a registry for command processors
385
+ */
386
+ interface ICliCommandProcessorRegistry {
440
387
  /**
441
- * Whether the panel can be resized by dragging.
442
- * @default true
388
+ * The processors registered with the registry
443
389
  */
444
- resizable?: boolean;
390
+ readonly processors: ICliCommandProcessor[];
445
391
  /**
446
- * Whether the hide button is shown. When hidden, the panel collapses
447
- * to a small tab at the viewport edge.
448
- * @default true
392
+ * Find a processor for a command
393
+ * @param mainCommand
394
+ * @param chainCommands
449
395
  */
450
- hideable?: boolean;
396
+ findProcessor(mainCommand: string, chainCommands: string[]): ICliCommandProcessor | undefined;
451
397
  /**
452
- * Alignment of the hide tab along the panel's viewport edge.
453
- * For bottom/top: 'start' = left, 'center', 'end' = right.
454
- * For left/right: 'start' = top, 'center', 'end' = bottom.
455
- * @default 'center'
398
+ * Recursively searches for a processor matching the given command.
399
+ * @param mainCommand The main command name.
400
+ * @param chainCommands The remaining chain commands (if any).
401
+ * @param processors The list of available processors.
402
+ * @returns The matching processor or undefined if not found.
456
403
  */
457
- hideAlignment?: CliPanelHideAlignment;
404
+ findProcessorInCollection(mainCommand: string, chainCommands: string[], processors: ICliCommandProcessor[]): ICliCommandProcessor | undefined;
458
405
  /**
459
- * When true, the panel's chrome (header, border, background) automatically
460
- * syncs its colors with the active terminal theme. CSS custom properties
461
- * are derived from the xterm theme and applied as inline styles.
462
- * @default false
406
+ * Get the root processor for a child processor
407
+ * @param child The child processor
463
408
  */
464
- syncTheme?: boolean;
465
- }
466
-
467
- type MultiServices = {
409
+ getRootProcessor(child: ICliCommandChildProcessor): ICliCommandProcessor;
468
410
  /**
469
- * When true, injector returns an array of instances. This is useful to allow multiple
470
- * providers spread across many files to provide configuration information to a common token.
411
+ * Register a processor
412
+ * @param processor
471
413
  */
472
- multi?: boolean;
473
- };
474
- type CliValueProvider = MultiServices & {
475
- useValue: any;
476
- };
477
- type CliTypeProvider = MultiServices & {
478
- useClass: any;
479
- };
480
- type CliFactoryProvider = MultiServices & {
481
- useFactory: Function;
482
- };
483
- type CliProvider = {
484
- provide: any;
485
- } & (CliValueProvider | CliTypeProvider | CliFactoryProvider) & Record<string, any>;
486
-
414
+ registerProcessor(processor: ICliCommandProcessor): void;
415
+ /**
416
+ * Unregister a processor
417
+ * @param processor
418
+ */
419
+ unregisterProcessor(processor: ICliCommandProcessor): void;
420
+ }
487
421
  /**
488
- * Represents a user in the CLI
422
+ * Represents a tracked CLI process entry
489
423
  */
490
- interface ICliUser {
491
- /** Unique user identifier */
492
- id: string;
493
- /** Login name (unique) */
424
+ interface ICliProcessEntry {
425
+ pid: number;
426
+ /** Display name (service name for bg services, command text for commands) */
494
427
  name: string;
495
- /** Email address (unique) */
496
- email: string;
497
- /** Group IDs this user belongs to */
498
- groups: string[];
499
- /** Virtual home directory path, e.g. /home/root */
500
- homeDir?: string;
501
- /** Account creation timestamp */
502
- createdAt: number;
503
- /** Last modification timestamp */
504
- updatedAt: number;
505
- /** Whether the account is locked */
506
- disabled?: boolean;
428
+ /** Process type */
429
+ type: 'command' | 'daemon' | 'job' | 'service';
430
+ command: string;
431
+ startTime: number;
432
+ status: 'running' | 'completed' | 'failed' | 'killed';
433
+ exitCode?: number;
507
434
  }
508
- /**
509
- * Represents user credentials (shadow pattern — stored separately from ICliUser)
510
- */
511
- interface ICliUserCredentials {
512
- /** The user ID this credential belongs to */
513
- userId: string;
514
- /** SHA-256 hash of salt + password */
515
- passwordHash: string;
516
- /** Random salt for this user */
517
- salt: string;
518
- /** Timestamp of last password change */
519
- lastChanged: number;
435
+ /** Options for registering a process */
436
+ interface ICliProcessRegisterOptions {
437
+ /** Display name (defaults to command) */
438
+ name?: string;
439
+ /** Process type (defaults to 'command') */
440
+ type?: 'command' | 'daemon' | 'job' | 'service';
441
+ /** Custom kill handler (e.g. for background services) */
442
+ onKill?: () => Promise<void> | void;
520
443
  }
521
444
  /**
522
- * Represents a group in the CLI
445
+ * Registry for tracking CLI processes
523
446
  */
524
- interface ICliGroup {
525
- /** Unique group identifier */
526
- id: string;
527
- /** Group name (unique) */
528
- name: string;
529
- /** Optional group description */
530
- description?: string;
531
- /** Group creation timestamp */
532
- createdAt: number;
447
+ interface ICliProcessRegistry {
448
+ /** Register a new process, returns assigned PID */
449
+ register(command: string, options?: ICliProcessRegisterOptions): {
450
+ pid: number;
451
+ abortController: AbortController;
452
+ };
453
+ /** Mark process as completed */
454
+ complete(pid: number, exitCode: number): void;
455
+ /** Mark process as failed */
456
+ fail(pid: number): void;
457
+ /** Kill a process by PID */
458
+ kill(pid: number): boolean;
459
+ /** List all processes (running + recent completed) */
460
+ list(): ICliProcessEntry[];
461
+ /** Get the current foreground process PID */
462
+ readonly currentPid: number | undefined;
533
463
  }
534
- /**
535
- * Represents an active user session
536
- */
537
- interface ICliUserSession {
538
- /** The user associated with this session */
539
- user: ICliUser;
540
- /** Pre-computed display name for the prompt (set by users module) */
541
- displayName?: string;
542
- /** Timestamp when the session started */
543
- loginTime: number;
544
- /** Timestamp of last activity */
545
- lastActivity: number;
546
- /** Extensible data (tokens, metadata, etc.) */
547
- data?: Record<string, any>;
548
- }
549
- /** Fields required to create a new user */
550
- type CliAddUser = Omit<ICliUser, 'id' | 'createdAt' | 'updatedAt'>;
551
- /** Fields that can be updated on a user */
552
- type CliUpdateUser = Partial<Omit<ICliUser, 'id' | 'createdAt'>>;
553
-
554
- /** Ownership metadata attached to files, directories, or other resources */
555
- interface ICliOwnership {
556
- /** User ID of the owner */
557
- uid: string;
558
- /** Primary group name */
559
- gid: string;
560
- }
561
- /** Default file permissions: rw-r--r-- (644) */
562
- declare const DEFAULT_FILE_PERMISSIONS = "rw-r--r--";
563
- /** Default directory permissions: rwxr-xr-x (755) */
564
- declare const DEFAULT_DIR_PERMISSIONS = "rwxr-xr-x";
565
-
566
- declare const enums: {
567
- CliForegroundColor: typeof CliForegroundColor;
568
- CliBackgroundColor: typeof CliBackgroundColor;
569
- CliIcon: typeof CliIcon;
570
- CliLogLevel: typeof CliLogLevel;
571
- };
572
464
 
573
- interface CliTableOptions {
574
- /** Expand columns to fill the full terminal width. Default: false */
575
- fullWidth?: boolean;
576
- }
577
- interface ICliTerminalWriter {
465
+ interface ICliExecutionProcess {
578
466
  /**
579
- * Write text to the terminal
580
- * @param text The text to write
467
+ * Indicates if the process has exited
581
468
  */
582
- write(text: string): void;
469
+ exited?: boolean;
583
470
  /**
584
- * Write text to the terminal followed by a newline
585
- * @param text The text to write
471
+ * The exit code of the process
586
472
  */
587
- writeln(text?: string): void;
473
+ exitCode?: number;
588
474
  /**
589
- * Write a success message to the terminal
590
- * @param message The message to write
475
+ * Indicates if the process is running
591
476
  */
592
- writeSuccess(message: string): void;
477
+ running: boolean;
593
478
  /**
594
- * Write an info message to the terminal
595
- * @param message The message to write
479
+ * The data of the process
596
480
  */
597
- writeInfo(message: string): void;
481
+ data: any | undefined;
598
482
  /**
599
- * Write an error message to the terminal
600
- * @param message The message to write
483
+ * Exit the process
484
+ * @param code The exit code
485
+ * @returns void
601
486
  */
602
- writeError(message: string): void;
487
+ exit: (
603
488
  /**
604
- * Write a warning message to the terminal
605
- * @param message The message to write
489
+ * The exit code
606
490
  */
607
- writeWarning(message: string): void;
491
+ code?: number,
608
492
  /**
609
- * Wrap text in the specified foreground color.
610
- * @param text The text to wrap
611
- * @param color The foreground color to apply
612
- * @returns The text wrapped in ANSI color codes
493
+ * Options for exiting the process
613
494
  */
614
- wrapInColor(text: string, color: CliForegroundColor): string;
495
+ options?: {
496
+ /**
497
+ * Indicates if the exit should be silent, i.e. not throw an error
498
+ */
499
+ silent?: boolean;
500
+ }) => void;
615
501
  /**
616
- * Wrap text in the specified background color.
617
- * @param text The text to wrap
618
- * @param color The background color to apply
619
- * @returns The text wrapped in ANSI background color codes
502
+ * Output data from the process
503
+ * @param data The data to output
620
504
  */
621
- wrapInBackgroundColor(text: string, color: CliBackgroundColor): string;
505
+ output(data: any): void;
506
+ }
507
+
508
+ /**
509
+ * Represents a key-value store for the CLI
510
+ */
511
+ interface ICliKeyValueStore {
622
512
  /**
623
- * Write a JSON object to the terminal
624
- * @param json The JSON object to write
513
+ * Retrieves a value by key.
514
+ * @param key - The key to retrieve the value for.
515
+ * @returns A promise resolving to the value or undefined if not found.
625
516
  */
626
- writeJson(json: any): void;
517
+ get<T = any>(key: string): Promise<T | undefined>;
627
518
  /**
628
- * Write content to a file (triggers a browser download)
629
- * @param fileName The name of the file to download
630
- * @param content The content to write to the file
519
+ * Sets a key-value pair in the store.
520
+ * @param key - The key to set.
521
+ * @param value - The value to store.
522
+ * @returns A promise resolving when the value is stored.
631
523
  */
632
- writeToFile(fileName: string, content: string): void;
524
+ set(key: string, value: any): Promise<void>;
633
525
  /**
634
- * Write an object array as a table to the terminal
635
- * @param objects The objects to write to the table
526
+ * Removes a key-value pair by key.
527
+ * @param key - The key to remove.
528
+ * @returns A promise resolving when the key is removed.
636
529
  */
637
- writeObjectsAsTable(objects: any[]): void;
530
+ remove(key: string): Promise<void>;
638
531
  /**
639
- * Write a table to the terminal
640
- * @param headers The headers of the table
641
- * @param rows The rows of the table
642
- * @param options Optional table rendering options
532
+ * Clears all key-value pairs from the store.
533
+ * @returns A promise resolving when the store is cleared.
643
534
  */
644
- writeTable(headers: string[], rows: string[][], options?: CliTableOptions): void;
535
+ clear(): Promise<void>;
536
+ }
537
+ /**
538
+ * Reactive state store scoped to a command processor or module.
539
+ *
540
+ * State is a plain object (`Record<string, any>`). Updates are shallow-merged
541
+ * (top-level keys are replaced, nested objects are not deep-merged).
542
+ * `getState()` returns a snapshot — mutating the returned object has no effect;
543
+ * always use `updateState()` to change state.
544
+ */
545
+ interface ICliStateStore {
645
546
  /**
646
- * Write a divider to the terminal
647
- * @param options Optional: color, length, character
547
+ * Get a snapshot of the current state.
548
+ * @returns A shallow copy of the state object
648
549
  */
649
- writeDivider(options?: {
650
- color?: CliForegroundColor;
651
- length?: number;
652
- char?: string;
653
- }): void;
550
+ getState<T extends CliState = CliState>(): T;
654
551
  /**
655
- * Write a list of items to the terminal with bullet, numbered, or custom prefix.
656
- * @param items The list items to display
657
- * @param options Optional: ordered (numbered), prefix (custom marker), color
552
+ * Shallow-merge new values into the current state.
553
+ * Only the provided keys are updated; other keys are preserved.
554
+ * Triggers subscribers and persists if a storage backend is configured.
555
+ * @param newState Partial state to merge with the current state
658
556
  */
659
- writeList(items: string[], options?: {
660
- ordered?: boolean;
661
- prefix?: string;
662
- color?: CliForegroundColor;
663
- }): void;
557
+ updateState(newState: Partial<CliState>): void;
664
558
  /**
665
- * Write aligned key-value pairs to the terminal.
666
- * @param entries Key-value pairs as a Record or array of tuples
667
- * @param options Optional: separator string, key color
559
+ * Select a specific property or computed value from the state.
560
+ * @param selector A function to project a slice of the state.
561
+ * @returns Observable of the selected value.
668
562
  */
669
- writeKeyValue(entries: Record<string, string> | [string, string][], options?: {
670
- separator?: string;
671
- keyColor?: CliForegroundColor;
672
- }): void;
563
+ select<K>(selector: (state: CliState) => K): Observable<K>;
673
564
  /**
674
- * Write items in a multi-column layout.
675
- * @param items The items to arrange in columns
676
- * @param options Optional: number of columns, padding between columns
565
+ * Subscribe to state changes.
566
+ * @param callback Callback function to handle state changes.
567
+ * @returns Subscription object to manage the subscription.
677
568
  */
678
- writeColumns(items: string[], options?: {
679
- columns?: number;
680
- padding?: number;
681
- }): void;
569
+ subscribe(callback: (state: CliState) => void): Subscription;
682
570
  /**
683
- * Write a clickable hyperlink using OSC 8 escape sequences.
684
- * In terminals that support OSC 8, the text is clickable. In others,
685
- * it falls back to displaying "text (url)".
686
- * @param text The visible link text
687
- * @param url The URL to link to
571
+ * Reset the state to its initial value.
688
572
  */
689
- writeLink(text: string, url: string): void;
573
+ reset(): void;
690
574
  /**
691
- * Write text inside a bordered box.
692
- * Useful for callouts, notices, and highlighted information.
693
- * @param content The content lines to display inside the box
694
- * @param options Optional: title, border color, padding
575
+ * Persist the state to storage.
695
576
  */
696
- writeBox(content: string | string[], options?: {
697
- title?: string;
698
- borderColor?: CliForegroundColor;
699
- padding?: number;
700
- }): void;
577
+ persist(): Promise<void>;
701
578
  /**
702
- * Write text with a specified indentation level.
703
- * Each level adds 2 spaces of indentation.
704
- * @param text The text to write
705
- * @param level The indentation level (default: 1)
579
+ * Initialize the state from storage.
706
580
  */
707
- writeIndented(text: string, level?: number): void;
581
+ initialize(): Promise<void>;
708
582
  }
709
583
 
710
- /**
711
- * Represents a clipboard for the CLI
712
- */
713
- interface ICliClipboard {
714
- /**
715
- * Write text to the clipboard
716
- * @param text The text to write to the clipboard
717
- * @returns void
718
- */
719
- write: (text: string) => Promise<void>;
720
- /**
721
- * Read text from the clipboard
722
- * @returns The text read from the clipboard
723
- */
724
- read: () => Promise<string>;
584
+ /** Ownership metadata attached to files, directories, or other resources */
585
+ interface ICliOwnership {
586
+ /** User ID of the owner */
587
+ uid: string;
588
+ /** Primary group name */
589
+ gid: string;
725
590
  }
591
+ /** Default file permissions: rw-r--r-- (644) */
592
+ declare const DEFAULT_FILE_PERMISSIONS = "rw-r--r--";
593
+ /** Default directory permissions: rwxr-xr-x (755) */
594
+ declare const DEFAULT_DIR_PERMISSIONS = "rwxr-xr-x";
726
595
 
727
596
  /**
728
597
  * Token for the CLI user session service.
@@ -764,37 +633,88 @@ declare const ICliPermissionService_TOKEN = "cli-permission-service";
764
633
  * Token for the file picker provider.
765
634
  */
766
635
  declare const ICliFilePickerProvider_TOKEN = "cli-file-picker-provider";
767
-
768
636
  /**
769
- * Options for readFile file picker.
637
+ * Token for the HTTP client service.
770
638
  */
771
- interface CliFilePickerOptions {
772
- /** Allow selecting multiple files. Default: false. */
773
- multiple?: boolean;
774
- /**
775
- * File type filter. Accepts MIME types (e.g. 'image/*') or extensions
776
- * (e.g. '.json,.txt'). Maps to the HTML accept attribute in browsers
777
- * and file filter in Electron.
778
- */
779
- accept?: string;
780
- /**
781
- * When true, pick a directory instead of files. Default: false.
782
- * Cannot be combined with `multiple` — if both are set, `directory` takes precedence
783
- * and a single directory is returned.
784
- */
785
- directory?: boolean;
786
- /**
787
- * How to read file content. Default: 'text'.
788
- * - 'text': Read as UTF-8 string (suitable for text files)
789
- * - 'arraybuffer': Read as ArrayBuffer (suitable for binary files like images)
790
- */
791
- readAs?: 'text' | 'arraybuffer';
792
- }
639
+ declare const ICliHttpClient_TOKEN = "cli-http-client";
793
640
  /**
794
- * Result returned by readFile for each selected file.
641
+ * Token for the user event service (lifecycle events).
795
642
  */
796
- interface CliFileResult {
797
- /** File name without path (e.g. 'config.json') */
643
+ declare const ICliUserEventService_TOKEN = "cli-user-event-service";
644
+ /**
645
+ * Token for the audit service (persistent event logging).
646
+ */
647
+ declare const ICliAuditService_TOKEN = "cli-audit-service";
648
+ /**
649
+ * Token for the authorization service ({@link ICliAuthorizationService}).
650
+ * Provided by an auth module (e.g. `@qodalis/cli-users`); absent otherwise.
651
+ */
652
+ declare const ICliAuthorizationService_TOKEN = "cli-authorization-service";
653
+ /**
654
+ * Token for the permission registry ({@link ICliPermissionRegistry}).
655
+ * Always provided by the CLI engine.
656
+ */
657
+ declare const ICliPermissionRegistry_TOKEN = "cli-permission-registry";
658
+ /**
659
+ * Token for the sessions service ({@link ICliSessionsService}).
660
+ */
661
+ declare const ICliSessionsService_TOKEN = "cli-sessions-service";
662
+ /**
663
+ * Token for the persistent key-value store used by modules (setup state, etc.).
664
+ */
665
+ declare const ICliKeyValueStore_TOKEN = "cli-key-value-store";
666
+
667
+ /**
668
+ * Abstract permission service for checking rwx permissions on resources.
669
+ * The default implementation lives in @qodalis/cli-users; consumers can
670
+ * replace it via the DI container.
671
+ */
672
+ interface ICliPermissionService {
673
+ /**
674
+ * Check whether a user may perform an action on a resource.
675
+ * @param user The acting user.
676
+ * @param action The requested operation.
677
+ * @param ownership Owner/group of the resource.
678
+ * @param permissions Permission string (e.g. "rwxr-xr-x").
679
+ * @returns true if allowed.
680
+ */
681
+ check(user: ICliUser, action: 'read' | 'write' | 'execute', ownership: ICliOwnership, permissions: string): boolean;
682
+ /** Parse octal (e.g. "755") to a permission string (e.g. "rwxr-xr-x"). */
683
+ parseOctal(octal: string): string;
684
+ /** Convert a permission string (e.g. "rwxr-xr-x") to octal (e.g. "755"). */
685
+ toOctal(permissions: string): string;
686
+ }
687
+
688
+ /**
689
+ * Options for readFile file picker.
690
+ */
691
+ interface CliFilePickerOptions {
692
+ /** Allow selecting multiple files. Default: false. */
693
+ multiple?: boolean;
694
+ /**
695
+ * File type filter. Accepts MIME types (e.g. 'image/*') or extensions
696
+ * (e.g. '.json,.txt'). Maps to the HTML accept attribute in browsers
697
+ * and file filter in Electron.
698
+ */
699
+ accept?: string;
700
+ /**
701
+ * When true, pick a directory instead of files. Default: false.
702
+ * Cannot be combined with `multiple` — if both are set, `directory` takes precedence
703
+ * and a single directory is returned.
704
+ */
705
+ directory?: boolean;
706
+ /**
707
+ * How to read file content. Default: 'text'.
708
+ * - 'text': Read as UTF-8 string (suitable for text files)
709
+ * - 'arraybuffer': Read as ArrayBuffer (suitable for binary files like images)
710
+ */
711
+ readAs?: 'text' | 'arraybuffer';
712
+ }
713
+ /**
714
+ * Result returned by readFile for each selected file.
715
+ */
716
+ interface CliFileResult {
717
+ /** File name without path (e.g. 'config.json') */
798
718
  name: string;
799
719
  /** Full file path. Available in Electron, undefined in browser. */
800
720
  path?: string;
@@ -989,69 +909,523 @@ interface ICliInputReader {
989
909
  }
990
910
 
991
911
  /**
992
- * Background service lifecycle type.
993
- * - `'daemon'` — Long-running service whose `onStart` blocks until the abort signal
994
- * fires. Suitable for event loops, polling, or WebSocket connections. Can be
995
- * stopped and restarted.
996
- * - `'job'` — Run-to-completion task whose `onStart` resolves when the work is done.
997
- * Automatically transitions to `'done'` status on success. Cannot be restarted.
912
+ * Context passed to completion providers describing the current input state.
998
913
  */
999
- type CliBackgroundServiceType = 'daemon' | 'job';
914
+ interface ICliCompletionContext {
915
+ /** Full current line text */
916
+ input: string;
917
+ /** Cursor position in the line */
918
+ cursor: number;
919
+ /** The token (word) being completed at the cursor position */
920
+ token: string;
921
+ /** Start index of the token in the input string */
922
+ tokenStart: number;
923
+ /** Index of the token among all tokens (0 = command, 1+ = arguments) */
924
+ tokenIndex: number;
925
+ /** All tokens parsed from the input */
926
+ tokens: string[];
927
+ }
1000
928
  /**
1001
- * Lifecycle status of a background service.
929
+ * Provides tab-completion candidates for CLI input.
930
+ * Plugins can register providers to extend completion (e.g. file paths, custom values).
1002
931
  */
1003
- type CliBackgroundServiceStatus = 'pending' | 'running' | 'stopped' | 'failed' | 'done';
932
+ interface ICliCompletionProvider {
933
+ /**
934
+ * Priority order. Lower values are checked first.
935
+ * Built-in defaults: 100 (commands), 200 (parameters).
936
+ * Plugins should use lower values (e.g. 50) to take precedence for specific commands.
937
+ */
938
+ priority: number;
939
+ /**
940
+ * Return completion candidates for the current input context.
941
+ * Return an empty array if this provider does not apply.
942
+ */
943
+ getCompletions(context: ICliCompletionContext): string[] | Promise<string[]>;
944
+ }
945
+
946
+ interface ICliProgressBar {
947
+ /**
948
+ * Indicates if the progress bar is running
949
+ */
950
+ isRunning: boolean;
951
+ /**
952
+ * Show the progress bar
953
+ */
954
+ show: (text?: string) => void;
955
+ /**
956
+ * Hide the progress bar
957
+ */
958
+ hide: () => void;
959
+ }
1004
960
  /**
1005
- * Where the service code is executing.
961
+ * Represents a spinner for the CLI
1006
962
  */
1007
- type CliServiceExecutionMode = 'main-thread' | 'worker';
963
+ interface ICliSpinner extends ICliProgressBar {
964
+ /**
965
+ * Set the text of the spinner
966
+ * @param text The text to set
967
+ */
968
+ setText: (text: string) => void;
969
+ }
970
+ type CliPercentageProgressBarUpdateValueOptions = {
971
+ /**
972
+ * The type of update to perform
973
+ * @default 'replace'
974
+ */
975
+ type?: 'replace' | 'increment';
976
+ };
1008
977
  /**
1009
- * An event emitted by a background service.
1010
- * Services control terminal notification visibility by setting `data.notify: true`.
978
+ * Represents a progress bar for the CLI
1011
979
  */
1012
- interface ICliServiceEvent {
1013
- /** Name of the service that emitted this event */
1014
- source: string;
1015
- /** Event type — services define their own, registry emits lifecycle events */
1016
- type: string;
1017
- /** Arbitrary payload */
1018
- data?: unknown;
1019
- /** Set automatically by the registry */
1020
- timestamp?: Date;
980
+ interface ICliPercentageProgressBar extends ICliProgressBar {
981
+ /**
982
+ * Update the progress of the progress bar.
983
+ * @param progress The progress value (0–100). Values outside this range are clamped.
984
+ * @param options Optional: update type ('replace' or 'increment')
985
+ */
986
+ update: (progress: number, options?: CliPercentageProgressBarUpdateValueOptions) => void;
987
+ /**
988
+ * Complete the progress bar
989
+ * @returns void
990
+ */
991
+ complete: () => void;
992
+ /**
993
+ * Set the text of the spinner
994
+ * @param text The text to set
995
+ */
996
+ setText: (text: string) => void;
997
+ }
998
+ type CliTextAnimatorOptions = {
999
+ /**
1000
+ * The speed of the animation
1001
+ * @default 100
1002
+ */
1003
+ speed?: number;
1004
+ /**
1005
+ * The text will be removed after typing
1006
+ */
1007
+ removeAfterTyping?: boolean;
1008
+ };
1009
+ interface ICliTextAnimator extends ICliProgressBar {
1010
+ /**
1011
+ * Show animated text in a typing and erasing effect.
1012
+ * @param text The text to animate
1013
+ * @param options Optional: speed (ms per character), removeAfterTyping
1014
+ * @returns A promise that resolves when the animation completes
1015
+ */
1016
+ showText: (text: string, options?: CliTextAnimatorOptions) => Promise<void>;
1021
1017
  }
1018
+
1022
1019
  /**
1023
- * Handler function for service events.
1024
- */
1025
- type CliServiceEventHandler = (event: ICliServiceEvent) => void;
1020
+ * Result of a guard check.
1021
+ *
1022
+ * - `true` or `undefined`: allow the command.
1023
+ * - `false`: block it with the generic message `<command>: permission denied`.
1024
+ * - a non-empty `string`: block it and print this message.
1025
+ * - `{ allowed, message }`: explicit form; `message` is used when `allowed` is false.
1026
+ */
1027
+ type CliCommandGuardResult = boolean | string | void | {
1028
+ allowed: boolean;
1029
+ message?: string;
1030
+ };
1026
1031
  /**
1027
- * Log severity level for background service messages.
1028
- * Shared by `ICliServiceLogEntry`, `ICliServiceContext.log()`,
1029
- * and `WorkerServiceContext.log()`.
1032
+ * A global command guard: a check that runs before **every** command, across
1033
+ * all processors and modules, and can block it.
1034
+ *
1035
+ * Use guards for cross-cutting policies such as authentication, permission
1036
+ * checks, feature flags, maintenance windows or rate limiting, instead of
1037
+ * repeating the check inside each processor.
1038
+ *
1039
+ * ## When guards run
1040
+ *
1041
+ * For each command the executor:
1042
+ * 1. parses the input and resolves the processor (and sub-processor);
1043
+ * 2. handles global parameters such as `--help` (help is never guarded);
1044
+ * 3. extracts the positional value into `command.value`;
1045
+ * 4. **runs the guards**;
1046
+ * 5. validates required values/parameters, runs processor hooks and finally
1047
+ * calls `processCommand`.
1048
+ *
1049
+ * Guards run in ascending `priority` (default `0`). For each guard the executor
1050
+ * calls `appliesTo` (if defined) and skips the guard when it returns `false`;
1051
+ * otherwise it awaits `canActivate`. The **first** guard that blocks stops the
1052
+ * chain: its message is written as an error and the command exits with code
1053
+ * `-1`. A guard that throws (in `appliesTo` or `canActivate`) also blocks the
1054
+ * command, so a broken guard fails closed.
1055
+ *
1056
+ * ## Access to the context
1057
+ *
1058
+ * Both `appliesTo` and `canActivate` receive the {@link ICliExecutionContext}
1059
+ * of the command about to run, so a guard can:
1060
+ * - resolve services with `context.services.get(...)` / `getRequired(...)`
1061
+ * (e.g. a policy, feature-flag or permission service registered by any
1062
+ * module) to decide **what** should be guarded and **whether** it may run;
1063
+ * - inspect `context.userSession` for the signed-in user and their groups;
1064
+ * - read the processor's `metadata` (for example `requireAuth` or
1065
+ * `requiredGroups`) and the parsed `command` (`command.value`, `command.args`);
1066
+ * - prompt the user via `context.reader` (e.g. a confirmation guard).
1067
+ *
1068
+ * Guards should not write to the terminal to report a denial; return a message
1069
+ * instead so the executor prints it consistently.
1070
+ *
1071
+ * ## Registering guards
1072
+ *
1073
+ * The preferred way is the module's `guards` array; the boot sequence
1074
+ * registers each entry as a multi-provider under {@link ICliCommandGuard_TOKEN}.
1075
+ * Several modules can contribute guards; all of them run.
1076
+ *
1077
+ * ```ts
1078
+ * const billingModule: ICliModule = {
1079
+ * apiVersion: 2,
1080
+ * name: 'billing',
1081
+ * processors: [new InvoiceProcessor()],
1082
+ * guards: [billingGuard],
1083
+ * };
1084
+ * ```
1085
+ *
1086
+ * Guards can also be added at runtime (e.g. from `onInit`):
1087
+ *
1088
+ * ```ts
1089
+ * context.services.set({ provide: ICliCommandGuard_TOKEN, useValue: myGuard, multi: true });
1090
+ * ```
1091
+ *
1092
+ * ## Example: policy resolved from a service
1093
+ *
1094
+ * ```ts
1095
+ * const featureFlagGuard: ICliCommandGuard = {
1096
+ * name: 'feature-flags',
1097
+ * // Only guard commands that are behind a flag; the flag list comes from a service
1098
+ * appliesTo: (processor, _command, context) =>
1099
+ * context.services.get<FeatureFlags>(FeatureFlags_TOKEN)?.isFlagged(processor.command) ?? false,
1100
+ * canActivate: async (_command, processor, context) => {
1101
+ * const flags = context.services.getRequired<FeatureFlags>(FeatureFlags_TOKEN);
1102
+ * return (await flags.isEnabled(processor.command, context.userSession?.user))
1103
+ * ? true
1104
+ * : `${processor.command}: not available for your account`;
1105
+ * },
1106
+ * };
1107
+ * ```
1108
+ *
1109
+ * ## Without a guard-providing module
1110
+ *
1111
+ * Guards are opt-in. When no guard is registered every command runs as before.
1112
+ * The engine registers three built-in guards that enforce the processor access
1113
+ * metadata (`requireAuth`, `requiredGroups`, `requiredPermissions`); they stay
1114
+ * inactive until an auth module (e.g. `@qodalis/cli-users`) registers an
1115
+ * `ICliAuthorizationService`, so without one that metadata is informational.
1116
+ */
1117
+ interface ICliCommandGuard {
1118
+ /** Unique name, shown in diagnostics when the guard throws. */
1119
+ name: string;
1120
+ /** Execution order; lower runs first. Defaults to `0`. */
1121
+ priority?: number;
1122
+ /**
1123
+ * Optional filter deciding whether this guard is relevant for the command.
1124
+ * Return `false` to skip the guard. When omitted the guard applies to
1125
+ * every command. May be async, and may use `context` to resolve services.
1126
+ */
1127
+ appliesTo?(processor: ICliCommandProcessor, command: CliProcessCommand, context: ICliExecutionContext): boolean | Promise<boolean>;
1128
+ /**
1129
+ * Decide whether the command may run. See {@link CliCommandGuardResult}
1130
+ * for the accepted return values.
1131
+ */
1132
+ canActivate(command: CliProcessCommand, processor: ICliCommandProcessor, context: ICliExecutionContext): CliCommandGuardResult | Promise<CliCommandGuardResult>;
1133
+ }
1134
+ /** DI token for {@link ICliCommandGuard}; register guards as multi-providers. */
1135
+ declare const ICliCommandGuard_TOKEN = "cli-command-guard";
1136
+
1137
+ /**
1138
+ * A permission an extension uses, declared so tools (`perms --list`,
1139
+ * `groupmod --grant` completion, docs) can show it. Declaring is optional:
1140
+ * any `resource:action` string can be granted and checked.
1030
1141
  */
1031
- type CliServiceLogLevel = 'info' | 'warn' | 'error';
1142
+ interface CliPermissionDefinition {
1143
+ /** Permission string, `resource:action` (e.g. `files:chown`). */
1144
+ name: string;
1145
+ /** Human readable description. */
1146
+ description?: string;
1147
+ /** Module that declared it (filled in at registration). */
1148
+ module?: string;
1149
+ }
1150
+ /** Registry of permissions declared by modules (`ICliModule.permissions`). */
1151
+ interface ICliPermissionRegistry {
1152
+ register(definitions: CliPermissionDefinition[], module?: string): void;
1153
+ list(): CliPermissionDefinition[];
1154
+ get(name: string): CliPermissionDefinition | undefined;
1155
+ }
1156
+ /** The identity checks are made against. */
1157
+ interface ICliPrincipal {
1158
+ /** User id */
1159
+ id: string;
1160
+ /** Login name (used as file owner) */
1161
+ name: string;
1162
+ /** Group (role) names */
1163
+ groups: string[];
1164
+ /** Effective permissions (union of the groups' permissions) */
1165
+ permissions: string[];
1166
+ }
1167
+ /** rwx-style protected resource (files, directories, or anything an extension owns). */
1168
+ interface ICliProtectedResource {
1169
+ ownership?: ICliOwnership;
1170
+ /** 9-char mode string, e.g. `rwxr-x---` */
1171
+ permissions?: string;
1172
+ }
1173
+ type CliAccessAction = 'read' | 'write' | 'execute';
1032
1174
  /**
1033
- * A single log entry from a background service.
1175
+ * Authorization service. Provided by an auth module (the users module provides
1176
+ * one in local and in server mode). Extensions should use the helpers
1177
+ * {@link cliAuthorize} / {@link cliCanAccess}, which allow everything when no
1178
+ * authorization service is registered.
1034
1179
  */
1035
- interface ICliServiceLogEntry {
1036
- timestamp: Date;
1037
- level: CliServiceLogLevel;
1038
- message: string;
1180
+ interface ICliAuthorizationService {
1181
+ /** The signed-in principal, or undefined when nobody is signed in. */
1182
+ getPrincipal(): ICliPrincipal | undefined;
1183
+ /** Does the current principal hold `permission` (wildcards honoured)? */
1184
+ can(permission: string): boolean;
1185
+ /**
1186
+ * rwx check on a resource for the current principal (owner / group / other).
1187
+ * Principals holding `*` or any of `bypassPermissions` are always allowed.
1188
+ * Resources without a mode or ownership are allowed.
1189
+ */
1190
+ canAccess(resource: ICliProtectedResource, action: CliAccessAction, bypassPermissions?: string[]): boolean;
1039
1191
  }
1192
+ /** Thrown when an operation is not permitted. The message is user facing. */
1193
+ declare class CliAccessDeniedError extends Error {
1194
+ readonly code = "EACCES";
1195
+ constructor(message?: string);
1196
+ }
1197
+ /** True for {@link CliAccessDeniedError} (also across bundle copies). */
1198
+ declare function isCliAccessDeniedError(error: unknown): error is CliAccessDeniedError;
1040
1199
  /**
1041
- * Per-service runtime context.
1042
- * Each background service receives its own context with an independent abort signal
1043
- * and scoped timers (not shared with the full-screen timer set).
1200
+ * Check a permission for the current principal. Returns `true` when no
1201
+ * authorization service is registered (no auth module loaded).
1044
1202
  */
1045
- interface ICliServiceContext {
1046
- /** AbortSignal scoped to this service — independent of Ctrl+C */
1047
- signal: AbortSignal;
1048
- /** Emit an event from this service */
1049
- emit(event: ICliServiceEvent): void;
1050
- /** Log a message to this service's private log buffer */
1051
- log(message: string, level?: CliServiceLogLevel): void;
1052
- /** Create an interval scoped to this service (auto-cleared on stop) */
1053
- createInterval(callback: () => void, ms: number): ICliManagedInterval;
1054
- /** Create a timeout scoped to this service (auto-cleared on stop) */
1203
+ declare function cliAuthorize(context: Pick<ICliExecutionContext, 'services'>, permission: string): boolean;
1204
+ /**
1205
+ * rwx check for the current principal. Returns `true` when no authorization
1206
+ * service is registered.
1207
+ */
1208
+ declare function cliCanAccess(context: Pick<ICliExecutionContext, 'services'>, resource: ICliProtectedResource, action: CliAccessAction, bypassPermissions?: string[]): boolean;
1209
+ /**
1210
+ * Reference rwx evaluation shared by implementations of
1211
+ * {@link ICliAuthorizationService.canAccess}.
1212
+ */
1213
+ declare function cliEvaluateAccess(principal: ICliPrincipal | undefined, resource: ICliProtectedResource, action: CliAccessAction, bypassPermissions?: string[]): boolean;
1214
+
1215
+ /** A sign-in session as listed by {@link ICliSessionsService}. Never carries a token. */
1216
+ interface ICliSessionInfo {
1217
+ id: string;
1218
+ userId: string;
1219
+ userName: string;
1220
+ createdAt: number;
1221
+ lastActivity: number;
1222
+ /** When the session expires if idle; undefined when there is no timeout */
1223
+ expiresAt?: number;
1224
+ /** True for the caller's own session */
1225
+ current?: boolean;
1226
+ metadata?: {
1227
+ userAgent?: string;
1228
+ remoteAddress?: string;
1229
+ [key: string]: unknown;
1230
+ };
1231
+ }
1232
+ /**
1233
+ * Lists and revokes sign-in sessions. Implemented locally (sessions of this
1234
+ * browser) and by servers (all sessions). Used by `sessions` / `session-kick`.
1235
+ */
1236
+ interface ICliSessionsService {
1237
+ /** All sessions visible to the caller */
1238
+ list(): Promise<ICliSessionInfo[]>;
1239
+ /** Revoke a session by id */
1240
+ revoke(sessionId: string, reason?: string): Promise<void>;
1241
+ /** Revoke every session of a user (optionally keeping one) */
1242
+ revokeUser(userId: string, options?: {
1243
+ exceptSessionId?: string;
1244
+ reason?: string;
1245
+ }): Promise<void>;
1246
+ }
1247
+
1248
+ /**
1249
+ * Represents a service that manages user sessions in the CLI
1250
+ */
1251
+ interface ICliUserSessionService {
1252
+ /** Gets the current user session as a reactive stream */
1253
+ getUserSession(): Observable<ICliUserSession | undefined>;
1254
+ /** Sets the current user session */
1255
+ setUserSession(session: ICliUserSession): Promise<void>;
1256
+ /** Clears the current session (logout) */
1257
+ clearSession(): Promise<void>;
1258
+ /** Persists the current session to storage */
1259
+ persistSession(): Promise<void>;
1260
+ /** Restores a session from storage (called on boot) */
1261
+ restoreSession(): Promise<ICliUserSession | undefined>;
1262
+ }
1263
+ /**
1264
+ * Represents a service that manages users in the CLI
1265
+ */
1266
+ interface ICliUsersStoreService {
1267
+ /** Gets users with optional filtering and pagination */
1268
+ getUsers(options?: {
1269
+ query?: string;
1270
+ skip?: number;
1271
+ take?: number;
1272
+ }): Observable<ICliUser[]>;
1273
+ /** Creates a new user */
1274
+ createUser(user: CliAddUser): Promise<ICliUser>;
1275
+ /** Gets a user by id, name, or email */
1276
+ getUser(id: string): Observable<ICliUser | undefined>;
1277
+ /** Updates an existing user */
1278
+ updateUser(id: string, updates: CliUpdateUser): Promise<ICliUser>;
1279
+ /** Deletes a user by id */
1280
+ deleteUser(id: string): Promise<void>;
1281
+ }
1282
+ /**
1283
+ * Represents a service that manages groups in the CLI
1284
+ */
1285
+ interface ICliGroupsStoreService {
1286
+ /** Gets all groups */
1287
+ getGroups(): Observable<ICliGroup[]>;
1288
+ /** Gets a group by id or name */
1289
+ getGroup(id: string): Observable<ICliGroup | undefined>;
1290
+ /** Creates a new group (role), optionally with permissions */
1291
+ createGroup(name: string, description?: string, permissions?: string[]): Promise<ICliGroup>;
1292
+ /** Updates a group's description and/or permissions (by id or name) */
1293
+ updateGroup(id: string, updates: {
1294
+ description?: string;
1295
+ permissions?: string[];
1296
+ }): Promise<ICliGroup>;
1297
+ /** Adds a user (id or name) to a group (id or name) */
1298
+ addMember(groupId: string, userId: string): Promise<void>;
1299
+ /** Removes a user (id or name) from a group (id or name) */
1300
+ removeMember(groupId: string, userId: string): Promise<void>;
1301
+ /** Deletes a group by id */
1302
+ deleteGroup(id: string): Promise<void>;
1303
+ /** Gets all users that belong to a group */
1304
+ getGroupMembers(groupId: string): Observable<ICliUser[]>;
1305
+ }
1306
+ /**
1307
+ * Represents a service that handles authentication in the CLI
1308
+ */
1309
+ interface ICliAuthService {
1310
+ /**
1311
+ * Authenticates a user and returns a session. `password` may be omitted
1312
+ * only when the implementation allows passwordless sign-in.
1313
+ */
1314
+ login(username: string, password?: string): Promise<ICliUserSession>;
1315
+ /** Ends the current session (the CLI is left signed out) */
1316
+ logout(): Promise<void>;
1317
+ /**
1318
+ * Switch to another user (`su`). Requires the target's password unless
1319
+ * the caller may skip it (implementation-defined, e.g. `users:update`).
1320
+ * Goes through the same lockout, events and audit as `login`.
1321
+ */
1322
+ switchUser?(username: string, password?: string): Promise<ICliUserSession>;
1323
+ /**
1324
+ * Sets or updates a user's password.
1325
+ * @param currentPassword The user's current password, when changing one's own
1326
+ * password. Server-backed implementations forward it so the server can
1327
+ * re-check it; local implementations may ignore it.
1328
+ */
1329
+ setPassword(userId: string, password: string, currentPassword?: string): Promise<void>;
1330
+ /** Verifies a password against stored credentials */
1331
+ verifyPassword(userId: string, password: string): Promise<boolean>;
1332
+ /** Hashes a password with a salt using Web Crypto SHA-256 */
1333
+ hashPassword(password: string, salt: string): Promise<string>;
1334
+ /** Validate password against policy. Returns error message or null if valid. */
1335
+ validatePassword?(password: string): string | null;
1336
+ }
1337
+
1338
+ /**
1339
+ * Represents a serialized snapshot of a CLI engine's state.
1340
+ * Used to duplicate terminal tabs with full state preservation.
1341
+ */
1342
+ interface CliEngineSnapshot {
1343
+ /** Schema version for forward compatibility */
1344
+ version: 1;
1345
+ /** Timestamp (ms since epoch) when the snapshot was taken */
1346
+ timestamp: number;
1347
+ /** Serialized terminal state */
1348
+ terminal: {
1349
+ /** Terminal buffer serialized via @xterm/addon-serialize */
1350
+ serializedBuffer: string;
1351
+ /** Terminal column count at time of snapshot */
1352
+ cols: number;
1353
+ /** Terminal row count at time of snapshot */
1354
+ rows: number;
1355
+ };
1356
+ /** Command history entries (most recent last) */
1357
+ commandHistory: string[];
1358
+ /** Named state store entries */
1359
+ stateStores: Array<{
1360
+ name: string;
1361
+ state: Record<string, any>;
1362
+ }>;
1363
+ }
1364
+
1365
+ /**
1366
+ * Background service lifecycle type.
1367
+ * - `'daemon'` — Long-running service whose `onStart` blocks until the abort signal
1368
+ * fires. Suitable for event loops, polling, or WebSocket connections. Can be
1369
+ * stopped and restarted.
1370
+ * - `'job'` — Run-to-completion task whose `onStart` resolves when the work is done.
1371
+ * Automatically transitions to `'done'` status on success. Cannot be restarted.
1372
+ */
1373
+ type CliBackgroundServiceType = 'daemon' | 'job';
1374
+ /**
1375
+ * Lifecycle status of a background service.
1376
+ */
1377
+ type CliBackgroundServiceStatus = 'pending' | 'running' | 'stopped' | 'failed' | 'done';
1378
+ /**
1379
+ * Where the service code is executing.
1380
+ */
1381
+ type CliServiceExecutionMode = 'main-thread' | 'worker';
1382
+ /**
1383
+ * An event emitted by a background service.
1384
+ * Services control terminal notification visibility by setting `data.notify: true`.
1385
+ */
1386
+ interface ICliServiceEvent {
1387
+ /** Name of the service that emitted this event */
1388
+ source: string;
1389
+ /** Event type — services define their own, registry emits lifecycle events */
1390
+ type: string;
1391
+ /** Arbitrary payload */
1392
+ data?: unknown;
1393
+ /** Set automatically by the registry */
1394
+ timestamp?: Date;
1395
+ }
1396
+ /**
1397
+ * Handler function for service events.
1398
+ */
1399
+ type CliServiceEventHandler = (event: ICliServiceEvent) => void;
1400
+ /**
1401
+ * Log severity level for background service messages.
1402
+ * Shared by `ICliServiceLogEntry`, `ICliServiceContext.log()`,
1403
+ * and `WorkerServiceContext.log()`.
1404
+ */
1405
+ type CliServiceLogLevel = 'info' | 'warn' | 'error';
1406
+ /**
1407
+ * A single log entry from a background service.
1408
+ */
1409
+ interface ICliServiceLogEntry {
1410
+ timestamp: Date;
1411
+ level: CliServiceLogLevel;
1412
+ message: string;
1413
+ }
1414
+ /**
1415
+ * Per-service runtime context.
1416
+ * Each background service receives its own context with an independent abort signal
1417
+ * and scoped timers (not shared with the full-screen timer set).
1418
+ */
1419
+ interface ICliServiceContext {
1420
+ /** AbortSignal scoped to this service — independent of Ctrl+C */
1421
+ signal: AbortSignal;
1422
+ /** Emit an event from this service */
1423
+ emit(event: ICliServiceEvent): void;
1424
+ /** Log a message to this service's private log buffer */
1425
+ log(message: string, level?: CliServiceLogLevel): void;
1426
+ /** Create an interval scoped to this service (auto-cleared on stop) */
1427
+ createInterval(callback: () => void, ms: number): ICliManagedInterval;
1428
+ /** Create a timeout scoped to this service (auto-cleared on stop) */
1055
1429
  createTimeout(callback: () => void, ms: number): ICliManagedTimer;
1056
1430
  /** Access the shared state store */
1057
1431
  state: ICliStateStore;
@@ -1077,49 +1451,637 @@ interface ICliBackgroundService {
1077
1451
  /** Called for graceful shutdown */
1078
1452
  onStop?(context: ICliServiceContext): Promise<void>;
1079
1453
  /**
1080
- * Called when the service throws an unrecoverable error.
1081
- * Use this hook for logging or cleanup; throwing from `onError` itself
1082
- * is safe — the registry catches it and marks the service as failed.
1454
+ * Called when the service throws an unrecoverable error.
1455
+ * Use this hook for logging or cleanup; throwing from `onError` itself
1456
+ * is safe — the registry catches it and marks the service as failed.
1457
+ */
1458
+ onError?(error: Error, context: ICliServiceContext): void;
1459
+ }
1460
+ /**
1461
+ * Read-only snapshot of a background service's current state.
1462
+ */
1463
+ interface ICliBackgroundServiceInfo {
1464
+ /** Process ID assigned by the process registry */
1465
+ pid?: number;
1466
+ name: string;
1467
+ description?: string;
1468
+ type: CliBackgroundServiceType;
1469
+ status: CliBackgroundServiceStatus;
1470
+ executionMode: CliServiceExecutionMode;
1471
+ startedAt?: Date;
1472
+ stoppedAt?: Date;
1473
+ error?: string;
1474
+ /** Uptime in milliseconds (only when running) */
1475
+ uptime?: number;
1476
+ }
1477
+ /**
1478
+ * Registry that manages background service lifecycle.
1479
+ * Exposed on `ICliExecutionContext.backgroundServices`.
1480
+ */
1481
+ interface ICliBackgroundServiceRegistry {
1482
+ register(service: ICliBackgroundService): void;
1483
+ start(name: string): Promise<void>;
1484
+ stop(name: string): Promise<void>;
1485
+ restart(name: string): Promise<void>;
1486
+ startJob(name: string, task: (context: ICliServiceContext) => Promise<void>, options?: {
1487
+ workerCompatible?: boolean;
1488
+ }): Promise<void>;
1489
+ getStatus(name: string): ICliBackgroundServiceInfo | undefined;
1490
+ list(): ICliBackgroundServiceInfo[];
1491
+ getLogs(name: string, limit?: number): ICliServiceLogEntry[];
1492
+ on(handler: CliServiceEventHandler, filter?: {
1493
+ source?: string;
1494
+ type?: string;
1495
+ }): () => void;
1496
+ destroyAll(): Promise<void>;
1497
+ }
1498
+
1499
+ /**
1500
+ * Messages sent from the main thread to a background service Worker.
1501
+ */
1502
+ type CliWorkerInboundMessage = {
1503
+ type: 'start';
1504
+ config?: Record<string, unknown>;
1505
+ } | {
1506
+ type: 'stop';
1507
+ } | {
1508
+ type: 'abort';
1509
+ };
1510
+ /**
1511
+ * Messages sent from a background service Worker to the main thread.
1512
+ */
1513
+ type CliWorkerOutboundMessage = {
1514
+ type: 'log';
1515
+ level: 'info' | 'warn' | 'error';
1516
+ message: string;
1517
+ } | {
1518
+ type: 'event';
1519
+ event: Omit<ICliServiceEvent, 'timestamp'>;
1520
+ } | {
1521
+ type: 'state-update';
1522
+ patch: Record<string, unknown>;
1523
+ } | {
1524
+ type: 'status';
1525
+ status: CliBackgroundServiceStatus;
1526
+ } | {
1527
+ type: 'error';
1528
+ message: string;
1529
+ stack?: string;
1530
+ };
1531
+
1532
+ /**
1533
+ * Represents an entry in a file listing.
1534
+ */
1535
+ interface ICliFileEntry {
1536
+ name: string;
1537
+ type: 'file' | 'directory';
1538
+ size: number;
1539
+ modified: string;
1540
+ }
1541
+
1542
+ /**
1543
+ * Abstraction for file transfer operations.
1544
+ * Default implementation uses browser-native APIs (file picker, download dialog).
1545
+ * @qodalis/cli-files overrides with virtual FS backed by IndexedDB.
1546
+ */
1547
+ interface ICliFileTransferService {
1548
+ readFile(path: string): Promise<string | null>;
1549
+ writeFile(path: string, content: string): Promise<void>;
1550
+ listFiles(path: string): Promise<ICliFileEntry[]>;
1551
+ exists(path: string): Promise<boolean>;
1552
+ downloadToBrowser(filename: string, content: string | Blob): void;
1553
+ uploadFromBrowser(accept?: string): Promise<{
1554
+ name: string;
1555
+ content: string;
1556
+ } | null>;
1557
+ }
1558
+
1559
+ interface ICliDragDropService {
1560
+ /** Emits an array of dropped File objects each time files are dropped onto the terminal */
1561
+ readonly onFileDrop: Observable<File[]>;
1562
+ }
1563
+
1564
+ /**
1565
+ * Opaque state value carried between lines during syntax highlighting.
1566
+ * Encodes context such as "inside a multi-line string" or "inside a block comment"
1567
+ * so the highlighter can resume correctly on the next line.
1568
+ * `0` typically represents the default/root state.
1569
+ */
1570
+ type LineState = number;
1571
+ interface ISyntaxHighlightRule {
1572
+ pattern: RegExp;
1573
+ tokenType: string;
1574
+ captureGroup?: number;
1575
+ }
1576
+ interface ISyntaxHighlighter {
1577
+ id: string;
1578
+ /** Human-readable language name (e.g. 'TypeScript'). Falls back to `id` when absent. */
1579
+ name?: string;
1580
+ extensions: string[];
1581
+ initialState: LineState;
1582
+ getRules(state: LineState): ISyntaxHighlightRule[];
1583
+ getNextLineState(line: string, currentState: LineState): LineState;
1584
+ }
1585
+ interface ISyntaxTheme {
1586
+ name: string;
1587
+ colors: Record<string, string>;
1588
+ }
1589
+
1590
+ /**
1591
+ * Represents a package source for the CLI package manager
1592
+ */
1593
+ interface CliPackageSource {
1594
+ /**
1595
+ * The name of the source, e.g. 'local', 'unpkg', 'jsdelivr'
1596
+ */
1597
+ name: string;
1598
+ /**
1599
+ * The base URL for fetching package files, e.g. 'http://localhost:3000/'
1600
+ */
1601
+ url: string;
1602
+ /**
1603
+ * The kind of source.
1604
+ * - 'registry': npm-compatible registry with search API (e.g. npmjs.org, Verdaccio)
1605
+ * - 'file': static file server, packages are discovered by probing known paths
1606
+ * @default 'file'
1607
+ */
1608
+ kind?: 'registry' | 'file';
1609
+ }
1610
+ /**
1611
+ * Represents a log level for the CLI
1612
+ */
1613
+ declare enum CliLogLevel {
1614
+ None = 0,
1615
+ DEBUG = 1,
1616
+ LOG = 2,
1617
+ INFO = 3,
1618
+ WARN = 4,
1619
+ ERROR = 5
1620
+ }
1621
+ /**
1622
+ * Options for the CLI
1623
+ */
1624
+ type CliOptions = Record<string, any> & {
1625
+ /**
1626
+ * Custom terminal options
1627
+ */
1628
+ terminalOptions?: ITerminalOptions & ITerminalInitOnlyOptions;
1629
+ /**
1630
+ * The minimum log level to display
1631
+ */
1632
+ logLevel?: CliLogLevel;
1633
+ /**
1634
+ * Custom package sources for the package manager.
1635
+ * Built-in sources (unpkg, jsdelivr) are always available.
1636
+ */
1637
+ packageSources?: {
1638
+ /**
1639
+ * Name of the primary source. Defaults to 'unpkg'.
1640
+ */
1641
+ primary?: string;
1642
+ /**
1643
+ * Additional custom sources
1644
+ */
1645
+ sources?: CliPackageSource[];
1646
+ };
1647
+ /**
1648
+ * Remote CLI servers to connect to.
1649
+ * Commands from each server are discovered and registered as proxy processors.
1650
+ */
1651
+ servers?: CliServerConfig[];
1652
+ /**
1653
+ * Whether the `eval` / `js` / `calc` command is enabled.
1654
+ * Disabled by default because it runs arbitrary JavaScript via `eval()`
1655
+ * in the host page context — a security risk in multi-tenant or
1656
+ * user-facing deployments.
1657
+ * @default false
1658
+ */
1659
+ allowEval?: boolean;
1660
+ };
1661
+ /**
1662
+ * Represents a package that can be installed
1663
+ */
1664
+ interface Package {
1665
+ /**
1666
+ * The name of the package
1667
+ */
1668
+ name: string;
1669
+ /**
1670
+ * The global name used to access the package
1671
+ */
1672
+ globalName?: string;
1673
+ /**
1674
+ * The version of the package
1675
+ */
1676
+ version: string;
1677
+ /**
1678
+ * The unpkg url to the package
1679
+ */
1680
+ url: string;
1681
+ /**
1682
+ * The dependencies for the module
1683
+ */
1684
+ dependencies?: Package[];
1685
+ }
1686
+ /**
1687
+ * Represents command processor metadata
1688
+ */
1689
+ type CliProcessorMetadata = Record<string, any> & {
1690
+ /**
1691
+ * If true, the processor is sealed and cannot be replaced or removed.
1692
+ * Sealed processors can still be extended (wrapped) by processors with `extendsProcessor = true`.
1693
+ */
1694
+ sealed?: boolean;
1695
+ /**
1696
+ * If true, the processor requires the server to be running
1697
+ */
1698
+ requireServer?: boolean;
1699
+ /**
1700
+ * The module the processor belongs to
1701
+ */
1702
+ module?: string;
1703
+ /**
1704
+ * An icon to display for the processor
1705
+ */
1706
+ icon?: CliIcon | string;
1707
+ /**
1708
+ * The minimum required version of @qodalis/cli-core for this processor.
1709
+ * If the installed core version is lower, the processor will be skipped during boot.
1710
+ */
1711
+ requiredCoreVersion?: string;
1712
+ /**
1713
+ * The minimum required version of @qodalis/cli for this processor.
1714
+ * If the installed CLI version is lower, the processor will be skipped during boot.
1715
+ */
1716
+ requiredCliVersion?: string;
1717
+ /**
1718
+ * If true, the command requires a signed-in user. Enforced by a
1719
+ * core guard that activates when an auth module registers an
1720
+ * `ICliAuthorizationService` (e.g. `@qodalis/cli-users`); without one the
1721
+ * flag is informational.
1722
+ */
1723
+ requireAuth?: boolean;
1724
+ /**
1725
+ * The signed-in user must belong to at least one of these groups
1726
+ * (e.g. `['admin']`). Implies `requireAuth`. Enforced like `requireAuth`.
1727
+ */
1728
+ requiredGroups?: string[];
1729
+ /**
1730
+ * The signed-in user must hold **all** of these permissions
1731
+ * (e.g. `['users:create']`), granted through their groups (roles).
1732
+ * Implies `requireAuth`. Enforced like `requireAuth`.
1733
+ */
1734
+ requiredPermissions?: string[];
1735
+ /**
1736
+ * If true, the processor is hidden from the help command listing.
1737
+ * The command still works when typed directly and `help <command>` still shows its details.
1738
+ */
1739
+ hidden?: boolean;
1740
+ };
1741
+ /**
1742
+ * Represents a state configuration for the CLI processor
1743
+ */
1744
+ type CliStateConfiguration = {
1745
+ /**
1746
+ * The initial state for the processor
1747
+ */
1748
+ initialState: Record<string, any>;
1749
+ /**
1750
+ * The store identifier for the processor, if any
1751
+ * @remarks If the store identifier is not set, the processor command name is used
1752
+ */
1753
+ storeName?: string;
1754
+ };
1755
+ type CliState = Record<string, any>;
1756
+ /**
1757
+ * Position of the CLI panel relative to the viewport edge.
1758
+ * - `'bottom'` — anchored to the bottom (default)
1759
+ * - `'top'` — anchored to the top
1760
+ * - `'left'` — anchored to the left (vertical layout)
1761
+ * - `'right'` — anchored to the right (vertical layout)
1762
+ */
1763
+ type CliPanelPosition = 'bottom' | 'top' | 'left' | 'right';
1764
+ /**
1765
+ * Alignment of the hidden-mode tab along its viewport edge.
1766
+ * - `'start'` — left for horizontal panels, top for vertical panels
1767
+ * - `'center'` — centered along the edge (default)
1768
+ * - `'end'` — right for horizontal panels, bottom for vertical panels
1769
+ */
1770
+ type CliPanelHideAlignment = 'start' | 'center' | 'end';
1771
+ /**
1772
+ * Configuration for the CLI panel component.
1773
+ */
1774
+ interface CliPanelConfig {
1775
+ /**
1776
+ * Whether the CLI should be collapsed by default.
1777
+ * @default true
1778
+ */
1779
+ isCollapsed?: boolean;
1780
+ /**
1781
+ * Whether the panel starts hidden (showing only the small tab/arrow).
1782
+ * @default false
1783
+ */
1784
+ isHidden?: boolean;
1785
+ /**
1786
+ * Position of the panel relative to the viewport.
1787
+ * @default 'bottom'
1788
+ */
1789
+ position?: CliPanelPosition;
1790
+ /**
1791
+ * Whether the close button is shown.
1792
+ * @default true
1793
+ */
1794
+ closable?: boolean;
1795
+ /**
1796
+ * Whether the panel can be resized by dragging.
1797
+ * @default true
1798
+ */
1799
+ resizable?: boolean;
1800
+ /**
1801
+ * Whether the hide button is shown. When hidden, the panel collapses
1802
+ * to a small tab at the viewport edge.
1803
+ * @default true
1804
+ */
1805
+ hideable?: boolean;
1806
+ /**
1807
+ * Alignment of the hide tab along the panel's viewport edge.
1808
+ * For bottom/top: 'start' = left, 'center', 'end' = right.
1809
+ * For left/right: 'start' = top, 'center', 'end' = bottom.
1810
+ * @default 'center'
1811
+ */
1812
+ hideAlignment?: CliPanelHideAlignment;
1813
+ /**
1814
+ * When true, the panel's chrome (header, border, background) automatically
1815
+ * syncs its colors with the active terminal theme. CSS custom properties
1816
+ * are derived from the xterm theme and applied as inline styles.
1817
+ * @default false
1818
+ */
1819
+ syncTheme?: boolean;
1820
+ }
1821
+
1822
+ /**
1823
+ * Framework-agnostic interface for programmatic control of the CLI panel.
1824
+ *
1825
+ * CliEngine is referenced as a generic to avoid a circular dependency
1826
+ * between @qodalis/cli-core and @qodalis/cli. Framework implementations
1827
+ * resolve this to the concrete CliEngine class.
1828
+ */
1829
+ interface ICliPanelRef<TEngine = unknown> {
1830
+ /** Expand the panel (sets collapsed=false). Initializes the first tab on first call. */
1831
+ open(): void;
1832
+ /** Collapse the panel body (sets collapsed=true). */
1833
+ collapse(): void;
1834
+ toggleCollapse(): void;
1835
+ /** Hide the panel to a small viewport-edge tab (reversible via unhide). */
1836
+ hide(): void;
1837
+ unhide(): void;
1838
+ toggleHide(): void;
1839
+ /** Destroy the panel (removes from DOM). Irreversible. */
1840
+ close(): void;
1841
+ maximize(): void;
1842
+ restore(): void;
1843
+ toggleMaximize(): void;
1844
+ /**
1845
+ * Set panel dimensions. Only the dimension relevant to the current
1846
+ * position is applied immediately (height for top/bottom, width for
1847
+ * left/right). The other dimension is stored for when the position changes.
1848
+ */
1849
+ resize(dimensions: {
1850
+ height?: number;
1851
+ width?: number;
1852
+ }): void;
1853
+ setPosition(position: CliPanelPosition): void;
1854
+ /** Create a new tab. Returns the new tab's ID. Title defaults to "Terminal {id}". */
1855
+ addTab(title?: string): number;
1856
+ /** Close a tab by ID. No-op if the ID does not exist. */
1857
+ closeTab(tabId: number): void;
1858
+ /** Activate a tab by ID. No-op if the ID does not exist. */
1859
+ selectTab(tabId: number): void;
1860
+ /** Programmatically rename a tab. No-op if the ID does not exist. */
1861
+ renameTab(tabId: number, title: string): void;
1862
+ /**
1863
+ * Split the active pane (or a pane within the specified tab) to create
1864
+ * a new pane to the right. Returns the new pane's ID.
1865
+ */
1866
+ splitPane(tabId?: number): number;
1867
+ /**
1868
+ * Close a pane by ID. Searches all tabs for the pane.
1869
+ * No-op if the ID does not exist.
1870
+ */
1871
+ closePane(paneId: number): void;
1872
+ /** Get the engine for a specific pane, or the active pane if omitted. */
1873
+ getEngine(paneId?: number): TEngine | undefined;
1874
+ getState(): CliPanelState;
1875
+ }
1876
+ interface CliPanelState {
1877
+ collapsed: boolean;
1878
+ hidden: boolean;
1879
+ maximized: boolean;
1880
+ position: CliPanelPosition;
1881
+ height: number;
1882
+ width: number;
1883
+ activeTabId: number;
1884
+ activePaneId: number;
1885
+ tabs: CliPanelTabState[];
1886
+ }
1887
+ interface CliPanelTabState {
1888
+ id: number;
1889
+ title: string;
1890
+ panes: CliPanelPaneState[];
1891
+ }
1892
+ interface CliPanelPaneState {
1893
+ id: number;
1894
+ widthPercent: number;
1895
+ }
1896
+
1897
+ /**
1898
+ * Options for HTTP client requests.
1899
+ * Extends standard RequestInit with convenience fields.
1900
+ */
1901
+ interface CliHttpRequestOptions extends Omit<RequestInit, 'method' | 'body'> {
1902
+ /**
1903
+ * Request timeout in milliseconds. When set, the request is automatically
1904
+ * aborted after this duration. Combines with any existing abort signal.
1083
1905
  */
1084
- onError?(error: Error, context: ICliServiceContext): void;
1906
+ timeout?: number;
1085
1907
  }
1086
1908
  /**
1087
- * Read-only snapshot of a background service's current state.
1909
+ * HTTP client for making requests from command processors.
1910
+ *
1911
+ * Automatically merges the command's abort signal so that requests are
1912
+ * cancelled when the user presses Ctrl+C. Provides typed convenience
1913
+ * methods for common HTTP verbs as well as a raw `fetch()` for cases
1914
+ * that need full Response access (streaming, progress, etc.).
1915
+ *
1916
+ * Available on the execution context as `context.http`.
1917
+ *
1918
+ * @example
1919
+ * ```ts
1920
+ * // Typed JSON response
1921
+ * const users = await context.http.get<User[]>('https://api.example.com/users');
1922
+ *
1923
+ * // POST with body
1924
+ * await context.http.post('https://api.example.com/users', { name: 'John' });
1925
+ *
1926
+ * // Raw fetch for streaming
1927
+ * const response = await context.http.fetch('https://example.com/file.zip');
1928
+ * const reader = response.body?.getReader();
1929
+ * ```
1088
1930
  */
1089
- interface ICliBackgroundServiceInfo {
1090
- /** Process ID assigned by the process registry */
1091
- pid?: number;
1092
- name: string;
1093
- description?: string;
1094
- type: CliBackgroundServiceType;
1095
- status: CliBackgroundServiceStatus;
1096
- executionMode: CliServiceExecutionMode;
1097
- startedAt?: Date;
1098
- stoppedAt?: Date;
1099
- error?: string;
1100
- /** Uptime in milliseconds (only when running) */
1101
- uptime?: number;
1931
+ interface ICliHttpClient {
1932
+ /**
1933
+ * Make a raw HTTP request. Returns the full Response object for cases
1934
+ * that need streaming, headers, or status inspection.
1935
+ * The command's abort signal is automatically merged.
1936
+ */
1937
+ fetch(url: string, init?: RequestInit): Promise<Response>;
1938
+ /**
1939
+ * HTTP GET request. Returns the parsed JSON response body.
1940
+ */
1941
+ get<T = any>(url: string, options?: CliHttpRequestOptions): Promise<T>;
1942
+ /**
1943
+ * HTTP POST request. Automatically serializes the body as JSON
1944
+ * (unless body is FormData, Blob, string, or ArrayBuffer).
1945
+ * Returns the parsed JSON response body.
1946
+ */
1947
+ post<T = any>(url: string, body?: any, options?: CliHttpRequestOptions): Promise<T>;
1948
+ /**
1949
+ * HTTP PUT request. Automatically serializes the body as JSON.
1950
+ * Returns the parsed JSON response body.
1951
+ */
1952
+ put<T = any>(url: string, body?: any, options?: CliHttpRequestOptions): Promise<T>;
1953
+ /**
1954
+ * HTTP PATCH request. Automatically serializes the body as JSON.
1955
+ * Returns the parsed JSON response body.
1956
+ */
1957
+ patch<T = any>(url: string, body?: any, options?: CliHttpRequestOptions): Promise<T>;
1958
+ /**
1959
+ * HTTP DELETE request. Returns the parsed JSON response body (if any).
1960
+ */
1961
+ delete<T = any>(url: string, options?: CliHttpRequestOptions): Promise<T>;
1962
+ /**
1963
+ * HTTP HEAD request. Returns the full Response (no body parsing).
1964
+ */
1965
+ head(url: string, options?: CliHttpRequestOptions): Promise<Response>;
1966
+ /**
1967
+ * HTTP OPTIONS request. Returns the parsed JSON response body.
1968
+ */
1969
+ options<T = any>(url: string, options?: CliHttpRequestOptions): Promise<T>;
1102
1970
  }
1103
1971
  /**
1104
- * Registry that manages background service lifecycle.
1105
- * Exposed on `ICliExecutionContext.backgroundServices`.
1972
+ * Error thrown by `ICliHttpClient` JSON helpers (`get`, `post`, `put`,
1973
+ * `patch`, `delete`, `options`) when the server responds with a non-2xx status.
1974
+ *
1975
+ * Exposes the HTTP status so callers can react to specific responses
1976
+ * (for example, clearing a stored session token on `401`).
1977
+ *
1978
+ * @example
1979
+ * ```ts
1980
+ * try {
1981
+ * await context.http.get(url);
1982
+ * } catch (e) {
1983
+ * if (e instanceof CliHttpError && e.status === 401) {
1984
+ * // token expired or revoked
1985
+ * }
1986
+ * }
1987
+ * ```
1106
1988
  */
1107
- interface ICliBackgroundServiceRegistry {
1108
- register(service: ICliBackgroundService): void;
1109
- start(name: string): Promise<void>;
1110
- stop(name: string): Promise<void>;
1111
- restart(name: string): Promise<void>;
1112
- startJob(name: string, task: (context: ICliServiceContext) => Promise<void>, options?: {
1113
- workerCompatible?: boolean;
1114
- }): Promise<void>;
1115
- getStatus(name: string): ICliBackgroundServiceInfo | undefined;
1116
- list(): ICliBackgroundServiceInfo[];
1117
- getLogs(name: string, limit?: number): ICliServiceLogEntry[];
1118
- on(handler: CliServiceEventHandler, filter?: {
1119
- source?: string;
1120
- type?: string;
1121
- }): () => void;
1122
- destroyAll(): Promise<void>;
1989
+ declare class CliHttpError extends Error {
1990
+ /** HTTP status code of the failed response */
1991
+ readonly status: number;
1992
+ /** HTTP status text of the failed response */
1993
+ readonly statusText: string;
1994
+ /** Parsed JSON error body, when the server returned one */
1995
+ readonly body?: unknown | undefined;
1996
+ constructor(message: string,
1997
+ /** HTTP status code of the failed response */
1998
+ status: number,
1999
+ /** HTTP status text of the failed response */
2000
+ statusText?: string,
2001
+ /** Parsed JSON error body, when the server returned one */
2002
+ body?: unknown | undefined);
2003
+ }
2004
+
2005
+ type CliNotificationLevel = 'info' | 'success' | 'warn' | 'error';
2006
+ interface CliNotification {
2007
+ level: CliNotificationLevel;
2008
+ message: string;
2009
+ timestamp: number;
2010
+ }
2011
+
2012
+ interface ICliNotifier {
2013
+ /** Set an info-level notification (progress, status updates). */
2014
+ info(message: string): void;
2015
+ /** Set a success-level notification. */
2016
+ success(message: string): void;
2017
+ /** Set a warn-level notification. */
2018
+ warn(message: string): void;
2019
+ /** Set an error-level notification. */
2020
+ error(message: string): void;
2021
+ /** Clear the current notification. */
2022
+ clear(): void;
2023
+ /** The current notification, or undefined if cleared. */
2024
+ readonly current: CliNotification | undefined;
2025
+ /** Observable that emits on every change (set or clear). */
2026
+ readonly change$: Observable<CliNotification | undefined>;
2027
+ }
2028
+
2029
+ /** Represents a user lifecycle event */
2030
+ interface ICliUserEvent {
2031
+ /** The type of event that occurred */
2032
+ type: 'login' | 'logout' | 'login_failed' | 'user_created' | 'user_deleted' | 'user_updated' | 'password_changed' | 'user_switched' | 'user_disabled' | 'user_enabled' | 'group_created' | 'group_updated' | 'group_deleted' | 'group_member_added' | 'group_member_removed' | 'session_revoked' | 'audit_cleared';
2033
+ /** The user involved in the event (for group events: the acting user) */
2034
+ user: ICliUser;
2035
+ /** The acting user's name, when different from `user` (e.g. an admin) */
2036
+ actor?: string;
2037
+ /** When the event occurred */
2038
+ timestamp: number;
2039
+ /** Optional additional details */
2040
+ details?: string;
2041
+ /** `user_updated`: the user's name before a rename (set only when the name changed) */
2042
+ previousName?: string;
2043
+ }
2044
+ /** Service that emits user lifecycle events for plugins to subscribe to */
2045
+ interface ICliUserEventService {
2046
+ /** Observable stream of user events */
2047
+ readonly events$: Observable<ICliUserEvent>;
2048
+ /** Emit a user event */
2049
+ emit(event: ICliUserEvent): void;
2050
+ }
2051
+
2052
+ /** A single audit log entry */
2053
+ interface ICliAuditEntry {
2054
+ /** Entry id (server-assigned in server mode) */
2055
+ id?: string;
2056
+ /** When the event occurred */
2057
+ timestamp: number;
2058
+ /** Event type (e.g., 'login', 'logout', 'login_failed') */
2059
+ event: string;
2060
+ /** Username involved */
2061
+ username: string;
2062
+ /** Optional additional details */
2063
+ details?: string;
2064
+ /** Id of the user the entry is about, when known */
2065
+ userId?: string;
2066
+ /** The user who performed the action, when different from `username` */
2067
+ actor?: string;
2068
+ /** Id of the acting user, when different from the subject */
2069
+ actorId?: string;
2070
+ /** Extra data (e.g. remote address) */
2071
+ metadata?: Record<string, unknown>;
2072
+ }
2073
+ /** Service for persistent audit logging */
2074
+ interface ICliAuditService {
2075
+ /** Log an audit entry (timestamp is added automatically) */
2076
+ log(entry: Omit<ICliAuditEntry, 'timestamp'>): Promise<void>;
2077
+ /** Retrieve audit entries with optional filtering */
2078
+ getEntries(options?: {
2079
+ limit?: number;
2080
+ event?: string;
2081
+ username?: string;
2082
+ }): Promise<ICliAuditEntry[]>;
2083
+ /** Clear all audit entries */
2084
+ clear(): Promise<void>;
1123
2085
  }
1124
2086
 
1125
2087
  /**
@@ -1221,19 +2183,20 @@ interface ICliExecutionContext {
1221
2183
  */
1222
2184
  submitCommand(command: string): Promise<void>;
1223
2185
  /**
1224
- * Set a custom status text displayed in the panel header bar.
1225
- * Use to show progress or state (e.g. "Downloading 45%...", "Connected to server").
2186
+ * Notifier service for displaying status notifications in the panel header bar.
2187
+ * Supports severity levels: info, success, warn, error.
1226
2188
  * Cleared automatically when the command finishes.
2189
+ *
2190
+ * @example
2191
+ * ```ts
2192
+ * context.notifier.info('Downloading 45%...');
2193
+ * context.notifier.success('Connected to server');
2194
+ * context.notifier.warn('Rate limited, retrying...');
2195
+ * context.notifier.error('Connection failed');
2196
+ * context.notifier.clear();
2197
+ * ```
1227
2198
  */
1228
- setStatusText(text: string): void;
1229
- /**
1230
- * Clear the custom status text from the panel header bar.
1231
- */
1232
- clearStatusText(): void;
1233
- /**
1234
- * Get the current custom status text, if any.
1235
- */
1236
- getStatusText(): string | undefined;
2199
+ notifier: ICliNotifier;
1237
2200
  /**
1238
2201
  * Set the current processor as the context processor, i.e. the processor that will handle the command
1239
2202
  * @param processor The processor to set
@@ -1337,6 +2300,16 @@ interface ICliExecutionContext {
1337
2300
  * are scoped to this session and destroyed when the engine shuts down.
1338
2301
  */
1339
2302
  backgroundServices: ICliBackgroundServiceRegistry;
2303
+ /**
2304
+ * HTTP client for making requests. Automatically wires the command's
2305
+ * abort signal so requests are cancelled on Ctrl+C.
2306
+ *
2307
+ * @example
2308
+ * ```ts
2309
+ * const data = await context.http.get<MyData>('https://api.example.com/data');
2310
+ * ```
2311
+ */
2312
+ http: ICliHttpClient;
1340
2313
  }
1341
2314
 
1342
2315
  interface ICliProcessorHook {
@@ -1344,512 +2317,255 @@ interface ICliProcessorHook {
1344
2317
  execute: (context: ICliExecutionContext) => Promise<void>;
1345
2318
  }
1346
2319
 
1347
- interface ICliCommandAuthor {
1348
- /**
1349
- * The name of the author
1350
- */
1351
- name: string;
1352
- /**
1353
- * The email of the author
1354
- */
1355
- email: string;
1356
- }
1357
- /**
1358
- * Represents a command parameter
1359
- */
1360
- interface ICliCommandParameterDescriptor {
1361
- /**
1362
- * The name of the parameter
1363
- */
1364
- name: string;
1365
- /**
1366
- * Aliases for the parameter
1367
- */
1368
- aliases?: string[];
1369
- /**
1370
- * A description of the parameter
1371
- */
1372
- description: string;
1373
- /**
1374
- * If true, the parameter is required
1375
- */
1376
- required: boolean;
1377
- /**
1378
- * The type of the parameter
1379
- */
1380
- type: 'string' | 'number' | 'boolean' | 'array' | 'object' | string;
1381
- /**
1382
- * The default value of the parameter
1383
- */
1384
- defaultValue?: any;
1385
- /**
1386
- * A validator function that validates the value of the parameter
1387
- * @param value The value to validate
1388
- * @returns An object with a valid property that indicates if the value is valid and an optional message property that contains a message to display if the value is not valid
1389
- */
1390
- validator?: (value: any) => {
1391
- /**
1392
- * Indicates if the value is valid
1393
- */
1394
- valid: boolean;
1395
- /**
1396
- * An optional message to display if the value is not valid
1397
- */
1398
- message?: string;
1399
- };
1400
- }
1401
- /**
1402
- * Represents a configuration option that a command processor exposes
1403
- * for management via the `configure` command.
1404
- */
1405
- interface ICliConfigurationOption {
1406
- /**
1407
- * The key used to store and retrieve this option, e.g. 'maxItems'
1408
- */
1409
- key: string;
1410
- /**
1411
- * Human-readable label shown in the interactive menu
1412
- */
1413
- label: string;
1414
- /**
1415
- * Description of what this option controls
1416
- */
1417
- description: string;
1418
- /**
1419
- * The data type of the option
1420
- */
1421
- type: 'string' | 'number' | 'boolean' | 'select';
1422
- /**
1423
- * The default value when no configuration has been set
1424
- */
1425
- defaultValue: any;
1426
- /**
1427
- * Available choices for 'select' type options
1428
- */
1429
- options?: {
1430
- label: string;
1431
- value: any;
1432
- }[];
1433
- /**
1434
- * Optional validator function
1435
- */
1436
- validator?: (value: any) => {
1437
- valid: boolean;
1438
- message?: string;
1439
- };
1440
- /**
1441
- * Override the category grouping (defaults to the processor command name)
1442
- */
1443
- category?: string;
1444
- }
1445
- /**
1446
- * Represents a command processor
1447
- */
1448
- interface ICliCommandProcessor {
1449
- /**
1450
- * The command that this processor handles
1451
- */
1452
- command: string;
1453
- /**
1454
- * Alternative names for the command
1455
- */
1456
- aliases?: string[];
1457
- /**
1458
- * A description of the command
1459
- */
1460
- description?: string;
1461
- /**
1462
- * The author of the command
1463
- */
1464
- author?: ICliCommandAuthor;
1465
- /**
1466
- * If true, the processor accepts raw text input after the command name as its value.
1467
- * The captured text is available via `command.value` in `processCommand`.
1468
- * @default false
1469
- * @remarks When true, any text following the command name is extracted as the command value
1470
- * rather than being interpreted as sub-commands. Use `valueRequired` instead if the value is mandatory.
1471
- */
1472
- acceptsRawInput?: boolean;
1473
- /**
1474
- * If true, the command requires some form of input before it can execute.
1475
- *
1476
- * The requirement is satisfied by **any** of:
1477
- * - A positional value (text after the command name, available via `command.value`)
1478
- * - Piped data from a previous command (available via `command.data`)
1479
- * - Named arguments (e.g. `--server=node --path=/app`)
1480
- *
1481
- * When set, `command.value` is automatically extracted from the raw input
1482
- * (equivalent to setting `acceptsRawInput`).
1483
- *
1484
- * Additionally, when positional input is present the engine skips
1485
- * required-parameter validation — the processor is responsible for
1486
- * parsing positional text into individual values. Required-parameter
1487
- * checks still apply when the user provides only named arguments.
1488
- *
1489
- * @default false
1490
- */
1491
- valueRequired?: boolean;
1492
- /**
1493
- * The version of the command processor
1494
- * @default '1.0.0'
1495
- */
1496
- version?: string;
1497
- /**
1498
- * The metadata for the command processor
1499
- */
1500
- metadata?: CliProcessorMetadata;
1501
- /**
1502
- * Processors that are nested under this processor
1503
- */
1504
- processors?: ICliCommandChildProcessor[];
2320
+ interface ICliCommandAuthor {
1505
2321
  /**
1506
- * Parameters that the command accepts
2322
+ * The name of the author
1507
2323
  */
1508
- parameters?: ICliCommandParameterDescriptor[];
2324
+ name: string;
1509
2325
  /**
1510
- * Hooks that are executed before and after the command is processed
2326
+ * The email of the author
1511
2327
  */
1512
- hooks?: ICliProcessorHook[];
2328
+ email: string;
2329
+ }
2330
+ /**
2331
+ * Represents a command parameter
2332
+ */
2333
+ interface ICliCommandParameterDescriptor {
1513
2334
  /**
1514
- * Represents the state configuration for the command processor
1515
- * @remarks The state configuration is used to store and retrieve state information for the command processor
1516
- * @remarks State configuration is optional and can be used to store and retrieve state information for the command processor
1517
- * @remarks The state configuration is used only for root command processors and not for child command processors
2335
+ * The name of the parameter
1518
2336
  */
1519
- stateConfiguration?: CliStateConfiguration;
2337
+ name: string;
1520
2338
  /**
1521
- * Configuration options exposed by this processor for the `configure` command.
1522
- * When defined, these options appear in the interactive configuration menu
1523
- * and can be managed via `configure get/set` subcommands.
2339
+ * Aliases for the parameter
1524
2340
  */
1525
- configurationOptions?: ICliConfigurationOption[];
2341
+ aliases?: string[];
1526
2342
  /**
1527
- * When true, this processor extends (wraps) an existing processor with the same
1528
- * command name instead of replacing it. The registry sets `originalProcessor`
1529
- * to the previous processor so this one can delegate to it.
2343
+ * A description of the parameter
1530
2344
  */
1531
- extendsProcessor?: boolean;
2345
+ description: string;
1532
2346
  /**
1533
- * Reference to the original processor that this one extends.
1534
- * Set automatically by the registry — do not set manually.
1535
- * Call `this.originalProcessor.processCommand(command, context)` to delegate.
2347
+ * If true, the parameter is required
1536
2348
  */
1537
- originalProcessor?: ICliCommandProcessor;
2349
+ required: boolean;
1538
2350
  /**
1539
- * Process the command
1540
- * @param command The command to process
1541
- * @param context The context in which the command is executed
2351
+ * The type of the parameter
1542
2352
  */
1543
- processCommand(command: CliProcessCommand, context: ICliExecutionContext): Promise<void>;
2353
+ type: 'string' | 'number' | 'boolean' | 'array' | 'object' | string;
1544
2354
  /**
1545
- * Write the description of the command
1546
- * @param context The context in which the command is executed
2355
+ * The default value of the parameter
1547
2356
  */
1548
- writeDescription?(context: ICliExecutionContext): void;
2357
+ defaultValue?: any;
1549
2358
  /**
1550
- * A function that validates the command before execution
2359
+ * A validator function that validates the value of the parameter
1551
2360
  * @param value The value to validate
1552
2361
  * @returns An object with a valid property that indicates if the value is valid and an optional message property that contains a message to display if the value is not valid
1553
2362
  */
1554
- validateBeforeExecution?: (command: CliProcessCommand, context: ICliExecutionContext) => {
2363
+ validator?: (value: any) => {
2364
+ /**
2365
+ * Indicates if the value is valid
2366
+ */
1555
2367
  valid: boolean;
2368
+ /**
2369
+ * An optional message to display if the value is not valid
2370
+ */
1556
2371
  message?: string;
1557
2372
  };
1558
- /**
1559
- * Initialize the command processor
1560
- * @param context The context in which the command is executed
1561
- */
1562
- initialize?(context: ICliExecutionContext): Promise<void>;
1563
- /**
1564
- * Handle raw terminal data when this processor is the active context processor.
1565
- * When implemented, ALL terminal input is routed here instead of normal CLI input handling.
1566
- * Used for full-screen interactive modes (editors, pagers, etc.).
1567
- * @param data The raw terminal data string (escape sequences, control chars, printable text)
1568
- * @param context The execution context
1569
- */
1570
- onData?(data: string, context: ICliExecutionContext): Promise<void>;
1571
- /**
1572
- * Called when the terminal is resized while this processor is the active
1573
- * full-screen context processor. Allows processors to adapt their rendering
1574
- * to the new terminal dimensions.
1575
- * @param cols The new number of columns
1576
- * @param rows The new number of rows
1577
- * @param context The execution context
1578
- */
1579
- onResize?(cols: number, rows: number, context: ICliExecutionContext): void;
1580
- /**
1581
- * Called when the processor is being disposed — either because full-screen
1582
- * mode ended, the terminal disconnected, or the CLI component is being
1583
- * destroyed. Use this to clean up resources (timers, subscriptions, etc.).
1584
- * @param context The execution context
1585
- */
1586
- onDispose?(context: ICliExecutionContext): void;
1587
2373
  }
1588
2374
  /**
1589
- * Represents a child command processor
2375
+ * Represents a configuration option that a command processor exposes
2376
+ * for management via the `configure` command.
1590
2377
  */
1591
- interface ICliCommandChildProcessor extends ICliCommandProcessor {
2378
+ interface ICliConfigurationOption {
1592
2379
  /**
1593
- * The parent processor, it is populated by the processor manager
2380
+ * The key used to store and retrieve this option, e.g. 'maxItems'
1594
2381
  */
1595
- parent?: ICliCommandProcessor;
1596
- }
1597
-
1598
- /**
1599
- * Describes a global parameter (e.g. --help, --version) together with
1600
- * the handler that executes when the parameter is present.
1601
- *
1602
- * Global parameters are checked for every command before the command
1603
- * processor's own `processCommand` runs. If the handler returns `true`,
1604
- * command execution is halted (the parameter "consumed" the invocation).
1605
- */
1606
- interface ICliGlobalParameterHandler {
2382
+ key: string;
1607
2383
  /**
1608
- * The parameter descriptor shown in help output and used for tab-completion.
2384
+ * Human-readable label shown in the interactive menu
1609
2385
  */
1610
- parameter: ICliCommandParameterDescriptor;
2386
+ label: string;
1611
2387
  /**
1612
- * Execution priority — lower values run first.
1613
- * @default 0
2388
+ * Description of what this option controls
1614
2389
  */
1615
- priority?: number;
2390
+ description: string;
1616
2391
  /**
1617
- * Evaluate and optionally handle the parameter.
1618
- * @returns `true` to halt command execution, `false` to continue.
2392
+ * The data type of the option
1619
2393
  */
1620
- handle(args: Record<string, any>, processor: ICliCommandProcessor, command: CliProcessCommand, context: ICliExecutionContext): boolean | Promise<boolean>;
1621
- }
1622
-
1623
- /**
1624
- * Represents a service that executes commands
1625
- */
1626
- interface ICliCommandExecutorService {
2394
+ type: 'string' | 'number' | 'boolean' | 'select';
1627
2395
  /**
1628
- * Display the help output for a command, including its description,
1629
- * sub-commands, and parameters.
1630
- * @param command The parsed command to show help for
1631
- * @param context The execution context
2396
+ * The default value when no configuration has been set
1632
2397
  */
1633
- showHelp(command: CliProcessCommand, context: ICliExecutionContext): Promise<void>;
2398
+ defaultValue: any;
1634
2399
  /**
1635
- * Execute a command
1636
- * @param command The command to execute
1637
- * @param context The context in which the command is executed
2400
+ * Available choices for 'select' type options
1638
2401
  */
1639
- executeCommand(command: string, context: ICliExecutionContext): Promise<void>;
2402
+ options?: {
2403
+ label: string;
2404
+ value: any;
2405
+ }[];
1640
2406
  /**
1641
- * Register a global parameter handler.
1642
- * Global parameters (e.g. --help, --version) are evaluated for every
1643
- * command before the processor's own processCommand runs.
1644
- * @param handler The global parameter handler to register
2407
+ * Optional validator function
1645
2408
  */
1646
- registerGlobalParameter(handler: ICliGlobalParameterHandler): void;
2409
+ validator?: (value: any) => {
2410
+ valid: boolean;
2411
+ message?: string;
2412
+ };
1647
2413
  /**
1648
- * Returns the descriptors of all registered global parameters.
1649
- * Used by help output and tab-completion.
2414
+ * Override the category grouping (defaults to the processor command name)
1650
2415
  */
1651
- getGlobalParameters(): ICliCommandParameterDescriptor[];
2416
+ category?: string;
1652
2417
  }
1653
-
1654
2418
  /**
1655
- * Represents a registry for command processors
2419
+ * Represents a command processor
1656
2420
  */
1657
- interface ICliCommandProcessorRegistry {
1658
- /**
1659
- * The processors registered with the registry
1660
- */
1661
- readonly processors: ICliCommandProcessor[];
1662
- /**
1663
- * Find a processor for a command
1664
- * @param mainCommand
1665
- * @param chainCommands
1666
- */
1667
- findProcessor(mainCommand: string, chainCommands: string[]): ICliCommandProcessor | undefined;
2421
+ interface ICliCommandProcessor {
1668
2422
  /**
1669
- * Recursively searches for a processor matching the given command.
1670
- * @param mainCommand The main command name.
1671
- * @param chainCommands The remaining chain commands (if any).
1672
- * @param processors The list of available processors.
1673
- * @returns The matching processor or undefined if not found.
2423
+ * The command that this processor handles
1674
2424
  */
1675
- findProcessorInCollection(mainCommand: string, chainCommands: string[], processors: ICliCommandProcessor[]): ICliCommandProcessor | undefined;
2425
+ command: string;
1676
2426
  /**
1677
- * Get the root processor for a child processor
1678
- * @param child The child processor
2427
+ * Alternative names for the command
1679
2428
  */
1680
- getRootProcessor(child: ICliCommandChildProcessor): ICliCommandProcessor;
2429
+ aliases?: string[];
1681
2430
  /**
1682
- * Register a processor
1683
- * @param processor
2431
+ * A description of the command
1684
2432
  */
1685
- registerProcessor(processor: ICliCommandProcessor): void;
2433
+ description?: string;
1686
2434
  /**
1687
- * Unregister a processor
1688
- * @param processor
2435
+ * The author of the command
1689
2436
  */
1690
- unregisterProcessor(processor: ICliCommandProcessor): void;
1691
- }
1692
- /**
1693
- * Represents a tracked CLI process entry
1694
- */
1695
- interface ICliProcessEntry {
1696
- pid: number;
1697
- /** Display name (service name for bg services, command text for commands) */
1698
- name: string;
1699
- /** Process type */
1700
- type: 'command' | 'daemon' | 'job' | 'service';
1701
- command: string;
1702
- startTime: number;
1703
- status: 'running' | 'completed' | 'failed' | 'killed';
1704
- exitCode?: number;
1705
- }
1706
- /** Options for registering a process */
1707
- interface ICliProcessRegisterOptions {
1708
- /** Display name (defaults to command) */
1709
- name?: string;
1710
- /** Process type (defaults to 'command') */
1711
- type?: 'command' | 'daemon' | 'job' | 'service';
1712
- /** Custom kill handler (e.g. for background services) */
1713
- onKill?: () => Promise<void> | void;
1714
- }
1715
- /**
1716
- * Registry for tracking CLI processes
1717
- */
1718
- interface ICliProcessRegistry {
1719
- /** Register a new process, returns assigned PID */
1720
- register(command: string, options?: ICliProcessRegisterOptions): {
1721
- pid: number;
1722
- abortController: AbortController;
1723
- };
1724
- /** Mark process as completed */
1725
- complete(pid: number, exitCode: number): void;
1726
- /** Mark process as failed */
1727
- fail(pid: number): void;
1728
- /** Kill a process by PID */
1729
- kill(pid: number): boolean;
1730
- /** List all processes (running + recent completed) */
1731
- list(): ICliProcessEntry[];
1732
- /** Get the current foreground process PID */
1733
- readonly currentPid: number | undefined;
1734
- }
1735
-
1736
- interface ICliExecutionProcess {
2437
+ author?: ICliCommandAuthor;
1737
2438
  /**
1738
- * Indicates if the process has exited
2439
+ * If true, the processor accepts raw text input after the command name as its value.
2440
+ * The captured text is available via `command.value` in `processCommand`.
2441
+ * @default false
2442
+ * @remarks When true, any text following the command name is extracted as the command value
2443
+ * rather than being interpreted as sub-commands. Use `valueRequired` instead if the value is mandatory.
1739
2444
  */
1740
- exited?: boolean;
2445
+ acceptsRawInput?: boolean;
1741
2446
  /**
1742
- * The exit code of the process
2447
+ * If true, the command requires some form of input before it can execute.
2448
+ *
2449
+ * The requirement is satisfied by **any** of:
2450
+ * - A positional value (text after the command name, available via `command.value`)
2451
+ * - Piped data from a previous command (available via `command.data`)
2452
+ * - Named arguments (e.g. `--server=node --path=/app`)
2453
+ *
2454
+ * When set, `command.value` is automatically extracted from the raw input
2455
+ * (equivalent to setting `acceptsRawInput`).
2456
+ *
2457
+ * Additionally, when positional input is present the engine skips
2458
+ * required-parameter validation — the processor is responsible for
2459
+ * parsing positional text into individual values. Required-parameter
2460
+ * checks still apply when the user provides only named arguments.
2461
+ *
2462
+ * @default false
1743
2463
  */
1744
- exitCode?: number;
2464
+ valueRequired?: boolean;
1745
2465
  /**
1746
- * Indicates if the process is running
2466
+ * The version of the command processor
2467
+ * @default '1.0.0'
1747
2468
  */
1748
- running: boolean;
2469
+ version?: string;
1749
2470
  /**
1750
- * The data of the process
2471
+ * The metadata for the command processor
1751
2472
  */
1752
- data: any | undefined;
2473
+ metadata?: CliProcessorMetadata;
1753
2474
  /**
1754
- * Exit the process
1755
- * @param code The exit code
1756
- * @returns void
2475
+ * Processors that are nested under this processor
1757
2476
  */
1758
- exit: (
2477
+ processors?: ICliCommandChildProcessor[];
1759
2478
  /**
1760
- * The exit code
2479
+ * Parameters that the command accepts
1761
2480
  */
1762
- code?: number,
2481
+ parameters?: ICliCommandParameterDescriptor[];
1763
2482
  /**
1764
- * Options for exiting the process
2483
+ * Hooks that are executed before and after the command is processed
1765
2484
  */
1766
- options?: {
1767
- /**
1768
- * Indicates if the exit should be silent, i.e. not throw an error
1769
- */
1770
- silent?: boolean;
1771
- }) => void;
2485
+ hooks?: ICliProcessorHook[];
1772
2486
  /**
1773
- * Output data from the process
1774
- * @param data The data to output
2487
+ * Represents the state configuration for the command processor
2488
+ * @remarks The state configuration is used to store and retrieve state information for the command processor
2489
+ * @remarks State configuration is optional and can be used to store and retrieve state information for the command processor
2490
+ * @remarks The state configuration is used only for root command processors and not for child command processors
1775
2491
  */
1776
- output(data: any): void;
1777
- }
1778
-
1779
- /**
1780
- * Represents a key-value store for the CLI
1781
- */
1782
- interface ICliKeyValueStore {
2492
+ stateConfiguration?: CliStateConfiguration;
1783
2493
  /**
1784
- * Retrieves a value by key.
1785
- * @param key - The key to retrieve the value for.
1786
- * @returns A promise resolving to the value or undefined if not found.
2494
+ * Configuration options exposed by this processor for the `configure` command.
2495
+ * When defined, these options appear in the interactive configuration menu
2496
+ * and can be managed via `configure get/set` subcommands.
1787
2497
  */
1788
- get<T = any>(key: string): Promise<T | undefined>;
2498
+ configurationOptions?: ICliConfigurationOption[];
1789
2499
  /**
1790
- * Sets a key-value pair in the store.
1791
- * @param key - The key to set.
1792
- * @param value - The value to store.
1793
- * @returns A promise resolving when the value is stored.
2500
+ * When true, this processor extends (wraps) an existing processor with the same
2501
+ * command name instead of replacing it. The registry sets `originalProcessor`
2502
+ * to the previous processor so this one can delegate to it.
1794
2503
  */
1795
- set(key: string, value: any): Promise<void>;
2504
+ extendsProcessor?: boolean;
1796
2505
  /**
1797
- * Removes a key-value pair by key.
1798
- * @param key - The key to remove.
1799
- * @returns A promise resolving when the key is removed.
2506
+ * Reference to the original processor that this one extends.
2507
+ * Set automatically by the registry — do not set manually.
2508
+ * Call `this.originalProcessor.processCommand(command, context)` to delegate.
1800
2509
  */
1801
- remove(key: string): Promise<void>;
2510
+ originalProcessor?: ICliCommandProcessor;
1802
2511
  /**
1803
- * Clears all key-value pairs from the store.
1804
- * @returns A promise resolving when the store is cleared.
2512
+ * Process the command
2513
+ * @param command The command to process
2514
+ * @param context The context in which the command is executed
1805
2515
  */
1806
- clear(): Promise<void>;
1807
- }
1808
- /**
1809
- * Reactive state store scoped to a command processor or module.
1810
- *
1811
- * State is a plain object (`Record<string, any>`). Updates are shallow-merged
1812
- * (top-level keys are replaced, nested objects are not deep-merged).
1813
- * `getState()` returns a snapshot — mutating the returned object has no effect;
1814
- * always use `updateState()` to change state.
1815
- */
1816
- interface ICliStateStore {
2516
+ processCommand(command: CliProcessCommand, context: ICliExecutionContext): Promise<void>;
1817
2517
  /**
1818
- * Get a snapshot of the current state.
1819
- * @returns A shallow copy of the state object
2518
+ * Write the description of the command
2519
+ * @param context The context in which the command is executed
1820
2520
  */
1821
- getState<T extends CliState = CliState>(): T;
2521
+ writeDescription?(context: ICliExecutionContext): void;
1822
2522
  /**
1823
- * Shallow-merge new values into the current state.
1824
- * Only the provided keys are updated; other keys are preserved.
1825
- * Triggers subscribers and persists if a storage backend is configured.
1826
- * @param newState Partial state to merge with the current state
2523
+ * A function that validates the command before execution
2524
+ * @param value The value to validate
2525
+ * @returns An object with a valid property that indicates if the value is valid and an optional message property that contains a message to display if the value is not valid
1827
2526
  */
1828
- updateState(newState: Partial<CliState>): void;
2527
+ validateBeforeExecution?: (command: CliProcessCommand, context: ICliExecutionContext) => {
2528
+ valid: boolean;
2529
+ message?: string;
2530
+ };
1829
2531
  /**
1830
- * Select a specific property or computed value from the state.
1831
- * @param selector A function to project a slice of the state.
1832
- * @returns Observable of the selected value.
2532
+ * Initialize the command processor
2533
+ * @param context The context in which the command is executed
1833
2534
  */
1834
- select<K>(selector: (state: CliState) => K): Observable<K>;
2535
+ initialize?(context: ICliExecutionContext): Promise<void>;
1835
2536
  /**
1836
- * Subscribe to state changes.
1837
- * @param callback Callback function to handle state changes.
1838
- * @returns Subscription object to manage the subscription.
2537
+ * Handle raw terminal data when this processor is the active context processor.
2538
+ * When implemented, ALL terminal input is routed here instead of normal CLI input handling.
2539
+ * Used for full-screen interactive modes (editors, pagers, etc.).
2540
+ * @param data The raw terminal data string (escape sequences, control chars, printable text)
2541
+ * @param context The execution context
1839
2542
  */
1840
- subscribe(callback: (state: CliState) => void): Subscription;
2543
+ onData?(data: string, context: ICliExecutionContext): Promise<void>;
1841
2544
  /**
1842
- * Reset the state to its initial value.
2545
+ * Called when the terminal is resized while this processor is the active
2546
+ * full-screen context processor. Allows processors to adapt their rendering
2547
+ * to the new terminal dimensions.
2548
+ * @param cols The new number of columns
2549
+ * @param rows The new number of rows
2550
+ * @param context The execution context
1843
2551
  */
1844
- reset(): void;
2552
+ onResize?(cols: number, rows: number, context: ICliExecutionContext): void;
1845
2553
  /**
1846
- * Persist the state to storage.
2554
+ * Called when the processor is being disposed — either because full-screen
2555
+ * mode ended, the terminal disconnected, or the CLI component is being
2556
+ * destroyed. Use this to clean up resources (timers, subscriptions, etc.).
2557
+ * @param context The execution context
1847
2558
  */
1848
- persist(): Promise<void>;
2559
+ onDispose?(context: ICliExecutionContext): void;
2560
+ }
2561
+ /**
2562
+ * Represents a child command processor
2563
+ */
2564
+ interface ICliCommandChildProcessor extends ICliCommandProcessor {
1849
2565
  /**
1850
- * Initialize the state from storage.
2566
+ * The parent processor, it is populated by the processor manager
1851
2567
  */
1852
- initialize(): Promise<void>;
2568
+ parent?: ICliCommandProcessor;
1853
2569
  }
1854
2570
 
1855
2571
  /**
@@ -1900,6 +2616,17 @@ interface ICliModule {
1900
2616
  processors?: ICliCommandProcessor[];
1901
2617
  /** Services registered into the shared service container */
1902
2618
  services?: CliProvider[];
2619
+ /**
2620
+ * Global command guards contributed by this module. They are registered
2621
+ * when the module boots and run before every command (across all modules),
2622
+ * e.g. authentication or permission checks. See {@link ICliCommandGuard}.
2623
+ */
2624
+ guards?: ICliCommandGuard[];
2625
+ /**
2626
+ * Permissions this module uses (`resource:action`), registered in the
2627
+ * {@link ICliPermissionRegistry} at boot so they can be listed and granted.
2628
+ */
2629
+ permissions?: CliPermissionDefinition[];
1903
2630
  /** Module configuration, set via configure() */
1904
2631
  config?: Record<string, any>;
1905
2632
  /**
@@ -1991,449 +2718,411 @@ interface ICliLogger {
1991
2718
  */
1992
2719
  error(...args: any[]): void;
1993
2720
  /**
1994
- * Log a debug message (DEBUG level)
1995
- * @param args The arguments to log
1996
- */
1997
- debug(...args: any[]): void;
1998
- }
1999
-
2000
- /**
2001
- * Provides internationalization (i18n) support for CLI strings.
2002
- */
2003
- interface ICliTranslationService {
2004
- /**
2005
- * Translate a key to the current locale.
2006
- * @param key The translation key (e.g. 'cli.echo.description')
2007
- * @param defaultValue The English fallback string (returned if no translation found)
2008
- * @param params Optional interpolation parameters for `{param}` placeholders
2009
- */
2010
- t(key: string, defaultValue: string, params?: Record<string, string | number>): string;
2011
- /**
2012
- * Get the current locale code (e.g. 'en', 'es', 'fr').
2013
- */
2014
- getLocale(): string;
2015
- /**
2016
- * Set the active locale.
2017
- * @param locale The locale code to switch to
2018
- */
2019
- setLocale(locale: string): void;
2020
- /**
2021
- * Register translations for a locale.
2022
- * Can be called multiple times — translations are merged, with later calls overriding earlier ones.
2023
- * @param locale The locale code
2024
- * @param translations Flat key-value map of translation keys to translated strings
2025
- */
2026
- addTranslations(locale: string, translations: Record<string, string>): void;
2027
- /**
2028
- * Get all locale codes that have at least one registered translation.
2029
- */
2030
- getAvailableLocales(): string[];
2031
- }
2032
- /**
2033
- * Dependency injection container for CLI services.
2034
- * Services are registered with string tokens and retrieved by type.
2035
- */
2036
- interface ICliServiceProvider {
2037
- /**
2038
- * Retrieve a service by its injection token.
2039
- * @param service The token (typically a `*_TOKEN` constant) identifying the service
2040
- * @returns The service instance, cast to `T`
2041
- * @throws If no service is registered for the given token
2042
- */
2043
- get<T = unknown>(service: any): T;
2044
- /**
2045
- * Register one or more service definitions.
2046
- * @param definition A single provider or an array of providers to register
2047
- */
2048
- set(definition: CliProvider | CliProvider[]): void;
2049
- }
2050
-
2051
- /**
2052
- * Abstract permission service for checking rwx permissions on resources.
2053
- * The default implementation lives in @qodalis/cli-users; consumers can
2054
- * replace it via the DI container.
2055
- */
2056
- interface ICliPermissionService {
2057
- /**
2058
- * Check whether a user may perform an action on a resource.
2059
- * @param user The acting user.
2060
- * @param action The requested operation.
2061
- * @param ownership Owner/group of the resource.
2062
- * @param permissions Permission string (e.g. "rwxr-xr-x").
2063
- * @returns true if allowed.
2064
- */
2065
- check(user: ICliUser, action: 'read' | 'write' | 'execute', ownership: ICliOwnership, permissions: string): boolean;
2066
- /** Parse octal (e.g. "755") to a permission string (e.g. "rwxr-xr-x"). */
2067
- parseOctal(octal: string): string;
2068
- /** Convert a permission string (e.g. "rwxr-xr-x") to octal (e.g. "755"). */
2069
- toOctal(permissions: string): string;
2070
- }
2071
-
2072
- /**
2073
- * Context passed to completion providers describing the current input state.
2074
- */
2075
- interface ICliCompletionContext {
2076
- /** Full current line text */
2077
- input: string;
2078
- /** Cursor position in the line */
2079
- cursor: number;
2080
- /** The token (word) being completed at the cursor position */
2081
- token: string;
2082
- /** Start index of the token in the input string */
2083
- tokenStart: number;
2084
- /** Index of the token among all tokens (0 = command, 1+ = arguments) */
2085
- tokenIndex: number;
2086
- /** All tokens parsed from the input */
2087
- tokens: string[];
2721
+ * Log a debug message (DEBUG level)
2722
+ * @param args The arguments to log
2723
+ */
2724
+ debug(...args: any[]): void;
2088
2725
  }
2726
+
2089
2727
  /**
2090
- * Provides tab-completion candidates for CLI input.
2091
- * Plugins can register providers to extend completion (e.g. file paths, custom values).
2728
+ * Provides internationalization (i18n) support for CLI strings.
2092
2729
  */
2093
- interface ICliCompletionProvider {
2730
+ interface ICliTranslationService {
2094
2731
  /**
2095
- * Priority order. Lower values are checked first.
2096
- * Built-in defaults: 100 (commands), 200 (parameters).
2097
- * Plugins should use lower values (e.g. 50) to take precedence for specific commands.
2732
+ * Translate a key to the current locale.
2733
+ * @param key The translation key (e.g. 'cli.echo.description')
2734
+ * @param defaultValue The English fallback string (returned if no translation found)
2735
+ * @param params Optional interpolation parameters for `{param}` placeholders
2098
2736
  */
2099
- priority: number;
2737
+ t(key: string, defaultValue: string, params?: Record<string, string | number>): string;
2100
2738
  /**
2101
- * Return completion candidates for the current input context.
2102
- * Return an empty array if this provider does not apply.
2739
+ * Get the current locale code (e.g. 'en', 'es', 'fr').
2103
2740
  */
2104
- getCompletions(context: ICliCompletionContext): string[] | Promise<string[]>;
2105
- }
2106
-
2107
- interface ICliProgressBar {
2741
+ getLocale(): string;
2108
2742
  /**
2109
- * Indicates if the progress bar is running
2743
+ * Set the active locale.
2744
+ * @param locale The locale code to switch to
2110
2745
  */
2111
- isRunning: boolean;
2746
+ setLocale(locale: string): void;
2112
2747
  /**
2113
- * Show the progress bar
2748
+ * Register translations for a locale.
2749
+ * Can be called multiple times — translations are merged, with later calls overriding earlier ones.
2750
+ * @param locale The locale code
2751
+ * @param translations Flat key-value map of translation keys to translated strings
2114
2752
  */
2115
- show: (text?: string) => void;
2753
+ addTranslations(locale: string, translations: Record<string, string>): void;
2116
2754
  /**
2117
- * Hide the progress bar
2755
+ * Get all locale codes that have at least one registered translation.
2118
2756
  */
2119
- hide: () => void;
2757
+ getAvailableLocales(): string[];
2120
2758
  }
2121
2759
  /**
2122
- * Represents a spinner for the CLI
2760
+ * Dependency injection container for CLI services.
2761
+ * Services are registered with string tokens and retrieved by type.
2123
2762
  */
2124
- interface ICliSpinner extends ICliProgressBar {
2763
+ interface ICliServiceProvider {
2125
2764
  /**
2126
- * Set the text of the spinner
2127
- * @param text The text to set
2765
+ * Retrieve a service by its injection token.
2766
+ * Returns `undefined` when no provider is registered for the token.
2767
+ * For multi-provider tokens, returns the last registered value (use `getAll` for the full array).
2768
+ * @param service The token (typically a `*_TOKEN` constant) identifying the service
2769
+ * @returns The service instance cast to `T`, or `undefined` if not registered
2128
2770
  */
2129
- setText: (text: string) => void;
2130
- }
2131
- type CliPercentageProgressBarUpdateValueOptions = {
2771
+ get<T = unknown>(service: any): T | undefined;
2132
2772
  /**
2133
- * The type of update to perform
2134
- * @default 'replace'
2773
+ * Retrieve all registered services for a multi-provider token.
2774
+ * Returns an empty array when no provider is registered for the token.
2775
+ * @param service The token (typically a `*_TOKEN` constant) identifying the service
2776
+ * @returns Array of all resolved service instances
2135
2777
  */
2136
- type?: 'replace' | 'increment';
2137
- };
2138
- /**
2139
- * Represents a progress bar for the CLI
2140
- */
2141
- interface ICliPercentageProgressBar extends ICliProgressBar {
2778
+ getAll<T = unknown>(service: any): T[];
2142
2779
  /**
2143
- * Update the progress of the progress bar.
2144
- * @param progress The progress value (0–100). Values outside this range are clamped.
2145
- * @param options Optional: update type ('replace' or 'increment')
2780
+ * Retrieve a service by its injection token, throwing if not registered.
2781
+ * Use this when the service is required and its absence is a programming error.
2782
+ * For multi-provider tokens, returns the last registered value (use `getAll` for the full array).
2783
+ * @param service The token (typically a `*_TOKEN` constant) identifying the service
2784
+ * @returns The service instance, cast to `T`
2785
+ * @throws If no service is registered for the given token
2146
2786
  */
2147
- update: (progress: number, options?: CliPercentageProgressBarUpdateValueOptions) => void;
2787
+ getRequired<T = unknown>(service: any): T;
2148
2788
  /**
2149
- * Complete the progress bar
2150
- * @returns void
2789
+ * Check whether a service is registered for the given token.
2790
+ * @param service The token identifying the service
2791
+ * @returns `true` if a service is registered, `false` otherwise
2151
2792
  */
2152
- complete: () => void;
2793
+ has(service: any): boolean;
2153
2794
  /**
2154
- * Set the text of the spinner
2155
- * @param text The text to set
2795
+ * Register one or more service definitions.
2796
+ * @param definition A single provider or an array of providers to register
2156
2797
  */
2157
- setText: (text: string) => void;
2798
+ set(definition: CliProvider | CliProvider[]): void;
2158
2799
  }
2159
- type CliTextAnimatorOptions = {
2800
+ /** DI token for `ICliServerAuthTokenProvider` (multi-provider) */
2801
+ declare const ICliServerAuthTokenProvider_TOKEN = "cli-server-auth-token-provider";
2802
+ /** DI token for `ICliServerAuthService` */
2803
+ declare const ICliServerAuthService_TOKEN = "cli-server-auth-service";
2804
+ /**
2805
+ * Integrators implement this interface to supply auth credentials
2806
+ * for server requests. Multiple providers can be registered — the
2807
+ * auth service merges headers from all of them (later providers
2808
+ * override earlier ones for the same header name).
2809
+ *
2810
+ * @example
2811
+ * ```ts
2812
+ * class MyAuthProvider implements ICliServerAuthTokenProvider {
2813
+ * getHeaders(serverName: string) {
2814
+ * return { Authorization: `Bearer ${this.oauthService.getAccessToken()}` };
2815
+ * }
2816
+ * }
2817
+ *
2818
+ * // Register in a module:
2819
+ * const myModule: ICliModule = {
2820
+ * name: 'my-auth',
2821
+ * services: [{
2822
+ * provide: ICliServerAuthTokenProvider_TOKEN,
2823
+ * useValue: new MyAuthProvider(),
2824
+ * multi: true,
2825
+ * }],
2826
+ * };
2827
+ * ```
2828
+ */
2829
+ interface ICliServerAuthTokenProvider {
2160
2830
  /**
2161
- * The speed of the animation
2162
- * @default 100
2831
+ * Return headers for a given server. Called on every request,
2832
+ * so implementations can return fresh tokens.
2833
+ *
2834
+ * @param serverName The `name` field from `CliServerConfig`
2835
+ * @returns Headers to merge into the request, or an empty object
2163
2836
  */
2164
- speed?: number;
2837
+ getHeaders(serverName: string): Record<string, string>;
2165
2838
  /**
2166
- * The text will be removed after typing
2839
+ * Optional: query parameters for WebSocket upgrades to this server (e.g. a
2840
+ * short-lived single-use ticket). Called right before each connection.
2841
+ * Return `undefined` when this provider does not handle the server.
2842
+ * When any provider returns params, headers are NOT copied into the URL
2843
+ * (so bearer tokens never appear in WebSocket URLs).
2167
2844
  */
2168
- removeAfterTyping?: boolean;
2169
- };
2170
- interface ICliTextAnimator extends ICliProgressBar {
2845
+ getWebSocketParams?(serverName: string): Promise<Record<string, string> | undefined>;
2846
+ }
2847
+ /**
2848
+ * Minimal view of the CLI server manager (token `ICliServerManager_TOKEN`),
2849
+ * e.g. for auth modules that must refresh a server after sign-in.
2850
+ */
2851
+ interface ICliServerManager {
2852
+ /** Reconnect a server: re-fetch its commands and reopen its event socket. */
2853
+ reconnect(name: string): Promise<{
2854
+ success: boolean;
2855
+ commandCount: number;
2856
+ }>;
2171
2857
  /**
2172
- * Show animated text in a typing and erasing effect.
2173
- * @param text The text to animate
2174
- * @param options Optional: speed (ms per character), removeAfterTyping
2175
- * @returns A promise that resolves when the animation completes
2858
+ * The server a command should target when the user names none: the
2859
+ * default server (`server default`) when connected, otherwise the first
2860
+ * connected server. `null` when no server is connected (for example every
2861
+ * server requires a login the user has not done yet).
2176
2862
  */
2177
- showText: (text: string, options?: CliTextAnimatorOptions) => Promise<void>;
2863
+ preferredServer?(): string | null;
2178
2864
  }
2179
-
2865
+ /** Token for {@link ICliServerManager} */
2866
+ declare const ICliServerManager_TOKEN = "cli-server-manager";
2180
2867
  /**
2181
- * Represents a service that manages user sessions in the CLI
2868
+ * Internal service that resolves auth headers for a server by merging:
2869
+ * 1. Static/dynamic headers from `CliServerConfig.headers`
2870
+ * 2. Headers from all registered `ICliServerAuthTokenProvider`s
2871
+ *
2872
+ * Consumers should use this service instead of reading `config.headers` directly.
2182
2873
  */
2183
- interface ICliUserSessionService {
2184
- /** Gets the current user session as a reactive stream */
2185
- getUserSession(): Observable<ICliUserSession | undefined>;
2186
- /** Sets the current user session */
2187
- setUserSession(session: ICliUserSession): Promise<void>;
2188
- /** Clears the current session (logout) */
2189
- clearSession(): Promise<void>;
2190
- /** Persists the current session to storage */
2191
- persistSession(): Promise<void>;
2192
- /** Restores a session from storage (called on boot) */
2193
- restoreSession(): Promise<ICliUserSession | undefined>;
2874
+ interface ICliServerAuthService {
2875
+ /**
2876
+ * Resolve the final set of headers for a server request.
2877
+ * Merges config headers with all registered token providers.
2878
+ *
2879
+ * @param serverName The server's `name` field
2880
+ * @param configHeaders The `headers` field from `CliServerConfig`
2881
+ * @returns Merged headers ready for use in fetch/WebSocket calls
2882
+ */
2883
+ getHeaders(serverName: string, configHeaders?: CliHeadersProvider): Record<string, string>;
2884
+ /**
2885
+ * WebSocket upgrade query params from the registered token providers, or
2886
+ * `undefined` when no provider handles this server.
2887
+ */
2888
+ getWebSocketParams?(serverName: string): Promise<Record<string, string> | undefined>;
2194
2889
  }
2890
+
2195
2891
  /**
2196
- * Represents a service that manages users in the CLI
2892
+ * A static header map or a function that returns one.
2893
+ * Use a function when headers contain short-lived tokens (e.g. JWTs)
2894
+ * so that every request gets a fresh value.
2895
+ *
2896
+ * @example
2897
+ * // Static
2898
+ * headers: { 'X-Api-Key': 'abc123' }
2899
+ *
2900
+ * // Dynamic (token refresh)
2901
+ * headers: () => ({ Authorization: `Bearer ${auth.getToken()}` })
2197
2902
  */
2198
- interface ICliUsersStoreService {
2199
- /** Gets users with optional filtering and pagination */
2200
- getUsers(options?: {
2201
- query?: string;
2202
- skip?: number;
2203
- take?: number;
2204
- }): Observable<ICliUser[]>;
2205
- /** Creates a new user */
2206
- createUser(user: CliAddUser): Promise<ICliUser>;
2207
- /** Gets a user by id, name, or email */
2208
- getUser(id: string): Observable<ICliUser | undefined>;
2209
- /** Updates an existing user */
2210
- updateUser(id: string, updates: CliUpdateUser): Promise<ICliUser>;
2211
- /** Deletes a user by id */
2212
- deleteUser(id: string): Promise<void>;
2213
- }
2903
+ type CliHeadersProvider = Record<string, string> | (() => Record<string, string>);
2214
2904
  /**
2215
- * Represents a service that manages groups in the CLI
2905
+ * Resolves a CliHeadersProvider to a plain header map.
2216
2906
  */
2217
- interface ICliGroupsStoreService {
2218
- /** Gets all groups */
2219
- getGroups(): Observable<ICliGroup[]>;
2220
- /** Gets a group by id or name */
2221
- getGroup(id: string): Observable<ICliGroup | undefined>;
2222
- /** Creates a new group */
2223
- createGroup(name: string, description?: string): Promise<ICliGroup>;
2224
- /** Deletes a group by id */
2225
- deleteGroup(id: string): Promise<void>;
2226
- /** Gets all users that belong to a group */
2227
- getGroupMembers(groupId: string): Observable<ICliUser[]>;
2228
- }
2907
+ declare function resolveHeaders(provider?: CliHeadersProvider): Record<string, string>;
2229
2908
  /**
2230
- * Represents a service that handles authentication in the CLI
2909
+ * Resolves auth headers for a server request using the auth service if available,
2910
+ * otherwise falls back to resolving config headers directly.
2911
+ *
2912
+ * This is the single entry point plugins should use to get headers for server requests.
2913
+ *
2914
+ * @param services The service provider (from context.services)
2915
+ * @param serverName The server name
2916
+ * @param configHeaders The headers from CliServerConfig
2917
+ * @returns Resolved headers ready for use
2231
2918
  */
2232
- interface ICliAuthService {
2233
- /** Authenticates a user and returns a session */
2234
- login(username: string, password: string): Promise<ICliUserSession>;
2235
- /** Ends the current session */
2236
- logout(): Promise<void>;
2237
- /** Sets or updates a user's password */
2238
- setPassword(userId: string, password: string): Promise<void>;
2239
- /** Verifies a password against stored credentials */
2240
- verifyPassword(userId: string, password: string): Promise<boolean>;
2241
- /** Hashes a password with a salt using Web Crypto SHA-256 */
2242
- hashPassword(password: string, salt: string): Promise<string>;
2243
- }
2244
-
2919
+ declare function resolveServerHeaders(services: ICliServiceProvider | undefined, serverName: string, configHeaders?: CliHeadersProvider): Record<string, string>;
2245
2920
  /**
2246
- * Represents a serialized snapshot of a CLI engine's state.
2247
- * Used to duplicate terminal tabs with full state preservation.
2921
+ * Configuration for a remote CLI server
2248
2922
  */
2249
- interface CliEngineSnapshot {
2250
- /** Schema version for forward compatibility */
2251
- version: 1;
2252
- /** Timestamp (ms since epoch) when the snapshot was taken */
2253
- timestamp: number;
2254
- /** Serialized terminal state */
2255
- terminal: {
2256
- /** Terminal buffer serialized via @xterm/addon-serialize */
2257
- serializedBuffer: string;
2258
- /** Terminal column count at time of snapshot */
2259
- cols: number;
2260
- /** Terminal row count at time of snapshot */
2261
- rows: number;
2262
- };
2263
- /** Command history entries (most recent last) */
2264
- commandHistory: string[];
2265
- /** Named state store entries */
2266
- stateStores: Array<{
2267
- name: string;
2268
- state: Record<string, any>;
2269
- }>;
2270
- }
2271
-
2923
+ type CliServerConfig = {
2924
+ /** Unique identifier, used for namespacing commands */
2925
+ name: string;
2926
+ /** Base URL of the server, e.g. "https://api.example.com" */
2927
+ url: string;
2928
+ /** Whether this server is enabled. @default true */
2929
+ enabled?: boolean;
2930
+ /**
2931
+ * Custom headers sent with every request (e.g. auth tokens).
2932
+ * Can be a static object or a function that returns fresh headers
2933
+ * on each call (useful for rotating tokens / JWTs).
2934
+ */
2935
+ headers?: CliHeadersProvider;
2936
+ /** Request timeout in milliseconds. @default 30000 */
2937
+ timeout?: number;
2938
+ };
2272
2939
  /**
2273
- * Messages sent from the main thread to a background service Worker.
2940
+ * A single output item in a server response
2274
2941
  */
2275
- type CliWorkerInboundMessage = {
2276
- type: 'start';
2277
- config?: Record<string, unknown>;
2942
+ type CliServerOutput = {
2943
+ type: 'text';
2944
+ value: string;
2945
+ style?: 'success' | 'error' | 'info' | 'warning';
2278
2946
  } | {
2279
- type: 'stop';
2947
+ type: 'table';
2948
+ headers: string[];
2949
+ rows: string[][];
2280
2950
  } | {
2281
- type: 'abort';
2951
+ type: 'list';
2952
+ items: string[];
2953
+ ordered?: boolean;
2954
+ } | {
2955
+ type: 'json';
2956
+ value: any;
2957
+ } | {
2958
+ type: 'key-value';
2959
+ entries: {
2960
+ key: string;
2961
+ value: string;
2962
+ }[];
2963
+ };
2964
+ /**
2965
+ * Structured response from a server command execution
2966
+ */
2967
+ type CliServerResponse = {
2968
+ exitCode: number;
2969
+ outputs: CliServerOutput[];
2970
+ };
2971
+ /**
2972
+ * Metadata about a remote command processor, returned by GET /api/qcli/commands
2973
+ */
2974
+ type CliServerCommandDescriptor = {
2975
+ command: string;
2976
+ description?: string;
2977
+ version?: string;
2978
+ parameters?: {
2979
+ name: string;
2980
+ aliases?: string[];
2981
+ description: string;
2982
+ required: boolean;
2983
+ type: string;
2984
+ defaultValue?: any;
2985
+ }[];
2986
+ processors?: CliServerCommandDescriptor[];
2282
2987
  };
2283
2988
  /**
2284
- * Messages sent from a background service Worker to the main thread.
2989
+ * Server capabilities returned by GET /api/qcli/capabilities
2285
2990
  */
2286
- type CliWorkerOutboundMessage = {
2287
- type: 'log';
2288
- level: 'info' | 'warn' | 'error';
2289
- message: string;
2290
- } | {
2291
- type: 'event';
2292
- event: Omit<ICliServiceEvent, 'timestamp'>;
2293
- } | {
2294
- type: 'state-update';
2295
- patch: Record<string, unknown>;
2296
- } | {
2297
- type: 'status';
2298
- status: CliBackgroundServiceStatus;
2299
- } | {
2300
- type: 'error';
2301
- message: string;
2302
- stack?: string;
2991
+ type CliServerCapabilities = {
2992
+ /** Whether this server supports remote shell access */
2993
+ shell: boolean;
2994
+ /** Server operating system (e.g. "linux", "win32", "darwin") */
2995
+ os?: string;
2996
+ /** Path to the shell binary on the server */
2997
+ shellPath?: string;
2998
+ /** Server version string */
2999
+ version?: string;
3000
+ /** Whether this server supports SSE streaming execution */
3001
+ streaming?: boolean;
3002
+ };
3003
+
3004
+ type MultiServices = {
3005
+ /**
3006
+ * When true, injector returns an array of instances. This is useful to allow multiple
3007
+ * providers spread across many files to provide configuration information to a common token.
3008
+ */
3009
+ multi?: boolean;
3010
+ };
3011
+ type CliValueProvider = MultiServices & {
3012
+ useValue: any;
3013
+ };
3014
+ type CliTypeProvider = MultiServices & {
3015
+ useClass: any;
3016
+ /** DI tokens resolved from the container and passed as constructor arguments. */
3017
+ deps?: any[];
2303
3018
  };
3019
+ type CliFactoryProvider = MultiServices & {
3020
+ useFactory: Function;
3021
+ /** DI tokens resolved from the container and passed as factory arguments. */
3022
+ deps?: any[];
3023
+ };
3024
+ type CliProvider = {
3025
+ provide: any;
3026
+ /** When true, this token is sealed after registration and cannot be overwritten. */
3027
+ sealed?: boolean;
3028
+ /** When true, a new instance is created on every `get()` call instead of returning a singleton. */
3029
+ transient?: boolean;
3030
+ } & (CliValueProvider | CliTypeProvider | CliFactoryProvider) & Record<string, any>;
2304
3031
 
2305
3032
  /**
2306
- * Represents an entry in a file listing.
3033
+ * Represents a user in the CLI
2307
3034
  */
2308
- interface ICliFileEntry {
3035
+ interface ICliUser {
3036
+ /** Unique user identifier */
3037
+ id: string;
3038
+ /** Login name (unique) */
2309
3039
  name: string;
2310
- type: 'file' | 'directory';
2311
- size: number;
2312
- modified: string;
3040
+ /** Email address (unique) */
3041
+ email: string;
3042
+ /** Group IDs this user belongs to */
3043
+ groups: string[];
3044
+ /** Virtual home directory path, e.g. /home/root */
3045
+ homeDir?: string;
3046
+ /** Account creation timestamp */
3047
+ createdAt: number;
3048
+ /** Last modification timestamp */
3049
+ updatedAt: number;
3050
+ /** Whether the account is locked */
3051
+ disabled?: boolean;
3052
+ /** The user must set a new password before doing anything else */
3053
+ mustChangePassword?: boolean;
3054
+ /** Extensible metadata (department, phone, preferences, etc.) */
3055
+ data?: Record<string, any>;
3056
+ /** Timestamp of last successful login */
3057
+ lastLoginAt?: number;
2313
3058
  }
2314
-
2315
3059
  /**
2316
- * Abstraction for file transfer operations.
2317
- * Default implementation uses browser-native APIs (file picker, download dialog).
2318
- * @qodalis/cli-files overrides with virtual FS backed by IndexedDB.
3060
+ * Represents user credentials (shadow pattern — stored separately from ICliUser)
2319
3061
  */
2320
- interface ICliFileTransferService {
2321
- readFile(path: string): Promise<string | null>;
2322
- writeFile(path: string, content: string): Promise<void>;
2323
- listFiles(path: string): Promise<ICliFileEntry[]>;
2324
- exists(path: string): Promise<boolean>;
2325
- downloadToBrowser(filename: string, content: string | Blob): void;
2326
- uploadFromBrowser(accept?: string): Promise<{
2327
- name: string;
2328
- content: string;
2329
- } | null>;
2330
- }
2331
-
2332
- interface ICliDragDropService {
2333
- /** Emits an array of dropped File objects each time files are dropped onto the terminal */
2334
- readonly onFileDrop: Observable<File[]>;
3062
+ interface ICliUserCredentials {
3063
+ /** The user ID this credential belongs to */
3064
+ userId: string;
3065
+ /** SHA-256 hash of salt + password */
3066
+ passwordHash: string;
3067
+ /** Random salt for this user */
3068
+ salt: string;
3069
+ /** Timestamp of last password change */
3070
+ lastChanged: number;
2335
3071
  }
2336
-
2337
3072
  /**
2338
- * Opaque state value carried between lines during syntax highlighting.
2339
- * Encodes context such as "inside a multi-line string" or "inside a block comment"
2340
- * so the highlighter can resume correctly on the next line.
2341
- * `0` typically represents the default/root state.
3073
+ * Represents a group in the CLI
2342
3074
  */
2343
- type LineState = number;
2344
- interface ISyntaxHighlightRule {
2345
- pattern: RegExp;
2346
- tokenType: string;
2347
- captureGroup?: number;
2348
- }
2349
- interface ISyntaxHighlighter {
3075
+ interface ICliGroup {
3076
+ /** Unique group identifier */
2350
3077
  id: string;
2351
- /** Human-readable language name (e.g. 'TypeScript'). Falls back to `id` when absent. */
2352
- name?: string;
2353
- extensions: string[];
2354
- initialState: LineState;
2355
- getRules(state: LineState): ISyntaxHighlightRule[];
2356
- getNextLineState(line: string, currentState: LineState): LineState;
2357
- }
2358
- interface ISyntaxTheme {
3078
+ /** Group name (unique) */
2359
3079
  name: string;
2360
- colors: Record<string, string>;
3080
+ /** Optional group description */
3081
+ description?: string;
3082
+ /** Group creation timestamp */
3083
+ createdAt: number;
3084
+ /**
3085
+ * Permissions granted to every member of the group. Groups act as roles.
3086
+ * Permission strings are `resource:action` (e.g. `users:create`), with
3087
+ * `*` wildcards (`users:*`, `*`). See {@link cliPermissionMatches}.
3088
+ */
3089
+ permissions?: string[];
3090
+ /** Built-in groups (`admin`, `users`) cannot be deleted */
3091
+ builtIn?: boolean;
3092
+ /** Last modification timestamp */
3093
+ updatedAt?: number;
2361
3094
  }
2362
-
2363
3095
  /**
2364
- * Framework-agnostic interface for programmatic control of the CLI panel.
2365
- *
2366
- * CliEngine is referenced as a generic to avoid a circular dependency
2367
- * between @qodalis/cli-core and @qodalis/cli. Framework implementations
2368
- * resolve this to the concrete CliEngine class.
3096
+ * Represents an active user session
2369
3097
  */
2370
- interface ICliPanelRef<TEngine = unknown> {
2371
- /** Expand the panel (sets collapsed=false). Initializes the first tab on first call. */
2372
- open(): void;
2373
- /** Collapse the panel body (sets collapsed=true). */
2374
- collapse(): void;
2375
- toggleCollapse(): void;
2376
- /** Hide the panel to a small viewport-edge tab (reversible via unhide). */
2377
- hide(): void;
2378
- unhide(): void;
2379
- toggleHide(): void;
2380
- /** Destroy the panel (removes from DOM). Irreversible. */
2381
- close(): void;
2382
- maximize(): void;
2383
- restore(): void;
2384
- toggleMaximize(): void;
2385
- /**
2386
- * Set panel dimensions. Only the dimension relevant to the current
2387
- * position is applied immediately (height for top/bottom, width for
2388
- * left/right). The other dimension is stored for when the position changes.
2389
- */
2390
- resize(dimensions: {
2391
- height?: number;
2392
- width?: number;
2393
- }): void;
2394
- setPosition(position: CliPanelPosition): void;
2395
- /** Create a new tab. Returns the new tab's ID. Title defaults to "Terminal {id}". */
2396
- addTab(title?: string): number;
2397
- /** Close a tab by ID. No-op if the ID does not exist. */
2398
- closeTab(tabId: number): void;
2399
- /** Activate a tab by ID. No-op if the ID does not exist. */
2400
- selectTab(tabId: number): void;
2401
- /** Programmatically rename a tab. No-op if the ID does not exist. */
2402
- renameTab(tabId: number, title: string): void;
2403
- /**
2404
- * Split the active pane (or a pane within the specified tab) to create
2405
- * a new pane to the right. Returns the new pane's ID.
2406
- */
2407
- splitPane(tabId?: number): number;
3098
+ interface ICliUserSession {
3099
+ /** The user associated with this session */
3100
+ user: ICliUser;
3101
+ /** Pre-computed display name for the prompt (set by users module) */
3102
+ displayName?: string;
3103
+ /** Timestamp when the session started */
3104
+ loginTime: number;
3105
+ /** Timestamp of last activity */
3106
+ lastActivity: number;
2408
3107
  /**
2409
- * Close a pane by ID. Searches all tabs for the pane.
2410
- * No-op if the ID does not exist.
3108
+ * Effective permissions of the user (union of their groups' permissions),
3109
+ * resolved by the users module at login and refreshed when groups change.
2411
3110
  */
2412
- closePane(paneId: number): void;
2413
- /** Get the engine for a specific pane, or the active pane if omitted. */
2414
- getEngine(paneId?: number): TEngine | undefined;
2415
- getState(): CliPanelState;
2416
- }
2417
- interface CliPanelState {
2418
- collapsed: boolean;
2419
- hidden: boolean;
2420
- maximized: boolean;
2421
- position: CliPanelPosition;
2422
- height: number;
2423
- width: number;
2424
- activeTabId: number;
2425
- activePaneId: number;
2426
- tabs: CliPanelTabState[];
2427
- }
2428
- interface CliPanelTabState {
2429
- id: number;
2430
- title: string;
2431
- panes: CliPanelPaneState[];
2432
- }
2433
- interface CliPanelPaneState {
2434
- id: number;
2435
- widthPercent: number;
3111
+ permissions?: string[];
3112
+ /** Extensible data (tokens, metadata, etc.) */
3113
+ data?: Record<string, any>;
2436
3114
  }
3115
+ /** Fields required to create a new user */
3116
+ type CliAddUser = Omit<ICliUser, 'id' | 'createdAt' | 'updatedAt'>;
3117
+ /** Fields that can be updated on a user */
3118
+ type CliUpdateUser = Partial<Omit<ICliUser, 'id' | 'createdAt'>>;
3119
+
3120
+ declare const enums: {
3121
+ CliForegroundColor: typeof CliForegroundColor;
3122
+ CliBackgroundColor: typeof CliBackgroundColor;
3123
+ CliIcon: typeof CliIcon;
3124
+ CliLogLevel: typeof CliLogLevel;
3125
+ };
2437
3126
 
2438
3127
  /**
2439
3128
  * Registry that tracks loaded web CLI modules (frontend plugins) and dispatches
@@ -2597,12 +3286,45 @@ declare class ServerVersionNegotiator {
2597
3286
  * Returns the negotiated API version and the base path for all subsequent calls,
2598
3287
  * or null if the server is unreachable or incompatible.
2599
3288
  */
2600
- static discover(baseUrl: string): Promise<{
3289
+ static discover(baseUrl: string, headers?: CliHeadersProvider): Promise<{
2601
3290
  apiVersion: number;
2602
3291
  basePath: string;
2603
3292
  } | null>;
2604
3293
  }
2605
3294
 
3295
+ /**
3296
+ * Builds a WebSocket URL that includes authentication credentials from headers
3297
+ * as query parameters. Since the browser WebSocket API does not support custom
3298
+ * request headers, this allows the host server's auth middleware to validate
3299
+ * the connection during the HTTP upgrade handshake.
3300
+ *
3301
+ * All configured headers are forwarded as query parameters prefixed with
3302
+ * `_header_` to avoid collisions with application-level query params.
3303
+ *
3304
+ * Accepts both static header maps and dynamic provider functions.
3305
+ *
3306
+ * **Security note:** Query parameters are visible in browser history, server
3307
+ * access logs, proxy/CDN logs, and the `Referer` header. Tokens placed in the
3308
+ * URL are therefore more exposed than those sent via HTTP headers. This is an
3309
+ * inherent limitation of the browser WebSocket API (no custom headers).
3310
+ * Server-side middleware that reads `_header_*` params should strip them from
3311
+ * logs and avoid forwarding them in redirects.
3312
+ *
3313
+ * @param wsUrl The base WebSocket URL (may already contain query parameters)
3314
+ * @param headers The headers provider from CliServerConfig (may include Authorization, etc.)
3315
+ * @returns The WebSocket URL with auth credentials appended as query parameters
3316
+ */
3317
+ declare function buildAuthenticatedWebSocketUrl(wsUrl: string, headers?: CliHeadersProvider): string;
3318
+ /**
3319
+ * Build the WebSocket URL for a configured server. Prefers upgrade params
3320
+ * from auth providers (e.g. a single-use ticket from the users module); only
3321
+ * when no provider handles the server are headers copied into the URL
3322
+ * (legacy behaviour, see {@link buildAuthenticatedWebSocketUrl}).
3323
+ */
3324
+ declare function resolveServerWebSocketUrl(services: ICliServiceProvider | undefined, serverName: string, wsUrl: string, configHeaders?: CliHeadersProvider): Promise<string>;
3325
+ /** Append query params to a URL that may already have some. */
3326
+ declare function appendQueryParams(url: string, params: Record<string, string>): string;
3327
+
2606
3328
  /**
2607
3329
  * Derives CLI panel CSS custom property values from an xterm ITheme object.
2608
3330
  * Used when `syncTheme` is enabled on the panel config.
@@ -2643,13 +3365,93 @@ declare function nextPanelPosition(current: CliPanelPosition): CliPanelPosition;
2643
3365
  */
2644
3366
  declare function stripAnsi(text: string): string;
2645
3367
  /**
2646
- * Return the visible length of a string, ignoring ANSI escape codes.
2647
- * Useful for padding/alignment calculations in terminal output.
3368
+ * Return the visible (display) width of a string in terminal columns,
3369
+ * ignoring ANSI escape codes and accounting for wide characters (emoji,
3370
+ * East Asian fullwidth, etc.) that occupy two columns.
2648
3371
  * @param text The text potentially containing ANSI codes
2649
- * @returns The number of visible characters
3372
+ * @returns The number of terminal columns the text occupies
2650
3373
  */
2651
3374
  declare function visibleLength(text: string): number;
2652
3375
 
3376
+ /**
3377
+ * Permission strings are `resource:action` (for example `users:create`).
3378
+ * A trailing `*` segment matches anything at that level and below; a `*`
3379
+ * in any other position matches exactly one segment:
3380
+ *
3381
+ * | granted | matches |
3382
+ * |---------------|---------------------------------------|
3383
+ * | `*` | every permission |
3384
+ * | `users:*` | `users:create`, `users:delete`, ... |
3385
+ * | `*:read` | `users:read`, `groups:read`, ... |
3386
+ * | `users:read` | only `users:read` |
3387
+ *
3388
+ * Matching is case-insensitive.
3389
+ */
3390
+ declare function cliPermissionMatches(granted: string, required: string): boolean;
3391
+ /** True when any granted permission matches `required`. */
3392
+ declare function cliHasPermission(granted: readonly string[] | undefined, required: string): boolean;
3393
+ /** The subset of `required` permissions not covered by `granted`. */
3394
+ declare function cliMissingPermissions(granted: readonly string[] | undefined, required: readonly string[]): string[];
3395
+ /** Union of permissions, de-duplicated, preserving first-seen order. */
3396
+ declare function cliMergePermissions(...lists: (readonly string[] | undefined)[]): string[];
3397
+ /**
3398
+ * Built-in permissions used by the users module. Other modules define their
3399
+ * own (e.g. `deploy:run`); any string in `resource:action` form works.
3400
+ */
3401
+ /** Permissions for remote CLI server features. */
3402
+ declare const CliServerPermissions: {
3403
+ /** Open a remote shell on a server (`ssh`) */
3404
+ readonly ShellExecute: "shell:execute";
3405
+ };
3406
+ declare const CliUsersPermissions: {
3407
+ readonly UsersRead: "users:read";
3408
+ readonly UsersCreate: "users:create";
3409
+ readonly UsersUpdate: "users:update";
3410
+ readonly UsersDelete: "users:delete";
3411
+ readonly GroupsRead: "groups:read";
3412
+ readonly GroupsCreate: "groups:create";
3413
+ readonly GroupsUpdate: "groups:update";
3414
+ readonly GroupsDelete: "groups:delete";
3415
+ readonly SessionsRead: "sessions:read";
3416
+ readonly SessionsRevoke: "sessions:revoke";
3417
+ readonly AuditRead: "audit:read";
3418
+ readonly AuditClear: "audit:clear";
3419
+ };
3420
+
3421
+ /**
3422
+ * `metadata.requireAuth` (also implied by `requiredGroups` /
3423
+ * `requiredPermissions`): the command needs a signed-in principal.
3424
+ * Active only when an {@link ICliAuthorizationService} is registered.
3425
+ */
3426
+ declare const cliRequireAuthGuard: ICliCommandGuard;
3427
+ /**
3428
+ * `metadata.requiredGroups`: the principal must belong to at least one group.
3429
+ * Active only when an {@link ICliAuthorizationService} is registered.
3430
+ */
3431
+ declare const cliRequiredGroupsGuard: ICliCommandGuard;
3432
+ /**
3433
+ * `metadata.requiredPermissions`: the principal must hold all permissions.
3434
+ * Active only when an {@link ICliAuthorizationService} is registered.
3435
+ */
3436
+ declare const cliRequiredPermissionsGuard: ICliCommandGuard;
3437
+ /**
3438
+ * Guards enforcing access metadata on processors. Registered by the CLI
3439
+ * engine; they do nothing until an auth module registers an
3440
+ * {@link ICliAuthorizationService}.
3441
+ */
3442
+ declare const cliAccessMetadataGuards: ICliCommandGuard[];
3443
+
3444
+ /**
3445
+ * The server a command should use when the user does not name one.
3446
+ *
3447
+ * Prefers the server manager's choice (the default server, else the first
3448
+ * connected one) so a command does not pick a server that is down or that
3449
+ * rejects the user (a protected server they have not signed in to). Falls
3450
+ * back to the first configured server when no manager is available or
3451
+ * nothing is connected, so callers still get a clear error from that server.
3452
+ */
3453
+ declare function cliDefaultServer(context: Pick<ICliExecutionContext, 'services' | 'options'>): CliServerConfig | undefined;
3454
+
2653
3455
  /**
2654
3456
  * Retrieve a parameter's value from the parsed arguments, checking aliases.
2655
3457
  * @param p The parameter descriptor
@@ -2802,7 +3604,7 @@ interface CliThemeInfo {
2802
3604
 
2803
3605
  declare const DefaultThemeInfos: Record<string, CliThemeInfo>;
2804
3606
 
2805
- declare const LIBRARY_VERSION = "2.0.0-beta.12";
3607
+ declare const LIBRARY_VERSION = "2.0.0-beta.14";
2806
3608
  declare const API_VERSION = 2;
2807
3609
 
2808
3610
  /**
@@ -2863,4 +3665,4 @@ declare class BrowserFileTransferService implements ICliFileTransferService {
2863
3665
  private _pickFile;
2864
3666
  }
2865
3667
 
2866
- export { API_VERSION, BrowserFileTransferService, CANCELLATION_ERROR_MESSAGE, CLI_CONFIGURE_STORE_NAME, CancellablePromise, type CliAddUser, CliBackgroundColor, type CliBackgroundServiceStatus, type CliBackgroundServiceType, type CliDateOptions, type CliEngineSnapshot, type CliFactoryProvider, type CliFilePickerOptions, type CliFileResult, CliForegroundColor, type CliFullScreenOptions, CliIcon, type CliLineOptions, CliLogLevel, CliModuleRegistry, type CliMultiSelectOption, type CliMultiSelectOptions, type CliNumberOptions, type CliOptions, type CliPackageSource, type CliPanelConfig, type CliPanelHideAlignment, type CliPanelPaneState, type CliPanelPosition, type CliPanelState, type CliPanelTabState, type CliPercentageProgressBarUpdateValueOptions, type CliProcessCommand, type CliProcessorMetadata, type CliProvider, type CliSelectOption, type CliSelectOptions, type CliServerCapabilities, type CliServerCommandDescriptor, type CliServerConfig, type CliServerOutput, type CliServerResponse, type CliServiceEventHandler, type CliServiceExecutionMode, type CliServiceLogLevel, type CliState, type CliStateConfiguration, type CliTableOptions, type CliTextAnimatorOptions, type CliTheme, type CliThemeCategory, type CliThemeInfo, type CliTypeProvider, type CliUpdateUser, type CliValueProvider, type CliWorkerInboundMessage, type CliWorkerOutboundMessage, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, DefaultLibraryAuthor, DefaultThemeInfos, DefaultThemes, type DefaultThemesType, type ICliAuthService, ICliAuthService_TOKEN, type ICliBackgroundService, type ICliBackgroundServiceInfo, type ICliBackgroundServiceRegistry, type ICliClipboard, type ICliCommandAuthor, type ICliCommandChildProcessor, type ICliCommandExecutorService, type ICliCommandParameterDescriptor, type ICliCommandProcessor, type ICliCommandProcessorRegistry, type ICliCompletionContext, type ICliCompletionProvider, ICliCompletionProvider_TOKEN, type ICliConfigurableModule, type ICliConfigurationOption, type ICliDragDropService, ICliDragDropService_TOKEN, type ICliExecutionContext, type ICliExecutionProcess, type ICliFileEntry, type ICliFilePickerProvider, ICliFilePickerProvider_TOKEN, type ICliFileTransferService, ICliFileTransferService_TOKEN, type ICliGlobalParameterHandler, type ICliGroup, type ICliGroupsStoreService, ICliGroupsStoreService_TOKEN, type ICliInputReader, type ICliKeyValueStore, type ICliLogger, type ICliManagedInterval, type ICliManagedTimer, type ICliModule, type ICliOwnership, type ICliPanelRef, type ICliPercentageProgressBar, type ICliPermissionService, ICliPermissionService_TOKEN, type ICliPingServerService, type ICliProcessEntry, type ICliProcessRegisterOptions, type ICliProcessRegistry, type ICliProcessorHook, type ICliProgressBar, type ICliServiceContext, type ICliServiceEvent, type ICliServiceLogEntry, type ICliServiceProvider, type ICliSpinner, type ICliStateStore, type ICliTerminalWriter, type ICliTextAnimator, type ICliTranslationService, ICliTranslationService_TOKEN, type ICliUmdModule, type ICliUser, type ICliUserCredentials, type ICliUserSession, type ICliUserSessionService, ICliUserSessionService_TOKEN, type ICliUsersStoreService, ICliUsersStoreService_TOKEN, type ISyntaxHighlightRule, type ISyntaxHighlighter, type ISyntaxTheme, LIBRARY_VERSION, type LineState, ObjectDescriber, type Package, type ServerVersionInfo, ServerVersionNegotiator, type WorkerServiceContext, bootCliModule, bootUmdModule, clearTerminalLine, colorFirstWord, colorizeJson, compareVersions, constants, createWorkerService, delay, derivePanelThemeStyles, enums, formatJson, getConfigValue, getParameterValue, getPluginConfigValue, getRightOfWord, highlightTextWithBg, initializeBrowserEnvironment, loadPanelPosition, nextPanelPosition, resolveConfigurationCategories, satisfiesMinVersion, satisfiesVersionRange, savePanelPosition, stripAnsi, toQueryString, utils, visibleLength };
3668
+ export { API_VERSION, BrowserFileTransferService, CANCELLATION_ERROR_MESSAGE, CLI_CONFIGURE_STORE_NAME, CancellablePromise, type CliAccessAction, CliAccessDeniedError, type CliAddUser, CliBackgroundColor, type CliBackgroundServiceStatus, type CliBackgroundServiceType, type CliCommandGuardResult, type CliDateOptions, type CliEngineSnapshot, type CliFactoryProvider, type CliFilePickerOptions, type CliFileResult, CliForegroundColor, type CliFullScreenOptions, type CliHeadersProvider, CliHttpError, type CliHttpRequestOptions, CliIcon, type CliLineOptions, CliLogLevel, CliModuleRegistry, type CliMultiSelectOption, type CliMultiSelectOptions, type CliNotification, type CliNotificationLevel, type CliNumberOptions, type CliOptions, type CliPackageSource, type CliPanelConfig, type CliPanelHideAlignment, type CliPanelPaneState, type CliPanelPosition, type CliPanelState, type CliPanelTabState, type CliPercentageProgressBarUpdateValueOptions, type CliPermissionDefinition, type CliProcessCommand, type CliProcessorMetadata, type CliProvider, type CliSelectOption, type CliSelectOptions, type CliServerCapabilities, type CliServerCommandDescriptor, type CliServerConfig, type CliServerOutput, CliServerPermissions, type CliServerResponse, type CliServiceEventHandler, type CliServiceExecutionMode, type CliServiceLogLevel, type CliState, type CliStateConfiguration, type CliTableOptions, type CliTextAnimatorOptions, type CliTheme, type CliThemeCategory, type CliThemeInfo, type CliTypeProvider, type CliUpdateUser, CliUsersPermissions, type CliValueProvider, type CliWorkerInboundMessage, type CliWorkerOutboundMessage, DEFAULT_DIR_PERMISSIONS, DEFAULT_FILE_PERMISSIONS, DefaultLibraryAuthor, DefaultThemeInfos, DefaultThemes, type DefaultThemesType, type ICliAuditEntry, type ICliAuditService, ICliAuditService_TOKEN, type ICliAuthService, ICliAuthService_TOKEN, type ICliAuthorizationService, ICliAuthorizationService_TOKEN, type ICliBackgroundService, type ICliBackgroundServiceInfo, type ICliBackgroundServiceRegistry, type ICliClipboard, type ICliCommandAuthor, type ICliCommandChildProcessor, type ICliCommandExecutorService, type ICliCommandGuard, ICliCommandGuard_TOKEN, type ICliCommandParameterDescriptor, type ICliCommandProcessor, type ICliCommandProcessorRegistry, type ICliCompletionContext, type ICliCompletionProvider, ICliCompletionProvider_TOKEN, type ICliConfigurableModule, type ICliConfigurationOption, type ICliDragDropService, ICliDragDropService_TOKEN, type ICliExecutionContext, type ICliExecutionProcess, type ICliFileEntry, type ICliFilePickerProvider, ICliFilePickerProvider_TOKEN, type ICliFileTransferService, ICliFileTransferService_TOKEN, type ICliGlobalParameterHandler, type ICliGroup, type ICliGroupsStoreService, ICliGroupsStoreService_TOKEN, type ICliHttpClient, ICliHttpClient_TOKEN, type ICliInputReader, type ICliKeyValueStore, ICliKeyValueStore_TOKEN, type ICliLogger, type ICliManagedInterval, type ICliManagedTimer, type ICliModule, type ICliNotifier, type ICliOwnership, type ICliPanelRef, type ICliPercentageProgressBar, type ICliPermissionRegistry, ICliPermissionRegistry_TOKEN, type ICliPermissionService, ICliPermissionService_TOKEN, type ICliPingServerService, type ICliPrincipal, type ICliProcessEntry, type ICliProcessRegisterOptions, type ICliProcessRegistry, type ICliProcessorHook, type ICliProgressBar, type ICliProtectedResource, type ICliServerAuthService, ICliServerAuthService_TOKEN, type ICliServerAuthTokenProvider, ICliServerAuthTokenProvider_TOKEN, type ICliServerManager, ICliServerManager_TOKEN, type ICliServiceContext, type ICliServiceEvent, type ICliServiceLogEntry, type ICliServiceProvider, type ICliSessionInfo, type ICliSessionsService, ICliSessionsService_TOKEN, type ICliSpinner, type ICliStateStore, type ICliTerminalWriter, type ICliTextAnimator, type ICliTranslationService, ICliTranslationService_TOKEN, type ICliUmdModule, type ICliUser, type ICliUserCredentials, type ICliUserEvent, type ICliUserEventService, ICliUserEventService_TOKEN, type ICliUserSession, type ICliUserSessionService, ICliUserSessionService_TOKEN, type ICliUsersStoreService, ICliUsersStoreService_TOKEN, type ISyntaxHighlightRule, type ISyntaxHighlighter, type ISyntaxTheme, LIBRARY_VERSION, type LineState, ObjectDescriber, type Package, type ServerVersionInfo, ServerVersionNegotiator, type WorkerServiceContext, appendQueryParams, bootCliModule, bootUmdModule, buildAuthenticatedWebSocketUrl, clearTerminalLine, cliAccessMetadataGuards, cliAuthorize, cliCanAccess, cliDefaultServer, cliEvaluateAccess, cliHasPermission, cliMergePermissions, cliMissingPermissions, cliPermissionMatches, cliRequireAuthGuard, cliRequiredGroupsGuard, cliRequiredPermissionsGuard, colorFirstWord, colorizeJson, compareVersions, constants, createWorkerService, delay, derivePanelThemeStyles, enums, formatJson, getConfigValue, getParameterValue, getPluginConfigValue, getRightOfWord, highlightTextWithBg, initializeBrowserEnvironment, isCliAccessDeniedError, loadPanelPosition, nextPanelPosition, resolveConfigurationCategories, resolveHeaders, resolveServerHeaders, resolveServerWebSocketUrl, satisfiesMinVersion, satisfiesVersionRange, savePanelPosition, stripAnsi, toQueryString, utils, visibleLength };