@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,377 @@
1
+ import chalk from 'chalk'
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: web [OPTIONS] [URL]
8
+ Open a URL in a new contained window.
9
+ Many sites will block the COR, so this is mostly useful for local/bespoke/credentialless resources.
10
+ To open in a new tab/browser window, use the 'open' command instead.
11
+
12
+ --help display this help and exit
13
+ --no-navbar hide the navigation bar
14
+
15
+ Examples:
16
+ web https://example.com open a URL in a browser window
17
+ web --no-navbar https://example.com open a URL without navigation bar
18
+ web example.com open a URL (https:// will be prepended)
19
+ web http://example.com open a URL with http protocol`
20
+ writelnStderr(process, terminal, usage)
21
+ }
22
+
23
+ function normalizeUrl(url: string): string {
24
+ const urlPattern = /^[a-zA-Z][a-zA-Z\d+\-.]*:/
25
+ if (urlPattern.test(url)) return url
26
+
27
+ return `https://${url}`
28
+ }
29
+
30
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
31
+ return new TerminalCommand({
32
+ command: 'web',
33
+ description: 'Open a URL in a browser window',
34
+ kernel,
35
+ shell,
36
+ terminal,
37
+ run: async (pid: number, argv: string[]) => {
38
+ const process = kernel.processes.get(pid) as Process | undefined
39
+
40
+ if (!process) return 1
41
+
42
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
43
+ printUsage(process, terminal)
44
+ return 0
45
+ }
46
+
47
+ // Parse arguments
48
+ let hideNavbar = false
49
+ const urlArgs: string[] = []
50
+
51
+ for (const arg of argv) {
52
+ const trimmedArg = arg.trim()
53
+ if (trimmedArg === '--no-navbar' || trimmedArg === '--hide-navbar') {
54
+ hideNavbar = true
55
+ } else if (trimmedArg && trimmedArg !== '--help' && trimmedArg !== '-h') {
56
+ // Remove any flag prefixes that might have been concatenated
57
+ let cleanArg = trimmedArg
58
+ if (cleanArg.startsWith('--no-navbar')) {
59
+ cleanArg = cleanArg.replace(/^--no-navbar/, '').trim()
60
+ hideNavbar = true
61
+ } else if (cleanArg.startsWith('--hide-navbar')) {
62
+ cleanArg = cleanArg.replace(/^--hide-navbar/, '').trim()
63
+ hideNavbar = true
64
+ }
65
+ if (cleanArg) {
66
+ urlArgs.push(cleanArg)
67
+ }
68
+ }
69
+ }
70
+
71
+ if (urlArgs.length === 0) {
72
+ await writelnStderr(process, terminal, `web: missing URL argument`)
73
+ await writelnStderr(process, terminal, `Try 'web --help' for more information.`)
74
+ return 1
75
+ }
76
+
77
+ const urlString = urlArgs.join(' ').trim()
78
+ if (!urlString) {
79
+ await writelnStderr(process, terminal, `web: missing URL argument`)
80
+ return 1
81
+ }
82
+
83
+ const url = normalizeUrl(urlString)
84
+
85
+ try {
86
+ // Validate URL
87
+ new URL(url)
88
+ } catch (error) {
89
+ await writelnStderr(process, terminal, chalk.red(`web: invalid URL: ${urlString}`))
90
+ return 1
91
+ }
92
+
93
+ // Create container for browser UI
94
+ const container = document.createElement('div')
95
+ container.style.width = '100%'
96
+ container.style.height = '100%'
97
+ container.style.display = 'flex'
98
+ container.style.flexDirection = 'column'
99
+ container.style.background = '#fff'
100
+ container.style.overflow = 'hidden'
101
+ container.style.boxSizing = 'border-box'
102
+
103
+ // Create navigation bar
104
+ const navBar = document.createElement('div')
105
+ navBar.style.display = 'flex'
106
+ navBar.style.alignItems = 'center'
107
+ navBar.style.gap = '4px'
108
+ navBar.style.padding = '6px 8px'
109
+ navBar.style.borderBottom = '1px solid #ccc'
110
+ navBar.style.background = '#f5f5f5'
111
+ navBar.style.flexShrink = '0'
112
+ navBar.style.boxSizing = 'border-box'
113
+
114
+ // Back button
115
+ const backButton = document.createElement('button')
116
+ backButton.innerHTML = '←'
117
+ backButton.style.padding = '0'
118
+ backButton.style.margin = '0'
119
+ backButton.style.cursor = 'pointer'
120
+ backButton.style.border = '1px solid #ccc'
121
+ backButton.style.borderRadius = '4px'
122
+ backButton.style.background = '#fff'
123
+ backButton.style.color = '#000'
124
+ backButton.style.fontSize = '16px'
125
+ backButton.style.width = '32px'
126
+ backButton.style.height = '28px'
127
+ backButton.style.display = 'flex'
128
+ backButton.style.alignItems = 'center'
129
+ backButton.style.justifyContent = 'center'
130
+ backButton.style.flexShrink = '0'
131
+ backButton.style.boxSizing = 'border-box'
132
+ backButton.style.lineHeight = '1'
133
+ backButton.style.verticalAlign = 'middle'
134
+ backButton.disabled = true
135
+ backButton.style.opacity = backButton.disabled ? '0.5' : '1'
136
+
137
+ // Forward button
138
+ const forwardButton = document.createElement('button')
139
+ forwardButton.innerHTML = '→'
140
+ forwardButton.style.padding = '0'
141
+ forwardButton.style.margin = '0'
142
+ forwardButton.style.cursor = 'pointer'
143
+ forwardButton.style.border = '1px solid #ccc'
144
+ forwardButton.style.borderRadius = '4px'
145
+ forwardButton.style.background = '#fff'
146
+ forwardButton.style.color = '#000'
147
+ forwardButton.style.fontSize = '16px'
148
+ forwardButton.style.width = '32px'
149
+ forwardButton.style.height = '28px'
150
+ forwardButton.style.display = 'flex'
151
+ forwardButton.style.alignItems = 'center'
152
+ forwardButton.style.justifyContent = 'center'
153
+ forwardButton.style.flexShrink = '0'
154
+ forwardButton.style.boxSizing = 'border-box'
155
+ forwardButton.style.lineHeight = '1'
156
+ forwardButton.style.verticalAlign = 'middle'
157
+ forwardButton.disabled = true
158
+ forwardButton.style.opacity = forwardButton.disabled ? '0.5' : '1'
159
+
160
+ // URL input
161
+ const urlInput = document.createElement('input')
162
+ urlInput.type = 'text'
163
+ urlInput.value = url
164
+ urlInput.style.flex = '1'
165
+ urlInput.style.minWidth = '0'
166
+ urlInput.style.padding = '6px 12px'
167
+ urlInput.style.border = '1px solid #ccc'
168
+ urlInput.style.borderRadius = '4px'
169
+ urlInput.style.fontSize = '14px'
170
+ urlInput.style.height = '28px'
171
+ urlInput.style.boxSizing = 'border-box'
172
+ urlInput.style.margin = '0'
173
+ urlInput.style.verticalAlign = 'middle'
174
+
175
+ // Refresh button
176
+ const refreshButton = document.createElement('button')
177
+ refreshButton.innerHTML = '↻'
178
+ refreshButton.style.padding = '0'
179
+ refreshButton.style.margin = '0'
180
+ refreshButton.style.cursor = 'pointer'
181
+ refreshButton.style.border = '1px solid #ccc'
182
+ refreshButton.style.borderRadius = '4px'
183
+ refreshButton.style.background = '#fff'
184
+ refreshButton.style.color = '#000'
185
+ refreshButton.style.fontSize = '16px'
186
+ refreshButton.style.width = '32px'
187
+ refreshButton.style.height = '28px'
188
+ refreshButton.style.display = 'flex'
189
+ refreshButton.style.alignItems = 'center'
190
+ refreshButton.style.justifyContent = 'center'
191
+ refreshButton.style.flexShrink = '0'
192
+ refreshButton.style.boxSizing = 'border-box'
193
+ refreshButton.style.lineHeight = '1'
194
+ refreshButton.style.verticalAlign = 'middle'
195
+
196
+ navBar.appendChild(backButton)
197
+ navBar.appendChild(forwardButton)
198
+ navBar.appendChild(urlInput)
199
+ navBar.appendChild(refreshButton)
200
+
201
+ const iframeContainer = document.createElement('div')
202
+ iframeContainer.style.flex = '1'
203
+ iframeContainer.style.position = 'relative'
204
+ iframeContainer.style.overflow = 'hidden'
205
+
206
+ const iframe = document.createElement('iframe')
207
+ iframe.style.width = '100%'
208
+ iframe.style.height = '100%'
209
+ iframe.style.border = 'none'
210
+ iframe.setAttribute('credentialless', '')
211
+ iframe.src = url
212
+
213
+ iframeContainer.appendChild(iframe)
214
+
215
+ // Only append navbar if not hidden
216
+ if (!hideNavbar) container.appendChild(navBar)
217
+ container.appendChild(iframeContainer)
218
+
219
+ // Navigation history
220
+ const history: string[] = [url]
221
+ let historyIndex = 0
222
+
223
+ // Update navigation buttons state
224
+ const updateNavButtons = () => {
225
+ if (!hideNavbar) {
226
+ backButton.disabled = historyIndex <= 0
227
+ backButton.style.opacity = backButton.disabled ? '0.5' : '1'
228
+ forwardButton.disabled = historyIndex >= history.length - 1
229
+ forwardButton.style.opacity = forwardButton.disabled ? '0.5' : '1'
230
+ }
231
+ }
232
+
233
+ // Navigate to URL
234
+ const navigateTo = (targetUrl: string, addToHistory = true) => {
235
+ try {
236
+ const normalizedUrl = normalizeUrl(targetUrl)
237
+ new URL(normalizedUrl) // Validate URL
238
+
239
+ if (addToHistory) {
240
+ // Remove forward history if we're navigating to a new URL
241
+ if (historyIndex < history.length - 1) history.splice(historyIndex + 1)
242
+
243
+ history.push(normalizedUrl)
244
+ historyIndex = history.length - 1
245
+ }
246
+
247
+ iframe.src = normalizedUrl
248
+ if (!hideNavbar) {
249
+ urlInput.value = normalizedUrl
250
+ }
251
+ updateNavButtons()
252
+ } catch (error) {
253
+ terminal.writeln(chalk.red(`Invalid URL: ${targetUrl}`))
254
+ }
255
+ }
256
+
257
+ // Only set up button handlers if navbar is visible
258
+ if (!hideNavbar) {
259
+ // Add hover effects
260
+ const addHoverEffect = (button: HTMLButtonElement) => {
261
+ button.addEventListener('mouseenter', () => {
262
+ if (!button.disabled) {
263
+ button.style.background = '#f0f0f0'
264
+ }
265
+ })
266
+ button.addEventListener('mouseleave', () => {
267
+ button.style.background = '#fff'
268
+ })
269
+ }
270
+ addHoverEffect(backButton)
271
+ addHoverEffect(forwardButton)
272
+ addHoverEffect(refreshButton)
273
+
274
+ // Back button handler
275
+ backButton.addEventListener('click', () => {
276
+ if (historyIndex > 0) {
277
+ historyIndex--
278
+ const targetUrl = history[historyIndex]
279
+ if (targetUrl) {
280
+ iframe.src = targetUrl
281
+ urlInput.value = targetUrl
282
+ updateNavButtons()
283
+ }
284
+ }
285
+ })
286
+
287
+ // Forward button handler
288
+ forwardButton.addEventListener('click', () => {
289
+ if (historyIndex < history.length - 1) {
290
+ historyIndex++
291
+ const targetUrl = history[historyIndex]
292
+ if (targetUrl) {
293
+ iframe.src = targetUrl
294
+ urlInput.value = targetUrl
295
+ updateNavButtons()
296
+ }
297
+ }
298
+ })
299
+ }
300
+
301
+ // Only set up navbar event handlers if navbar is visible
302
+ if (!hideNavbar) {
303
+ // URL input handler (Enter key or blur)
304
+ const handleUrlSubmit = () => {
305
+ const inputValue = urlInput.value.trim()
306
+ if (inputValue) {
307
+ navigateTo(inputValue)
308
+ }
309
+ }
310
+
311
+ urlInput.addEventListener('keydown', (e: KeyboardEvent) => {
312
+ if (e.key === 'Enter') {
313
+ e.preventDefault()
314
+ handleUrlSubmit()
315
+ }
316
+ })
317
+
318
+ urlInput.addEventListener('blur', handleUrlSubmit)
319
+
320
+ // Refresh button handler
321
+ refreshButton.addEventListener('click', () => {
322
+ iframe.src = iframe.src
323
+ })
324
+
325
+ // Update URL bar when iframe navigates (for same-origin or when possible)
326
+ iframe.addEventListener('load', () => {
327
+ try {
328
+ // Try to get the current URL from iframe (may fail due to CORS)
329
+ const iframeUrl = iframe.contentWindow?.location.href
330
+ if (iframeUrl && iframeUrl !== urlInput.value) {
331
+ urlInput.value = iframeUrl
332
+ // Only add to history if it's a different URL
333
+ if (iframeUrl !== history[historyIndex]) navigateTo(iframeUrl)
334
+ }
335
+ } catch (e) {
336
+ // CORS: Can't access iframe location, that's okay
337
+ // The URL bar will show what we set it to
338
+ }
339
+ updateNavButtons()
340
+ })
341
+ } else {
342
+ // Still track history even without navbar
343
+ iframe.addEventListener('load', () => {
344
+ try {
345
+ const iframeUrl = iframe.contentWindow?.location.href
346
+ if (iframeUrl && iframeUrl !== history[historyIndex]) {
347
+ navigateTo(iframeUrl, true)
348
+ }
349
+ } catch (e) {
350
+ // CORS: Can't access iframe location
351
+ }
352
+ })
353
+ }
354
+
355
+ // Create window
356
+ const win = kernel.windows.create({
357
+ title: url,
358
+ width: Math.floor(window.innerWidth * 0.75),
359
+ height: Math.floor(window.innerHeight * 0.75),
360
+ x: 'center',
361
+ y: 'center',
362
+ onclose: () => false
363
+ })
364
+
365
+ // Mount container to window
366
+ win.mount(container)
367
+
368
+ // Update window title when URL changes (only if navbar is visible)
369
+ if (!hideNavbar) {
370
+ const updateTitle = () => win.setTitle(urlInput.value)
371
+ urlInput.addEventListener('input', updateTitle)
372
+ }
373
+
374
+ return 0
375
+ }
376
+ })
377
+ }
@@ -1,13 +1,13 @@
1
1
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
2
2
  import { TerminalCommand } from '../shared/terminal-command.js'
3
- import { writelnStdout } from '../shared/helpers.js'
3
+ import { writelnStdout, writelnStderr } from '../shared/helpers.js'
4
4
 
5
5
  function printUsage(process: Process | undefined, terminal: Terminal): void {
6
6
  const usage = `Usage: whoami
7
7
  Print effective user ID.
8
8
 
9
9
  --help display this help and exit`
10
- writelnStdout(process, terminal, usage)
10
+ writelnStderr(process, terminal, usage)
11
11
  }
12
12
 
13
13
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {