@ecmaos/coreutils 0.2.0 → 0.3.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 (123) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +25 -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/date.js +2 -2
  14. package/dist/commands/date.js.map +1 -1
  15. package/dist/commands/echo.js +2 -2
  16. package/dist/commands/echo.js.map +1 -1
  17. package/dist/commands/false.js +2 -2
  18. package/dist/commands/fetch.d.ts +4 -0
  19. package/dist/commands/fetch.d.ts.map +1 -0
  20. package/dist/commands/fetch.js +210 -0
  21. package/dist/commands/fetch.js.map +1 -0
  22. package/dist/commands/format.d.ts +4 -0
  23. package/dist/commands/format.d.ts.map +1 -0
  24. package/dist/commands/format.js +178 -0
  25. package/dist/commands/format.js.map +1 -0
  26. package/dist/commands/hash.d.ts +4 -0
  27. package/dist/commands/hash.d.ts.map +1 -0
  28. package/dist/commands/hash.js +200 -0
  29. package/dist/commands/hash.js.map +1 -0
  30. package/dist/commands/head.js +2 -2
  31. package/dist/commands/id.js +2 -2
  32. package/dist/commands/id.js.map +1 -1
  33. package/dist/commands/less.d.ts.map +1 -1
  34. package/dist/commands/less.js +53 -2
  35. package/dist/commands/less.js.map +1 -1
  36. package/dist/commands/ls.d.ts.map +1 -1
  37. package/dist/commands/ls.js +120 -97
  38. package/dist/commands/ls.js.map +1 -1
  39. package/dist/commands/man.d.ts +4 -0
  40. package/dist/commands/man.d.ts.map +1 -0
  41. package/dist/commands/man.js +554 -0
  42. package/dist/commands/man.js.map +1 -0
  43. package/dist/commands/mkdir.js +2 -2
  44. package/dist/commands/mkdir.js.map +1 -1
  45. package/dist/commands/mktemp.d.ts +4 -0
  46. package/dist/commands/mktemp.d.ts.map +1 -0
  47. package/dist/commands/mktemp.js +229 -0
  48. package/dist/commands/mktemp.js.map +1 -0
  49. package/dist/commands/nc.js +2 -2
  50. package/dist/commands/nc.js.map +1 -1
  51. package/dist/commands/passkey.js +3 -3
  52. package/dist/commands/pwd.js +2 -2
  53. package/dist/commands/pwd.js.map +1 -1
  54. package/dist/commands/rm.d.ts.map +1 -1
  55. package/dist/commands/rm.js +57 -12
  56. package/dist/commands/rm.js.map +1 -1
  57. package/dist/commands/rmdir.js +2 -2
  58. package/dist/commands/rmdir.js.map +1 -1
  59. package/dist/commands/sockets.js +1 -1
  60. package/dist/commands/stat.d.ts.map +1 -1
  61. package/dist/commands/stat.js +37 -15
  62. package/dist/commands/stat.js.map +1 -1
  63. package/dist/commands/tail.js +2 -2
  64. package/dist/commands/tar.d.ts +4 -0
  65. package/dist/commands/tar.d.ts.map +1 -0
  66. package/dist/commands/tar.js +693 -0
  67. package/dist/commands/tar.js.map +1 -0
  68. package/dist/commands/touch.js +2 -2
  69. package/dist/commands/touch.js.map +1 -1
  70. package/dist/commands/true.js +2 -2
  71. package/dist/commands/unzip.d.ts +4 -0
  72. package/dist/commands/unzip.d.ts.map +1 -0
  73. package/dist/commands/unzip.js +443 -0
  74. package/dist/commands/unzip.js.map +1 -0
  75. package/dist/commands/user.d.ts +4 -0
  76. package/dist/commands/user.d.ts.map +1 -0
  77. package/dist/commands/user.js +427 -0
  78. package/dist/commands/user.js.map +1 -0
  79. package/dist/commands/whoami.js +2 -2
  80. package/dist/commands/whoami.js.map +1 -1
  81. package/dist/commands/zip.d.ts +4 -0
  82. package/dist/commands/zip.d.ts.map +1 -0
  83. package/dist/commands/zip.js +264 -0
  84. package/dist/commands/zip.js.map +1 -0
  85. package/dist/index.d.ts +9 -0
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +28 -1
  88. package/dist/index.js.map +1 -1
  89. package/package.json +6 -4
  90. package/src/commands/cal.ts +2 -2
  91. package/src/commands/cat.ts +2 -2
  92. package/src/commands/cd.ts +2 -2
  93. package/src/commands/chmod.ts +19 -11
  94. package/src/commands/cp.ts +2 -2
  95. package/src/commands/date.ts +2 -2
  96. package/src/commands/echo.ts +2 -2
  97. package/src/commands/false.ts +2 -2
  98. package/src/commands/fetch.ts +205 -0
  99. package/src/commands/format.ts +204 -0
  100. package/src/commands/hash.ts +215 -0
  101. package/src/commands/head.ts +2 -2
  102. package/src/commands/id.ts +2 -2
  103. package/src/commands/less.ts +50 -2
  104. package/src/commands/ls.ts +131 -91
  105. package/src/commands/man.ts +643 -0
  106. package/src/commands/mkdir.ts +2 -2
  107. package/src/commands/mktemp.ts +235 -0
  108. package/src/commands/nc.ts +2 -2
  109. package/src/commands/passkey.ts +3 -3
  110. package/src/commands/pwd.ts +2 -2
  111. package/src/commands/rm.ts +54 -12
  112. package/src/commands/rmdir.ts +2 -2
  113. package/src/commands/sockets.ts +1 -1
  114. package/src/commands/stat.ts +44 -16
  115. package/src/commands/tail.ts +2 -2
  116. package/src/commands/tar.ts +737 -0
  117. package/src/commands/touch.ts +2 -2
  118. package/src/commands/true.ts +2 -2
  119. package/src/commands/unzip.ts +517 -0
  120. package/src/commands/user.ts +436 -0
  121. package/src/commands/whoami.ts +2 -2
  122. package/src/commands/zip.ts +319 -0
  123. package/src/index.ts +28 -1
