@ecmaos/coreutils 0.2.1 → 0.3.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 (150) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +37 -0
  3. package/LICENSE +1 -1
  4. package/dist/commands/cal.js +2 -2
  5. package/dist/commands/cal.js.map +1 -1
  6. package/dist/commands/cat.js +2 -2
  7. package/dist/commands/cd.js +2 -2
  8. package/dist/commands/chmod.d.ts.map +1 -1
  9. package/dist/commands/chmod.js +16 -11
  10. package/dist/commands/chmod.js.map +1 -1
  11. package/dist/commands/cp.js +2 -2
  12. package/dist/commands/cp.js.map +1 -1
  13. package/dist/commands/cron.d.ts +4 -0
  14. package/dist/commands/cron.d.ts.map +1 -0
  15. package/dist/commands/cron.js +439 -0
  16. package/dist/commands/cron.js.map +1 -0
  17. package/dist/commands/date.js +2 -2
  18. package/dist/commands/date.js.map +1 -1
  19. package/dist/commands/echo.js +2 -2
  20. package/dist/commands/echo.js.map +1 -1
  21. package/dist/commands/false.js +2 -2
  22. package/dist/commands/fetch.d.ts +4 -0
  23. package/dist/commands/fetch.d.ts.map +1 -0
  24. package/dist/commands/fetch.js +210 -0
  25. package/dist/commands/fetch.js.map +1 -0
  26. package/dist/commands/format.d.ts +4 -0
  27. package/dist/commands/format.d.ts.map +1 -0
  28. package/dist/commands/format.js +178 -0
  29. package/dist/commands/format.js.map +1 -0
  30. package/dist/commands/hash.d.ts +4 -0
  31. package/dist/commands/hash.d.ts.map +1 -0
  32. package/dist/commands/hash.js +200 -0
  33. package/dist/commands/hash.js.map +1 -0
  34. package/dist/commands/head.js +2 -2
  35. package/dist/commands/id.js +2 -2
  36. package/dist/commands/id.js.map +1 -1
  37. package/dist/commands/less.d.ts.map +1 -1
  38. package/dist/commands/less.js +53 -2
  39. package/dist/commands/less.js.map +1 -1
  40. package/dist/commands/ls.d.ts.map +1 -1
  41. package/dist/commands/ls.js +41 -15
  42. package/dist/commands/ls.js.map +1 -1
  43. package/dist/commands/man.d.ts +4 -0
  44. package/dist/commands/man.d.ts.map +1 -0
  45. package/dist/commands/man.js +564 -0
  46. package/dist/commands/man.js.map +1 -0
  47. package/dist/commands/mkdir.js +2 -2
  48. package/dist/commands/mkdir.js.map +1 -1
  49. package/dist/commands/mktemp.d.ts +4 -0
  50. package/dist/commands/mktemp.d.ts.map +1 -0
  51. package/dist/commands/mktemp.js +229 -0
  52. package/dist/commands/mktemp.js.map +1 -0
  53. package/dist/commands/nc.js +2 -2
  54. package/dist/commands/nc.js.map +1 -1
  55. package/dist/commands/open.d.ts +4 -0
  56. package/dist/commands/open.d.ts.map +1 -0
  57. package/dist/commands/open.js +74 -0
  58. package/dist/commands/open.js.map +1 -0
  59. package/dist/commands/passkey.js +3 -3
  60. package/dist/commands/play.d.ts +4 -0
  61. package/dist/commands/play.d.ts.map +1 -0
  62. package/dist/commands/play.js +231 -0
  63. package/dist/commands/play.js.map +1 -0
  64. package/dist/commands/pwd.js +2 -2
  65. package/dist/commands/pwd.js.map +1 -1
  66. package/dist/commands/rm.d.ts.map +1 -1
  67. package/dist/commands/rm.js +57 -12
  68. package/dist/commands/rm.js.map +1 -1
  69. package/dist/commands/rmdir.js +2 -2
  70. package/dist/commands/rmdir.js.map +1 -1
  71. package/dist/commands/sockets.js +1 -1
  72. package/dist/commands/stat.d.ts.map +1 -1
  73. package/dist/commands/stat.js +37 -15
  74. package/dist/commands/stat.js.map +1 -1
  75. package/dist/commands/tail.js +2 -2
  76. package/dist/commands/tar.d.ts +4 -0
  77. package/dist/commands/tar.d.ts.map +1 -0
  78. package/dist/commands/tar.js +743 -0
  79. package/dist/commands/tar.js.map +1 -0
  80. package/dist/commands/touch.js +2 -2
  81. package/dist/commands/touch.js.map +1 -1
  82. package/dist/commands/true.js +2 -2
  83. package/dist/commands/unzip.d.ts +4 -0
  84. package/dist/commands/unzip.d.ts.map +1 -0
  85. package/dist/commands/unzip.js +443 -0
  86. package/dist/commands/unzip.js.map +1 -0
  87. package/dist/commands/user.js +1 -1
  88. package/dist/commands/video.d.ts +4 -0
  89. package/dist/commands/video.d.ts.map +1 -0
  90. package/dist/commands/video.js +250 -0
  91. package/dist/commands/video.js.map +1 -0
  92. package/dist/commands/view.d.ts +4 -0
  93. package/dist/commands/view.d.ts.map +1 -0
  94. package/dist/commands/view.js +488 -0
  95. package/dist/commands/view.js.map +1 -0
  96. package/dist/commands/web.d.ts +4 -0
  97. package/dist/commands/web.d.ts.map +1 -0
  98. package/dist/commands/web.js +348 -0
  99. package/dist/commands/web.js.map +1 -0
  100. package/dist/commands/whoami.js +2 -2
  101. package/dist/commands/whoami.js.map +1 -1
  102. package/dist/commands/zip.d.ts +4 -0
  103. package/dist/commands/zip.d.ts.map +1 -0
  104. package/dist/commands/zip.js +264 -0
  105. package/dist/commands/zip.js.map +1 -0
  106. package/dist/index.d.ts +14 -0
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +44 -2
  109. package/dist/index.js.map +1 -1
  110. package/package.json +7 -4
  111. package/src/commands/cal.ts +2 -2
  112. package/src/commands/cat.ts +2 -2
  113. package/src/commands/cd.ts +2 -2
  114. package/src/commands/chmod.ts +19 -11
  115. package/src/commands/cp.ts +2 -2
  116. package/src/commands/cron.ts +499 -0
  117. package/src/commands/date.ts +2 -2
  118. package/src/commands/echo.ts +2 -2
  119. package/src/commands/false.ts +2 -2
  120. package/src/commands/fetch.ts +205 -0
  121. package/src/commands/format.ts +204 -0
  122. package/src/commands/hash.ts +215 -0
  123. package/src/commands/head.ts +2 -2
  124. package/src/commands/id.ts +2 -2
  125. package/src/commands/less.ts +50 -2
  126. package/src/commands/ls.ts +40 -14
  127. package/src/commands/man.ts +651 -0
  128. package/src/commands/mkdir.ts +2 -2
  129. package/src/commands/mktemp.ts +235 -0
  130. package/src/commands/nc.ts +2 -2
  131. package/src/commands/open.ts +84 -0
  132. package/src/commands/passkey.ts +3 -3
  133. package/src/commands/play.ts +249 -0
  134. package/src/commands/pwd.ts +2 -2
  135. package/src/commands/rm.ts +54 -12
  136. package/src/commands/rmdir.ts +2 -2
  137. package/src/commands/sockets.ts +1 -1
  138. package/src/commands/stat.ts +44 -16
  139. package/src/commands/tail.ts +2 -2
  140. package/src/commands/tar.ts +780 -0
  141. package/src/commands/touch.ts +2 -2
  142. package/src/commands/true.ts +2 -2
  143. package/src/commands/unzip.ts +517 -0
  144. package/src/commands/user.ts +1 -1
  145. package/src/commands/video.ts +267 -0
  146. package/src/commands/view.ts +526 -0
  147. package/src/commands/web.ts +377 -0
  148. package/src/commands/whoami.ts +2 -2
  149. package/src/commands/zip.ts +319 -0
  150. package/src/index.ts +44 -2
