@ecmaos/coreutils 0.1.4 → 0.2.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +55 -0
- package/dist/commands/basename.d.ts +4 -0
- package/dist/commands/basename.d.ts.map +1 -0
- package/dist/commands/basename.js +78 -0
- package/dist/commands/basename.js.map +1 -0
- package/dist/commands/cal.d.ts +4 -0
- package/dist/commands/cal.d.ts.map +1 -0
- package/dist/commands/cal.js +105 -0
- package/dist/commands/cal.js.map +1 -0
- package/dist/commands/cat.d.ts.map +1 -1
- package/dist/commands/cat.js +49 -22
- package/dist/commands/cat.js.map +1 -1
- package/dist/commands/cd.d.ts.map +1 -1
- package/dist/commands/cd.js +38 -10
- package/dist/commands/cd.js.map +1 -1
- package/dist/commands/chmod.d.ts.map +1 -1
- package/dist/commands/chmod.js +31 -9
- package/dist/commands/chmod.js.map +1 -1
- package/dist/commands/comm.d.ts +4 -0
- package/dist/commands/comm.d.ts.map +1 -0
- package/dist/commands/comm.js +162 -0
- package/dist/commands/comm.js.map +1 -0
- package/dist/commands/cp.d.ts.map +1 -1
- package/dist/commands/cp.js +61 -12
- package/dist/commands/cp.js.map +1 -1
- package/dist/commands/cut.d.ts +4 -0
- package/dist/commands/cut.d.ts.map +1 -0
- package/dist/commands/cut.js +208 -0
- package/dist/commands/cut.js.map +1 -0
- package/dist/commands/date.d.ts +4 -0
- package/dist/commands/date.d.ts.map +1 -0
- package/dist/commands/date.js +100 -0
- package/dist/commands/date.js.map +1 -0
- package/dist/commands/diff.d.ts +4 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +194 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/dirname.d.ts +4 -0
- package/dist/commands/dirname.d.ts.map +1 -0
- package/dist/commands/dirname.js +50 -0
- package/dist/commands/dirname.js.map +1 -0
- package/dist/commands/echo.d.ts.map +1 -1
- package/dist/commands/echo.js +51 -9
- package/dist/commands/echo.js.map +1 -1
- package/dist/commands/false.d.ts +4 -0
- package/dist/commands/false.d.ts.map +1 -0
- package/dist/commands/false.js +27 -0
- package/dist/commands/false.js.map +1 -0
- package/dist/commands/find.d.ts +4 -0
- package/dist/commands/find.d.ts.map +1 -0
- package/dist/commands/find.js +181 -0
- package/dist/commands/find.js.map +1 -0
- package/dist/commands/grep.d.ts +4 -0
- package/dist/commands/grep.d.ts.map +1 -0
- package/dist/commands/grep.js +175 -0
- package/dist/commands/grep.js.map +1 -0
- package/dist/commands/head.d.ts +4 -0
- package/dist/commands/head.d.ts.map +1 -0
- package/dist/commands/head.js +199 -0
- package/dist/commands/head.js.map +1 -0
- package/dist/commands/hex.d.ts.map +1 -1
- package/dist/commands/hex.js +82 -20
- package/dist/commands/hex.js.map +1 -1
- package/dist/commands/id.d.ts +4 -0
- package/dist/commands/id.d.ts.map +1 -0
- package/dist/commands/id.js +97 -0
- package/dist/commands/id.js.map +1 -0
- package/dist/commands/join.d.ts +4 -0
- package/dist/commands/join.d.ts.map +1 -0
- package/dist/commands/join.js +152 -0
- package/dist/commands/join.js.map +1 -0
- package/dist/commands/less.d.ts.map +1 -1
- package/dist/commands/less.js +16 -7
- package/dist/commands/less.js.map +1 -1
- package/dist/commands/ln.d.ts.map +1 -1
- package/dist/commands/ln.js +54 -12
- package/dist/commands/ln.js.map +1 -1
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +19 -9
- package/dist/commands/ls.js.map +1 -1
- package/dist/commands/mkdir.d.ts.map +1 -1
- package/dist/commands/mkdir.js +34 -9
- package/dist/commands/mkdir.js.map +1 -1
- package/dist/commands/mv.d.ts.map +1 -1
- package/dist/commands/mv.js +25 -7
- package/dist/commands/mv.js.map +1 -1
- package/dist/commands/nc.d.ts +4 -0
- package/dist/commands/nc.d.ts.map +1 -0
- package/dist/commands/nc.js +451 -0
- package/dist/commands/nc.js.map +1 -0
- package/dist/commands/nl.d.ts +4 -0
- package/dist/commands/nl.d.ts.map +1 -0
- package/dist/commands/nl.js +208 -0
- package/dist/commands/nl.js.map +1 -0
- package/dist/commands/passkey.d.ts.map +1 -1
- package/dist/commands/passkey.js +44 -18
- package/dist/commands/passkey.js.map +1 -1
- package/dist/commands/paste.d.ts +4 -0
- package/dist/commands/paste.d.ts.map +1 -0
- package/dist/commands/paste.js +161 -0
- package/dist/commands/paste.js.map +1 -0
- package/dist/commands/pwd.d.ts.map +1 -1
- package/dist/commands/pwd.js +13 -4
- package/dist/commands/pwd.js.map +1 -1
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +105 -12
- package/dist/commands/rm.js.map +1 -1
- package/dist/commands/rmdir.d.ts.map +1 -1
- package/dist/commands/rmdir.js +34 -9
- package/dist/commands/rmdir.js.map +1 -1
- package/dist/commands/sed.d.ts.map +1 -1
- package/dist/commands/sed.js +73 -28
- package/dist/commands/sed.js.map +1 -1
- package/dist/commands/seq.d.ts +4 -0
- package/dist/commands/seq.d.ts.map +1 -0
- package/dist/commands/seq.js +148 -0
- package/dist/commands/seq.js.map +1 -0
- package/dist/commands/sockets.d.ts +4 -0
- package/dist/commands/sockets.d.ts.map +1 -0
- package/dist/commands/sockets.js +239 -0
- package/dist/commands/sockets.js.map +1 -0
- package/dist/commands/sort.d.ts +4 -0
- package/dist/commands/sort.d.ts.map +1 -0
- package/dist/commands/sort.js +175 -0
- package/dist/commands/sort.js.map +1 -0
- package/dist/commands/split.d.ts +4 -0
- package/dist/commands/split.d.ts.map +1 -0
- package/dist/commands/split.js +147 -0
- package/dist/commands/split.js.map +1 -0
- package/dist/commands/stat.d.ts.map +1 -1
- package/dist/commands/stat.js +14 -6
- package/dist/commands/stat.js.map +1 -1
- package/dist/commands/tail.d.ts +4 -0
- package/dist/commands/tail.d.ts.map +1 -0
- package/dist/commands/tail.js +189 -0
- package/dist/commands/tail.js.map +1 -0
- package/dist/commands/tee.d.ts.map +1 -1
- package/dist/commands/tee.js +45 -10
- package/dist/commands/tee.js.map +1 -1
- package/dist/commands/test.d.ts +4 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +99 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/touch.d.ts.map +1 -1
- package/dist/commands/touch.js +34 -9
- package/dist/commands/touch.js.map +1 -1
- package/dist/commands/tr.d.ts +4 -0
- package/dist/commands/tr.d.ts.map +1 -0
- package/dist/commands/tr.js +162 -0
- package/dist/commands/tr.js.map +1 -0
- package/dist/commands/true.d.ts +4 -0
- package/dist/commands/true.d.ts.map +1 -0
- package/dist/commands/true.js +27 -0
- package/dist/commands/true.js.map +1 -0
- package/dist/commands/uniq.d.ts +4 -0
- package/dist/commands/uniq.d.ts.map +1 -0
- package/dist/commands/uniq.js +187 -0
- package/dist/commands/uniq.js.map +1 -0
- package/dist/commands/wc.d.ts +4 -0
- package/dist/commands/wc.d.ts.map +1 -0
- package/dist/commands/wc.js +178 -0
- package/dist/commands/wc.js.map +1 -0
- package/dist/commands/which.d.ts +4 -0
- package/dist/commands/which.d.ts.map +1 -0
- package/dist/commands/which.js +78 -0
- package/dist/commands/which.js.map +1 -0
- package/dist/commands/whoami.d.ts +4 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +28 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -1
- package/dist/index.js.map +1 -1
- package/dist/shared/terminal-command.d.ts +8 -2
- package/dist/shared/terminal-command.d.ts.map +1 -1
- package/dist/shared/terminal-command.js +42 -17
- package/dist/shared/terminal-command.js.map +1 -1
- package/package.json +4 -2
- package/src/commands/basename.ts +84 -0
- package/src/commands/cal.ts +111 -0
- package/src/commands/cat.ts +55 -24
- package/src/commands/cd.ts +40 -12
- package/src/commands/chmod.ts +37 -11
- package/src/commands/comm.ts +169 -0
- package/src/commands/cp.ts +73 -15
- package/src/commands/cut.ts +214 -0
- package/src/commands/date.ts +97 -0
- package/src/commands/diff.ts +204 -0
- package/src/commands/dirname.ts +57 -0
- package/src/commands/echo.ts +53 -10
- package/src/commands/false.ts +31 -0
- package/src/commands/find.ts +184 -0
- package/src/commands/grep.ts +187 -0
- package/src/commands/head.ts +206 -0
- package/src/commands/hex.ts +93 -25
- package/src/commands/id.ts +94 -0
- package/src/commands/join.ts +162 -0
- package/src/commands/less.ts +20 -8
- package/src/commands/ln.ts +50 -13
- package/src/commands/ls.ts +21 -10
- package/src/commands/mkdir.ts +41 -12
- package/src/commands/mv.ts +31 -9
- package/src/commands/nc.ts +499 -0
- package/src/commands/nl.ts +201 -0
- package/src/commands/passkey.ts +46 -19
- package/src/commands/paste.ts +172 -0
- package/src/commands/pwd.ts +16 -4
- package/src/commands/rm.ts +118 -13
- package/src/commands/rmdir.ts +41 -12
- package/src/commands/sed.ts +64 -30
- package/src/commands/seq.ts +147 -0
- package/src/commands/sockets.ts +283 -0
- package/src/commands/sort.ts +175 -0
- package/src/commands/split.ts +154 -0
- package/src/commands/stat.ts +17 -8
- package/src/commands/tail.ts +200 -0
- package/src/commands/tee.ts +43 -11
- package/src/commands/test.ts +116 -0
- package/src/commands/touch.ts +41 -12
- package/src/commands/tr.ts +170 -0
- package/src/commands/true.ts +31 -0
- package/src/commands/uniq.ts +189 -0
- package/src/commands/wc.ts +181 -0
- package/src/commands/which.ts +89 -0
- package/src/commands/whoami.ts +32 -0
- package/src/index.ts +72 -1
- package/src/shared/terminal-command.ts +43 -16
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
3
|
+
import { TerminalEvents } 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: nl [OPTION]... [FILE]...
|
|
9
|
+
Number lines of files.
|
|
10
|
+
|
|
11
|
+
-v, --starting-line=NUMBER first line number for each section (default: 1)
|
|
12
|
+
-i, --increment=NUMBER line number increment at each line (default: 1)
|
|
13
|
+
-n, --format=FORMAT line number format: ln, rn, rz (default: rn)
|
|
14
|
+
-w, --width=NUMBER use NUMBER columns for line numbers (default: 6)
|
|
15
|
+
-s, --separator=STRING add STRING after (possible) line number (default: TAB)
|
|
16
|
+
--help display this help and exit`
|
|
17
|
+
writelnStderr(process, terminal, usage)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
21
|
+
return new TerminalCommand({
|
|
22
|
+
command: 'nl',
|
|
23
|
+
description: 'Number lines of files',
|
|
24
|
+
kernel,
|
|
25
|
+
shell,
|
|
26
|
+
terminal,
|
|
27
|
+
run: async (pid: number, argv: string[]) => {
|
|
28
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
29
|
+
|
|
30
|
+
if (!process) return 1
|
|
31
|
+
|
|
32
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
33
|
+
printUsage(process, terminal)
|
|
34
|
+
return 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const files: string[] = []
|
|
38
|
+
let startLine = 1
|
|
39
|
+
let increment = 1
|
|
40
|
+
let format = 'rn'
|
|
41
|
+
let width = 6
|
|
42
|
+
let separator = '\t'
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < argv.length; i++) {
|
|
45
|
+
const arg = argv[i]
|
|
46
|
+
if (!arg) continue
|
|
47
|
+
|
|
48
|
+
if (arg === '--help' || arg === '-h') {
|
|
49
|
+
printUsage(process, terminal)
|
|
50
|
+
return 0
|
|
51
|
+
} else if (arg === '-v' || arg === '--starting-line') {
|
|
52
|
+
if (i + 1 < argv.length) {
|
|
53
|
+
const nextArg = argv[++i]
|
|
54
|
+
if (nextArg !== undefined) {
|
|
55
|
+
const num = parseInt(nextArg, 10)
|
|
56
|
+
if (!isNaN(num)) startLine = num
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} else if (arg.startsWith('--starting-line=')) {
|
|
60
|
+
const num = parseInt(arg.slice(16), 10)
|
|
61
|
+
if (!isNaN(num)) startLine = num
|
|
62
|
+
} else if (arg === '-i' || arg === '--increment') {
|
|
63
|
+
if (i + 1 < argv.length) {
|
|
64
|
+
const nextArg = argv[++i]
|
|
65
|
+
if (nextArg !== undefined) {
|
|
66
|
+
const num = parseInt(nextArg, 10)
|
|
67
|
+
if (!isNaN(num)) increment = num
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} else if (arg.startsWith('--increment=')) {
|
|
71
|
+
const num = parseInt(arg.slice(12), 10)
|
|
72
|
+
if (!isNaN(num)) increment = num
|
|
73
|
+
} else if (arg === '-n' || arg === '--format') {
|
|
74
|
+
if (i + 1 < argv.length) {
|
|
75
|
+
format = argv[++i] || 'rn'
|
|
76
|
+
}
|
|
77
|
+
} else if (arg.startsWith('--format=')) {
|
|
78
|
+
format = arg.slice(9) || 'rn'
|
|
79
|
+
} else if (arg === '-w' || arg === '--width') {
|
|
80
|
+
if (i + 1 < argv.length) {
|
|
81
|
+
const nextArg = argv[++i]
|
|
82
|
+
if (nextArg !== undefined) {
|
|
83
|
+
const num = parseInt(nextArg, 10)
|
|
84
|
+
if (!isNaN(num)) width = num
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} else if (arg.startsWith('--width=')) {
|
|
88
|
+
const num = parseInt(arg.slice(8), 10)
|
|
89
|
+
if (!isNaN(num)) width = num
|
|
90
|
+
} else if (arg === '-s' || arg === '--separator') {
|
|
91
|
+
if (i + 1 < argv.length) {
|
|
92
|
+
separator = argv[++i] || '\t'
|
|
93
|
+
}
|
|
94
|
+
} else if (arg.startsWith('--separator=')) {
|
|
95
|
+
separator = arg.slice(12) || '\t'
|
|
96
|
+
} else if (!arg.startsWith('-')) {
|
|
97
|
+
files.push(arg)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const writer = process.stdout.getWriter()
|
|
102
|
+
|
|
103
|
+
const formatNumber = (num: number): string => {
|
|
104
|
+
const numStr = num.toString()
|
|
105
|
+
if (format === 'rz') {
|
|
106
|
+
return numStr.padStart(width, '0')
|
|
107
|
+
} else if (format === 'ln') {
|
|
108
|
+
return numStr.padEnd(width, ' ')
|
|
109
|
+
} else {
|
|
110
|
+
return numStr.padStart(width, ' ')
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
let lines: string[] = []
|
|
116
|
+
|
|
117
|
+
if (files.length === 0) {
|
|
118
|
+
if (!process.stdin) {
|
|
119
|
+
return 0
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const reader = process.stdin.getReader()
|
|
123
|
+
const decoder = new TextDecoder()
|
|
124
|
+
let buffer = ''
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
while (true) {
|
|
128
|
+
const { done, value } = await reader.read()
|
|
129
|
+
if (done) break
|
|
130
|
+
if (value) {
|
|
131
|
+
buffer += decoder.decode(value, { stream: true })
|
|
132
|
+
const newLines = buffer.split('\n')
|
|
133
|
+
buffer = newLines.pop() || ''
|
|
134
|
+
lines.push(...newLines)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (buffer) {
|
|
138
|
+
lines.push(buffer)
|
|
139
|
+
}
|
|
140
|
+
} finally {
|
|
141
|
+
reader.releaseLock()
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
const fullPath = path.resolve(shell.cwd, file)
|
|
146
|
+
|
|
147
|
+
let interrupted = false
|
|
148
|
+
const interruptHandler = () => { interrupted = true }
|
|
149
|
+
kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
if (fullPath.startsWith('/dev')) {
|
|
153
|
+
await writelnStderr(process, terminal, `nl: ${file}: cannot number device files`)
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const handle = await shell.context.fs.promises.open(fullPath, 'r')
|
|
158
|
+
const stat = await shell.context.fs.promises.stat(fullPath)
|
|
159
|
+
|
|
160
|
+
const decoder = new TextDecoder()
|
|
161
|
+
let content = ''
|
|
162
|
+
let bytesRead = 0
|
|
163
|
+
const chunkSize = 1024
|
|
164
|
+
|
|
165
|
+
while (bytesRead < stat.size) {
|
|
166
|
+
if (interrupted) break
|
|
167
|
+
const data = new Uint8Array(chunkSize)
|
|
168
|
+
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
169
|
+
await handle.read(data, 0, readSize, bytesRead)
|
|
170
|
+
const chunk = data.subarray(0, readSize)
|
|
171
|
+
content += decoder.decode(chunk, { stream: true })
|
|
172
|
+
bytesRead += readSize
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const fileLines = content.split('\n')
|
|
176
|
+
if (fileLines[fileLines.length - 1] === '') {
|
|
177
|
+
fileLines.pop()
|
|
178
|
+
}
|
|
179
|
+
lines.push(...fileLines)
|
|
180
|
+
} catch (error) {
|
|
181
|
+
await writelnStderr(process, terminal, `nl: ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
182
|
+
} finally {
|
|
183
|
+
kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let lineNumber = startLine
|
|
189
|
+
for (const line of lines) {
|
|
190
|
+
const formattedNum = formatNumber(lineNumber)
|
|
191
|
+
await writer.write(new TextEncoder().encode(`${formattedNum}${separator}${line}\n`))
|
|
192
|
+
lineNumber += increment
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return 0
|
|
196
|
+
} finally {
|
|
197
|
+
writer.releaseLock()
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
}
|
package/src/commands/passkey.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
|
-
import type { CommandLineOptions } from 'command-line-args'
|
|
3
2
|
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
4
3
|
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
5
4
|
import { writelnStdout, writelnStderr } from '../shared/helpers.js'
|
|
6
5
|
|
|
6
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
7
|
+
const usage = `Usage: passkey <subcommand> [options]
|
|
8
|
+
|
|
9
|
+
Subcommands:
|
|
10
|
+
register [--name <name>] Register a new passkey
|
|
11
|
+
list List all registered passkeys
|
|
12
|
+
remove --id <id> Remove a specific passkey
|
|
13
|
+
remove-all Remove all passkeys
|
|
14
|
+
|
|
15
|
+
--help display this help and exit`
|
|
16
|
+
writelnStdout(process, terminal, usage)
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
8
20
|
return new TerminalCommand({
|
|
9
21
|
command: 'passkey',
|
|
@@ -11,15 +23,16 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
11
23
|
kernel,
|
|
12
24
|
shell,
|
|
13
25
|
terminal,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{ name: 'name', type: String, description: 'Name/description for the passkey (used with register)' },
|
|
18
|
-
{ name: 'id', type: String, description: 'Passkey ID to remove (used with remove)' }
|
|
19
|
-
],
|
|
20
|
-
run: async (argv: CommandLineOptions, process?: Process) => {
|
|
26
|
+
run: async (pid: number, argv: string[]) => {
|
|
27
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
28
|
+
|
|
21
29
|
if (!process) return 1
|
|
22
30
|
|
|
31
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
32
|
+
printUsage(process, terminal)
|
|
33
|
+
return 0
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
const currentUid = shell.credentials.uid
|
|
24
37
|
const user = kernel.users.get(currentUid)
|
|
25
38
|
if (!user) {
|
|
@@ -27,16 +40,32 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
27
40
|
return 1
|
|
28
41
|
}
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
if (argv.length === 0) {
|
|
44
|
+
printUsage(process, terminal)
|
|
45
|
+
return 0
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const subcommand = argv[0]?.toLowerCase()
|
|
49
|
+
let name: string | undefined
|
|
50
|
+
let id: string | undefined
|
|
51
|
+
|
|
52
|
+
for (let i = 1; i < argv.length; i++) {
|
|
53
|
+
const arg = argv[i]
|
|
54
|
+
if (!arg) continue
|
|
55
|
+
|
|
56
|
+
if (arg === '--name' && i + 1 < argv.length) {
|
|
57
|
+
name = argv[++i]
|
|
58
|
+
} else if (arg.startsWith('--name=')) {
|
|
59
|
+
name = arg.slice(7)
|
|
60
|
+
} else if (arg === '--id' && i + 1 < argv.length) {
|
|
61
|
+
id = argv[++i]
|
|
62
|
+
} else if (arg.startsWith('--id=')) {
|
|
63
|
+
id = arg.slice(5)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
31
66
|
|
|
32
|
-
if (!subcommand || subcommand === 'help'
|
|
33
|
-
|
|
34
|
-
await writelnStdout(process, terminal, '')
|
|
35
|
-
await writelnStdout(process, terminal, 'Subcommands:')
|
|
36
|
-
await writelnStdout(process, terminal, ' register [--name <name>] Register a new passkey')
|
|
37
|
-
await writelnStdout(process, terminal, ' list List all registered passkeys')
|
|
38
|
-
await writelnStdout(process, terminal, ' remove --id <id> Remove a specific passkey')
|
|
39
|
-
await writelnStdout(process, terminal, ' remove-all Remove all passkeys')
|
|
67
|
+
if (!subcommand || subcommand === 'help') {
|
|
68
|
+
printUsage(process, terminal)
|
|
40
69
|
return 0
|
|
41
70
|
}
|
|
42
71
|
|
|
@@ -48,7 +77,6 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
48
77
|
return 1
|
|
49
78
|
}
|
|
50
79
|
|
|
51
|
-
const name = (argv.name as string) || undefined
|
|
52
80
|
const username = user.username
|
|
53
81
|
const userId = new TextEncoder().encode(username)
|
|
54
82
|
|
|
@@ -156,7 +184,6 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
156
184
|
}
|
|
157
185
|
|
|
158
186
|
case 'remove': {
|
|
159
|
-
const id = argv.id as string
|
|
160
187
|
if (!id) {
|
|
161
188
|
await writelnStderr(process, terminal, chalk.red('Error: --id is required for remove command'))
|
|
162
189
|
await writelnStdout(process, terminal, 'Usage: passkey remove --id <id>')
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
3
|
+
import { TerminalEvents } 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: paste [OPTION]... [FILE]...
|
|
9
|
+
Merge lines of files.
|
|
10
|
+
|
|
11
|
+
-d, --delimiters=LIST reuse characters from LIST instead of TABs
|
|
12
|
+
-s, --serial paste one file at a time instead of in parallel
|
|
13
|
+
--help display this help and exit`
|
|
14
|
+
writelnStderr(process, terminal, usage)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
18
|
+
return new TerminalCommand({
|
|
19
|
+
command: 'paste',
|
|
20
|
+
description: 'Merge lines of files',
|
|
21
|
+
kernel,
|
|
22
|
+
shell,
|
|
23
|
+
terminal,
|
|
24
|
+
run: async (pid: number, argv: string[]) => {
|
|
25
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
26
|
+
|
|
27
|
+
if (!process) return 1
|
|
28
|
+
|
|
29
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
30
|
+
printUsage(process, terminal)
|
|
31
|
+
return 0
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const files: string[] = []
|
|
35
|
+
let delimiters = '\t'
|
|
36
|
+
let serial = false
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < argv.length; i++) {
|
|
39
|
+
const arg = argv[i]
|
|
40
|
+
if (!arg) continue
|
|
41
|
+
|
|
42
|
+
if (arg === '--help' || arg === '-h') {
|
|
43
|
+
printUsage(process, terminal)
|
|
44
|
+
return 0
|
|
45
|
+
} else if (arg === '-d' || arg === '--delimiters') {
|
|
46
|
+
if (i + 1 < argv.length) {
|
|
47
|
+
delimiters = argv[++i] || '\t'
|
|
48
|
+
}
|
|
49
|
+
} else if (arg.startsWith('--delimiters=')) {
|
|
50
|
+
delimiters = arg.slice(13)
|
|
51
|
+
} else if (arg.startsWith('-d')) {
|
|
52
|
+
delimiters = arg.slice(2) || '\t'
|
|
53
|
+
} else if (arg === '-s' || arg === '--serial') {
|
|
54
|
+
serial = true
|
|
55
|
+
} else if (!arg.startsWith('-')) {
|
|
56
|
+
files.push(arg)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const writer = process.stdout.getWriter()
|
|
61
|
+
|
|
62
|
+
const readFileLines = async (filePath: string): Promise<string[]> => {
|
|
63
|
+
if (filePath.startsWith('/dev')) {
|
|
64
|
+
throw new Error('cannot paste device files')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let interrupted = false
|
|
68
|
+
const interruptHandler = () => { interrupted = true }
|
|
69
|
+
kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler)
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const handle = await shell.context.fs.promises.open(filePath, 'r')
|
|
73
|
+
const stat = await shell.context.fs.promises.stat(filePath)
|
|
74
|
+
|
|
75
|
+
const decoder = new TextDecoder()
|
|
76
|
+
let content = ''
|
|
77
|
+
let bytesRead = 0
|
|
78
|
+
const chunkSize = 1024
|
|
79
|
+
|
|
80
|
+
while (bytesRead < stat.size) {
|
|
81
|
+
if (interrupted) break
|
|
82
|
+
const data = new Uint8Array(chunkSize)
|
|
83
|
+
const readSize = Math.min(chunkSize, stat.size - bytesRead)
|
|
84
|
+
await handle.read(data, 0, readSize, bytesRead)
|
|
85
|
+
const chunk = data.subarray(0, readSize)
|
|
86
|
+
content += decoder.decode(chunk, { stream: true })
|
|
87
|
+
bytesRead += readSize
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const lines = content.split('\n')
|
|
91
|
+
if (lines[lines.length - 1] === '') {
|
|
92
|
+
lines.pop()
|
|
93
|
+
}
|
|
94
|
+
return lines
|
|
95
|
+
} finally {
|
|
96
|
+
kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
if (files.length === 0) {
|
|
102
|
+
if (!process.stdin) {
|
|
103
|
+
return 0
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const reader = process.stdin.getReader()
|
|
107
|
+
const decoder = new TextDecoder()
|
|
108
|
+
let content = ''
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
while (true) {
|
|
112
|
+
const { done, value } = await reader.read()
|
|
113
|
+
if (done) break
|
|
114
|
+
if (value) {
|
|
115
|
+
content += decoder.decode(value, { stream: true })
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} finally {
|
|
119
|
+
reader.releaseLock()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const lines = content.split('\n')
|
|
123
|
+
if (lines[lines.length - 1] === '') {
|
|
124
|
+
lines.pop()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
await writer.write(new TextEncoder().encode(line + '\n'))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return 0
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const fileLines: string[][] = []
|
|
135
|
+
|
|
136
|
+
for (const file of files) {
|
|
137
|
+
const fullPath = path.resolve(shell.cwd, file)
|
|
138
|
+
try {
|
|
139
|
+
const lines = await readFileLines(fullPath)
|
|
140
|
+
fileLines.push(lines)
|
|
141
|
+
} catch (error) {
|
|
142
|
+
await writelnStderr(process, terminal, `paste: ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
143
|
+
return 1
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (serial) {
|
|
148
|
+
for (const file of fileLines) {
|
|
149
|
+
for (const line of file) {
|
|
150
|
+
await writer.write(new TextEncoder().encode(line + '\n'))
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
const maxLines = Math.max(...fileLines.map(f => f.length))
|
|
155
|
+
const delimiter = delimiters[0] || '\t'
|
|
156
|
+
|
|
157
|
+
for (let i = 0; i < maxLines; i++) {
|
|
158
|
+
const parts: string[] = []
|
|
159
|
+
for (const file of fileLines) {
|
|
160
|
+
parts.push(file[i] || '')
|
|
161
|
+
}
|
|
162
|
+
await writer.write(new TextEncoder().encode(parts.join(delimiter) + '\n'))
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return 0
|
|
167
|
+
} finally {
|
|
168
|
+
writer.releaseLock()
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
}
|
package/src/commands/pwd.ts
CHANGED
|
@@ -2,6 +2,14 @@ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
|
|
|
2
2
|
import { TerminalCommand } from '../shared/terminal-command.js'
|
|
3
3
|
import { writelnStdout } from '../shared/helpers.js'
|
|
4
4
|
|
|
5
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
6
|
+
const usage = `Usage: pwd
|
|
7
|
+
Print the name of the current working directory.
|
|
8
|
+
|
|
9
|
+
--help display this help and exit`
|
|
10
|
+
writelnStdout(process, terminal, usage)
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
6
14
|
return new TerminalCommand({
|
|
7
15
|
command: 'pwd',
|
|
@@ -9,10 +17,14 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
9
17
|
kernel,
|
|
10
18
|
shell,
|
|
11
19
|
terminal,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
run: async (pid: number, argv: string[]) => {
|
|
21
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
22
|
+
|
|
23
|
+
if (argv.length > 0 && (argv[0] === '--help' || argv[0] === '-h')) {
|
|
24
|
+
printUsage(process, terminal)
|
|
25
|
+
return 0
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
await writelnStdout(process, terminal, shell.cwd)
|
|
17
29
|
return 0
|
|
18
30
|
}
|
package/src/commands/rm.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
-
import type {
|
|
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 { writelnStderr } from '../shared/helpers.js'
|
|
5
|
+
|
|
6
|
+
function printUsage(process: Process | undefined, terminal: Terminal): void {
|
|
7
|
+
const usage = `Usage: rm [OPTION]... FILE...
|
|
8
|
+
Remove (unlink) the FILE(s).
|
|
9
|
+
|
|
10
|
+
--help display this help and exit`
|
|
11
|
+
writelnStderr(process, terminal, usage)
|
|
12
|
+
}
|
|
5
13
|
|
|
6
14
|
export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
|
|
7
15
|
return new TerminalCommand({
|
|
@@ -10,17 +18,114 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
|
|
|
10
18
|
kernel,
|
|
11
19
|
shell,
|
|
12
20
|
terminal,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
run: async (pid: number, argv: string[]) => {
|
|
22
|
+
const process = kernel.processes.get(pid) as Process | undefined
|
|
23
|
+
|
|
24
|
+
if (argv.length === 0) {
|
|
25
|
+
await writelnStderr(process, terminal, 'rm: missing operand')
|
|
26
|
+
await writelnStderr(process, terminal, "Try 'rm --help' for more information.")
|
|
27
|
+
return 1
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (argv[0] === '--help' || argv[0] === '-h') {
|
|
31
|
+
printUsage(process, terminal)
|
|
32
|
+
return 0
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const pathArray: string[] = []
|
|
36
|
+
for (const arg of argv) {
|
|
37
|
+
if (arg.startsWith('-') && arg !== '--') {
|
|
38
|
+
if (arg !== '-f' && arg !== '-r' && arg !== '-rf' && arg !== '-fr') {
|
|
39
|
+
await writelnStderr(process, terminal, `rm: invalid option -- '${arg.slice(1)}'`)
|
|
40
|
+
await writelnStderr(process, terminal, "Try 'rm --help' for more information.")
|
|
41
|
+
return 1
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
pathArray.push(arg)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (pathArray.length === 0) {
|
|
49
|
+
await writelnStderr(process, terminal, 'rm: missing operand')
|
|
50
|
+
await writelnStderr(process, terminal, "Try 'rm --help' for more information.")
|
|
51
|
+
return 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const expandGlob = async (pattern: string): Promise<string[]> => {
|
|
55
|
+
if (!pattern.includes('*') && !pattern.includes('?')) {
|
|
56
|
+
return [pattern]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const lastSlashIndex = pattern.lastIndexOf('/')
|
|
60
|
+
const searchDir = lastSlashIndex !== -1
|
|
61
|
+
? path.resolve(shell.cwd, pattern.substring(0, lastSlashIndex + 1))
|
|
62
|
+
: shell.cwd
|
|
63
|
+
const globPattern = lastSlashIndex !== -1
|
|
64
|
+
? pattern.substring(lastSlashIndex + 1)
|
|
65
|
+
: pattern
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const entries = await shell.context.fs.promises.readdir(searchDir)
|
|
69
|
+
const regexPattern = globPattern
|
|
70
|
+
.replace(/\./g, '\\.')
|
|
71
|
+
.replace(/\*/g, '.*')
|
|
72
|
+
.replace(/\?/g, '.')
|
|
73
|
+
const regex = new RegExp(`^${regexPattern}$`)
|
|
74
|
+
|
|
75
|
+
const matches = entries.filter(entry => regex.test(entry))
|
|
76
|
+
|
|
77
|
+
if (lastSlashIndex !== -1) {
|
|
78
|
+
const dirPart = pattern.substring(0, lastSlashIndex + 1)
|
|
79
|
+
return matches.map(match => dirPart + match)
|
|
80
|
+
}
|
|
81
|
+
return matches
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return []
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const expandedPaths: string[] = []
|
|
88
|
+
for (const pattern of pathArray) {
|
|
89
|
+
const expanded = await expandGlob(pattern)
|
|
90
|
+
if (expanded.length === 0) {
|
|
91
|
+
expandedPaths.push(pattern)
|
|
92
|
+
} else {
|
|
93
|
+
expandedPaths.push(...expanded)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (expandedPaths.length === 0) {
|
|
98
|
+
await writelnStderr(process, terminal, 'rm: missing operand')
|
|
99
|
+
await writelnStderr(process, terminal, "Try 'rm --help' for more information.")
|
|
100
|
+
return 1
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let hasError = false
|
|
104
|
+
|
|
105
|
+
for (const target of expandedPaths) {
|
|
106
|
+
if (!target || typeof target !== 'string') {
|
|
107
|
+
await writelnStderr(process, terminal, `rm: ${String(target)}: No such file or directory`)
|
|
108
|
+
hasError = true
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const fullPath = path.resolve(shell.cwd, target)
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const stat = await shell.context.fs.promises.stat(fullPath)
|
|
116
|
+
if (stat.isDirectory()) {
|
|
117
|
+
await shell.context.fs.promises.rmdir(fullPath)
|
|
118
|
+
} else {
|
|
119
|
+
await shell.context.fs.promises.unlink(fullPath)
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
123
|
+
await writelnStderr(process, terminal, `rm: ${target}: ${errorMessage}`)
|
|
124
|
+
hasError = true
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return hasError ? 1 : 0
|
|
23
129
|
}
|
|
24
130
|
})
|
|
25
131
|
}
|
|
26
|
-
|