@@ -40,22 +40,30 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
40
40
  return 1
41
41
  }
42
42
 
43
- const [mode, target] = args
44
- if (!mode || !target) {
43
+ const mode = args[0]
44
+ const targets = args.slice(1)
45
+
46
+ if (!mode || targets.length === 0) {
45
47
  await writelnStderr(process, terminal, chalk.red('chmod: missing operand'))
48
+ await writelnStderr(process, terminal, 'Try \'chmod --help\' for more information.')
46
49
  return 1
47
50
  }
48
51
 
49
- const fullPath = path.resolve(shell.cwd, target)
50
-
51
- try {
52
- await shell.context.fs.promises.chmod(fullPath, mode)
53
- return 0
54
- } catch (error) {
55
- const errorMessage = error instanceof Error ? error.message : String(error)
56
- await writelnStderr(process, terminal, `chmod: ${target}: ${errorMessage}`)
57
- return 1
52
+ let hasError = false
53
+
54
+ for (const target of targets) {
55
+ const fullPath = path.resolve(shell.cwd, target)
56
+
57
+ try {
58
+ await shell.context.fs.promises.chmod(fullPath, mode)
59
+ } catch (error) {
60
+ const errorMessage = error instanceof Error ? error.message : String(error)
61
+ await writelnStderr(process, terminal, `chmod: ${target}: ${errorMessage}`)
62
+ hasError = true
63
+ }
58
64
  }
65
+
66
+ return hasError ? 1 : 0
59
67
  }
60
68
  })
61
69
  }
@@ -1,14 +1,14 @@
1
1
  import path from 'path'
2
2
  import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
3
3
  import { TerminalCommand } from '../shared/terminal-command.js'
4
- import { writelnStdout, writelnStderr } from '../shared/helpers.js'
4
+ import { writelnStderr } from '../shared/helpers.js'
5
5
 
6
6
  function printUsage(process: Process | undefined, terminal: Terminal): void {
7
7
  const usage = `Usage: cp [OPTION]... SOURCE... DEST
8
8
  Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.
9
9
 
10
10
  --help display this help and exit`
11
- writelnStdout(process, terminal, usage)
11
+ writelnStderr(process, terminal, usage)
12
12
  }
13
13
 
14
14
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
@@ -1,6 +1,6 @@
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: date [OPTION]... [+FORMAT]
@@ -10,7 +10,7 @@ Print or set the system date and time.
10
10
  -R, --rfc-2822 output date and time in RFC 2822 format
11
11
  -f, --format=FORMAT output date/time in specified format (strftime-like)
