@ecmaos/coreutils 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +55 -0
  3. package/dist/commands/basename.d.ts +4 -0
  4. package/dist/commands/basename.d.ts.map +1 -0
  5. package/dist/commands/basename.js +78 -0
  6. package/dist/commands/basename.js.map +1 -0
  7. package/dist/commands/cal.d.ts +4 -0
  8. package/dist/commands/cal.d.ts.map +1 -0
  9. package/dist/commands/cal.js +105 -0
  10. package/dist/commands/cal.js.map +1 -0
  11. package/dist/commands/cat.d.ts.map +1 -1
  12. package/dist/commands/cat.js +49 -22
  13. package/dist/commands/cat.js.map +1 -1
  14. package/dist/commands/cd.d.ts.map +1 -1
  15. package/dist/commands/cd.js +38 -10
  16. package/dist/commands/cd.js.map +1 -1
  17. package/dist/commands/chmod.d.ts.map +1 -1
  18. package/dist/commands/chmod.js +31 -9
  19. package/dist/commands/chmod.js.map +1 -1
  20. package/dist/commands/comm.d.ts +4 -0
  21. package/dist/commands/comm.d.ts.map +1 -0
  22. package/dist/commands/comm.js +162 -0
  23. package/dist/commands/comm.js.map +1 -0
  24. package/dist/commands/cp.d.ts.map +1 -1
  25. package/dist/commands/cp.js +61 -12
  26. package/dist/commands/cp.js.map +1 -1
  27. package/dist/commands/cut.d.ts +4 -0
  28. package/dist/commands/cut.d.ts.map +1 -0
  29. package/dist/commands/cut.js +208 -0
  30. package/dist/commands/cut.js.map +1 -0
  31. package/dist/commands/date.d.ts +4 -0
  32. package/dist/commands/date.d.ts.map +1 -0
  33. package/dist/commands/date.js +100 -0
  34. package/dist/commands/date.js.map +1 -0
  35. package/dist/commands/diff.d.ts +4 -0
  36. package/dist/commands/diff.d.ts.map +1 -0
  37. package/dist/commands/diff.js +194 -0
  38. package/dist/commands/diff.js.map +1 -0
  39. package/dist/commands/dirname.d.ts +4 -0
  40. package/dist/commands/dirname.d.ts.map +1 -0
  41. package/dist/commands/dirname.js +50 -0
  42. package/dist/commands/dirname.js.map +1 -0
  43. package/dist/commands/echo.d.ts.map +1 -1
  44. package/dist/commands/echo.js +51 -9
  45. package/dist/commands/echo.js.map +1 -1
  46. package/dist/commands/false.d.ts +4 -0
  47. package/dist/commands/false.d.ts.map +1 -0
  48. package/dist/commands/false.js +27 -0
  49. package/dist/commands/false.js.map +1 -0
  50. package/dist/commands/find.d.ts +4 -0
  51. package/dist/commands/find.d.ts.map +1 -0
  52. package/dist/commands/find.js +181 -0
  53. package/dist/commands/find.js.map +1 -0
  54. package/dist/commands/grep.d.ts +4 -0
  55. package/dist/commands/grep.d.ts.map +1 -0
  56. package/dist/commands/grep.js +175 -0
  57. package/dist/commands/grep.js.map +1 -0
  58. package/dist/commands/head.d.ts +4 -0
  59. package/dist/commands/head.d.ts.map +1 -0
  60. package/dist/commands/head.js +199 -0
  61. package/dist/commands/head.js.map +1 -0
  62. package/dist/commands/hex.d.ts.map +1 -1
  63. package/dist/commands/hex.js +82 -20
  64. package/dist/commands/hex.js.map +1 -1
  65. package/dist/commands/id.d.ts +4 -0
  66. package/dist/commands/id.d.ts.map +1 -0
  67. package/dist/commands/id.js +97 -0
  68. package/dist/commands/id.js.map +1 -0
  69. package/dist/commands/join.d.ts +4 -0
  70. package/dist/commands/join.d.ts.map +1 -0
  71. package/dist/commands/join.js +152 -0
  72. package/dist/commands/join.js.map +1 -0
  73. package/dist/commands/less.d.ts.map +1 -1
  74. package/dist/commands/less.js +16 -7
  75. package/dist/commands/less.js.map +1 -1
  76. package/dist/commands/ln.d.ts.map +1 -1
  77. package/dist/commands/ln.js +54 -12
  78. package/dist/commands/ln.js.map +1 -1
  79. package/dist/commands/ls.d.ts.map +1 -1
  80. package/dist/commands/ls.js +19 -9
  81. package/dist/commands/ls.js.map +1 -1
  82. package/dist/commands/mkdir.d.ts.map +1 -1
  83. package/dist/commands/mkdir.js +34 -9
  84. package/dist/commands/mkdir.js.map +1 -1
  85. package/dist/commands/mv.d.ts.map +1 -1
  86. package/dist/commands/mv.js +25 -7
  87. package/dist/commands/mv.js.map +1 -1
  88. package/dist/commands/nc.d.ts +4 -0
  89. package/dist/commands/nc.d.ts.map +1 -0
  90. package/dist/commands/nc.js +451 -0
  91. package/dist/commands/nc.js.map +1 -0
  92. package/dist/commands/nl.d.ts +4 -0
  93. package/dist/commands/nl.d.ts.map +1 -0
  94. package/dist/commands/nl.js +208 -0
  95. package/dist/commands/nl.js.map +1 -0
  96. package/dist/commands/passkey.d.ts.map +1 -1
  97. package/dist/commands/passkey.js +44 -18
  98. package/dist/commands/passkey.js.map +1 -1
  99. package/dist/commands/paste.d.ts +4 -0
  100. package/dist/commands/paste.d.ts.map +1 -0
  101. package/dist/commands/paste.js +161 -0
  102. package/dist/commands/paste.js.map +1 -0
  103. package/dist/commands/pwd.d.ts.map +1 -1
  104. package/dist/commands/pwd.js +13 -4
  105. package/dist/commands/pwd.js.map +1 -1
  106. package/dist/commands/rm.d.ts.map +1 -1
  107. package/dist/commands/rm.js +105 -12
  108. package/dist/commands/rm.js.map +1 -1
  109. package/dist/commands/rmdir.d.ts.map +1 -1
  110. package/dist/commands/rmdir.js +34 -9
  111. package/dist/commands/rmdir.js.map +1 -1
  112. package/dist/commands/sed.d.ts.map +1 -1
  113. package/dist/commands/sed.js +73 -28
  114. package/dist/commands/sed.js.map +1 -1
  115. package/dist/commands/seq.d.ts +4 -0
  116. package/dist/commands/seq.d.ts.map +1 -0
  117. package/dist/commands/seq.js +148 -0
  118. package/dist/commands/seq.js.map +1 -0
  119. package/dist/commands/sockets.d.ts +4 -0
  120. package/dist/commands/sockets.d.ts.map +1 -0
  121. package/dist/commands/sockets.js +239 -0
  122. package/dist/commands/sockets.js.map +1 -0
  123. package/dist/commands/sort.d.ts +4 -0
  124. package/dist/commands/sort.d.ts.map +1 -0
  125. package/dist/commands/sort.js +175 -0
  126. package/dist/commands/sort.js.map +1 -0
  127. package/dist/commands/split.d.ts +4 -0
  128. package/dist/commands/split.d.ts.map +1 -0
  129. package/dist/commands/split.js +147 -0
  130. package/dist/commands/split.js.map +1 -0
  131. package/dist/commands/stat.d.ts.map +1 -1
  132. package/dist/commands/stat.js +14 -6
  133. package/dist/commands/stat.js.map +1 -1
  134. package/dist/commands/tail.d.ts +4 -0
  135. package/dist/commands/tail.d.ts.map +1 -0
  136. package/dist/commands/tail.js +189 -0
  137. package/dist/commands/tail.js.map +1 -0
  138. package/dist/commands/tee.d.ts.map +1 -1
  139. package/dist/commands/tee.js +45 -10
  140. package/dist/commands/tee.js.map +1 -1
  141. package/dist/commands/test.d.ts +4 -0
  142. package/dist/commands/test.d.ts.map +1 -0
  143. package/dist/commands/test.js +99 -0
  144. package/dist/commands/test.js.map +1 -0
  145. package/dist/commands/touch.d.ts.map +1 -1
  146. package/dist/commands/touch.js +34 -9
  147. package/dist/commands/touch.js.map +1 -1
  148. package/dist/commands/tr.d.ts +4 -0
  149. package/dist/commands/tr.d.ts.map +1 -0
  150. package/dist/commands/tr.js +162 -0
  151. package/dist/commands/tr.js.map +1 -0
  152. package/dist/commands/true.d.ts +4 -0
  153. package/dist/commands/true.d.ts.map +1 -0
  154. package/dist/commands/true.js +27 -0
  155. package/dist/commands/true.js.map +1 -0
  156. package/dist/commands/uniq.d.ts +4 -0
  157. package/dist/commands/uniq.d.ts.map +1 -0
  158. package/dist/commands/uniq.js +187 -0
  159. package/dist/commands/uniq.js.map +1 -0
  160. package/dist/commands/wc.d.ts +4 -0
  161. package/dist/commands/wc.d.ts.map +1 -0
  162. package/dist/commands/wc.js +178 -0
  163. package/dist/commands/wc.js.map +1 -0
  164. package/dist/commands/which.d.ts +4 -0
  165. package/dist/commands/which.d.ts.map +1 -0
  166. package/dist/commands/which.js +78 -0
  167. package/dist/commands/which.js.map +1 -0
  168. package/dist/commands/whoami.d.ts +4 -0
  169. package/dist/commands/whoami.d.ts.map +1 -0
  170. package/dist/commands/whoami.js +28 -0
  171. package/dist/commands/whoami.js.map +1 -0
  172. package/dist/index.d.ts +15 -0
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +72 -1
  175. package/dist/index.js.map +1 -1
  176. package/dist/shared/terminal-command.d.ts +8 -2
  177. package/dist/shared/terminal-command.d.ts.map +1 -1
  178. package/dist/shared/terminal-command.js +42 -17
  179. package/dist/shared/terminal-command.js.map +1 -1
  180. package/package.json +4 -2
  181. package/src/commands/basename.ts +84 -0
  182. package/src/commands/cal.ts +111 -0
  183. package/src/commands/cat.ts +55 -24
  184. package/src/commands/cd.ts +40 -12
  185. package/src/commands/chmod.ts +37 -11
  186. package/src/commands/comm.ts +169 -0
  187. package/src/commands/cp.ts +73 -15
  188. package/src/commands/cut.ts +214 -0
  189. package/src/commands/date.ts +97 -0
  190. package/src/commands/diff.ts +204 -0
  191. package/src/commands/dirname.ts +57 -0
  192. package/src/commands/echo.ts +53 -10
  193. package/src/commands/false.ts +31 -0
  194. package/src/commands/find.ts +184 -0
  195. package/src/commands/grep.ts +187 -0
  196. package/src/commands/head.ts +206 -0
  197. package/src/commands/hex.ts +93 -25
  198. package/src/commands/id.ts +94 -0
  199. package/src/commands/join.ts +162 -0
  200. package/src/commands/less.ts +20 -8
  201. package/src/commands/ln.ts +50 -13
  202. package/src/commands/ls.ts +21 -10
  203. package/src/commands/mkdir.ts +41 -12
  204. package/src/commands/mv.ts +31 -9
  205. package/src/commands/nc.ts +499 -0
  206. package/src/commands/nl.ts +201 -0
  207. package/src/commands/passkey.ts +46 -19
  208. package/src/commands/paste.ts +172 -0
  209. package/src/commands/pwd.ts +16 -4
  210. package/src/commands/rm.ts +118 -13
  211. package/src/commands/rmdir.ts +41 -12
  212. package/src/commands/sed.ts +64 -30
  213. package/src/commands/seq.ts +147 -0
  214. package/src/commands/sockets.ts +283 -0
  215. package/src/commands/sort.ts +175 -0
  216. package/src/commands/split.ts +154 -0
  217. package/src/commands/stat.ts +17 -8
  218. package/src/commands/tail.ts +200 -0
  219. package/src/commands/tee.ts +43 -11
  220. package/src/commands/test.ts +116 -0
  221. package/src/commands/touch.ts +41 -12
  222. package/src/commands/tr.ts +170 -0
  223. package/src/commands/true.ts +31 -0
  224. package/src/commands/uniq.ts +189 -0
  225. package/src/commands/wc.ts +181 -0
  226. package/src/commands/which.ts +89 -0
  227. package/src/commands/whoami.ts +32 -0
  228. package/src/index.ts +72 -1
  229. package/src/shared/terminal-command.ts +43 -16
