@ecmaos/coreutils 0.1.5 → 0.2.1

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 (231) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +45 -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 +33 -25
  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 +49 -10
  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.map +1 -1
  55. package/dist/commands/grep.js +45 -10
  56. package/dist/commands/grep.js.map +1 -1
  57. package/dist/commands/head.d.ts.map +1 -1
  58. package/dist/commands/head.js +46 -9
  59. package/dist/commands/head.js.map +1 -1
  60. package/dist/commands/hex.d.ts.map +1 -1
  61. package/dist/commands/hex.js +16 -6
  62. package/dist/commands/hex.js.map +1 -1
  63. package/dist/commands/id.d.ts +4 -0
  64. package/dist/commands/id.d.ts.map +1 -0
  65. package/dist/commands/id.js +97 -0
  66. package/dist/commands/id.js.map +1 -0
  67. package/dist/commands/join.d.ts +4 -0
  68. package/dist/commands/join.d.ts.map +1 -0
  69. package/dist/commands/join.js +152 -0
  70. package/dist/commands/join.js.map +1 -0
  71. package/dist/commands/less.d.ts.map +1 -1
  72. package/dist/commands/less.js +16 -7
  73. package/dist/commands/less.js.map +1 -1
  74. package/dist/commands/ln.d.ts.map +1 -1
  75. package/dist/commands/ln.js +54 -12
  76. package/dist/commands/ln.js.map +1 -1
  77. package/dist/commands/ls.d.ts.map +1 -1
  78. package/dist/commands/ls.js +96 -91
  79. package/dist/commands/ls.js.map +1 -1
  80. package/dist/commands/mkdir.d.ts.map +1 -1
  81. package/dist/commands/mkdir.js +34 -9
  82. package/dist/commands/mkdir.js.map +1 -1
  83. package/dist/commands/mv.d.ts.map +1 -1
  84. package/dist/commands/mv.js +25 -7
  85. package/dist/commands/mv.js.map +1 -1
  86. package/dist/commands/nc.d.ts +4 -0
  87. package/dist/commands/nc.d.ts.map +1 -0
  88. package/dist/commands/nc.js +451 -0
  89. package/dist/commands/nc.js.map +1 -0
  90. package/dist/commands/nl.d.ts +4 -0
  91. package/dist/commands/nl.d.ts.map +1 -0
  92. package/dist/commands/nl.js +208 -0
  93. package/dist/commands/nl.js.map +1 -0
  94. package/dist/commands/passkey.d.ts.map +1 -1
  95. package/dist/commands/passkey.js +44 -18
  96. package/dist/commands/passkey.js.map +1 -1
  97. package/dist/commands/paste.d.ts +4 -0
  98. package/dist/commands/paste.d.ts.map +1 -0
  99. package/dist/commands/paste.js +161 -0
  100. package/dist/commands/paste.js.map +1 -0
  101. package/dist/commands/pwd.d.ts.map +1 -1
  102. package/dist/commands/pwd.js +13 -4
  103. package/dist/commands/pwd.js.map +1 -1
  104. package/dist/commands/rm.d.ts.map +1 -1
  105. package/dist/commands/rm.js +105 -12
  106. package/dist/commands/rm.js.map +1 -1
  107. package/dist/commands/rmdir.d.ts.map +1 -1
  108. package/dist/commands/rmdir.js +34 -9
  109. package/dist/commands/rmdir.js.map +1 -1
  110. package/dist/commands/sed.d.ts.map +1 -1
  111. package/dist/commands/sed.js +73 -28
  112. package/dist/commands/sed.js.map +1 -1
  113. package/dist/commands/seq.d.ts +4 -0
  114. package/dist/commands/seq.d.ts.map +1 -0
  115. package/dist/commands/seq.js +148 -0
  116. package/dist/commands/seq.js.map +1 -0
  117. package/dist/commands/sockets.d.ts +4 -0
  118. package/dist/commands/sockets.d.ts.map +1 -0
  119. package/dist/commands/sockets.js +239 -0
  120. package/dist/commands/sockets.js.map +1 -0
  121. package/dist/commands/sort.d.ts +4 -0
  122. package/dist/commands/sort.d.ts.map +1 -0
  123. package/dist/commands/sort.js +175 -0
  124. package/dist/commands/sort.js.map +1 -0
  125. package/dist/commands/split.d.ts +4 -0
  126. package/dist/commands/split.d.ts.map +1 -0
  127. package/dist/commands/split.js +147 -0
  128. package/dist/commands/split.js.map +1 -0
  129. package/dist/commands/stat.d.ts.map +1 -1
  130. package/dist/commands/stat.js +14 -6
  131. package/dist/commands/stat.js.map +1 -1
  132. package/dist/commands/tail.d.ts.map +1 -1
  133. package/dist/commands/tail.js +46 -9
  134. package/dist/commands/tail.js.map +1 -1
  135. package/dist/commands/tee.d.ts.map +1 -1
  136. package/dist/commands/tee.js +45 -10
  137. package/dist/commands/tee.js.map +1 -1
  138. package/dist/commands/test.d.ts +4 -0
  139. package/dist/commands/test.d.ts.map +1 -0
  140. package/dist/commands/test.js +99 -0
  141. package/dist/commands/test.js.map +1 -0
  142. package/dist/commands/touch.d.ts.map +1 -1
  143. package/dist/commands/touch.js +34 -9
  144. package/dist/commands/touch.js.map +1 -1
  145. package/dist/commands/tr.d.ts +4 -0
  146. package/dist/commands/tr.d.ts.map +1 -0
  147. package/dist/commands/tr.js +162 -0
  148. package/dist/commands/tr.js.map +1 -0
  149. package/dist/commands/true.d.ts +4 -0
  150. package/dist/commands/true.d.ts.map +1 -0
  151. package/dist/commands/true.js +27 -0
  152. package/dist/commands/true.js.map +1 -0
  153. package/dist/commands/uniq.d.ts +4 -0
  154. package/dist/commands/uniq.d.ts.map +1 -0
  155. package/dist/commands/uniq.js +187 -0
  156. package/dist/commands/uniq.js.map +1 -0
  157. package/dist/commands/user.d.ts +4 -0
  158. package/dist/commands/user.d.ts.map +1 -0
  159. package/dist/commands/user.js +427 -0
  160. package/dist/commands/user.js.map +1 -0
  161. package/dist/commands/wc.d.ts +4 -0
  162. package/dist/commands/wc.d.ts.map +1 -0
  163. package/dist/commands/wc.js +178 -0
  164. package/dist/commands/wc.js.map +1 -0
  165. package/dist/commands/which.d.ts +4 -0
  166. package/dist/commands/which.d.ts.map +1 -0
  167. package/dist/commands/which.js +78 -0
  168. package/dist/commands/which.js.map +1 -0
  169. package/dist/commands/whoami.d.ts +4 -0
  170. package/dist/commands/whoami.d.ts.map +1 -0
  171. package/dist/commands/whoami.js +28 -0
  172. package/dist/commands/whoami.js.map +1 -0
  173. package/dist/index.d.ts +14 -0
  174. package/dist/index.d.ts.map +1 -1
  175. package/dist/index.js +67 -1
  176. package/dist/index.js.map +1 -1
  177. package/dist/shared/terminal-command.d.ts +8 -2
  178. package/dist/shared/terminal-command.d.ts.map +1 -1
  179. package/dist/shared/terminal-command.js +42 -17
  180. package/dist/shared/terminal-command.js.map +1 -1
  181. package/package.json +4 -2
  182. package/src/commands/basename.ts +84 -0
  183. package/src/commands/cal.ts +111 -0
  184. package/src/commands/cat.ts +37 -27
  185. package/src/commands/cd.ts +40 -12
  186. package/src/commands/chmod.ts +37 -11
  187. package/src/commands/comm.ts +169 -0
  188. package/src/commands/cp.ts +73 -15
  189. package/src/commands/cut.ts +214 -0
  190. package/src/commands/date.ts +97 -0
  191. package/src/commands/diff.ts +204 -0
  192. package/src/commands/dirname.ts +57 -0
  193. package/src/commands/echo.ts +51 -11
  194. package/src/commands/false.ts +31 -0
  195. package/src/commands/find.ts +184 -0
  196. package/src/commands/grep.ts +44 -11
  197. package/src/commands/head.ts +46 -10
  198. package/src/commands/hex.ts +19 -7
  199. package/src/commands/id.ts +94 -0
  200. package/src/commands/join.ts +162 -0
  201. package/src/commands/less.ts +20 -8
  202. package/src/commands/ln.ts +50 -13
  203. package/src/commands/ls.ts +110 -87
  204. package/src/commands/mkdir.ts +41 -12
  205. package/src/commands/mv.ts +31 -9
  206. package/src/commands/nc.ts +499 -0
  207. package/src/commands/nl.ts +201 -0
  208. package/src/commands/passkey.ts +46 -19
  209. package/src/commands/paste.ts +172 -0
  210. package/src/commands/pwd.ts +16 -4
  211. package/src/commands/rm.ts +118 -13
  212. package/src/commands/rmdir.ts +41 -12
  213. package/src/commands/sed.ts +64 -30
  214. package/src/commands/seq.ts +147 -0
  215. package/src/commands/sockets.ts +283 -0
  216. package/src/commands/sort.ts +175 -0
  217. package/src/commands/split.ts +154 -0
  218. package/src/commands/stat.ts +17 -8
  219. package/src/commands/tail.ts +46 -10
  220. package/src/commands/tee.ts +43 -11
  221. package/src/commands/test.ts +116 -0
  222. package/src/commands/touch.ts +41 -12
  223. package/src/commands/tr.ts +170 -0
  224. package/src/commands/true.ts +31 -0
  225. package/src/commands/uniq.ts +189 -0
  226. package/src/commands/user.ts +436 -0
  227. package/src/commands/wc.ts +181 -0
  228. package/src/commands/which.ts +89 -0
  229. package/src/commands/whoami.ts +32 -0
  230. package/src/index.ts +67 -1
  231. package/src/shared/terminal-command.ts +43 -16
