@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
@@ -0,0 +1,181 @@
1
+ import path from 'path'
2
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
3
+ import { TerminalEvents } from '@ecmaos/types'
4
+ import { TerminalCommand } from '../shared/terminal-command.js'
5
+ import { writelnStderr } from '../shared/helpers.js'
6
+
7
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
8
+ const usage = `Usage: wc [OPTION]... [FILE]...
9
+ Print newline, word, and byte counts for each FILE.
10
+
11
+ -c, --bytes print the byte counts
12
+ -l, --lines print the newline counts
13
+ -w, --words print the word counts
14
+ --help display this help and exit`
15
+ writelnStderr(process, terminal, usage)
16
+ }
17
+
18
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
19
+ return new TerminalCommand({
20
+ command: 'wc',
21
+ description: 'Print newline, word, and byte counts for each file',
22
+ kernel,
23
+ shell,
24
+ terminal,
25
+ run: async (pid: number, argv: string[]) => {
26
+ const process = kernel.processes.get(pid) as Process | undefined
27
+
28
+ if (!process) return 1
29
+
30
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
31
+ printUsage(process, terminal)
32
+ return 0
33
+ }
34
+
35
+ const files: string[] = []
36
+ let showBytes = false
37
+ let showLines = false
38
+ let showWords = false
39
+
40
+ for (const arg of argv) {
41
+ if (arg === '--help' || arg === '-h') {
42
+ printUsage(process, terminal)
43
+ return 0
44
+ } else if (arg === '-c' || arg === '--bytes') {
45
+ showBytes = true
46
+ } else if (arg === '-l' || arg === '--lines') {
47
+ showLines = true
48
+ } else if (arg === '-w' || arg === '--words') {
49
+ showWords = true
50
+ } else if (arg.startsWith('-')) {
51
+ const flags = arg.slice(1).split('')
52
+ if (flags.includes('c')) showBytes = true
53
+ if (flags.includes('l')) showLines = true
54
+ if (flags.includes('w')) showWords = true
55
+ const invalidFlags = flags.filter(f => !['c', 'l', 'w'].includes(f))
56
+ if (invalidFlags.length > 0) {
57
+ await writelnStderr(process, terminal, `wc: invalid option -- '${invalidFlags[0]}'`)
58
+ return 1
59
+ }
60
+ } else {
61
+ files.push(arg)
62
+ }
63
+ }
64
+
65
+ const showAll = !showBytes && !showLines && !showWords
66
+
67
+ const writer = process.stdout.getWriter()
68
+
69
+ try {
70
+ if (files.length === 0) {
71
+ if (!process.stdin) {
72
+ return 0
73
+ }
74
+
75
+ const reader = process.stdin.getReader()
76
+ const decoder = new TextDecoder()
77
+ let content = ''
78
+
79
+ try {
80
+ while (true) {
81
+ const { done, value } = await reader.read()
82
+ if (done) break
83
+ if (value) {
84
+ content += decoder.decode(value, { stream: true })
85
+ }
86
+ }
87
+ } finally {
88
+ reader.releaseLock()
89
+ }
90
+
91
+ const lines = content.split('\n').length - (content.endsWith('\n') ? 0 : 1)
92
+ const words = content.trim().split(/\s+/).filter(w => w.length > 0).length
93
+ const bytes = new TextEncoder().encode(content).length
94
+
95
+ let output = ''
96
+ if (showAll || showLines) output += `${lines} `
97
+ if (showAll || showWords) output += `${words} `
98
+ if (showAll || showBytes) output += `${bytes} `
99
+ output = output.trim()
100
+
101
+ await writer.write(new TextEncoder().encode(output + '\n'))
102
+ return 0
103
+ }
104
+
105
+ let totalLines = 0
106
+ let totalWords = 0
107
+ let totalBytes = 0
108
+ const results: Array<{ lines: number, words: number, bytes: number, file: string }> = []
109
+
110
+ for (const file of files) {
111
+ const fullPath = path.resolve(shell.cwd, file)
112
+
113
+ let interrupted = false
114
+ const interruptHandler = () => { interrupted = true }
115
+ kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
116
+
117
+ try {
118
+ if (fullPath.startsWith('/dev')) {
119
+ await writelnStderr(process, terminal, `wc: ${file}: cannot count device files`)
120
+ continue
121
+ }
122
+
123
+ const handle = await shell.context.fs.promises.open(fullPath, 'r')
124
+ const stat = await shell.context.fs.promises.stat(fullPath)
125
+
126
+ const decoder = new TextDecoder()
127
+ let content = ''
128
+ let bytesRead = 0
129
+ const chunkSize = 1024
130
+
131
+ while (bytesRead < stat.size) {
132
+ if (interrupted) break
133
+ const data = new Uint8Array(chunkSize)
134
+ const readSize = Math.min(chunkSize, stat.size - bytesRead)
135
+ await handle.read(data, 0, readSize, bytesRead)
136
+ const chunk = data.subarray(0, readSize)
137
+ content += decoder.decode(chunk, { stream: true })
138
+ bytesRead += readSize
139
+ }
140
+
141
+ const lines = content.split('\n').length - (content.endsWith('\n') ? 0 : 1)
142
+ const words = content.trim().split(/\s+/).filter(w => w.length > 0).length
143
+ const bytes = new TextEncoder().encode(content).length
144
+
145
+ totalLines += lines
146
+ totalWords += words
147
+ totalBytes += bytes
148
+
149
+ results.push({ lines, words, bytes, file })
150
+ } catch (error) {
151
+ await writelnStderr(process, terminal, `wc: ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`)
152
+ } finally {
153
+ kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
154
+ }
155
+ }
156
+
157
+ for (const result of results) {
158
+ let output = ''
159
+ if (showAll || showLines) output += `${result.lines} `
160
+ if (showAll || showWords) output += `${result.words} `
161
+ if (showAll || showBytes) output += `${result.bytes} `
162
+ output += result.file
163
+ await writer.write(new TextEncoder().encode(output + '\n'))
164
+ }
165
+
166
+ if (files.length > 1) {
167
+ let output = ''
168
+ if (showAll || showLines) output += `${totalLines} `
169
+ if (showAll || showWords) output += `${totalWords} `
170
+ if (showAll || showBytes) output += `${totalBytes} `
171
+ output += 'total'
172
+ await writer.write(new TextEncoder().encode(output + '\n'))
173
+ }
174
+
175
+ return 0
176
+ } finally {
177
+ writer.releaseLock()
178
+ }
179
+ }
180
+ })
181
+ }
@@ -0,0 +1,89 @@
1
+ import path from 'path'
2
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
3
+ import { TerminalCommand } from '../shared/terminal-command.js'
4
+ import { writelnStderr } from '../shared/helpers.js'
5
+
6
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
7
+ const usage = `Usage: which [COMMAND]...
8
+ Locate a command.
9
+
10
+ COMMAND the command(s) to locate
11
+ --help display this help and exit`
12
+ writelnStderr(process, terminal, usage)
13
+ }
14
+
15
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
16
+ return new TerminalCommand({
17
+ command: 'which',
18
+ description: 'Locate a command',
19
+ kernel,
20
+ shell,
21
+ terminal,
22
+ run: async (pid: number, argv: string[]) => {
23
+ const process = kernel.processes.get(pid) as Process | undefined
24
+
25
+ if (!process) return 1
26
+
27
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
28
+ printUsage(process, terminal)
29
+ return 0
30
+ }
31
+
32
+ const commands: string[] = []
33
+ for (const arg of argv) {
34
+ if (arg !== '--help' && arg !== '-h' && !arg.startsWith('-')) {
35
+ commands.push(arg)
36
+ }
37
+ }
38
+
39
+ if (commands.length === 0) {
40
+ await writelnStderr(process, terminal, 'which: missing command name')
41
+ return 1
42
+ }
43
+
44
+ const writer = process.stdout.getWriter()
45
+ let exitCode = 0
46
+
47
+ const resolveCommand = async (command: string): Promise<string | undefined> => {
48
+ if (command.startsWith('./')) {
49
+ const cwdCommand = path.join(shell.cwd, command.slice(2))
50
+ if (await shell.context.fs.promises.exists(cwdCommand)) {
51
+ return cwdCommand
52
+ }
53
+ return undefined
54
+ }
55
+
56
+ const paths = shell.env.get('PATH')?.split(':') || ['/bin', '/usr/bin', '/usr/local/bin']
57
+ const resolvedCommand = path.resolve(command)
58
+
59
+ if (await shell.context.fs.promises.exists(resolvedCommand)) {
60
+ return resolvedCommand
61
+ }
62
+
63
+ for (const pathDir of paths) {
64
+ const expandedPath = pathDir.replace(/\$([A-Z_]+)/g, (_, name) => shell.env.get(name) || '')
65
+ const fullPath = `${expandedPath}/${command}`
66
+ if (await shell.context.fs.promises.exists(fullPath)) return fullPath
67
+ }
68
+
69
+ return undefined
70
+ }
71
+
72
+ try {
73
+ for (const cmd of commands) {
74
+ const commandPath = await resolveCommand(cmd)
75
+
76
+ if (commandPath) {
77
+ await writer.write(new TextEncoder().encode(commandPath + '\n'))
78
+ } else {
79
+ exitCode = 1
80
+ }
81
+ }
82
+
83
+ return exitCode
84
+ } finally {
85
+ writer.releaseLock()
86
+ }
87
+ }
88
+ })
89
+ }
@@ -0,0 +1,32 @@
1
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
2
+ import { TerminalCommand } from '../shared/terminal-command.js'
3
+ import { writelnStdout } from '../shared/helpers.js'
4
+
5
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
6
+ const usage = `Usage: whoami
7
+ Print effective user ID.
8
+
9
+ --help display this help and exit`
10
+ writelnStdout(process, terminal, usage)
11
+ }
12
+
13
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
14
+ return new TerminalCommand({
15
+ command: 'whoami',
16
+ description: 'Print effective user ID',
17
+ kernel,
18
+ shell,
19
+ terminal,
20
+ run: async (pid: number, argv: string[]) => {
21
+ const process = kernel.processes.get(pid) as Process | undefined
22
+
23
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
24
+ printUsage(process, terminal)
25
+ return 0
26
+ }
27
+
28
+ await writelnStdout(process, terminal, shell.username)
29
+ return 0
30
+ }
31
+ })
32
+ }
package/src/index.ts CHANGED
@@ -12,11 +12,15 @@ import { createCommand as createCd } from './commands/cd.js'
12
12
  import { createCommand as createChmod } from './commands/chmod.js'