12
12
  --help display this help and exit`
13
- writelnStdout(process, terminal, usage)
13
+ writelnStderr(process, terminal, usage)
14
14
  }
15
15
 
16
16
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
@@ -1,6 +1,6 @@
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: echo [OPTION]... [STRING]...
@@ -8,7 +8,7 @@ Echo the STRING(s) to standard output.
8
8
 
9
9
  -n do not output the trailing newline
10
10
  --help display this help and exit`
11
- writelnStdout(process, terminal, usage)
11
+ writelnStderr(process, terminal, usage)
12
12
  }
13
13
 
14
14
  export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
@@ -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 { writelnStderr } from '../shared/helpers.js'
4
4
 
5
5
  function printUsage(process: Process | undefined, terminal: Terminal): void {
6
6
  const usage = `Usage: false
7
7
  Return an unsuccessful exit status.
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 {
@@ -0,0 +1,205 @@
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 } from '../shared/helpers.js'
6
+
7
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
8
+ const usage = `Usage: fetch [OPTION]... URL
9
+ Fetch a resource from the network.
10
+
11
+ -o, --output=FILE write output to FILE instead of stdout
12
+ -X, --method=METHOD HTTP method to use (default: GET)
13
+ -d, --data=DATA request body data to send
14
+ -H, --header=HEADER add custom HTTP header (format: "Name: Value")
15
+ --help display this help and exit
16
+
17
+ Examples:
18
+ fetch https://example.com fetch and output to stdout
19
+ fetch -o file.txt https://example.com fetch and save to file.txt
20
+ fetch -X POST -d "data" https://api.example.com POST request with body`
21
+ writelnStderr(process, terminal, usage)
22
+ }
23
+
24
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
25
+ return new TerminalCommand({
26
+ command: 'fetch',
27
+ description: 'Fetch a resource from the network',
28
+ kernel,
29
+ shell,
30
+ terminal,
31
+ run: async (pid: number, argv: string[]) => {
32
+ const process = kernel.processes.get(pid) as Process | undefined
33
+
34
+ if (!process) return 1
35
+
36
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
37
+ printUsage(process, terminal)
38
+ return 0
39
+ }
40
+
41
+ let url: string | undefined
42
+ let outputFile: string | undefined
43
+ let method = 'GET'
44
+ let body: string | undefined
45
+ const headers: Record<string, string> = {}
46
+
47
+ // Parse arguments
48
+ for (let i = 0; i < argv.length; i++) {
49
+ const arg = argv[i]
50
+ if (arg === undefined) continue
51
+
52
+ if (arg === '--help' || arg === '-h') {
53
+ printUsage(process, terminal)
54
+ return 0
55
+ } else if (arg === '-o' || arg === '--output') {
56
+ const fileArg = argv[i + 1]
57
+ if (!fileArg) {
58
+ await writelnStderr(process, terminal, `fetch: option requires an argument -- '${arg === '-o' ? 'o' : 'output'}'`)
59
+ return 1
60
+ }
61
+ outputFile = fileArg
62
+ i++ // Skip the next argument as it's the file value
63
+ } else if (arg.startsWith('--output=')) {
64
+ const outputValue = arg.split('=')[1]
65
+ if (!outputValue) {
66
+ await writelnStderr(process, terminal, `fetch: option requires an argument -- 'output'`)
67
+ return 1
68
+ }
69
+ outputFile = outputValue
70
+ } else if (arg === '-X' || arg === '--method') {
71
+ const methodArg = argv[i + 1]
72
+ if (!methodArg) {
73
+ await writelnStderr(process, terminal, `fetch: option requires an argument -- '${arg === '-X' ? 'X' : 'method'}'`)
74
+ return 1
75
+ }
76
+ method = methodArg.toUpperCase()
77
+ i++ // Skip the next argument as it's the method value
78
+ } else if (arg.startsWith('--method=')) {
79
+ const methodValue = arg.split('=')[1]
80
+ if (!methodValue) {
81
+ await writelnStderr(process, terminal, `fetch: option requires an argument -- 'method'`)
82
+ return 1
83
+ }
84
+ method = methodValue.toUpperCase()
85
+ } else if (arg === '-d' || arg === '--data') {
86
+ const dataArg = argv[i + 1]
87
+ if (!dataArg) {
88
+ await writelnStderr(process, terminal, `fetch: option requires an argument -- '${arg === '-d' ? 'd' : 'data'}'`)
89
+ return 1
90
+ }
91
+ body = dataArg
92
+ i++ // Skip the next argument as it's the data value
93
+ } else if (arg.startsWith('--data=')) {
94
+ const dataValue = arg.split('=')[1]
95
+ if (!dataValue) {
96
+ await writelnStderr(process, terminal, `fetch: option requires an argument -- 'data'`)
97
+ return 1
98
+ }
99
+ body = dataValue
100
+ } else if (arg === '-H' || arg === '--header') {
101
+ const headerArg = argv[i + 1]
102
+ if (!headerArg) {
103
+ await writelnStderr(process, terminal, `fetch: option requires an argument -- '${arg === '-H' ? 'H' : 'header'}'`)
104
+ return 1
105
+ }
106
+ const [name, ...valueParts] = headerArg.split(':')
107
+ if (!name || valueParts.length === 0) {
108
+ await writelnStderr(process, terminal, `fetch: invalid header format. Expected "Name: Value"`)
109
+ return 1
110
+ }
111
+ headers[name.trim()] = valueParts.join(':').trim()
112
+ i++ // Skip the next argument as it's the header value
113
+ } else if (arg.startsWith('--header=')) {
114
+ const headerValue = arg.split('=')[1]
115
+ if (!headerValue) {
116
+ await writelnStderr(process, terminal, `fetch: option requires an argument -- 'header'`)
117
+ return 1
118
+ }
119
+ const [name, ...valueParts] = headerValue.split(':')
120
+ if (!name || valueParts.length === 0) {
121
+ await writelnStderr(process, terminal, `fetch: invalid header format. Expected "Name: Value"`)
122
+ return 1
123
+ }
124
+ headers[name.trim()] = valueParts.join(':').trim()
125
+ } else if (!arg.startsWith('-')) {
126
+ // Positional argument - should be the URL
127
+ if (!url) {
128
+ url = arg
129
+ } else {
130
+ await writelnStderr(process, terminal, `fetch: unexpected argument: ${arg}`)
131
+ return 1
132
+ }
133
+ } else {
134
+ await writelnStderr(process, terminal, `fetch: invalid option -- '${arg.replace(/^-+/, '')}'`)
135
+ await writelnStderr(process, terminal, `Try 'fetch --help' for more information.`)
136
+ return 1
137
+ }
138
+ }
139
+
140
+ if (!url) {
141
+ await writelnStderr(process, terminal, `fetch: URL is required`)
142
+ await writelnStderr(process, terminal, `Try 'fetch --help' for more information.`)
143
+ return 1
144
+ }
145
+
146
+ try {
147
+ const fetchOptions: RequestInit = { method }
148
+ if (body) fetchOptions.body = body
149
+ if (Object.keys(headers).length > 0) fetchOptions.headers = headers
150
+
151
+ const response = await globalThis.fetch(url, fetchOptions)
152
+
153
+ if (!response.ok) {
154
+ await writelnStderr(process, terminal, chalk.red(`fetch: HTTP error! status: ${response.status} ${response.statusText}`))
155
+ return 1
156
+ }
157
+
158
+ const reader = response.body?.getReader()
159
+ if (!reader) {
160
+ await writelnStderr(process, terminal, chalk.red(`fetch: No response body`))
161
+ return 1
162
+ }
163
+
164
+ let writer: WritableStreamDefaultWriter<Uint8Array> | { write: (chunk: Uint8Array) => Promise<void>, releaseLock: () => Promise<void> } | undefined
165
+
166
+ if (outputFile) {
167
+ // Write to file
168
+ const fullPath = path.resolve(shell.cwd, outputFile)
169
+ const fileHandle = await shell.context.fs.promises.open(fullPath, 'w')
170
+ writer = {
171
+ write: async (chunk: Uint8Array) => {
172
+ await fileHandle.write(chunk)
173
+ },
174
+ releaseLock: async () => {
175
+ await fileHandle.close()
176
+ }
177
+ }
178
+ } else {
179
+ // Write to stdout
180
+ if (!process.stdout) {
181
+ await writelnStderr(process, terminal, chalk.red(`fetch: No stdout available`))
182
+ return 1
183
+ }
184
+ writer = process.stdout.getWriter()
185
+ }
186
+
187
+ try {
188
+ while (true) {
189
+ const { done, value } = await reader.read()
190
+ if (done) break
191
+ if (value && value.length > 0) await writer.write(value)
192
+ }
193
+ } finally {
194
+ reader.releaseLock()
195
+ if (writer && 'releaseLock' in writer) await writer.releaseLock()
196
+ }
197
+
198
+ return 0
199
+ } catch (error) {
200
+ await writelnStderr(process, terminal, chalk.red(`fetch: ${error instanceof Error ? error.message : 'Unknown error'}`))
201
+ return 1
202
+ }
203
+ }
204
+ })
205
+ }
@@ -0,0 +1,204 @@
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 { writelnStdout, writelnStderr } from '../shared/helpers.js'
5
+
6
+ function printUsage(process: Process | undefined, terminal: Terminal): void {
7
+ const usage = `Usage: format [OPTION]...
8
+ Delete all IndexedDB and localStorage data.
9
+
10
+ -i, --indexeddb Delete only IndexedDB databases
11
+ -l, --localstorage Delete only localStorage
12
+ -k, --keep <name> Preserve specific IndexedDB database(s) (can be used multiple times)
13
+ --help Display this help and exit
14
+
15
+ By default, deletes all IndexedDB databases and localStorage.
16
+ Requires root privileges and interactive confirmation.`
17
+ writelnStderr(process, terminal, usage)
18
+ }
19
+
20
+ async function emptyIndexedDBDatabases(
21
+ keepDatabases: string[],
22
+ _kernel: Kernel,
23
+ process: Process | undefined,
24
+ terminal: Terminal
25
+ ): Promise<boolean> {
26
+ try {
27
+ if (!globalThis.indexedDB) {
28
+ await writelnStderr(process, terminal, chalk.yellow('format: IndexedDB is not available'))
29
+ return false
30
+ }
31
+
32
+ await writelnStdout(process, terminal, chalk.yellow('format: Emptying filesystem...'))
33
+
34
+ let databases: Array<{ name: string; version: number }> = []
35
+
36
+ if (typeof indexedDB.databases === 'function') {
37
+ const dbList = await indexedDB.databases()
38
+ databases = dbList
39
+ .filter((db): db is { name: string; version: number } => typeof db.name === 'string')
40
+ .map(db => ({ name: db.name, version: db.version }))
41
+ } else {
42
+ await writelnStdout(process, terminal, chalk.yellow('format: indexedDB.databases() not supported, assuming filesystem will be emptied on reboot'))
43
+ return true
44
+ }
45
+
46
+ const databasesToEmpty = databases.filter(db => !keepDatabases.includes(db.name))
47
+
48
+ if (databasesToEmpty.length === 0) {
49
+ await writelnStdout(process, terminal, chalk.green('format: All databases are preserved, nothing to empty'))
50
+ return true
51
+ }
52
+
53
+ if (keepDatabases.length > 0) {
54
+ const preserved = databases.filter(db => keepDatabases.includes(db.name))
55
+ if (preserved.length > 0) {
56
+ await writelnStdout(process, terminal, chalk.cyan(`format: Preserving databases: ${preserved.map(db => db.name).join(', ')}`))
57
+ }
58
+ }
59
+
60
+ await writelnStdout(process, terminal, chalk.green(`format: Will empty ${databasesToEmpty.length} IndexedDB database(s) on reboot`))
61
+ return true
62
+ } catch (error) {
63
+ const errorMessage = error instanceof Error ? error.message : String(error)
64
+ await writelnStderr(process, terminal, chalk.red(`format: Error preparing IndexedDB databases: ${errorMessage}`))
65
+ return false
66
+ }
67
+ }
68
+
69
+ async function deleteLocalStorage(
70
+ process: Process | undefined,
71
+ terminal: Terminal,
72
+ storage: Storage
73
+ ): Promise<boolean> {
74
+ try {
75
+ const keysCount = storage.length
76
+ if (keysCount === 0) {
77
+ await writelnStdout(process, terminal, chalk.green('format: localStorage is already empty'))
78
+ return true
79
+ }
80
+
81
+ storage.clear()
82
+ await writelnStdout(process, terminal, chalk.green(`format: Successfully cleared localStorage (${keysCount} item(s))`))
83
+ return true
84
+ } catch (error) {
85
+ const errorMessage = error instanceof Error ? error.message : String(error)
86
+ await writelnStderr(process, terminal, chalk.red(`format: Error clearing localStorage: ${errorMessage}`))
87
+ return false
88
+ }
89
+ }
90
+
91
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
92
+ return new TerminalCommand({
93
+ command: 'format',
94
+ description: 'Delete all IndexedDB and localStorage data',
95
+ kernel,
96
+ shell,
97
+ terminal,
98
+ run: async (pid: number, argv: string[]) => {
99
+ const process = kernel.processes.get(pid) as Process | undefined
100
+
101
+ if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
102
+ printUsage(process, terminal)
103
+ return 0
104
+ }
105
+
106
+ if (shell.credentials.suid !== 0) {
107
+ await writelnStderr(process, terminal, chalk.red('format: permission denied (requires root)'))
108
+ return 1
109
+ }
110
+
111
+ let onlyIndexedDB = false
112
+ let onlyLocalStorage = false
113
+ const keepDatabases: string[] = []
114
+
115
+ for (let i = 0; i < argv.length; i++) {
116
+ const arg = argv[i]
117
+ if (!arg || typeof arg !== 'string') continue
118
+
119
+ if (arg === '-i' || arg === '--indexeddb') {
120
+ onlyIndexedDB = true
121
+ } else if (arg === '-l' || arg === '--localstorage') {
122
+ onlyLocalStorage = true
123
+ } else if (arg === '-k' || arg === '--keep') {
124
+ if (i + 1 < argv.length) {
125
+ const dbName = argv[++i]
126
+ if (dbName && !dbName.startsWith('-')) {
127
+ keepDatabases.push(dbName)
128
+ } else {
129
+ await writelnStderr(process, terminal, chalk.red('format: --keep requires a database name'))
130
+ return 1
131
+ }
132
+ } else {
133
+ await writelnStderr(process, terminal, chalk.red('format: --keep requires a database name'))
134
+ return 1
135
+ }
136
+ } else if (arg.startsWith('-')) {
137
+ await writelnStderr(process, terminal, chalk.red(`format: invalid option -- '${arg.replace(/^-+/, '')}'`))
138
+ await writelnStdout(process, terminal, "Try 'format --help' for more information.")
139
+ return 1
140
+ }
141
+ }
142
+
143
+ if (onlyIndexedDB && onlyLocalStorage) {
144
+ await writelnStderr(process, terminal, chalk.red('format: cannot specify both --indexeddb and --localstorage'))
145
+ return 1
146
+ }
147
+
148
+ const deleteIndexedDB = !onlyLocalStorage
149
+ const deleteLocal = !onlyIndexedDB
150
+
151
+ let actionDescription = 'This will delete '
152
+ if (deleteIndexedDB && deleteLocal) {
153
+ actionDescription += 'ALL IndexedDB databases and localStorage data'
154
+ } else if (deleteIndexedDB) {
155
+ actionDescription += 'ALL IndexedDB databases'
156
+ } else {
157
+ actionDescription += 'ALL localStorage data'
158
+ }
159
+
160
+ if (keepDatabases.length > 0) {
161
+ actionDescription += ` (preserving: ${keepDatabases.join(', ')})`
162
+ }
163
+
164
+ actionDescription += '. This action cannot be undone!'
165
+
166
+ await writelnStderr(process, terminal, chalk.red.bold(`⚠️ WARNING: ${actionDescription}`))
167
+ await writelnStdout(process, terminal, chalk.yellow('Type "yes" to continue, or anything else to cancel: '))
168
+
169
+ const confirmation = await terminal.readline()
170
+ if (confirmation.trim().toLowerCase() !== 'yes') {
171
+ await writelnStdout(process, terminal, chalk.yellow('format: Operation cancelled'))
172
+ return 0
173
+ }
174
+
175
+ if (kernel.storage.db) {
176
+ kernel.storage.db.close()
177
+ }
178
+
179
+ let success = true
180
+
181
+ if (deleteIndexedDB) {
182
+ const indexedDBSuccess = await emptyIndexedDBDatabases(keepDatabases, kernel, process, terminal)
183
+ success = success && indexedDBSuccess
184
+ }
185
+
186
+ if (deleteLocal) {
187
+ const localStorageSuccess = await deleteLocalStorage(process, terminal, kernel.storage.local)
188
+ success = success && localStorageSuccess
189
+ }
190
+
191
+ if (success) {
192
+ await writelnStdout(process, terminal, chalk.green.bold('format: Format operation completed successfully'))
193
+ await writelnStdout(process, terminal, chalk.yellow('format: Rebooting system to complete format...'))
194
+ setTimeout(() => {
195
+ kernel.reboot()
196
+ }, 500)
197
+ return 0
198
+ } else {
199
+ await writelnStderr(process, terminal, chalk.red.bold('format: Format operation completed with errors'))
200
+ return 1
201
+ }
202
+ }
203
+ })
204
+ }