@@ -33,6 +33,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
33
33
 
34
34
  let lines: string[] = []
35
35
  let currentLine = 0
36
+ let horizontalOffset = 0
36
37
  let keyListener: IDisposable | null = null
37
38
  let linesRendered = 0
38
39
 
@@ -94,6 +95,40 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
94
95
  const displayRows = rows - 1
95
96
 
96
97
  const render = () => {
98
+ const cols = terminal.cols
99
+ const stripAnsi = (s: string) => s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '')
100
+
101
+ const getVisibleSlice = (line: string, offset: number): string => {
102
+ const visibleLen = stripAnsi(line).length
103
+
104
+ if (offset < 0) offset = 0
105
+ if (offset > visibleLen) offset = visibleLen
106
+
107
+ let visible = 0
108
+ let result = ''
109
+ let inEscape = false
110
+ let charsSkipped = 0
111
+
112
+ for (const char of line) {
113
+ if (char === '\x1b') inEscape = true
114
+ if (inEscape) {
115
+ if (charsSkipped >= offset) {
116
+ result += char
117
+ }
118
+ if (/[a-zA-Z]/.test(char)) inEscape = false
119
+ } else {
120
+ if (charsSkipped < offset) {
121
+ charsSkipped++
122
+ } else {
123
+ if (visible >= cols) break
124
+ result += char
125
+ visible++
126
+ }
127
+ }
128
+ }
129
+ return result
130
+ }
131
+
97
132
  const maxLine = Math.max(0, lines.length - displayRows)