13
13
  import { createCommand as createCp } from './commands/cp.js'
14
14
  import { createCommand as createEcho } from './commands/echo.js'
15
+ import { createCommand as createGrep } from './commands/grep.js'
16
+ import { createCommand as createHead } from './commands/head.js'
15
17
  import { createCommand as createLn } from './commands/ln.js'
16
18
  import { createCommand as createLs } from './commands/ls.js'
17
19
  import { createCommand as createMkdir } from './commands/mkdir.js'
18
20
  import { createCommand as createMv } from './commands/mv.js'
21
+ import { createCommand as createNc } from './commands/nc.js'
19
22
  import { createCommand as createPwd } from './commands/pwd.js'
23
+ import { createCommand as createSockets } from './commands/sockets.js'
20
24
  import { createCommand as createRm } from './commands/rm.js'
21
25
  import { createCommand as createRmdir } from './commands/rmdir.js'
22
26
  import { createCommand as createStat } from './commands/stat.js'
@@ -26,6 +30,30 @@ import { createCommand as createLess } from './commands/less.js'
26
30
  import { createCommand as createPasskey } from './commands/passkey.js'
27
31
  import { createCommand as createSed } from './commands/sed.js'
28
32
  import { createCommand as createTee } from './commands/tee.js'
33
+ import { createCommand as createTail } from './commands/tail.js'
34
+ import { createCommand as createBasename } from './commands/basename.js'
35
+ import { createCommand as createCal } from './commands/cal.js'
36
+ import { createCommand as createComm } from './commands/comm.js'
37
+ import { createCommand as createCut } from './commands/cut.js'
38
+ import { createCommand as createDate } from './commands/date.js'
39
+ import { createCommand as createDiff } from './commands/diff.js'
40
+ import { createCommand as createDirname } from './commands/dirname.js'
41
+ import { createCommand as createFalse } from './commands/false.js'
42
+ import { createCommand as createFind } from './commands/find.js'
43
+ import { createCommand as createId } from './commands/id.js'
44
+ import { createCommand as createJoin } from './commands/join.js'
45
+ import { createCommand as createNl } from './commands/nl.js'
46
+ import { createCommand as createPaste } from './commands/paste.js'
47
+ import { createCommand as createSeq } from './commands/seq.js'
48
+ import { createCommand as createSort } from './commands/sort.js'
49
+ import { createCommand as createSplit } from './commands/split.js'
50
+ import { createCommand as createTest } from './commands/test.js'
51
+ import { createCommand as createTr } from './commands/tr.js'
52
+ import { createCommand as createTrue } from './commands/true.js'
53
+ import { createCommand as createUniq } from './commands/uniq.js'
54
+ import { createCommand as createWc } from './commands/wc.js'
55
+ import { createCommand as createWhich } from './commands/which.js'
56
+ import { createCommand as createWhoami } from './commands/whoami.js'
29
57
 
