@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
@@ -0,0 +1,267 @@
1
+ import path from 'path'
2
+ import chalk from 'chalk'
3
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
4
+ import { TerminalCommand } from '../shared/terminal-command.js'
5
+ import { writelnStderr, writelnStdout } from '../shared/helpers.js'
6
+
7
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
8
+ const usage = `Usage: video [OPTIONS] [FILE...]
9
+ Play a video file in a window.
10
+
11
+ --help display this help and exit
12
+ --no-autoplay don't start playing automatically
13
+ --no-controls hide video controls
14
+ --loop loop the video
15
+ --muted start muted
16
+ --fullscreen open in fullscreen mode
17
+ --width <width> set window width (default: video width or screen width)
18
+ --height <height> set window height (default: video height or screen height)
19
+
20
+ Examples:
21
+ video movie.mp4 play a video file
22
+ video --loop clip.mp4 play a video in a loop
23
+ video --no-autoplay video.mp4 load video without auto-playing
24
+ video --fullscreen movie.mp4 play video in fullscreen mode
25
+ video video1.mp4 video2.mp4 play multiple videos`
26
+ writelnStderr(process, terminal, usage)
27
+ }
28
+
29
+ function getMimeType(filePath: string): string {
30
+ const ext = path.extname(filePath).toLowerCase()
31
+ const mimeTypes: Record<string, string> = {
32
+ '.mp4': 'video/mp4',
33
+ '.webm': 'video/webm',
34
+ '.ogg': 'video/ogg',
35
+ '.ogv': 'video/ogg',
36
+ '.mov': 'video/quicktime',
37
+ '.avi': 'video/x-msvideo',
38
+ '.mkv': 'video/x-matroska',
39
+ '.m4v': 'video/mp4',
40
+ '.flv': 'video/x-flv',
41
+ '.wmv': 'video/x-ms-wmv',
42
+ '.3gp': 'video/3gpp'
43
+ }
44
+ return mimeTypes[ext] || 'video/mp4'
45
+ }
46
+
47
+ async function loadVideoMetadata(videoElement: HTMLVideoElement): Promise<{ width: number; height: number; duration: number }> {
48
+ return new Promise((resolve, reject) => {
49
+ const timeout = setTimeout(() => {
50
+ reject(new Error('Timeout loading video metadata'))
51
+ }, 10000)
52
+
53
+ videoElement.onloadedmetadata = () => {
54
+ clearTimeout(timeout)
55
+ resolve({
56
+ width: videoElement.videoWidth,
57
+ height: videoElement.videoHeight,
58
+ duration: videoElement.duration
59
+ })
60
+ }
61
+
62
+ videoElement.onerror = () => {
63
+ clearTimeout(timeout)
64
+ reject(new Error('Failed to load video metadata'))
65
+ }
66
+ })
67
+ }
68
+
69
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
70
+ return new TerminalCommand({
71
+ command: 'video',
72
+ description: 'Play a video file',
73
+ kernel,
74
+ shell,
75
+ terminal,
76
+ run: async (pid: number, argv: string[]) => {
77
+ const process = kernel.processes.get(pid) as Process | undefined
78
+
79
+ if (!process) return 1
80
+
81
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
82
+ printUsage(process, terminal)
83
+ return 0
84
+ }
85
+
86
+ // Parse options
87
+ const options: {
88
+ autoplay: boolean
89
+ controls: boolean
90
+ loop: boolean
91
+ muted: boolean
92
+ fullscreen: boolean
93
+ width?: number
94
+ height?: number
95
+ } = {
96
+ autoplay: true,
97
+ controls: true,
98
+ loop: false,
99
+ muted: false,
100
+ fullscreen: false
101
+ }
102
+
103
+ const files: string[] = []
104
+
105
+ for (let i = 0; i < argv.length; i++) {
106
+ const arg = argv[i]
107
+ if (arg === '--no-autoplay') {
108
+ options.autoplay = false
109
+ } else if (arg === '--no-controls') {
110
+ options.controls = false
111
+ } else if (arg === '--loop') {
112
+ options.loop = true
113
+ } else if (arg === '--muted') {
114
+ options.muted = true
115
+ } else if (arg === '--fullscreen') {
116
+ options.fullscreen = true
117
+ } else if (arg === '--width' && i + 1 < argv.length) {
118
+ const widthArg = argv[i + 1]
119
+ if (!widthArg) {
120
+ await writelnStderr(process, terminal, chalk.red(`video: missing width value`))
121
+ return 1
122
+ }
123
+ const width = parseInt(widthArg, 10)
124
+ if (isNaN(width) || width <= 0) {
125
+ await writelnStderr(process, terminal, chalk.red(`video: invalid width: ${widthArg}`))
126
+ return 1
127
+ }
128
+ options.width = width
129
+ i++ // Skip next argument
130
+ } else if (arg === '--height' && i + 1 < argv.length) {
131
+ const heightArg = argv[i + 1]
132
+ if (!heightArg) {
133
+ await writelnStderr(process, terminal, chalk.red(`video: missing height value`))
134
+ return 1
135
+ }
136
+ const height = parseInt(heightArg, 10)
137
+ if (isNaN(height) || height <= 0) {
138
+ await writelnStderr(process, terminal, chalk.red(`video: invalid height: ${heightArg}`))
139
+ return 1
140
+ }
141
+ options.height = height
142
+ i++ // Skip next argument
143
+ } else if (arg && !arg.startsWith('--')) {
144
+ files.push(arg)
145
+ }
146
+ }
147
+
148
+ if (files.length === 0) {
149
+ await writelnStderr(process, terminal, `video: missing file argument`)
150
+ await writelnStderr(process, terminal, `Try 'video --help' for more information.`)
151
+ return 1
152
+ }
153
+
154
+ // Process each video file
155
+ for (const file of files) {
156
+ const fullPath = path.resolve(shell.cwd, file)
157
+
158
+ try {
159
+ // Check if file exists
160
+ if (!(await shell.context.fs.promises.exists(fullPath))) {
161
+ await writelnStderr(process, terminal, chalk.red(`video: file not found: ${fullPath}`))
162
+ continue
163
+ }
164
+
165
+ // Read file
166
+ await writelnStdout(process, terminal, chalk.blue(`Loading video: ${file}...`))
167
+ const fileData = await shell.context.fs.promises.readFile(fullPath)
168
+ const mimeType = getMimeType(fullPath)
169
+ const blob = new Blob([new Uint8Array(fileData)], { type: mimeType })
170
+ const url = URL.createObjectURL(blob)
171
+
172
+ // Load video metadata
173
+ const videoElement = document.createElement('video')
174
+ videoElement.src = url
175
+ videoElement.preload = 'metadata'
176
+
177
+ let videoWidth: number
178
+ let videoHeight: number
179
+ let duration: number
180
+
181
+ try {
182
+ const metadata = await loadVideoMetadata(videoElement)
183
+ videoWidth = metadata.width
184
+ videoHeight = metadata.height
185
+ duration = metadata.duration
186
+ } catch (error) {
187
+ await writelnStderr(process, terminal, chalk.yellow(`video: warning: could not load metadata for ${file}, using default size`))
188
+ videoWidth = 640
189
+ videoHeight = 360
190
+ duration = 0
191
+ }
192
+
193
+ // Calculate window dimensions
194
+ const { innerWidth, innerHeight } = window
195
+ let windowWidth: number
196
+ let windowHeight: number
197
+
198
+ if (options.fullscreen) {
199
+ windowWidth = innerWidth
200
+ windowHeight = innerHeight
201
+ } else if (options.width && options.height) {
202
+ windowWidth = options.width
203
+ windowHeight = options.height
204
+ } else if (options.width) {
205
+ windowWidth = options.width
206
+ windowHeight = Math.round((videoHeight / videoWidth) * windowWidth)
207
+ } else if (options.height) {
208
+ windowHeight = options.height
209
+ windowWidth = Math.round((videoWidth / videoHeight) * windowHeight)
210
+ } else {
211
+ // Auto-size: fit to screen if video is larger, otherwise use video dimensions
212
+ const scale = Math.min(innerWidth / videoWidth, innerHeight / videoHeight, 1)
213
+ windowWidth = Math.round(videoWidth * scale)
214
+ windowHeight = Math.round(videoHeight * scale)
215
+ }
216
+
217
+ // Ensure minimum size
218
+ windowWidth = Math.max(windowWidth, 320)
219
+ windowHeight = Math.max(windowHeight, 180)
220
+
221
+ // Build video attributes
222
+ const videoAttrs: string[] = []
223
+ if (options.autoplay) videoAttrs.push('autoplay')
224
+ if (options.controls) videoAttrs.push('controls')
225
+ if (options.loop) videoAttrs.push('loop')
226
+ if (options.muted) videoAttrs.push('muted')
227
+ videoAttrs.push('style="width:100%;height:100%;object-fit:contain"')
228
+
229
+ const videoHtml = `<video src="${url}" ${videoAttrs.join(' ')}></video>`
230
+
231
+ // Create window
232
+ const windowTitle = files.length > 1
233
+ ? `${path.basename(file)} (${files.indexOf(file) + 1}/${files.length})`
234
+ : path.basename(file)
235
+
236
+ kernel.windows.create({
237
+ title: windowTitle,
238
+ html: videoHtml,
239
+ width: windowWidth,
240
+ height: windowHeight,
241
+ max: options.fullscreen
242
+ })
243
+
244
+ // Clean up URL when window is closed (if possible)
245
+ // Note: This is a best-effort cleanup since we don't have direct access to window close events
246
+ setTimeout(() => {
247
+ // Cleanup after a delay to allow video to load
248
+ // In a real implementation, you'd want to hook into window close events
249
+ }, 1000)
250
+
251
+ if (duration > 0) {
252
+ const minutes = Math.floor(duration / 60)
253
+ const seconds = Math.floor(duration % 60)
254
+ await writelnStdout(process, terminal, chalk.green(`Playing: ${file} (${minutes}:${seconds.toString().padStart(2, '0')})`))
255
+ } else {
256
+ await writelnStdout(process, terminal, chalk.green(`Playing: ${file}`))
257
+ }
258
+ } catch (error) {
259
+ await writelnStderr(process, terminal, chalk.red(`video: error playing ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`))
260
+ return 1
261
+ }
262
+ }
263
+
264
+ return 0
265
+ }
266
+ })
267
+ }