98
133
  if (currentLine > maxLine) {
99
134
  currentLine = maxLine
@@ -102,6 +137,11 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
102
137
  currentLine = 0
103
138
  }
104
139
 
140
+ const maxLineLength = Math.max(...lines.map(l => stripAnsi(l).length), 0)
141
+ const maxHorizontalOffset = Math.max(0, maxLineLength - cols)
142
+ if (horizontalOffset > maxHorizontalOffset) horizontalOffset = maxHorizontalOffset
143
+ if (horizontalOffset < 0) horizontalOffset = 0
144
+
105
145
  if (linesRendered > 0) {
106
146
  terminal.write(ansi.cursor.up(linesRendered))
107
147
  }
@@ -111,7 +151,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
111
151
 
112
152
  for (let i = currentLine; i < endLine; i++) {
113
153
  terminal.write(ansi.erase.inLine(2))
114
- const line = lines[i] || ''
154
+ const line = getVisibleSlice(lines[i] || '', horizontalOffset)
115
155
  terminal.write(line)
116
156
  linesRendered++
117
157
  if (i < endLine - 1) {
@@ -129,7 +169,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
129
169
  const statusLine = `-- ${currentLine + 1}-${endLine} / ${lines.length} (${percentage}%)`
130
170
  terminal.write('\n')
131
171
  terminal.write(ansi.erase.inLine(2))
132
- terminal.write(statusLine)
172
+ terminal.write(getVisibleSlice(statusLine, 0))
133
173
  linesRendered++
134
174
  }
135
175
 
@@ -163,6 +203,14 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
163
203
  currentLine++
164
204
  render()
165
205
  break
206
+ case 'ArrowLeft':
207
+ horizontalOffset = Math.max(0, horizontalOffset - Math.floor(terminal.cols / 2))
208
+ render()
209
+ break
210
+ case 'ArrowRight':
211
+ horizontalOffset += Math.floor(terminal.cols / 2)
212
+ render()
213
+ break
166
214
  case 'PageDown':
167
215
  case ' ':
168
216
  currentLine = Math.min(currentLine + displayRows, Math.max(0, lines.length - displayRows))
@@ -4,14 +4,14 @@ import columnify from 'columnify'
4
4
  import humanFormat from 'human-format'
5
5
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
6
6
  import { TerminalCommand } from '../shared/terminal-command.js'
7
- import { writelnStdout } from '../shared/helpers.js'
7
+ import { writelnStdout, writelnStderr } from '../shared/helpers.js'
8
8
 
9
9
  function printUsage(process: Process | undefined, terminal: Terminal): void {
10
10
  const usage = `Usage: ls [OPTION]... [FILE]...
11
11
  List information about the FILEs (the current directory by default).
12
12
 
13
13
  --help display this help and exit`
14
- writelnStdout(process, terminal, usage)
14
+ writelnStderr(process, terminal, usage)
15
15
  }
16
16
 
17
17
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
@@ -29,12 +29,37 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
29
29
  return 0
30
30
  }
31
31
 
