@qodalis/cli-server-node 1.0.0-beta.1 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +456 -28
  3. package/dist/src/abstractions/cli-command-author.d.ts +2 -0
  4. package/dist/src/abstractions/cli-command-author.d.ts.map +1 -0
  5. package/dist/src/abstractions/cli-command-author.js +7 -0
  6. package/dist/src/abstractions/cli-command-author.js.map +1 -0
  7. package/dist/src/abstractions/cli-command-parameter-descriptor.d.ts +2 -0
  8. package/dist/src/abstractions/cli-command-parameter-descriptor.d.ts.map +1 -0
  9. package/dist/src/abstractions/cli-command-parameter-descriptor.js +6 -0
  10. package/dist/src/abstractions/cli-command-parameter-descriptor.js.map +1 -0
  11. package/dist/src/abstractions/cli-command-processor.d.ts +2 -0
  12. package/dist/src/abstractions/cli-command-processor.d.ts.map +1 -0
  13. package/dist/src/abstractions/cli-command-processor.js +6 -0
  14. package/dist/src/abstractions/cli-command-processor.js.map +1 -0
  15. package/dist/src/abstractions/cli-process-command.d.ts +2 -0
  16. package/dist/src/abstractions/cli-process-command.d.ts.map +1 -0
  17. package/dist/src/abstractions/cli-process-command.js.map +1 -0
  18. package/dist/src/abstractions/index.d.ts +2 -0
  19. package/dist/src/abstractions/index.d.ts.map +1 -0
  20. package/dist/src/abstractions/index.js +10 -0
  21. package/dist/src/abstractions/index.js.map +1 -0
  22. package/dist/src/controllers/cli-controller-v2.d.ts +4 -0
  23. package/dist/src/controllers/cli-controller-v2.d.ts.map +1 -0
  24. package/dist/{controllers/cli-controller.js → src/controllers/cli-controller-v2.js} +9 -5
  25. package/dist/src/controllers/cli-controller-v2.js.map +1 -0
  26. package/dist/src/controllers/cli-controller.d.ts.map +1 -0
  27. package/dist/src/controllers/cli-controller.js +94 -0
  28. package/dist/src/controllers/cli-controller.js.map +1 -0
  29. package/dist/src/controllers/cli-version-controller.d.ts +3 -0
  30. package/dist/src/controllers/cli-version-controller.d.ts.map +1 -0
  31. package/dist/src/controllers/cli-version-controller.js +68 -0
  32. package/dist/src/controllers/cli-version-controller.js.map +1 -0
  33. package/dist/src/controllers/filesystem-controller.d.ts +8 -0
  34. package/dist/src/controllers/filesystem-controller.d.ts.map +1 -0
  35. package/dist/src/controllers/filesystem-controller.js +151 -0
  36. package/dist/src/controllers/filesystem-controller.js.map +1 -0
  37. package/dist/src/create-cli-server.d.ts.map +1 -0
  38. package/dist/{create-cli-server.js → src/create-cli-server.js} +23 -1
  39. package/dist/src/create-cli-server.js.map +1 -0
  40. package/dist/src/extensions/cli-builder.d.ts +18 -0
  41. package/dist/src/extensions/cli-builder.d.ts.map +1 -0
  42. package/dist/src/extensions/cli-builder.js +40 -0
  43. package/dist/src/extensions/cli-builder.js.map +1 -0
  44. package/dist/src/extensions/index.d.ts.map +1 -0
  45. package/dist/src/extensions/index.js.map +1 -0
  46. package/dist/src/filesystem/filesystem-options.d.ts +5 -0
  47. package/dist/src/filesystem/filesystem-options.d.ts.map +1 -0
  48. package/dist/src/filesystem/filesystem-options.js +3 -0
  49. package/dist/src/filesystem/filesystem-options.js.map +1 -0
  50. package/dist/src/filesystem/filesystem-path-validator.d.ts +15 -0
  51. package/dist/src/filesystem/filesystem-path-validator.d.ts.map +1 -0
  52. package/dist/src/filesystem/filesystem-path-validator.js +72 -0
  53. package/dist/src/filesystem/filesystem-path-validator.js.map +1 -0
  54. package/dist/src/filesystem/index.d.ts +3 -0
  55. package/dist/src/filesystem/index.d.ts.map +1 -0
  56. package/dist/src/filesystem/index.js +6 -0
  57. package/dist/src/filesystem/index.js.map +1 -0
  58. package/dist/{index.d.ts → src/index.d.ts} +10 -2
  59. package/dist/src/index.d.ts.map +1 -0
  60. package/dist/src/index.js +70 -0
  61. package/dist/src/index.js.map +1 -0
  62. package/dist/{models → src/models}/cli-server-command-descriptor.d.ts +1 -0
  63. package/dist/src/models/cli-server-command-descriptor.d.ts.map +1 -0
  64. package/dist/src/models/cli-server-command-descriptor.js.map +1 -0
  65. package/dist/src/models/cli-server-output.d.ts.map +1 -0
  66. package/dist/src/models/cli-server-output.js.map +1 -0
  67. package/dist/src/models/cli-server-response.d.ts.map +1 -0
  68. package/dist/src/models/cli-server-response.js.map +1 -0
  69. package/dist/src/models/index.d.ts.map +1 -0
  70. package/dist/{models → src/models}/index.js.map +1 -1
  71. package/dist/src/processors/cli-base64-command-processor.d.ts.map +1 -0
  72. package/dist/src/processors/cli-base64-command-processor.js.map +1 -0
  73. package/dist/src/processors/cli-echo-command-processor.d.ts.map +1 -0
  74. package/dist/src/processors/cli-echo-command-processor.js.map +1 -0
  75. package/dist/src/processors/cli-hash-command-processor.d.ts.map +1 -0
  76. package/dist/src/processors/cli-hash-command-processor.js.map +1 -0
  77. package/dist/src/processors/cli-http-command-processor.d.ts.map +1 -0
  78. package/dist/src/processors/cli-http-command-processor.js.map +1 -0
  79. package/dist/src/processors/cli-status-command-processor.d.ts.map +1 -0
  80. package/dist/src/processors/cli-status-command-processor.js.map +1 -0
  81. package/dist/src/processors/cli-system-command-processor.d.ts.map +1 -0
  82. package/dist/src/processors/cli-system-command-processor.js.map +1 -0
  83. package/dist/src/processors/cli-uuid-command-processor.d.ts.map +1 -0
  84. package/dist/src/processors/cli-uuid-command-processor.js.map +1 -0
  85. package/dist/src/processors/index.d.ts.map +1 -0
  86. package/dist/src/processors/index.js.map +1 -0
  87. package/dist/{server.d.ts.map → src/server.d.ts.map} +1 -1
  88. package/dist/{server.js → src/server.js} +7 -4
  89. package/dist/src/server.js.map +1 -0
  90. package/dist/src/services/cli-command-executor-service.d.ts.map +1 -0
  91. package/dist/src/services/cli-command-executor-service.js.map +1 -0
  92. package/dist/src/services/cli-command-registry.d.ts.map +1 -0
  93. package/dist/src/services/cli-command-registry.js.map +1 -0
  94. package/dist/{services → src/services}/cli-event-socket-manager.d.ts +5 -1
  95. package/dist/src/services/cli-event-socket-manager.d.ts.map +1 -0
  96. package/dist/src/services/cli-event-socket-manager.js +92 -0
  97. package/dist/src/services/cli-event-socket-manager.js.map +1 -0
  98. package/dist/src/services/cli-response-builder.d.ts.map +1 -0
  99. package/dist/src/services/cli-response-builder.js.map +1 -0
  100. package/dist/src/services/cli-shell-session-manager.d.ts +8 -0
  101. package/dist/src/services/cli-shell-session-manager.d.ts.map +1 -0
  102. package/dist/src/services/cli-shell-session-manager.js +168 -0
  103. package/dist/src/services/cli-shell-session-manager.js.map +1 -0
  104. package/dist/{services → src/services}/index.d.ts +1 -0
  105. package/dist/src/services/index.d.ts.map +1 -0
  106. package/dist/{services → src/services}/index.js +3 -1
  107. package/dist/src/services/index.js.map +1 -0
  108. package/package.json +25 -6
  109. package/dist/abstractions/cli-command-author.d.ts +0 -11
  110. package/dist/abstractions/cli-command-author.d.ts.map +0 -1
  111. package/dist/abstractions/cli-command-author.js +0 -14
  112. package/dist/abstractions/cli-command-author.js.map +0 -1
  113. package/dist/abstractions/cli-command-parameter-descriptor.d.ts +0 -18
  114. package/dist/abstractions/cli-command-parameter-descriptor.d.ts.map +0 -1
  115. package/dist/abstractions/cli-command-parameter-descriptor.js +0 -21
  116. package/dist/abstractions/cli-command-parameter-descriptor.js.map +0 -1
  117. package/dist/abstractions/cli-command-processor.d.ts +0 -26
  118. package/dist/abstractions/cli-command-processor.d.ts.map +0 -1
  119. package/dist/abstractions/cli-command-processor.js +0 -14
  120. package/dist/abstractions/cli-command-processor.js.map +0 -1
  121. package/dist/abstractions/cli-process-command.d.ts +0 -9
  122. package/dist/abstractions/cli-process-command.d.ts.map +0 -1
  123. package/dist/abstractions/cli-process-command.js.map +0 -1
  124. package/dist/abstractions/index.d.ts +0 -5
  125. package/dist/abstractions/index.d.ts.map +0 -1
  126. package/dist/abstractions/index.js +0 -11
  127. package/dist/abstractions/index.js.map +0 -1
  128. package/dist/controllers/cli-controller.d.ts.map +0 -1
  129. package/dist/controllers/cli-controller.js.map +0 -1
  130. package/dist/create-cli-server.d.ts.map +0 -1
  131. package/dist/create-cli-server.js.map +0 -1
  132. package/dist/extensions/cli-builder.d.ts +0 -9
  133. package/dist/extensions/cli-builder.d.ts.map +0 -1
  134. package/dist/extensions/cli-builder.js +0 -18
  135. package/dist/extensions/cli-builder.js.map +0 -1
  136. package/dist/extensions/index.d.ts.map +0 -1
  137. package/dist/extensions/index.js.map +0 -1
  138. package/dist/index.d.ts.map +0 -1
  139. package/dist/index.js +0 -40
  140. package/dist/index.js.map +0 -1
  141. package/dist/models/cli-server-command-descriptor.d.ts.map +0 -1
  142. package/dist/models/cli-server-command-descriptor.js.map +0 -1
  143. package/dist/models/cli-server-output.d.ts.map +0 -1
  144. package/dist/models/cli-server-output.js.map +0 -1
  145. package/dist/models/cli-server-response.d.ts.map +0 -1
  146. package/dist/models/cli-server-response.js.map +0 -1
  147. package/dist/models/index.d.ts.map +0 -1
  148. package/dist/processors/cli-base64-command-processor.d.ts.map +0 -1
  149. package/dist/processors/cli-base64-command-processor.js.map +0 -1
  150. package/dist/processors/cli-echo-command-processor.d.ts.map +0 -1
  151. package/dist/processors/cli-echo-command-processor.js.map +0 -1
  152. package/dist/processors/cli-hash-command-processor.d.ts.map +0 -1
  153. package/dist/processors/cli-hash-command-processor.js.map +0 -1
  154. package/dist/processors/cli-http-command-processor.d.ts.map +0 -1
  155. package/dist/processors/cli-http-command-processor.js.map +0 -1
  156. package/dist/processors/cli-status-command-processor.d.ts.map +0 -1
  157. package/dist/processors/cli-status-command-processor.js.map +0 -1
  158. package/dist/processors/cli-system-command-processor.d.ts.map +0 -1
  159. package/dist/processors/cli-system-command-processor.js.map +0 -1
  160. package/dist/processors/cli-uuid-command-processor.d.ts.map +0 -1
  161. package/dist/processors/cli-uuid-command-processor.js.map +0 -1
  162. package/dist/processors/index.d.ts.map +0 -1
  163. package/dist/processors/index.js.map +0 -1
  164. package/dist/server.js.map +0 -1
  165. package/dist/services/cli-command-executor-service.d.ts.map +0 -1
  166. package/dist/services/cli-command-executor-service.js.map +0 -1
  167. package/dist/services/cli-command-registry.d.ts.map +0 -1
  168. package/dist/services/cli-command-registry.js.map +0 -1
  169. package/dist/services/cli-event-socket-manager.d.ts.map +0 -1
  170. package/dist/services/cli-event-socket-manager.js +0 -60
  171. package/dist/services/cli-event-socket-manager.js.map +0 -1
  172. package/dist/services/cli-response-builder.d.ts.map +0 -1
  173. package/dist/services/cli-response-builder.js.map +0 -1
  174. package/dist/services/index.d.ts.map +0 -1
  175. package/dist/services/index.js.map +0 -1
  176. /package/dist/{abstractions → src/abstractions}/cli-process-command.js +0 -0
  177. /package/dist/{controllers → src/controllers}/cli-controller.d.ts +0 -0
  178. /package/dist/{create-cli-server.d.ts → src/create-cli-server.d.ts} +0 -0
  179. /package/dist/{extensions → src/extensions}/index.d.ts +0 -0
  180. /package/dist/{extensions → src/extensions}/index.js +0 -0
  181. /package/dist/{models → src/models}/cli-server-command-descriptor.js +0 -0
  182. /package/dist/{models → src/models}/cli-server-output.d.ts +0 -0
  183. /package/dist/{models → src/models}/cli-server-output.js +0 -0
  184. /package/dist/{models → src/models}/cli-server-response.d.ts +0 -0
  185. /package/dist/{models → src/models}/cli-server-response.js +0 -0
  186. /package/dist/{models → src/models}/index.d.ts +0 -0
  187. /package/dist/{models → src/models}/index.js +0 -0
  188. /package/dist/{processors → src/processors}/cli-base64-command-processor.d.ts +0 -0
  189. /package/dist/{processors → src/processors}/cli-base64-command-processor.js +0 -0
  190. /package/dist/{processors → src/processors}/cli-echo-command-processor.d.ts +0 -0
  191. /package/dist/{processors → src/processors}/cli-echo-command-processor.js +0 -0
  192. /package/dist/{processors → src/processors}/cli-hash-command-processor.d.ts +0 -0
  193. /package/dist/{processors → src/processors}/cli-hash-command-processor.js +0 -0
  194. /package/dist/{processors → src/processors}/cli-http-command-processor.d.ts +0 -0
  195. /package/dist/{processors → src/processors}/cli-http-command-processor.js +0 -0
  196. /package/dist/{processors → src/processors}/cli-status-command-processor.d.ts +0 -0
  197. /package/dist/{processors → src/processors}/cli-status-command-processor.js +0 -0
  198. /package/dist/{processors → src/processors}/cli-system-command-processor.d.ts +0 -0
  199. /package/dist/{processors → src/processors}/cli-system-command-processor.js +0 -0
  200. /package/dist/{processors → src/processors}/cli-uuid-command-processor.d.ts +0 -0
  201. /package/dist/{processors → src/processors}/cli-uuid-command-processor.js +0 -0
  202. /package/dist/{processors → src/processors}/index.d.ts +0 -0
  203. /package/dist/{processors → src/processors}/index.js +0 -0
  204. /package/dist/{server.d.ts → src/server.d.ts} +0 -0
  205. /package/dist/{services → src/services}/cli-command-executor-service.d.ts +0 -0
  206. /package/dist/{services → src/services}/cli-command-executor-service.js +0 -0
  207. /package/dist/{services → src/services}/cli-command-registry.d.ts +0 -0
  208. /package/dist/{services → src/services}/cli-command-registry.js +0 -0
  209. /package/dist/{services → src/services}/cli-response-builder.d.ts +0 -0
  210. /package/dist/{services → src/services}/cli-response-builder.js +0 -0
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Qodalis Solutions
3
+ Copyright (c) 2025-2026 Qodalis Solutions
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Qodalis CLI Server (Node.js)
2
2
 