@@ -1,7 +1,15 @@
1
1
  import path from 'path'
2
- import type { CommandLineOptions } from 'command-line-args'
3
- import type { Kernel, Shell, Terminal } from '@ecmaos/types'
2
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
4
3
  import { TerminalCommand } from '../shared/terminal-command.js'
4
+ import { writelnStdout, writelnStderr } from '../shared/helpers.js'
5
+
6
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
7
+ const usage = `Usage: rmdir [OPTION]... DIRECTORY...
8
+ Remove the DIRECTORY(ies), if they are empty.
9
+
10
+ --help display this help and exit`
11
+ writelnStdout(process, terminal, usage)
12
+ }
5
13
 
6
14
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
7
15
  return new TerminalCommand({
@@ -10,16 +18,37 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
10
18
  kernel,
11
19
  shell,
12
20
  terminal,
13
- options: [
14
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
15
- { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the directory to remove' }
16
- ],
17
- run: async (argv: CommandLineOptions) => {
18
- const target = (argv.path as string) || shell.cwd
19
- const fullPath = target ? path.resolve(shell.cwd, target) : shell.cwd
20
- await shell.context.fs.promises.rm(fullPath, { recursive: true, force: true })
21
- return 0
21
+ run: async (pid: number, argv: string[]) => {
22
+ const process = kernel.processes.get(pid) as Process | undefined
23
+
24
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
25
+ printUsage(process, terminal)
26
+ return 0
27
+ }
28
+
29
+ if (argv.length === 0) {
30
+ await writelnStderr(process, terminal, 'rmdir: missing operand')
31
+ await writelnStderr(process, terminal, "Try 'rmdir --help' for more information.")
32
+ return 1
33
+ }
34
+
35
+ let hasError = false
36
+
37
+ for (const target of argv) {
38
+ if (!target || target.startsWith('-')) continue
39
+
40
+ const fullPath = target ? path.resolve(shell.cwd, target) : shell.cwd
41
+
42
+ try {
43
+ await shell.context.fs.promises.rm(fullPath, { recursive: true, force: true })
44
+ } catch (error) {
45
+ const errorMessage = error instanceof Error ? error.message : String(error)
46
+ await writelnStderr(process, terminal, `rmdir: ${target}: ${errorMessage}`)
47
+ hasError = true
48
+ }
49
+ }
50
+
51
+ return hasError ? 1 : 0
22
52
  }
23
53
  })
24
54
  }