32
- const target = argv.length > 0 && argv[0] !== undefined && !argv[0].startsWith('-') ? argv[0] : shell.cwd
33
- const fullPath = target ? path.resolve(shell.cwd, target === '' ? '.' : target) : shell.cwd
34
- const stats = await shell.context.fs.promises.stat(fullPath)
35
- const entries: string[] = stats.isDirectory() ? await shell.context.fs.promises.readdir(fullPath) : [fullPath]
32
+ // Filter out options/flags and get target paths
33
+ const targets = argv.length > 0
34
+ ? argv.filter(arg => !arg.startsWith('-'))
35
+ : [shell.cwd]
36
+
37
+ if (targets.length === 0) targets.push(shell.cwd)
38
+
36
39
  const descriptions = kernel.filesystem.descriptions(kernel.i18n.t)
37
40
 
41
+ // Process each target and collect all entries
42
+ // We'll determine if each entry is a directory when we stat it later
43
+ const allEntries: Array<{ fullPath: string, entry: string }> = []
44
+
45
+ for (const target of targets) {
46
+ const fullPath = path.resolve(shell.cwd, target === '' ? '.' : target)
47
+ try {
48
+ const stats = await shell.context.fs.promises.stat(fullPath)
49
+ if (stats.isDirectory()) {
50
+ // For directories, list all contents
51
+ const dirEntries = await shell.context.fs.promises.readdir(fullPath)
52
+ for (const entry of dirEntries) allEntries.push({ fullPath, entry })
53
+ } else {
54
+ // For files, add the file itself
55
+ allEntries.push({ fullPath: path.dirname(fullPath), entry: path.basename(fullPath) })
56
+ }
57
+ } catch {
58
+ // If target doesn't exist, skip it (standard ls behavior)
59
+ continue
60
+ }
61
+ }
62
+
38
63
  const getModeType = (stats: Awaited<ReturnType<typeof shell.context.fs.promises.stat>>) => {
39
64
  let type = '-'
40
65
  if (stats.isDirectory()) type = 'd'
@@ -85,9 +110,9 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
85
110
  return ''
86
111
  }
87
112
 
88
- const filesMap = await Promise.all(entries
89
- .map(async entry => {
90
- const target = path.resolve(fullPath, entry)
113
+ const filesMap = await Promise.all(allEntries
114
+ .map(async ({ fullPath: entryFullPath, entry }) => {
115
+ const target = path.resolve(entryFullPath, entry)
91
116
  try {
92
117
  let linkTarget: string | null = null
93
118
  let linkStats = null
@@ -119,9 +144,9 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
119
144
  .filter(entry => entry && entry.stats && !entry.stats.isDirectory())
120
145
  .filter((entry): entry is NonNullable<typeof entry> => entry !== null && entry !== undefined)
121
146
 
122
- const directoryMap = await Promise.all(entries
123
- .map(async entry => {
124
- const target = path.resolve(fullPath, entry)
147
+ const directoryMap = await Promise.all(allEntries
148
+ .map(async ({ fullPath: entryFullPath, entry }) => {
149
+ const target = path.resolve(entryFullPath, entry)
125
150
  try {
126
151
  let linkTarget: string | null = null
127
152
  let linkStats = null
@@ -154,7 +179,8 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
154
179
  .filter((entry, index, self) => self.findIndex(e => e?.name === entry?.name) === index)
155
180
  .filter((entry): entry is NonNullable<typeof entry> => entry !== null && entry !== undefined)
156
181
 
157
- const isDevDirectory = fullPath.startsWith('/dev')
182
+ // Check if any entry is in /dev directory
183
+ const isDevDirectory = allEntries.some(e => e.fullPath.startsWith('/dev'))
158
184
  const columns = isDevDirectory ? ['Name', 'Mode', 'Owner', 'Info'] : ['Name', 'Size', 'Modified', 'Mode', 'Owner', 'Info']
159
185
 
160
186
  const directoryRows = directories.sort((a, b) => a.name.localeCompare(b.name)).map(directory => {
@@ -209,7 +235,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
209
235
  const linkInfo = getLinkInfo(file.linkTarget, file.linkStats, file.stats)
210
236
  if (linkInfo) return linkInfo
211
237
 
212
- if (descriptions.has(path.resolve(fullPath, file.name))) return descriptions.get(path.resolve(fullPath, file.name)) || ''
238
+ if (descriptions.has(file.target)) return descriptions.get(file.target) || ''
213
239
  if (file.name.includes('.')) {
214
240
  const ext = file.name.split('.').pop()
215
241
  if (ext && descriptions.has('.' + ext)) return descriptions.get('.' + ext) || ''