30
58
  // Export individual command factories
31
59
  export { createCommand as createCat } from './commands/cat.js'
@@ -33,6 +61,7 @@ export { createCommand as createCd } from './commands/cd.js'
33
61
  export { createCommand as createChmod } from './commands/chmod.js'
34
62
  export { createCommand as createCp } from './commands/cp.js'
35
63
  export { createCommand as createEcho } from './commands/echo.js'
64
+ export { createCommand as createGrep } from './commands/grep.js'
36
65
  export { createCommand as createLn } from './commands/ln.js'
37
66
  export { createCommand as createLs } from './commands/ls.js'
38
67
  export { createCommand as createMkdir } from './commands/mkdir.js'
@@ -46,6 +75,20 @@ export { createCommand as createHex } from './commands/hex.js'
46
75
  export { createCommand as createLess } from './commands/less.js'
47
76
  export { createCommand as createSed } from './commands/sed.js'
48
77
  export { createCommand as createTee } from './commands/tee.js'
78
+ export { createCommand as createTail } from './commands/tail.js'
79
+ export { createCommand as createBasename } from './commands/basename.js'
80
+ export { createCommand as createCut } from './commands/cut.js'
81
+ export { createCommand as createDate } from './commands/date.js'
82
+ export { createCommand as createDiff } from './commands/diff.js'
83
+ export { createCommand as createDirname } from './commands/dirname.js'
84
+ export { createCommand as createFind } from './commands/find.js'
85
+ export { createCommand as createSort } from './commands/sort.js'
86
+ export { createCommand as createTest } from './commands/test.js'
87
+ export { createCommand as createTr } from './commands/tr.js'
88
+ export { createCommand as createUniq } from './commands/uniq.js'
89
+ export { createCommand as createWc } from './commands/wc.js'
90
+ export { createCommand as createWhich } from './commands/which.js'
91
+ export { createCommand as createSockets } from './commands/sockets.js'
49
92
 