@@ -1,11 +1,19 @@
1
1
  import path from 'path'
2
2
  import ansi from 'ansi-escape-sequences'
3
- import type { CommandLineOptions } from 'command-line-args'
4
3
  import type { IDisposable } from '@xterm/xterm'
5
4
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
6
5
  import { TerminalCommand } from '../shared/terminal-command.js'
7
6
  import { writelnStderr } from '../shared/helpers.js'
8
7
 
8
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
9
+ const usage = `Usage: less [OPTION]... FILE
10
+ View file contents interactively.
11
+
12
+ FILE the file to view (if omitted, reads from stdin)
13
+ --help display this help and exit`
14
+ writelnStderr(process, terminal, usage)
15
+ }
16
+
9
17
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
10
18
  return new TerminalCommand({
11
19
  command: 'less',
@@ -13,21 +21,25 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
13
21
  kernel,
14
22
  shell,
15
23
  terminal,
16
- options: [
17
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
18
- { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the file to view' }
19
- ],
20
- run: async (argv: CommandLineOptions, process?: Process) => {
24
+ run: async (pid: number, argv: string[]) => {
25
+ const process = kernel.processes.get(pid) as Process | undefined
26
+
21
27
  if (!process) return 1
22
28
 
29
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
30
+ printUsage(process, terminal)
31
+ return 0
32
+ }
33
+
23
34
  let lines: string[] = []
24
35
  let currentLine = 0
25
36
  let keyListener: IDisposable | null = null
26
37
  let linesRendered = 0
27
38
 
28
39
  try {
29
- if (argv.path) {
30
- const filePath = argv.path as string
40
+ const filePath = argv.length > 0 && argv[0] !== undefined && !argv[0].startsWith('-') ? argv[0] : undefined
41
+
42
+ if (filePath) {
31
43
  const expandedPath = shell.expandTilde(filePath)
32
44
  const fullPath = path.resolve(shell.cwd, expandedPath)
33
45
 
@@ -1,10 +1,22 @@
1
1
  import path from 'path'
2
2
  import chalk from 'chalk'
3
- import type { CommandLineOptions } from 'command-line-args'
4
3
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
5
4
  import { TerminalCommand } from '../shared/terminal-command.js'
6
5
  import { writelnStdout, writelnStderr } from '../shared/helpers.js'
7
6
 
7
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
8
+ const usage = `Usage: ln [OPTION]... [-T] TARGET LINK_NAME
9
+ or: ln [OPTION]... TARGET
10
+ or: ln [OPTION]... TARGET... DIRECTORY
11
+ Create links between files.
12
+
13
+ -s, --symbolic make symbolic links instead of hard links
14
+ -f, --force remove existing destination files
15
+ -v, --verbose print name of each linked file
16
+ --help display this help and exit`
17
+ writelnStderr(process, terminal, usage)
18
+ }
19
+
8
20
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
9
21
  return new TerminalCommand({
10
22
  command: 'ln',
@@ -12,18 +24,43 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
12
24
  kernel,
13
25
  shell,
14
26
  terminal,
15
- options: [
16
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
17
- { name: 'symbolic', type: Boolean, alias: 's', description: 'Create symbolic links instead of hard links' },
18
- { name: 'force', type: Boolean, alias: 'f', description: 'Remove existing destination files' },
19
- { name: 'verbose', type: Boolean, alias: 'v', description: 'Print name of each linked file' },
20
- { name: 'args', type: String, multiple: true, defaultOption: true, description: 'The target and optional link name' }
21
- ],
22
- run: async (argv: CommandLineOptions, process?: Process) => {
23
- const args = (argv.args as string[]) || []
24
- const symbolic = argv.symbolic as boolean || false
25
- const force = argv.force as boolean || false
26
- const verbose = argv.verbose as boolean || false
27
+ run: async (pid: number, argv: string[]) => {
28
+ const process = kernel.processes.get(pid) as Process | undefined
29
+
30
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
31
+ printUsage(process, terminal)
32
+ return 0
33
+ }
34
+
35
+ const args: string[] = []
36
+ let symbolic = false
37
+ let force = false
38
+ let verbose = 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 === '-s' || arg === '--symbolic') {
45
+ symbolic = true
46
+ } else if (arg === '-f' || arg === '--force') {
47
+ force = true
48
+ } else if (arg === '-v' || arg === '--verbose') {
49
+ verbose = true
50
+ } else if (arg.startsWith('-')) {
51
+ const flags = arg.slice(1).split('')
52
+ if (flags.includes('s')) symbolic = true
53
+ if (flags.includes('f')) force = true
54
+ if (flags.includes('v')) verbose = true
55
+ const invalidFlags = flags.filter(f => !['s', 'f', 'v'].includes(f))
56
+ if (invalidFlags.length > 0) {
57
+ await writelnStderr(process, terminal, `ln: invalid option -- '${invalidFlags[0]}'`)
58
+ return 1
59
+ }
60
+ } else {
61
+ args.push(arg)
62
+ }
63
+ }
27
64
 
28
65
  if (args.length === 0) {
29
66
  await writelnStderr(process, terminal, chalk.red('ln: missing file operand'))
@@ -1,11 +1,19 @@
1
1
  import path from 'path'
2
2
  import chalk from 'chalk'
3
+ import columnify from 'columnify'
3
4
  import humanFormat from 'human-format'
4
- import type { CommandLineOptions } from 'command-line-args'
5
5
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
6
6
  import { TerminalCommand } from '../shared/terminal-command.js'
7
7
  import { writelnStdout } from '../shared/helpers.js'
8
8
 
9
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
10
+ const usage = `Usage: ls [OPTION]... [FILE]...
11
+ List information about the FILEs (the current directory by default).
12
+
13
+ --help display this help and exit`
14
+ writelnStdout(process, terminal, usage)
15
+ }
16
+
9
17
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
10
18
  return new TerminalCommand({
11
19
  command: 'ls',
@@ -13,12 +21,15 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
13
21
  kernel,
14
22
  shell,
15
23
  terminal,
16
- options: [
17
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
18
- { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the directory to list' }
19
- ],
20
- run: async (argv: CommandLineOptions, process?: Process) => {
21
- const target = (argv.path as string) || shell.cwd
24
+ run: async (pid: number, argv: string[]) => {
25
+ const process = kernel.processes.get(pid) as Process | undefined
26
+
27
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
28
+ printUsage(process, terminal)
29
+ return 0
30
+ }
31
+
32
+ const target = argv.length > 0 && argv[0] !== undefined && !argv[0].startsWith('-') ? argv[0] : shell.cwd
22
33
  const fullPath = target ? path.resolve(shell.cwd, target === '' ? '.' : target) : shell.cwd
23
34
  const stats = await shell.context.fs.promises.stat(fullPath)
24
35
  const entries: string[] = stats.isDirectory() ? await shell.context.fs.promises.readdir(fullPath) : [fullPath]
@@ -143,91 +154,103 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
143
154
  .filter((entry, index, self) => self.findIndex(e => e?.name === entry?.name) === index)
144
155
  .filter((entry): entry is NonNullable<typeof entry> => entry !== null && entry !== undefined)
145
156
 
146
- const data = [
147
- ['Name', 'Size', 'Modified', 'Mode', 'Owner', 'Info'],
148
- ...directories.sort((a, b) => a.name.localeCompare(b.name)).map(directory => {
149
- const displayName = directory.linkTarget
150
- ? `${directory.name} ${chalk.cyan('⟶')} ${directory.linkTarget}`
151
- : directory.name
152
- const modeStats = directory.linkStats && directory.linkStats.isSymbolicLink()
153
- ? directory.linkStats
154
- : directory.stats
155
- const modeString = modeStats
156
- ? getModeString(modeStats, directory.linkStats?.isSymbolicLink() ? directory.stats : undefined)
157
- : ''
158
- const linkInfo = getLinkInfo(directory.linkTarget, directory.linkStats, directory.stats)
159
- return [
160
- displayName,
161
- '',
162
- directory.stats ? getTimestampString(directory.stats.mtime) : '',
163
- modeString,
164
- directory.stats ? getOwnerString(directory.stats) : '',
165
- linkInfo
166
- ]
167
- }),
168
- ...files.sort((a, b) => a.name.localeCompare(b.name)).map(file => {
169
- const displayName = file.linkTarget
170
- ? `${file.name} ${chalk.cyan('⟶')} ${file.linkTarget}`
171
- : file.name
172
- const modeStats = file.linkStats && file.linkStats.isSymbolicLink()
173
- ? file.linkStats
174
- : file.stats
175
- const modeString = modeStats
176
- ? getModeString(modeStats, file.linkStats?.isSymbolicLink() ? file.stats : undefined)
177
- : ''
178
- return [
179
- displayName,
180
- file.stats ? humanFormat(file.stats.size) : '',
181
- file.stats ? getTimestampString(file.stats.mtime) : '',
182
- modeString,
183
- file.stats ? getOwnerString(file.stats) : '',
184
- (() => {
185
- const linkInfo = getLinkInfo(file.linkTarget, file.linkStats, file.stats)
186
- if (linkInfo) return linkInfo
187
-
188
- if (descriptions.has(path.resolve(fullPath, file.name))) return descriptions.get(path.resolve(fullPath, file.name))
189
- if (file.name.includes('.')) {
190
- const ext = file.name.split('.').pop()
191
- if (ext && descriptions.has('.' + ext)) return descriptions.get('.' + ext)
192
- }
193
- if (!file.stats) return ''
194
- if (file.stats.isBlockDevice() || file.stats.isCharacterDevice()) {
195
- // TODO: zenfs `fs.mounts` is deprecated - use a better way of getting device info
196
- }
157
+ const isDevDirectory = fullPath.startsWith('/dev')
158
+ const columns = isDevDirectory ? ['Name', 'Mode', 'Owner', 'Info'] : ['Name', 'Size', 'Modified', 'Mode', 'Owner', 'Info']
159
+
160
+ const directoryRows = directories.sort((a, b) => a.name.localeCompare(b.name)).map(directory => {
161
+ const displayName = directory.linkTarget
162
+ ? `${directory.name} ${chalk.cyan('⟶')} ${directory.linkTarget}`
163
+ : directory.name
164
+ const modeStats = directory.linkStats && directory.linkStats.isSymbolicLink()
165
+ ? directory.linkStats
166
+ : directory.stats
167
+ const modeString = modeStats
168
+ ? getModeString(modeStats, directory.linkStats?.isSymbolicLink() ? directory.stats : undefined)
169
+ : ''
170
+ const linkInfo = getLinkInfo(directory.linkTarget, directory.linkStats, directory.stats)
171
+
172
+ const modeType = modeString?.charAt(0) || ''
173
+ const coloredName = modeType === 'd' ? chalk.blue(displayName)
174
+ : modeType === 'l' ? chalk.cyan(displayName)
175
+ : chalk.green(displayName)
176
+
177
+ const row: Record<string, string> = {
178
+ Name: coloredName,
179
+ Mode: chalk.gray(modeString),
180
+ Owner: directory.stats ? chalk.gray(getOwnerString(directory.stats)) : '',
181
+ Info: chalk.gray(linkInfo)
182
+ }
183
+
184
+ if (!isDevDirectory) {
185
+ row.Size = ''
186
+ row.Modified = directory.stats ? chalk.gray(getTimestampString(directory.stats.mtime)) : ''
187
+ }
188
+
189
+ return row
190
+ })
197
191
 
198
- return ''
199
- })()
200
- ]
192
+ const fileRows = files.sort((a, b) => a.name.localeCompare(b.name)).map(file => {
193
+ const displayName = file.linkTarget
194
+ ? `${file.name} ${chalk.cyan('⟶')} ${file.linkTarget}`
195
+ : file.name
196
+ const modeStats = file.linkStats && file.linkStats.isSymbolicLink()
197
+ ? file.linkStats
198
+ : file.stats
199
+ const modeString = modeStats
200
+ ? getModeString(modeStats, file.linkStats?.isSymbolicLink() ? file.stats : undefined)
201
+ : ''
202
+
203
+ const modeType = modeString?.charAt(0) || ''
204
+ const coloredName = modeType === 'd' ? chalk.blue(displayName)
205
+ : modeType === 'l' ? chalk.cyan(displayName)
206
+ : chalk.green(displayName)
207
+
208
+ const info = (() => {
209
+ const linkInfo = getLinkInfo(file.linkTarget, file.linkStats, file.stats)
210
+ if (linkInfo) return linkInfo
211
+
212
+ if (descriptions.has(path.resolve(fullPath, file.name))) return descriptions.get(path.resolve(fullPath, file.name)) || ''
213
+ if (file.name.includes('.')) {
214
+ const ext = file.name.split('.').pop()
215
+ if (ext && descriptions.has('.' + ext)) return descriptions.get('.' + ext) || ''
216
+ }
217
+ if (!file.stats) return ''
218
+ if (file.stats.isBlockDevice() || file.stats.isCharacterDevice()) {
219
+ // TODO: zenfs `fs.mounts` is deprecated - use a better way of getting device info
220
+ }
221
+
222
+ return ''
223
+ })()
224
+
225
+ const row: Record<string, string> = {
226
+ Name: coloredName,
227
+ Mode: chalk.gray(modeString),
228
+ Owner: file.stats ? chalk.gray(getOwnerString(file.stats)) : '',
229
+ Info: chalk.gray(info)
230
+ }
231
+
232
+ if (!isDevDirectory) {
233
+ row.Size = file.stats ? chalk.gray(humanFormat(file.stats.size)) : ''
234
+ row.Modified = file.stats ? chalk.gray(getTimestampString(file.stats.mtime)) : ''
235
+ }
236
+
237
+ return row
238
+ })
239
+
240
+ const data = [...directoryRows, ...fileRows]
241
+
242
+ if (data.length > 0) {
243
+ const table = columnify(data, {
244
+ columns,
245
+ columnSplitter: ' ',
246
+ showHeaders: true,
247
+ headingTransform: (heading: string) => chalk.bold(heading)
201
248
  })
202
- ] as string[][]
203
-
204
- // Special output for certain directories
205
- if (fullPath.startsWith('/dev')) data.forEach(row => row.splice(1, 2)) // remove size and modified columns
206
-
207
- const columnWidths = data[0]?.map((_, colIndex) => Math.max(...data.map(row => {
208
- // Remove ANSI escape sequences before calculating length
209
- const cleanedCell = row[colIndex]?.replace(/\u001b\[.*?m/g, '')
210
- // count all emojis as two characters
211
- return cleanedCell?.length || 0
212
- })))
213
-
214
- for (const [rowIndex, row] of data.entries()) {
215
- const line = row
216
- .map((cell, index) => {
217
- const paddedCell = cell.padEnd(columnWidths?.[index] ?? 0)
218
- if (index === 0 && rowIndex > 0) {
219
- if (row[3]?.startsWith('d')) return chalk.blue(paddedCell)
220
- else if (row[3]?.startsWith('l')) return chalk.cyan(paddedCell)
221
- else return chalk.green(paddedCell)
222
- } else return rowIndex === 0 ? chalk.bold(paddedCell) : chalk.gray(paddedCell)
223
- })
224
- .join(' ')
225
-
226
- if (data.length > 1) await writelnStdout(process, terminal, line)
249
+
250
+ await writelnStdout(process, terminal, table)
227
251
  }
228
252
 
229
253
  return 0
230
254
  }
231
255
  })
232
256
  }
233
-
@@ -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: mkdir [OPTION]... DIRECTORY...
8
+ Create the DIRECTORY(ies), if they do not already exist.
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 create' }
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.mkdir(fullPath)
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, 'mkdir: missing operand')
31
+ await writelnStderr(process, terminal, "Try 'mkdir --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.mkdir(fullPath)
44
+ } catch (error) {
45
+ const errorMessage = error instanceof Error ? error.message : String(error)
46
+ await writelnStderr(process, terminal, `mkdir: ${target}: ${errorMessage}`)
47
+ hasError = true
48
+ }
49
+ }
50
+
51
+ return hasError ? 1 : 0
22
52
  }
23
53
  })
24
54
  }
25
-
@@ -1,10 +1,17 @@
1
1
  import path from 'path'
2
2
  import chalk from 'chalk'
3
- import type { CommandLineOptions } from 'command-line-args'
4
3
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
5
4
  import { TerminalCommand } from '../shared/terminal-command.js'
6
5
  import { writelnStderr } from '../shared/helpers.js'
7
6
 
7
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
8
+ const usage = `Usage: mv [OPTION]... SOURCE... DEST
9
+ Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
10
+
11
+ --help display this help and exit`
12
+ writelnStderr(process, terminal, usage)
13
+ }
14
+
8
15
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
9
16
  return new TerminalCommand({
10
17
  command: 'mv',
@@ -12,13 +19,29 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
12
19
  kernel,
13
20
  shell,
14
21
  terminal,
15
- options: [
16
- { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
17
- { name: 'args', type: String, multiple: true, defaultOption: true, description: 'The source and destination paths' }
18
- ],
19
- run: async (argv: CommandLineOptions, process?: Process) => {
20
- const args = (argv.args as string[]) || []
21
- const [sourceInput, destinationInput] = args
22
+ run: async (pid: number, argv: string[]) => {
23
+ const process = kernel.processes.get(pid) as Process | undefined
24
+
25
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
26
+ printUsage(process, terminal)
27
+ return 0
28
+ }
29
+
30
+ const args: string[] = []
31
+ for (const arg of argv) {
32
+ if (arg && !arg.startsWith('-')) {
33
+ args.push(arg)
34
+ }
35
+ }
36
+
37
+ if (args.length < 2) {
38
+ await writelnStderr(process, terminal, chalk.red('Usage: mv <source> <destination>'))
39
+ return 1
40
+ }
41
+
42
+ const sourceInput = args[0]
43
+ const destinationInput = args[args.length - 1]
44
+
22
45
  if (!sourceInput || !destinationInput) {
23
46
  await writelnStderr(process, terminal, chalk.red('Usage: mv <source> <destination>'))
24
47
  return 1
@@ -48,4 +71,3 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
48
71
  }
49
72
  })
50
73
  }
51
-