25
-
@@ -1,5 +1,4 @@
1
1
  import path from 'path'
2
- import type { CommandLineOptions } from 'command-line-args'
3
2
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
4
3
  import { TerminalCommand } from '../shared/terminal-command.js'
5
4
  import { writelnStderr } from '../shared/helpers.js'
@@ -16,6 +15,19 @@ interface SedCommand {
16
15
  }
17
16
  }
18
17
 
18
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
19
+ const usage = `Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...
20
+
21
+ Stream editor for filtering and transforming text.
22
+
23
+ -e, --expression=script add the script to the commands to be executed
24
+ -f, --file=script-file add the contents of script-file to the commands
25
+ -i[SUFFIX], --in-place[=SUFFIX] edit files in place (makes backup if SUFFIX supplied)
26
+ -q, --quiet suppress normal output
27
+ --help display this help and exit`
28
+ writelnStderr(process, terminal, usage)
29
+ }
30
+
19
31
  function parseSedExpression(expr: string): SedCommand | null {
20
32
  expr = expr.trim()
21
33
 
@@ -203,21 +215,21 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
203
215
  kernel,
204
216
  shell,
205
217
  terminal,
206
- options: [
207
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
208
- { name: 'expression', type: String, alias: 'e', multiple: true, description: 'Add the script to the commands to be executed' },
209
- { name: 'file', type: String, alias: 'f', description: 'Add the contents of script-file to the commands to be executed' },
210
- { name: 'inplace', type: String, alias: 'i', description: 'Edit files in place (makes backup if extension supplied)' },
211
- { name: 'quiet', type: Boolean, alias: 'q', description: 'Suppress normal output' },
212
- { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, multiple: true, description: 'Expression or input file(s)' }
213
- ],
214
- run: async (argv: CommandLineOptions, process?: Process) => {
218
+ run: async (pid: number, argv: string[]) => {
219
+ const process = kernel.processes.get(pid) as Process | undefined
220
+
215
221
  if (!process) return 1
216
222
 
217
- let expressions = (argv.expression as string[]) || []
218
- let files = (argv.path as string[]) || []
219
- const inplace = argv.inplace as string | undefined
220
- const quiet = argv.quiet as boolean || false
223
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
224
+ printUsage(process, terminal)
225
+ return 0
226
+ }
227
+
228
+ let expressions: string[] = []
229
+ const files: string[] = []
230
+ let scriptFile: string | undefined
231
+ let inplace: string | undefined
232
+ let quiet = false
221
233
 
222
234
  const isSedExpression = (arg: string): boolean => {
223
235
  if (!arg) return false
@@ -232,34 +244,56 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
232
244
  )
233
245
  }
234
246
 
235
- const potentialExpressions: string[] = []
236
- const potentialFiles: string[] = []
247
+ for (let i = 0; i < argv.length; i++) {
248
+ const arg = argv[i]
249
+ if (!arg) continue
237
250
 
238
- for (const arg of files) {
239
- if (isSedExpression(arg)) {
240
- potentialExpressions.push(arg)
241
- } else {
242
- potentialFiles.push(arg)
251
+ if (arg === '--help' || arg === '-h') {
252
+ printUsage(process, terminal)
253
+ return 0
254
+ } else if (arg === '-e' || arg === '--expression') {
255
+ if (i + 1 < argv.length) {
256
+ expressions.push(argv[++i] || '')
257
+ }
258
+ } else if (arg.startsWith('--expression=')) {
259
+ expressions.push(arg.slice(13))
260
+ } else if (arg.startsWith('-e')) {
261
+ expressions.push(arg.slice(2))
262
+ } else if (arg === '-f' || arg === '--file') {
263
+ if (i + 1 < argv.length) {
264
+ scriptFile = argv[++i]
265
+ }
266
+ } else if (arg.startsWith('--file=')) {
267
+ scriptFile = arg.slice(7)
268
+ } else if (arg.startsWith('-f')) {
269
+ scriptFile = arg.slice(2)
270
+ } else if (arg === '-i' || arg === '--in-place') {
271
+ inplace = ''
272
+ } else if (arg.startsWith('--in-place=')) {
273
+ inplace = arg.slice(12)
274
+ } else if (arg.startsWith('-i')) {
275
+ inplace = arg.slice(2) || ''
276
+ } else if (arg === '-q' || arg === '--quiet') {
277
+ quiet = true
278
+ } else if (isSedExpression(arg)) {
279
+ expressions.push(arg)
280
+ } else if (!arg.startsWith('-')) {
281
+ files.push(arg)
243
282
  }
244
283
  }
245
284
 
246
- if (potentialExpressions.length > 0) {
247
- expressions = [...expressions, ...potentialExpressions]
248
- files = potentialFiles
249
- }
250
-
251
- if (expressions.length === 0 && !argv.file) {
285
+ if (expressions.length === 0 && !scriptFile) {
252
286
  await writelnStderr(process, terminal, 'sed: No expression provided')
253
287
  return 1
254
288
  }
255
289
 
256
290
  const commands: SedCommand[] = []
257
291
 
258
- if (argv.file) {
259
- const scriptPath = path.resolve(shell.cwd, argv.file as string)
292
+ if (scriptFile) {
293
+ const scriptPath = path.resolve(shell.cwd, scriptFile)
260
294
  const exists = await shell.context.fs.promises.exists(scriptPath)
261
295
  if (!exists) {
262
- await writelnStderr(process, terminal, `sed: ${argv.file}: No such file or directory`)
296
+ await writelnStderr(process, terminal, `sed: ${scriptFile}: No such file or directory`)
263
297
  return 1
264
298
  }
265
299
 
@@ -0,0 +1,147 @@
1
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
2
+ import { TerminalCommand } from '../shared/terminal-command.js'
3
+ import { writelnStderr } from '../shared/helpers.js'
4
+
5
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
6
+ const usage = `Usage: seq [OPTION]... LAST
7
+ seq [OPTION]... FIRST LAST
8
+ seq [OPTION]... FIRST INCREMENT LAST
9
+ Print numbers from FIRST to LAST, in steps of INCREMENT.
10
+
11
+ -s, --separator=STRING use STRING to separate numbers (default: \\n)
12
+ --help display this help and exit`
13
+ writelnStderr(process, terminal, usage)
14
+ }
15
+
16
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
17
+ return new TerminalCommand({
18
+ command: 'seq',
19
+ description: 'Print a sequence of numbers',
20
+ kernel,
21
+ shell,
22
+ terminal,
23
+ run: async (pid: number, argv: string[]) => {
24
+ const process = kernel.processes.get(pid) as Process | undefined
25
+
26
+ if (!process) return 1
27
+
28
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
29
+ printUsage(process, terminal)
30
+ return 0
31
+ }
32
+
33
+ let separator = '\n'
34
+ const args: string[] = []
35
+
36
+ for (let i = 0; i < argv.length; i++) {
37
+ const arg = argv[i]
38
+ if (!arg) continue
39
+ if (arg === '--help' || arg === '-h') {
40
+ printUsage(process, terminal)
41
+ return 0
42
+ } else if (arg === '-s' || arg === '--separator') {
43
+ if (i + 1 < argv.length) {
44
+ const nextArg = argv[++i]
45
+ if (nextArg !== undefined) {
46
+ separator = nextArg
47
+ }
48
+ } else {
49
+ await writelnStderr(process, terminal, 'seq: option requires an argument -- \'s\'')
50
+ return 1
51
+ }
52
+ } else if (arg.startsWith('--separator=')) {
53
+ separator = arg.slice(12)
54
+ } else if (arg.startsWith('-s')) {
55
+ separator = arg.slice(2)
56
+ } else if (!arg.startsWith('-')) {
57
+ args.push(arg)
58
+ }
59
+ }
60
+
61
+ if (args.length === 0) {
62
+ await writelnStderr(process, terminal, 'seq: missing operand')
63
+ return 1
64
+ }
65
+
66
+ let first = 1
67
+ let increment = 1
68
+ let last: number
69
+
70
+ if (args.length === 1) {
71
+ const arg0 = args[0]
72
+ if (arg0 === undefined) {
73
+ await writelnStderr(process, terminal, 'seq: missing operand')
74
+ return 1
75
+ }
76
+ last = parseFloat(arg0)
77
+ if (isNaN(last)) {
78
+ await writelnStderr(process, terminal, `seq: invalid number: ${arg0}`)
79
+ return 1
80
+ }
81
+ } else if (args.length === 2) {
82
+ const arg0 = args[0]
83
+ const arg1 = args[1]
84
+ if (arg0 === undefined || arg1 === undefined) {
85
+ await writelnStderr(process, terminal, 'seq: missing operand')
86
+ return 1
87
+ }
88
+ first = parseFloat(arg0)
89
+ last = parseFloat(arg1)
90
+ if (isNaN(first) || isNaN(last)) {
91
+ await writelnStderr(process, terminal, 'seq: invalid number')
92
+ return 1
93
+ }
94
+ } else if (args.length === 3) {
95
+ const arg0 = args[0]
96
+ const arg1 = args[1]
97
+ const arg2 = args[2]
98
+ if (arg0 === undefined || arg1 === undefined || arg2 === undefined) {
99
+ await writelnStderr(process, terminal, 'seq: missing operand')
100
+ return 1
101
+ }
102
+ first = parseFloat(arg0)
103
+ increment = parseFloat(arg1)
104
+ last = parseFloat(arg2)
105
+ if (isNaN(first) || isNaN(increment) || isNaN(last)) {
106
+ await writelnStderr(process, terminal, 'seq: invalid number')
107
+ return 1
108
+ }
109
+ } else {
110
+ await writelnStderr(process, terminal, 'seq: too many arguments')
111
+ return 1
112
+ }
113
+
114
+ const writer = process.stdout.getWriter()
115
+ const numbers: number[] = []
116
+
117
+ if (increment > 0) {
118
+ for (let i = first; i <= last; i += increment) {
119
+ numbers.push(i)
120
+ }
121
+ } else if (increment < 0) {
122
+ for (let i = first; i >= last; i += increment) {
123
+ numbers.push(i)
124
+ }
125
+ } else {
126
+ await writelnStderr(process, terminal, 'seq: zero increment')
127
+ writer.releaseLock()
128
+ return 1
129
+ }
130
+
131
+ const output = numbers.map(n => {
132
+ if (Number.isInteger(n)) {
133
+ return n.toString()
134
+ } else {
135
+ return n.toFixed(10).replace(/\.?0+$/, '')
136
+ }
137
+ }).join(separator)
138
+
139
+ try {
140
+ await writer.write(new TextEncoder().encode(output + (separator === '\n' ? '' : '\n')))
141
+ return 0
142
+ } finally {
143
+ writer.releaseLock()
144
+ }
145
+ }
146
+ })
147
+ }
@@ -0,0 +1,283 @@
1
+ import chalk from 'chalk'
2
+ import columnify from 'columnify'
3
+ import type { Kernel, Process, Shell, Terminal, SocketConnection } from '@ecmaos/types'
4
+ import { TerminalCommand } from '../shared/terminal-command.js'
5
+ import { writelnStdout, writelnStderr } from '../shared/helpers.js'
6
+
7
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
8
+ const usage = `Usage: sockets [COMMAND] [OPTIONS]
9
+
10
+ Manage socket connections (WebSocket and WebTransport).
11
+
12
+ Commands:
13
+ list, ls List all active connections
14
+ create, c <url> Create a new connection
15
+ close, d <id> Close a connection by ID
16
+ show, s <id> Show detailed information about a connection
17
+ --help, -h Display this help and exit
18
+
19
+ Options:
20
+ -t, --type <type> Connection type: websocket or webtransport (for create)
21
+ -p, --protocols WebSocket protocols (comma-separated, for create)
22
+
23
+ Examples:
24
+ sockets list
25
+ sockets create wss://echo.websocket.org
26
+ sockets create https://example.com:443 -t webtransport
27
+ sockets close abc-123-def-456
28
+ sockets show abc-123-def-456`
29
+ writelnStdout(process, terminal, usage)
30
+ }
31
+
32
+ function findConnectionById(kernel: Kernel, id: string): SocketConnection | undefined {
33
+ const fullMatch = kernel.sockets.get(id)
34
+ if (fullMatch) return fullMatch
35
+
36
+ const allConnections = kernel.sockets.all()
37
+ for (const [fullId, conn] of allConnections.entries()) {
38
+ if (fullId.startsWith(id) || fullId.substring(0, 8) === id) {
39
+ return conn
40
+ }
41
+ }
42
+ return undefined
43
+ }
44
+
45
+ async function listConnections(
46
+ process: Process | undefined,
47
+ kernel: Kernel,
48
+ terminal: Terminal
49
+ ): Promise<number> {
50
+ const allConnections = kernel.sockets.all()
51
+ const connections = Array.from(allConnections.values())
52
+
53
+ if (connections.length === 0) {
54
+ await writelnStdout(process, terminal, 'No active connections.')
55
+ return 0
56
+ }
57
+
58
+ const data = connections.map((conn, index) => {
59
+ const id = conn.id.substring(0, 8)
60
+ const type = conn.type === 'websocket' ? chalk.cyan('WS') : chalk.magenta('WT')
61
+ const state = conn.state === 'open' ? chalk.green(conn.state) :
62
+ conn.state === 'connecting' ? chalk.yellow(conn.state) :
63
+ conn.state === 'closing' ? chalk.yellow(conn.state) :
64
+ chalk.gray(conn.state)
65
+ const age = Math.floor((Date.now() - conn.created) / 1000)
66
+ const ageStr = age < 60 ? `${age}s` : age < 3600 ? `${Math.floor(age / 60)}m` : `${Math.floor(age / 3600)}h`
67
+ const url = conn.url.length > 50 ? conn.url.substring(0, 47) + '...' : conn.url
68
+
69
+ return {
70
+ '#': `${index + 1}`,
71
+ ID: chalk.bold(id),
72
+ TYPE: type,
73
+ STATE: state,
74
+ AGE: chalk.gray(ageStr),
75
+ URL: url
76
+ }
77
+ })
78
+
79
+ const table = columnify(data, {
80
+ columns: ['#', 'ID', 'TYPE', 'STATE', 'AGE', 'URL'],
81
+ columnSplitter: ' ',
82
+ config: {
83
+ '#': { maxWidth: 4 },
84
+ ID: { maxWidth: 10 },
85
+ TYPE: { maxWidth: 4 },
86
+ STATE: { maxWidth: 10 },
87
+ AGE: { maxWidth: 4 }
88
+ }
89
+ })
90
+
91
+ await writelnStdout(process, terminal, table)
92
+
93
+ return 0
94
+ }
95
+
96
+ async function showConnection(
97
+ process: Process | undefined,
98
+ kernel: Kernel,
99
+ terminal: Terminal,
100
+ id: string
101
+ ): Promise<number> {
102
+ const connection = findConnectionById(kernel, id)
103
+
104
+ if (!connection) {
105
+ await writelnStderr(process, terminal, `sockets: connection not found: ${id}`)
106
+ return 1
107
+ }
108
+
109
+ const age = Math.floor((Date.now() - connection.created) / 1000)
110
+ const ageSeconds = age
111
+ const ageMinutes = Math.floor(age / 60)
112
+ const ageHours = Math.floor(age / 3600)
113
+ const ageStr = age < 60 ? `${ageSeconds} seconds` :
114
+ age < 3600 ? `${ageMinutes} minutes, ${ageSeconds % 60} seconds` :
115
+ `${ageHours} hours, ${Math.floor((age % 3600) / 60)} minutes`
116
+
117
+ writelnStdout(process, terminal, chalk.bold('Connection Details'))
118
+ writelnStdout(process, terminal, chalk.gray('─'.repeat(40)))
119
+ writelnStdout(process, terminal, `ID: ${chalk.bold(connection.id)}`)
120
+ writelnStdout(process, terminal, `Type: ${connection.type === 'websocket' ? chalk.cyan('WebSocket') : chalk.magenta('WebTransport')}`)
121
+ writelnStdout(process, terminal, `State: ${connection.state === 'open' ? chalk.green(connection.state) :
122
+ connection.state === 'connecting' ? chalk.yellow(connection.state) :
123
+ connection.state === 'closing' ? chalk.yellow(connection.state) :
124
+ chalk.gray(connection.state)}`)
125
+ writelnStdout(process, terminal, `URL: ${connection.url}`)
126
+ writelnStdout(process, terminal, `Created: ${new Date(connection.created).toISOString()}`)
127
+ writelnStdout(process, terminal, `Age: ${ageStr}`)
128
+
129
+ if (connection.type === 'websocket') {
130
+ const ws = connection.socket
131
+ writelnStdout(process, terminal, `Protocol: ${ws.protocol || '(none)'}`)
132
+ writelnStdout(process, terminal, `Extensions: ${ws.extensions || '(none)'}`)
133
+ writelnStdout(process, terminal, `BinaryType: ${ws.binaryType}`)
134
+ writelnStdout(process, terminal, `ReadyState: ${ws.readyState} (${connection.state})`)
135
+ } else {
136
+ writelnStdout(process, terminal, `State: ${connection.state}`)
137
+ }
138
+
139
+ return 0
140
+ }
141
+
142
+ async function createConnection(
143
+ process: Process | undefined,
144
+ kernel: Kernel,
145
+ terminal: Terminal,
146
+ url: string,
147
+ type?: string,
148
+ protocols?: string
149
+ ): Promise<number> {
150
+ try {
151
+ let connection: SocketConnection
152
+
153
+ if (type === 'webtransport' || (!type && url.startsWith('https://'))) {
154
+ if (!('WebTransport' in globalThis)) {
155
+ await writelnStderr(process, terminal, 'sockets: WebTransport is not supported in this browser')
156
+ return 1
157
+ }
158
+ connection = await kernel.sockets.createWebTransport(url)
159
+ await writelnStdout(process, terminal, `Created WebTransport connection: ${chalk.bold(connection.id.substring(0, 8))}`)
160
+ } else if (type === 'websocket' || url.startsWith('ws://') || url.startsWith('wss://')) {
161
+ const options = protocols ? { protocols: protocols.split(',') } : undefined
162
+ connection = await kernel.sockets.createWebSocket(url, options)
163
+ await writelnStdout(process, terminal, `Created WebSocket connection: ${chalk.bold(connection.id.substring(0, 8))}`)
164
+ } else {
165
+ await writelnStderr(process, terminal, 'sockets: unable to determine connection type. Use -t to specify type or use a URL with ws://, wss://, or https:// scheme')
166
+ return 1
167
+ }
168
+
169
+ await writelnStdout(process, terminal, `URL: ${connection.url}`)
170
+ await writelnStdout(process, terminal, `State: ${connection.state === 'open' ? chalk.green(connection.state) : chalk.yellow(connection.state)}`)
171
+
172
+ return 0
173
+ } catch (error) {
174
+ await writelnStderr(process, terminal, `sockets: failed to create connection: ${error instanceof Error ? error.message : 'Unknown error'}`)
175
+ return 1
176
+ }
177
+ }
178
+
179
+ async function closeConnection(
180
+ process: Process | undefined,
181
+ kernel: Kernel,
182
+ terminal: Terminal,
183
+ id: string
184
+ ): Promise<number> {
185
+ const connection = findConnectionById(kernel, id)
186
+
187
+ if (!connection) {
188
+ await writelnStderr(process, terminal, `sockets: connection not found: ${id}`)
189
+ return 1
190
+ }
191
+
192
+ try {
193
+ await kernel.sockets.close(connection.id)
194
+ await writelnStdout(process, terminal, `Closed connection: ${chalk.bold(connection.id.substring(0, 8))}`)
195
+ return 0
196
+ } catch (error) {
197
+ await writelnStderr(process, terminal, `sockets: failed to close connection: ${error instanceof Error ? error.message : 'Unknown error'}`)
198
+ return 1
199
+ }
200
+ }
201
+
202
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
203
+ return new TerminalCommand({
204
+ command: 'sockets',
205
+ description: 'Manage socket connections (WebSocket and WebTransport)',
206
+ kernel,
207
+ shell,
208
+ terminal,
209
+ run: async (pid: number, argv: string[]) => {
210
+ const process = kernel.processes.get(pid) as Process | undefined
211
+
212
+ if (!process) return 1
213
+
214
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
215
+ printUsage(process, terminal)
216
+ return 0
217
+ }
218
+
219
+ if (argv.length === 0 || argv[0] === 'list' || argv[0] === 'ls') {
220
+ return await listConnections(process, kernel, terminal)
221
+ }
222
+
223
+ const command = argv[0]
224
+ let type: string | undefined
225
+ let protocols: string | undefined
226
+
227
+ for (let i = 1; i < argv.length; i++) {
228
+ const arg = argv[i]
229
+ if (arg === '-t' || arg === '--type') {
230
+ if (i + 1 < argv.length) {
231
+ i++
232
+ type = argv[i]
233
+ } else {
234
+ await writelnStderr(process, terminal, 'sockets: --type requires a value')
235
+ return 1
236
+ }
237
+ } else if (arg === '-p' || arg === '--protocols') {
238
+ if (i + 1 < argv.length) {
239
+ i++
240
+ protocols = argv[i]
241
+ } else {
242
+ await writelnStderr(process, terminal, 'sockets: --protocols requires a value')
243
+ return 1
244
+ }
245
+ }
246
+ }
247
+
248
+ if (command === 'create' || command === 'c') {
249
+ if (argv.length < 2 || !argv[1] || argv[1].startsWith('-')) {
250
+ await writelnStderr(process, terminal, 'sockets: create requires a URL')
251
+ await writelnStderr(process, terminal, 'Try "sockets --help" for more information.')
252
+ return 1
253
+ }
254
+ const url = argv[1]
255
+ return await createConnection(process, kernel, terminal, url, type, protocols)
256
+ }
257
+
258
+ if (command === 'close' || command === 'd') {
259
+ if (argv.length < 2 || !argv[1] || argv[1].startsWith('-')) {
260
+ await writelnStderr(process, terminal, 'sockets: close requires a connection ID')
261
+ await writelnStderr(process, terminal, 'Try "sockets --help" for more information.')
262
+ return 1
263
+ }
264
+ const id = argv[1]
265
+ return await closeConnection(process, kernel, terminal, id)
266
+ }
267
+
268
+ if (command === 'show' || command === 's') {
269
+ if (argv.length < 2 || !argv[1] || argv[1].startsWith('-')) {
270
+ await writelnStderr(process, terminal, 'sockets: show requires a connection ID')
271
+ await writelnStderr(process, terminal, 'Try "sockets --help" for more information.')
272
+ return 1
273
+ }
274
+ const id = argv[1]
275
+ return await showConnection(process, kernel, terminal, id)
276
+ }
277
+
278
+ await writelnStderr(process, terminal, `sockets: unknown command: ${command}`)
279
+ await writelnStderr(process, terminal, 'Try "sockets --help" for more information.')
280
+ return 1
281
+ }
282
+ })
283
+ }