50
93
  /**
51
94
  * Creates all coreutils commands.
@@ -58,11 +101,15 @@ export function createAllCommands(kernel: Kernel, shell: Shell, terminal: Termin
58
101
  chmod: createChmod(kernel, shell, terminal),
59
102
  cp: createCp(kernel, shell, terminal),
60
103
  echo: createEcho(kernel, shell, terminal),
104
+ grep: createGrep(kernel, shell, terminal),
105
+ head: createHead(kernel, shell, terminal),
61
106
  ln: createLn(kernel, shell, terminal),
62
107
  ls: createLs(kernel, shell, terminal),
63
108
  mkdir: createMkdir(kernel, shell, terminal),
64
109
  mv: createMv(kernel, shell, terminal),
65
110
  pwd: createPwd(kernel, shell, terminal),
111
+ nc: createNc(kernel, shell, terminal),
112
+ sockets: createSockets(kernel, shell, terminal),
66
113
  rm: createRm(kernel, shell, terminal),
67
114
  rmdir: createRmdir(kernel, shell, terminal),
68
115
  stat: createStat(kernel, shell, terminal),
@@ -71,7 +118,31 @@ export function createAllCommands(kernel: Kernel, shell: Shell, terminal: Termin
71
118
  less: createLess(kernel, shell, terminal),
72
119
  passkey: createPasskey(kernel, shell, terminal),
73
120
  sed: createSed(kernel, shell, terminal),
74
- tee: createTee(kernel, shell, terminal)
121
+ tail: createTail(kernel, shell, terminal),
122
+ tee: createTee(kernel, shell, terminal),
123
+ basename: createBasename(kernel, shell, terminal),
124
+ cal: createCal(kernel, shell, terminal),
125
+ comm: createComm(kernel, shell, terminal),
126
+ cut: createCut(kernel, shell, terminal),
127
+ date: createDate(kernel, shell, terminal),
128
+ diff: createDiff(kernel, shell, terminal),
129
+ dirname: createDirname(kernel, shell, terminal),
130
+ false: createFalse(kernel, shell, terminal),
131
+ find: createFind(kernel, shell, terminal),
132
+ id: createId(kernel, shell, terminal),
133
+ join: createJoin(kernel, shell, terminal),
134
+ nl: createNl(kernel, shell, terminal),
135
+ paste: createPaste(kernel, shell, terminal),
136
+ seq: createSeq(kernel, shell, terminal),
137
+ sort: createSort(kernel, shell, terminal),
138
+ split: createSplit(kernel, shell, terminal),
139
+ test: createTest(kernel, shell, terminal),
140
+ tr: createTr(kernel, shell, terminal),
141
+ true: createTrue(kernel, shell, terminal),
142
+ uniq: createUniq(kernel, shell, terminal),
143
+ wc: createWc(kernel, shell, terminal),
144
+ which: createWhich(kernel, shell, terminal),
145
+ whoami: createWhoami(kernel, shell, terminal)
75
146
  }
76
147
  }
77
148
 
@@ -5,8 +5,14 @@ import type { TerminalCommand as ITerminalCommand } from '@ecmaos/types'
5
5
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
6
6
  import { writelnStdout, writelnStderr } from './helpers.js'
7
7
 
8
+ type UnifiedParserRun = (argv: CommandLineOptions, process?: Process, rawArgv?: string[]) => Promise<number | void>
9
+ type RawArgvRun = (pid: number, argv: string[]) => Promise<number | void>
10
+
8
11
  /**
9
12
  * The TerminalCommand class sets up a common interface for builtin terminal commands
13
+ * Supports two modes:
14
+ * - Unified parser mode: When options are provided, uses command-line-args (for kernel commands)
15
+ * - Raw argv mode: When options are not provided, passes raw argv directly (for coreutils commands)
10
16
  */