3
- A Node.js CLI server framework for the [Qodalis CLI](https://github.com/qodalis-solutions/angular-web-cli) ecosystem. Built with Express.
3
+ A Node.js CLI server framework for the [Qodalis CLI](https://github.com/qodalis-solutions/web-cli) ecosystem. Build custom server-side commands that integrate with the Qodalis web terminal.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,25 +8,42 @@ A Node.js CLI server framework for the [Qodalis CLI](https://github.com/qodalis-
8
8
  npm install @qodalis/cli-server-node
9
9
  ```
10
10
 
11
+ The package exports all types, interfaces, base classes, and built-in processors. TypeScript declarations are included.
12
+
13
+ ### Plugin Authors
14
+
15
+ If you're building a command processor plugin and don't need the server runtime (Express, WebSocket), install the abstractions package instead:
16
+
17
+ ```bash
18
+ npm install @qodalis/cli-server-abstractions
19
+ ```
20
+
21
+ This gives you `CliCommandProcessor`, `CliProcessCommand`, `CliCommandParameterDescriptor`, and all other base types with **zero dependencies**. See [`@qodalis/cli-server-abstractions`](https://www.npmjs.com/package/@qodalis/cli-server-abstractions) for details.
22
+
11
23
  ## Quick Start
12
24
 
13
25
  ### As a Library
14
26
 
15
27
  ```typescript
16
- import { createCliServer, CliCommandProcessor, CliProcessCommand } from '@qodalis/cli-server-node';
28
+ import {
29
+ createCliServer,
30
+ CliCommandProcessor,
31
+ CliProcessCommand,
32
+ } from '@qodalis/cli-server-node';
17
33
 
18
- class MyCommandProcessor extends CliCommandProcessor {
34
+ class GreetProcessor extends CliCommandProcessor {
19
35
  command = 'greet';
20
36
  description = 'Says hello';
21
37
 
22
38
  async handleAsync(command: CliProcessCommand): Promise<string> {
23
- return 'Hello from my server!';
39
+ const name = command.value ?? 'World';
40
+ return `Hello, ${name}!`;
24
41
  }
25
42
  }
26
43
 
27
44
  const { app, eventSocketManager } = createCliServer({
28
45
  configure: (builder) => {
29
- builder.addProcessor(new MyCommandProcessor());
46
+ builder.addProcessor(new GreetProcessor());
30
47
  },
31
48
  });
32
49
 
@@ -55,16 +72,40 @@ Or with environment variables:
55
72
  PORT=9000 npx qodalis-cli-server
56
73
  ```
57
74
 
58
- ## API Endpoints
75
+ ## Creating Custom Command Processors
59
76
 
60
- | Method | Path | Description |
61
- |--------|------|-------------|
62
- | GET | `/api/cli/version` | Server version |
63
- | GET | `/api/cli/commands` | List available commands |
64
- | POST | `/api/cli/execute` | Execute a command |
65
- | WS | `/ws/cli/events` | WebSocket event channel |
77
+ ### Simple Command
78
+
79
+ Extend `CliCommandProcessor` and implement `command`, `description`, and `handleAsync`:
80
+
81
+ ```typescript
82
+ import { CliCommandProcessor, CliProcessCommand } from '@qodalis/cli-server-node';
83
+
84
+ class EchoProcessor extends CliCommandProcessor {
85
+ command = 'echo';
86
+ description = 'Echoes input text back';
87
+
88
+ async handleAsync(command: CliProcessCommand): Promise<string> {
89
+ return command.value ?? 'Usage: echo <text>';
90
+ }
91
+ }
92
+ ```
66
93
 
67
- ## Creating Command Processors
94
+ Register it during server creation:
95
+
96
+ ```typescript
97
+ const { app, eventSocketManager } = createCliServer({
98
+ configure: (builder) => {
99
+ builder
100
+ .addProcessor(new EchoProcessor())
101
+ .addProcessor(new AnotherProcessor()); // fluent chaining
102
+ },
103
+ });
104
+ ```
105
+
106
+ ### Command with Parameters
107
+
108
+ Declare parameters with names, types, aliases, and defaults. The CLI client uses this metadata for autocompletion and validation.
68
109
 
69
110
  ```typescript
70
111
  import {
@@ -73,13 +114,25 @@ import {
73
114
  CliProcessCommand,
74
115
  } from '@qodalis/cli-server-node';
75
116
 
76
- class TimeCommandProcessor extends CliCommandProcessor {
117
+ class TimeProcessor extends CliCommandProcessor {
77
118
  command = 'time';
78
119
  description = 'Shows the current server time';
79
120
 
80
121
  parameters = [
81
- new CliCommandParameterDescriptor('utc', 'Show time in UTC', false, 'boolean'),
82
- new CliCommandParameterDescriptor('format', 'Date/time format string', false, 'string', ['-f'], 'yyyy-MM-dd HH:mm:ss'),
122
+ new CliCommandParameterDescriptor(
123
+ 'utc', // name
124
+ 'Show UTC time', // description
125
+ false, // required
126
+ 'boolean', // type
127
+ ),
128
+ new CliCommandParameterDescriptor(
129
+ 'format', // name
130
+ 'Date/time format string', // description
131
+ false, // required
132
+ 'string', // type
133
+ ['-f'], // aliases
134
+ 'ISO', // defaultValue
135
+ ),
83
136
  ];
84
137
 
85
138
  async handleAsync(command: CliProcessCommand): Promise<string> {
@@ -90,10 +143,52 @@ class TimeCommandProcessor extends CliCommandProcessor {
90
143
  }
91
144
  ```
92
145
 
146
+ Parameter types: `'string'`, `'number'`, `'boolean'`.
147
+
93
148
  ### Sub-commands
94
149
 
150
+ Nest processors to create command hierarchies like `math add --a 5 --b 3`:
151
+
95
152
  ```typescript
96
- class MathCommandProcessor extends CliCommandProcessor {
153
+ import {
154
+ CliCommandProcessor,
155
+ CliCommandParameterDescriptor,
156
+ CliProcessCommand,
157
+ } from '@qodalis/cli-server-node';
158
+
159
+ class MathAddProcessor extends CliCommandProcessor {
160
+ command = 'add';
161
+ description = 'Adds two numbers';
162
+
163
+ parameters = [
164
+ new CliCommandParameterDescriptor('a', 'First number', true, 'number'),
165
+ new CliCommandParameterDescriptor('b', 'Second number', true, 'number'),
166
+ ];
167
+
168
+ async handleAsync(command: CliProcessCommand): Promise<string> {
169
+ const a = Number(command.args?.a);
170
+ const b = Number(command.args?.b);
171
+ return `${a} + ${b} = ${a + b}`;
172
+ }
173
+ }
174
+
175
+ class MathMultiplyProcessor extends CliCommandProcessor {
176
+ command = 'multiply';
177
+ description = 'Multiplies two numbers';
178
+
179
+ parameters = [
180
+ new CliCommandParameterDescriptor('a', 'First number', true, 'number'),
181
+ new CliCommandParameterDescriptor('b', 'Second number', true, 'number'),
182
+ ];
183
+
184
+ async handleAsync(command: CliProcessCommand): Promise<string> {
185
+ const a = Number(command.args?.a);
186
+ const b = Number(command.args?.b);
187
+ return `${a} * ${b} = ${a * b}`;
188
+ }
189
+ }
190
+
191
+ class MathProcessor extends CliCommandProcessor {
97
192
  command = 'math';
98
193
  description = 'Performs basic math operations';
99
194
  allowUnlistedCommands = false;
@@ -109,14 +204,314 @@ class MathCommandProcessor extends CliCommandProcessor {
109
204
  }
110
205
  ```
111
206
 
207
+ ## Modules
208
+
209
+ Modules group related command processors into a reusable unit. Implement `ICliModule` (or extend the `CliModule` base class) to bundle processors under a single name and version.
210
+
211
+ ### Defining a Module
212
+
213
+ ```typescript
214
+ import { CliModule, CliCommandProcessor, CliProcessCommand, ICliCommandProcessor } from '@qodalis/cli-server-node';
215
+
216
+ class WeatherCurrentProcessor extends CliCommandProcessor {
217
+ command = 'current';
218
+ description = 'Shows current weather conditions';
219
+
220
+ async handleAsync(command: CliProcessCommand): Promise<string> {
221
+ return `Weather: Sunny, 22°C`;
222
+ }
223
+ }
224
+
225
+ class WeatherForecastProcessor extends CliCommandProcessor {
226
+ command = 'forecast';
227
+ description = 'Shows a 3-day weather forecast';
228
+
229
+ async handleAsync(command: CliProcessCommand): Promise<string> {
230
+ return `Forecast: Sunny for 3 days`;
231
+ }
232
+ }
233
+
234
+ class CliWeatherCommandProcessor extends CliCommandProcessor {
235
+ command = 'weather';
236
+ description = 'Shows weather information for a location';
237
+ processors: ICliCommandProcessor[] = [
238
+ new WeatherCurrentProcessor(),
239
+ new WeatherForecastProcessor(),
240
+ ];
241
+
242
+ async handleAsync(command: CliProcessCommand): Promise<string> {
243
+ return `Weather: Sunny, 22°C`;
244
+ }
245
+ }
246
+
247
+ export class WeatherModule extends CliModule {
248
+ name = 'weather';
249
+ version = '1.0.0';
250
+ description = 'Provides weather information commands';
251
+ processors: ICliCommandProcessor[] = [new CliWeatherCommandProcessor()];
252
+ }
253
+ ```
254
+
255
+ ### Registering a Module
256
+
257
+ ```typescript
258
+ const { app, eventSocketManager } = createCliServer({
259
+ configure: (builder) => {
260
+ builder.addModule(new WeatherModule());
261
+ },
262
+ });
263
+ ```
264
+
265
+ `addModule()` iterates over the module's `processors` and registers each one, just like calling `addProcessor()` for each individually.
266
+
267
+ ### ICliModule Interface
268
+
269
+ | Member | Type | Description |
270
+ |--------|------|-------------|
271
+ | `name` | `string` | Unique module identifier |
272
+ | `version` | `string` | Module version |
273
+ | `description` | `string` | Short description |
274
+ | `author` | `ICliCommandAuthor` | Author metadata (defaults to library author) |
275
+ | `processors` | `ICliCommandProcessor[]` | Command processors provided by the module |
276
+
277
+ ### Example: Weather Module
278
+
279
+ The repository includes a weather module under `plugins/weather/` as a reference implementation. It registers a `weather` command with `current` and `forecast` sub-commands, using the [wttr.in](https://wttr.in) API:
280
+
281
+ ```
282
+ weather # Shows current weather (default: London)
283
+ weather current London # Current conditions for London
284
+ weather forecast --location Paris # 3-day forecast for Paris
285
+ ```
286
+
287
+ ## Command Input
288
+
289
+ Every processor receives a `CliProcessCommand` with the parsed command input:
290
+
291
+ | Property | Type | Description |
292
+ |----------|------|-------------|
293
+ | `command` | `string` | Command name (e.g., `"time"`) |
294
+ | `value` | `string \| undefined` | Positional argument (e.g., `"hello"` in `echo hello`) |
295
+ | `args` | `Record<string, any>` | Named parameters (e.g., `--format "HH:mm"`) |
296
+ | `chainCommands` | `string[]` | Sub-command chain (e.g., `["add"]` in `math add`) |
297
+ | `rawCommand` | `string` | Original unprocessed input |
298
+ | `data` | `any` | Arbitrary data payload from the client |
299
+
300
+ ## API Versioning
301
+
302
+ Processors declare which API version they target. The default is version 1.
303
+
304
+ ```typescript
305
+ class DashboardProcessor extends CliCommandProcessor {
306
+ command = 'dashboard';
307
+ description = 'Server dashboard (v2 only)';
308
+ apiVersion = 2;
309
+
310
+ async handleAsync(command: CliProcessCommand): Promise<string> {
311
+ return 'Dashboard data...';
312
+ }
313
+ }
314
+ ```
315
+
316
+ The server exposes versioned endpoints:
317
+
318
+ | Method | Path | Description |
319
+ |--------|------|-------------|
320
+ | GET | `/api/cli/versions` | Version discovery (supported versions, preferred version) |
321
+ | GET | `/api/v1/cli/version` | V1 server version |
322
+ | GET | `/api/v1/cli/commands` | V1 commands (all processors) |
323
+ | POST | `/api/v1/cli/execute` | V1 execute |
324
+ | GET | `/api/v2/cli/version` | V2 server version |
325
+ | GET | `/api/v2/cli/commands` | V2 commands (only `apiVersion >= 2`) |
326
+ | POST | `/api/v2/cli/execute` | V2 execute |
327
+ | WS | `/ws/cli/events` | WebSocket events (also `/ws/v1/cli/events`, `/ws/v2/cli/events`) |
328
+
329
+ The Qodalis CLI client auto-negotiates the highest mutually supported version via the `/api/cli/versions` discovery endpoint.
330
+
331
+ ## Processor Base Class Reference
332
+
333
+ `CliCommandProcessor` provides these members:
334
+
335
+ | Member | Type | Default | Description |
336
+ |--------|------|---------|-------------|
337
+ | `command` | `string` | (required) | Command name |
338
+ | `description` | `string` | (required) | Help text shown to users |
339
+ | `handleAsync` | method | (required) | Execution logic |
340
+ | `parameters` | `ICliCommandParameterDescriptor[]` | `undefined` | Declared parameters |
341
+ | `processors` | `ICliCommandProcessor[]` | `undefined` | Sub-commands |
342
+ | `allowUnlistedCommands` | `boolean` | `undefined` | Accept sub-commands not in `processors` |
343
+ | `valueRequired` | `boolean` | `undefined` | Require a positional value |
344
+ | `version` | `string` | `'1.0.0'` | Processor version string |
345
+ | `apiVersion` | `number` | `1` | Target API version |
346
+ | `author` | `ICliCommandAuthor` | default author | Author metadata (name, email) |
347
+
348
+ ## Server Options
349
+
350
+ ```typescript
351
+ interface CliServerOptions {
352
+ basePath?: string; // API base path (default: '/api/cli')
353
+ cors?: boolean | cors.CorsOptions; // CORS config (default: false)
354
+ configure?: (builder: CliBuilder) => void; // Processor registration
355
+ }
356
+ ```
357
+
358
+ `createCliServer()` returns:
359
+
360
+ ```typescript
361
+ {
362
+ app: Express; // Configured Express app
363
+ registry: CliCommandRegistry; // Processor registry
364
+ builder: CliBuilder; // Registration builder
365
+ eventSocketManager: CliEventSocketManager; // WebSocket manager
366
+ }
367
+ ```
368
+
369
+ ## Exported Types
370
+
371
+ All types are exported from the package root for TypeScript consumers:
372
+
373
+ ```typescript
374
+ // Abstractions (for creating custom processors and modules)
375
+ import {
376
+ ICliCommandProcessor,
377
+ CliCommandProcessor,
378
+ ICliCommandParameterDescriptor,
379
+ CliCommandParameterDescriptor,
380
+ CliProcessCommand,
381
+ ICliCommandAuthor,
382
+ CliCommandAuthor,
383
+ ICliModule,
384
+ CliModule,
385
+ } from '@qodalis/cli-server-node';
386
+
387
+ // Models
388
+ import {
389
+ CliServerResponse,
390
+ CliServerOutput,
391
+ CliServerCommandDescriptor,
392
+ } from '@qodalis/cli-server-node';
393
+
394
+ // Services (for advanced integration)
395
+ import {
396
+ ICliCommandRegistry,
397
+ CliCommandRegistry,
398
+ ICliCommandExecutorService,
399
+ CliCommandExecutorService,
400
+ ICliResponseBuilder,
401
+ CliResponseBuilder,
402
+ CliEventSocketManager,
403
+ } from '@qodalis/cli-server-node';
404
+
405
+ // Factory
406
+ import {
407
+ createCliServer,
408
+ CliServerOptions,
409
+ } from '@qodalis/cli-server-node';
410
+ ```
411
+
412
+ ## File Storage
413
+
414
+ The server includes a pluggable file storage system exposed at `/api/cli/fs/*`. Enable it with `setFileStorageProvider()` and choose a storage backend.
415
+
416
+ ### Filesystem API Endpoints
417
+
418
+ | Method | Path | Description |
419
+ |--------|------|-------------|
420
+ | GET | `/api/cli/fs/ls?path=/` | List directory contents |
421
+ | GET | `/api/cli/fs/cat?path=/file.txt` | Read file content |
422
+ | GET | `/api/cli/fs/stat?path=/file.txt` | File/directory metadata |
423
+ | GET | `/api/cli/fs/download?path=/file.txt` | Download file |
424
+ | POST | `/api/cli/fs/upload` | Upload file (multipart) |
425
+ | POST | `/api/cli/fs/mkdir` | Create directory |
426
+ | DELETE | `/api/cli/fs/rm?path=/file.txt` | Delete file or directory |
427
+
428
+ ### Storage Providers
429
+
430
+ ```typescript
431
+ import { InMemoryFileStorageProvider, OsFileStorageProvider } from '@qodalis/cli-server-plugin-filesystem';
432
+ import { JsonFileStorageProvider } from '@qodalis/cli-server-plugin-filesystem-json';
433
+ import { SqliteFileStorageProvider } from '@qodalis/cli-server-plugin-filesystem-sqlite';
434
+ import { S3FileStorageProvider } from '@qodalis/cli-server-plugin-filesystem-s3';
435
+
436
+ const { app, eventSocketManager } = createCliServer({
437
+ configure: (builder) => {
438
+ // In-memory (default) — files lost on restart
439
+ builder.setFileStorageProvider(new InMemoryFileStorageProvider());
440
+
441
+ // OS filesystem
442
+ builder.setFileStorageProvider(new OsFileStorageProvider());
443
+
444
+ // JSON file — persists to a single JSON file
445
+ builder.setFileStorageProvider(
446
+ new JsonFileStorageProvider({ filePath: './data/files.json' }),
447
+ );
448
+
449
+ // SQLite — persists to a SQLite database
450
+ builder.setFileStorageProvider(
451
+ new SqliteFileStorageProvider({ dbPath: './data/files.db' }),
452
+ );
453
+
454
+ // Amazon S3
455
+ builder.setFileStorageProvider(
456
+ new S3FileStorageProvider({
457
+ bucket: 'my-cli-files',
458
+ region: 'us-east-1',
459
+ prefix: 'uploads/',
460
+ credentials: {
461
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
462
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
463
+ },
464
+ }),
465
+ );
466
+ },
467
+ });
468
+ ```
469
+
470
+ ### Custom Provider
471
+
472
+ Implement `IFileStorageProvider` to add your own backend:
473
+
474
+ ```typescript
475
+ import { IFileStorageProvider, FileEntry, FileStat } from '@qodalis/cli-server-node';
476
+
477
+ class MyProvider implements IFileStorageProvider {
478
+ readonly name = 'my-provider';
479
+ async list(path: string): Promise<FileEntry[]> { /* ... */ }
480
+ async readFile(path: string): Promise<string> { /* ... */ }
481
+ async writeFile(path: string, content: string | Buffer): Promise<void> { /* ... */ }
482
+ async stat(path: string): Promise<FileStat> { /* ... */ }
483
+ async mkdir(path: string, recursive?: boolean): Promise<void> { /* ... */ }
484
+ async remove(path: string, recursive?: boolean): Promise<void> { /* ... */ }
485
+ async copy(src: string, dest: string): Promise<void> { /* ... */ }
486
+ async move(src: string, dest: string): Promise<void> { /* ... */ }
487
+ async exists(path: string): Promise<boolean> { /* ... */ }
488
+ async getDownloadStream(path: string): Promise<Readable> { /* ... */ }
489
+ async uploadFile(path: string, content: Buffer): Promise<void> { /* ... */ }
490
+ }
491
+
492
+ builder.setFileStorageProvider(new MyProvider());
493
+ ```
494
+
495
+ ## Built-in Processors
496
+
497
+ These processors ship with the library and are included in the standalone server:
498
+
499
+ | Command | Description |
500
+ |---------|-------------|
501
+ | `echo` | Echoes input text |
502
+ | `status` | Server status (uptime, OS info) |
503
+ | `system` | Detailed system information (memory, CPU, uptime) |
504
+ | `http` | HTTP request operations |
505
+ | `hash` | Hash computation (MD5, SHA1, SHA256, SHA512) |
506
+ | `base64` | Base64 encode/decode (sub-commands) |
507
+ | `uuid` | UUID generation |
508
+
112
509
  ## Docker
113
510
 
114
511
  ```bash
115
512
  docker run -p 8047:8047 ghcr.io/qodalis-solutions/cli-server-node
116
513
  ```
117
514
 
118
- The Docker image runs a demo server with sample processors (echo, status, time, hello, math).
119
-
120
515
  ## Demo
121
516
 
122
517
  ```bash
@@ -126,19 +521,52 @@ npm start
126
521
  # Server starts on http://localhost:8047
127
522
  ```
128
523
 
524
+ ## Testing
525
+
526
+ ```bash
527
+ npm test # Run test suite (Vitest)
528
+ npm run test:watch # Watch mode
529
+ ```
530
+
129
531
  ## Project Structure
130
532
 
131
533
  ```
534
+ packages/
535
+ abstractions/ # @qodalis/cli-server-abstractions (zero-dep)
536
+ src/
537
+ cli-command-processor.ts # ICliCommandProcessor interface & base class
538
+ cli-module.ts # ICliModule interface & base class
539
+ cli-process-command.ts # Command input model
540
+ cli-command-parameter-descriptor.ts # Parameter declaration
541
+ cli-command-author.ts # Author metadata
542
+ plugins/
543
+ filesystem/ # Core file storage abstraction (IFileStorageProvider, InMemory, OS)
544
+ filesystem-json/ # JSON file persistence provider
545
+ filesystem-sqlite/ # SQLite persistence provider (better-sqlite3)
546
+ filesystem-s3/ # Amazon S3 storage provider (@aws-sdk/client-s3)
547
+ weather/ # Weather module (example plugin)
132
548
  src/
133
- abstractions/ # ICliCommandProcessor, CliProcessCommand, parameter descriptors
134
- models/ # CliServerOutput, CliServerResponse, command descriptors
135
- services/ # Registry, executor, response builder, WebSocket manager
136
- controllers/ # Express router for /api/cli routes
137
- extensions/ # CliBuilder fluent API
138
- processors/ # Built-in echo and status processors
139
- create-cli-server.ts # Factory function for standalone/library use
140
- server.ts # Standalone entry point
141
- demo/ # Demo app with 5 sample processors
549
+ abstractions/ # Re-exports from @qodalis/cli-server-abstractions
550
+ models/
551
+ cli-server-response.ts # Response wrapper (exitCode + outputs)
552
+ cli-server-output.ts # Output types (text, table, list, json, key-value)
553
+ cli-server-command-descriptor.ts # Command metadata for /commands endpoint
554
+ services/
555
+ cli-command-registry.ts # Processor registry and lookup
556
+ cli-command-executor-service.ts # Command execution pipeline
557
+ cli-response-builder.ts # Structured output builder
558
+ cli-event-socket-manager.ts # WebSocket event broadcasting
559
+ controllers/
560
+ cli-controller.ts # V1 REST API (/api/v1/cli)
561
+ cli-controller-v2.ts # V2 REST API (/api/v2/cli)
562
+ cli-version-controller.ts # Version discovery (/api/cli/versions)
563
+ extensions/
564
+ cli-builder.ts # Fluent registration API (addProcessor, addModule)
565
+ processors/ # Built-in processors
566
+ create-cli-server.ts # Factory function
567
+ server.ts # Standalone CLI entry point
568
+ index.ts # Package exports
569
+ demo/ # Demo app with sample processors
142
570
  ```
143
571
 
144
572
  ## License
@@ -0,0 +1,2 @@
1
+ export { ICliCommandAuthor, CliCommandAuthor, DefaultLibraryAuthor } from '@qodalis/cli-server-abstractions';
2
+ //# sourceMappingURL=cli-command-author.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-command-author.d.ts","sourceRoot":"","sources":["../../../src/abstractions/cli-command-author.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultLibraryAuthor = exports.CliCommandAuthor = void 0;
4
+ var cli_server_abstractions_1 = require("@qodalis/cli-server-abstractions");
5
+ Object.defineProperty(exports, "CliCommandAuthor", { enumerable: true, get: function () { return cli_server_abstractions_1.CliCommandAuthor; } });
6
+ Object.defineProperty(exports, "DefaultLibraryAuthor", { enumerable: true, get: function () { return cli_server_abstractions_1.DefaultLibraryAuthor; } });
7
+ //# sourceMappingURL=cli-command-author.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-command-author.js","sourceRoot":"","sources":["../../../src/abstractions/cli-command-author.ts"],"names":[],"mappings":";;;AAAA,4EAA6G;AAAjF,2HAAA,gBAAgB,OAAA;AAAE,+HAAA,oBAAoB,OAAA"}
@@ -0,0 +1,2 @@
1
+ export { ICliCommandParameterDescriptor, CliCommandParameterDescriptor } from '@qodalis/cli-server-abstractions';
2
+ //# sourceMappingURL=cli-command-parameter-descriptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-command-parameter-descriptor.d.ts","sourceRoot":"","sources":["../../../src/abstractions/cli-command-parameter-descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliCommandParameterDescriptor = void 0;
4
+ var cli_server_abstractions_1 = require("@qodalis/cli-server-abstractions");
5
+ Object.defineProperty(exports, "CliCommandParameterDescriptor", { enumerable: true, get: function () { return cli_server_abstractions_1.CliCommandParameterDescriptor; } });
6
+ //# sourceMappingURL=cli-command-parameter-descriptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-command-parameter-descriptor.js","sourceRoot":"","sources":["../../../src/abstractions/cli-command-parameter-descriptor.ts"],"names":[],"mappings":";;;AAAA,4EAAiH;AAAxE,wIAAA,6BAA6B,OAAA"}
@@ -0,0 +1,2 @@
1
+ export { ICliCommandProcessor, CliCommandProcessor } from '@qodalis/cli-server-abstractions';
2
+ //# sourceMappingURL=cli-command-processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-command-processor.d.ts","sourceRoot":"","sources":["../../../src/abstractions/cli-command-processor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliCommandProcessor = void 0;
4
+ var cli_server_abstractions_1 = require("@qodalis/cli-server-abstractions");
5
+ Object.defineProperty(exports, "CliCommandProcessor", { enumerable: true, get: function () { return cli_server_abstractions_1.CliCommandProcessor; } });
6
+ //# sourceMappingURL=cli-command-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-command-processor.js","sourceRoot":"","sources":["../../../src/abstractions/cli-command-processor.ts"],"names":[],"mappings":";;;AAAA,4EAA6F;AAA9D,8HAAA,mBAAmB,OAAA"}
@@ -0,0 +1,2 @@
1
+ export { CliProcessCommand } from '@qodalis/cli-server-abstractions';
2
+ //# sourceMappingURL=cli-process-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-process-command.d.ts","sourceRoot":"","sources":["../../../src/abstractions/cli-process-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-process-command.js","sourceRoot":"","sources":["../../../src/abstractions/cli-process-command.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export { ICliCommandAuthor, CliCommandAuthor, DefaultLibraryAuthor, CliProcessCommand, ICliCommandParameterDescriptor, CliCommandParameterDescriptor, ICliCommandProcessor, CliCommandProcessor, ICliModule, CliModule, } from '@qodalis/cli-server-abstractions';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/abstractions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,8BAA8B,EAC9B,6BAA6B,EAC7B,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACV,SAAS,GACZ,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliModule = exports.CliCommandProcessor = exports.CliCommandParameterDescriptor = exports.DefaultLibraryAuthor = exports.CliCommandAuthor = void 0;
4
+ var cli_server_abstractions_1 = require("@qodalis/cli-server-abstractions");
5
+ Object.defineProperty(exports, "CliCommandAuthor", { enumerable: true, get: function () { return cli_server_abstractions_1.CliCommandAuthor; } });
6
+ Object.defineProperty(exports, "DefaultLibraryAuthor", { enumerable: true, get: function () { return cli_server_abstractions_1.DefaultLibraryAuthor; } });
7
+ Object.defineProperty(exports, "CliCommandParameterDescriptor", { enumerable: true, get: function () { return cli_server_abstractions_1.CliCommandParameterDescriptor; } });
8
+ Object.defineProperty(exports, "CliCommandProcessor", { enumerable: true, get: function () { return cli_server_abstractions_1.CliCommandProcessor; } });
9
+ Object.defineProperty(exports, "CliModule", { enumerable: true, get: function () { return cli_server_abstractions_1.CliModule; } });
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/abstractions/index.ts"],"names":[],"mappings":";;;AAAA,4EAW0C;AATtC,2HAAA,gBAAgB,OAAA;AAChB,+HAAA,oBAAoB,OAAA;AAGpB,wIAAA,6BAA6B,OAAA;AAE7B,8HAAA,mBAAmB,OAAA;AAEnB,oHAAA,SAAS,OAAA"}