11
17
  export class TerminalCommand implements ITerminalCommand {
12
18
  command: string = ''
@@ -24,8 +30,8 @@ export class TerminalCommand implements ITerminalCommand {
24
30
  command: string
25
31
  description: string
26
32
  kernel: Kernel
27
- options: parseUsage.OptionDefinition[]
28
- run: (argv: CommandLineOptions, process?: Process) => Promise<number | void>
33
+ options?: parseUsage.OptionDefinition[]
34
+ run: UnifiedParserRun | RawArgvRun
29
35
  shell: Shell
30
36
  terminal: Terminal
31
37
  stdin?: ReadableStream<Uint8Array>
@@ -35,32 +41,50 @@ export class TerminalCommand implements ITerminalCommand {
35
41
  this.command = command
36
42
  this.description = description
37
43
  this.kernel = kernel
38
- this.options = options
44
+ this.options = options || []
39
45
  this.shell = shell
40
46
  this.terminal = terminal
41
47
  this.stdin = stdin
42
48
  this.stdout = stdout
43
49
  this.stderr = stderr
44
50
 
45
- this.run = async (pid: number, argv: string[]) => {
46
- if (argv === null) return 1
47
- const process = this.kernel.processes.get(pid) as Process | undefined
48
- try {
49
- const parsed = parseArgs(this.options, { argv })
50
- if (parsed.help) {
51
- await writelnStdout(process, this.terminal, this.usage)
52
- return 0
53
- }
51
+ const useUnifiedParser = this.options.length > 0
54
52
 
55
- return await run(parsed, process)
56
- } catch (error) {
57
- await writelnStderr(process, this.terminal, chalk.red(String(error)))
58
- return 1
53
+ if (useUnifiedParser) {
54
+ const unifiedRun = run as UnifiedParserRun
55
+ this.run = async (pid: number, argv: string[]) => {
56
+ if (argv === null) return 1
57
+ const process = this.kernel.processes.get(pid) as Process | undefined
58
+ try {
59
+ const parsed = parseArgs(this.options, { argv, stopAtFirstUnknown: true })
60
+ if (parsed.help) {
61
+ await writelnStdout(process, this.terminal, this.usage)
62
+ return 0
63
+ }
64
+
65
+ return await unifiedRun(parsed, process, argv)
66
+ } catch (error) {
67
+ const errorMessage = error instanceof Error ? error.message : String(error)
68
+ if (errorMessage.includes('UNKNOWN_OPTION') || errorMessage.includes('Unknown option')) {
69
+ return await unifiedRun({} as CommandLineOptions, process, argv)
70
+ }
71
+ await writelnStderr(process, this.terminal, chalk.red(errorMessage))
72
+ return 1
73
+ }
74
+ }
75
+ } else {
76
+ const rawRun = run as RawArgvRun
77
+ this.run = async (pid: number, argv: string[]) => {
78
+ if (argv === null) return 1
79
+ return await rawRun(pid, argv)
59
80
  }
60
81
  }
61
82
  }
62
83
 
63
84
  get usage() {
85
+ if (this.options.length === 0) {
86
+ return ''
87
+ }
64
88
  return parseUsage([
65
89
  { header: this.command, content: this.description },
66
90
  { header: 'Usage', content: this.usageContent },
@@ -69,6 +93,9 @@ export class TerminalCommand implements ITerminalCommand {
69
93
  }
70
94
 
71
95
  get usageContent() {
96
+ if (this.options.length === 0) {
97
+ return ''
98
+ }
72
99
  return `${this.command} ${this.options.map(option => {
73
100
  let optionStr = option.name
74
101
  if (option.type === Boolean) optionStr = `[--${option.name}]`