@ecmaos/coreutils 0.1.1 → 0.1.3

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 (41) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +182 -0
  3. package/dist/commands/cd.d.ts.map +1 -1
  4. package/dist/commands/cd.js +1 -7
  5. package/dist/commands/cd.js.map +1 -1
  6. package/dist/commands/hex.d.ts +4 -0
  7. package/dist/commands/hex.d.ts.map +1 -0
  8. package/dist/commands/hex.js +82 -0
  9. package/dist/commands/hex.js.map +1 -0
  10. package/dist/commands/less.d.ts +4 -0
  11. package/dist/commands/less.d.ts.map +1 -0
  12. package/dist/commands/less.js +173 -0
  13. package/dist/commands/less.js.map +1 -0
  14. package/dist/commands/ln.d.ts +4 -0
  15. package/dist/commands/ln.d.ts.map +1 -0
  16. package/dist/commands/ln.js +104 -0
  17. package/dist/commands/ln.js.map +1 -0
  18. package/dist/commands/ls.d.ts.map +1 -1
  19. package/dist/commands/ls.js +91 -11
  20. package/dist/commands/ls.js.map +1 -1
  21. package/dist/commands/sed.d.ts +4 -0
  22. package/dist/commands/sed.d.ts.map +1 -0
  23. package/dist/commands/sed.js +381 -0
  24. package/dist/commands/sed.js.map +1 -0
  25. package/dist/commands/tee.d.ts +4 -0
  26. package/dist/commands/tee.d.ts.map +1 -0
  27. package/dist/commands/tee.js +87 -0
  28. package/dist/commands/tee.js.map +1 -0
  29. package/dist/index.d.ts +5 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +16 -1
  32. package/dist/index.js.map +1 -1
  33. package/package.json +4 -2
  34. package/src/commands/cd.ts +1 -8
  35. package/src/commands/hex.ts +92 -0
  36. package/src/commands/less.ts +192 -0
  37. package/src/commands/ln.ts +108 -0
  38. package/src/commands/ls.ts +85 -11
  39. package/src/commands/sed.ts +436 -0
  40. package/src/commands/tee.ts +93 -0
  41. package/src/index.ts +16 -1
@@ -0,0 +1,87 @@
1
+ import path from 'path';
2
+ import { TerminalEvents } from '@ecmaos/types';
3
+ import { TerminalCommand } from '../shared/terminal-command.js';
4
+ import { writelnStderr } from '../shared/helpers.js';
5
+ export function createCommand(kernel, shell, terminal) {
6
+ return new TerminalCommand({
7
+ command: 'tee',
8
+ description: 'Read from standard input and write to standard output and files',
9
+ kernel,
10
+ shell,
11
+ terminal,
12
+ options: [
13
+ { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
14
+ { name: 'append', type: Boolean, alias: 'a', description: 'Append to the given files, do not overwrite' },
15
+ { name: 'ignore-interrupts', type: Boolean, alias: 'i', description: 'Ignore interrupt signals' },
16
+ { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, multiple: true, description: 'File(s) to write to' }
17
+ ],
18
+ run: async (argv, process) => {
19
+ if (!process)
20
+ return 1;
21
+ if (!process.stdin) {
22
+ await writelnStderr(process, terminal, 'tee: No input provided');
23
+ return 1;
24
+ }
25
+ const files = argv.path || [];
26
+ const append = argv.append || false;
27
+ const ignoreInterrupts = argv['ignore-interrupts'] || false;
28
+ const writer = process.stdout.getWriter();
29
+ try {
30
+ const filePaths = [];
31
+ for (const file of files) {
32
+ const expandedPath = shell.expandTilde(file);
33
+ const fullPath = path.resolve(shell.cwd, expandedPath);
34
+ if (!append) {
35
+ try {
36
+ await shell.context.fs.promises.writeFile(fullPath, '');
37
+ }
38
+ catch (error) {
39
+ await writelnStderr(process, terminal, `tee: ${file}: ${error instanceof Error ? error.message : 'Unknown error'}`);
40
+ return 1;
41
+ }
42
+ }
43
+ filePaths.push({ path: file, fullPath });
44
+ }
45
+ const reader = process.stdin.getReader();
46
+ let interrupted = false;
47
+ const interruptHandler = () => { interrupted = true; };
48
+ if (!ignoreInterrupts) {
49
+ kernel.terminal.events.on(TerminalEvents.INTERRUPT, interruptHandler);
50
+ }
51
+ try {
52
+ while (true) {
53
+ if (interrupted)
54
+ break;
55
+ const { done, value } = await reader.read();
56
+ if (done)
57
+ break;
58
+ await writer.write(value);
59
+ for (const fileInfo of filePaths) {
60
+ try {
61
+ await shell.context.fs.promises.appendFile(fileInfo.fullPath, value);
62
+ }
63
+ catch (error) {
64
+ await writelnStderr(process, terminal, `tee: ${fileInfo.path}: ${error instanceof Error ? error.message : 'Write error'}`);
65
+ }
66
+ }
67
+ }
68
+ }
69
+ finally {
70
+ reader.releaseLock();
71
+ if (!ignoreInterrupts) {
72
+ kernel.terminal.events.off(TerminalEvents.INTERRUPT, interruptHandler);
73
+ }
74
+ }
75
+ return 0;
76
+ }
77
+ catch (error) {
78
+ await writelnStderr(process, terminal, `tee: ${error instanceof Error ? error.message : 'Unknown error'}`);
79
+ return 1;
80
+ }
81
+ finally {
82
+ writer.releaseLock();
83
+ }
84
+ }
85
+ });
86
+ }
87
+ //# sourceMappingURL=tee.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tee.js","sourceRoot":"","sources":["../../src/commands/tee.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AAGvB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEpD,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,KAAY,EAAE,QAAkB;IAC5E,OAAO,IAAI,eAAe,CAAC;QACzB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,iEAAiE;QAC9E,MAAM;QACN,KAAK;QACL,QAAQ;QACR,OAAO,EAAE;YACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,EAAE;YAC3E,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,6CAA6C,EAAE;YACzG,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,0BAA0B,EAAE;YACjG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,qBAAqB,EAAE;SACvI;QACD,GAAG,EAAE,KAAK,EAAE,IAAwB,EAAE,OAAiB,EAAE,EAAE;YACzD,IAAI,CAAC,OAAO;gBAAE,OAAO,CAAC,CAAA;YAEtB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBACnB,MAAM,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,wBAAwB,CAAC,CAAA;gBAChE,OAAO,CAAC,CAAA;YACV,CAAC;YAED,MAAM,KAAK,GAAI,IAAI,CAAC,IAAiB,IAAI,EAAE,CAAA;YAC3C,MAAM,MAAM,GAAI,IAAI,CAAC,MAAkB,IAAI,KAAK,CAAA;YAChD,MAAM,gBAAgB,GAAI,IAAI,CAAC,mBAAmB,CAAa,IAAI,KAAK,CAAA;YAExE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAA;YAEzC,IAAI,CAAC;gBACH,MAAM,SAAS,GAA8C,EAAE,CAAA;gBAC/D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;oBAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;oBAEtD,IAAI,CAAC,MAAM,EAAE,CAAC;wBACZ,IAAI,CAAC;4BACH,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;wBACzD,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BACf,MAAM,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAA;4BACnH,OAAO,CAAC,CAAA;wBACV,CAAC;oBACH,CAAC;oBAED,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;gBAC1C,CAAC;gBAED,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,CAAA;gBACxC,IAAI,WAAW,GAAG,KAAK,CAAA;gBAEvB,MAAM,gBAAgB,GAAG,GAAG,EAAE,GAAG,WAAW,GAAG,IAAI,CAAA,CAAC,CAAC,CAAA;gBACrD,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBACtB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,cAAc,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;gBACvE,CAAC;gBAED,IAAI,CAAC;oBACH,OAAO,IAAI,EAAE,CAAC;wBACZ,IAAI,WAAW;4BAAE,MAAK;wBACtB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;wBAC3C,IAAI,IAAI;4BAAE,MAAK;wBAEf,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;wBAEzB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;4BACjC,IAAI,CAAC;gCACH,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;4BACtE,CAAC;4BAAC,OAAO,KAAK,EAAE,CAAC;gCACf,MAAM,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,QAAQ,CAAC,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAA;4BAC5H,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;wBAAS,CAAC;oBACT,MAAM,CAAC,WAAW,EAAE,CAAA;oBACpB,IAAI,CAAC,gBAAgB,EAAE,CAAC;wBACtB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA;oBACxE,CAAC;gBACH,CAAC;gBAED,OAAO,CAAC,CAAA;YACV,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAA;gBAC1G,OAAO,CAAC,CAAA;YACV,CAAC;oBAAS,CAAC;gBACT,MAAM,CAAC,WAAW,EAAE,CAAA;YACtB,CAAC;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export { createCommand as createCd } from './commands/cd.js';
8
8
  export { createCommand as createChmod } from './commands/chmod.js';
9
9
  export { createCommand as createCp } from './commands/cp.js';
10
10
  export { createCommand as createEcho } from './commands/echo.js';
11
+ export { createCommand as createLn } from './commands/ln.js';
11
12
  export { createCommand as createLs } from './commands/ls.js';
12
13
  export { createCommand as createMkdir } from './commands/mkdir.js';
13
14
  export { createCommand as createMv } from './commands/mv.js';
@@ -16,6 +17,10 @@ export { createCommand as createRm } from './commands/rm.js';
16
17
  export { createCommand as createRmdir } from './commands/rmdir.js';
17
18
  export { createCommand as createStat } from './commands/stat.js';
18
19
  export { createCommand as createTouch } from './commands/touch.js';
20
+ export { createCommand as createHex } from './commands/hex.js';
21
+ export { createCommand as createLess } from './commands/less.js';
22
+ export { createCommand as createSed } from './commands/sed.js';
23
+ export { createCommand as createTee } from './commands/tee.js';
19
24
  /**
20
25
  * Creates all coreutils commands.
21
26
  * This function replaces the TerminalCommands function from the kernel.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAG9D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAkB5F,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAElE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAA;CAAE,CAgBtH;AAGD,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAG9D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAuB5F,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,EAAE,aAAa,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE9D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAA;CAAE,CAqBtH;AAGD,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,CAAA"}
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { createCommand as createCd } from './commands/cd.js';
7
7
  import { createCommand as createChmod } from './commands/chmod.js';
8
8
  import { createCommand as createCp } from './commands/cp.js';
9
9
  import { createCommand as createEcho } from './commands/echo.js';
10
+ import { createCommand as createLn } from './commands/ln.js';
10
11
  import { createCommand as createLs } from './commands/ls.js';
11
12
  import { createCommand as createMkdir } from './commands/mkdir.js';
12
13
  import { createCommand as createMv } from './commands/mv.js';
@@ -15,12 +16,17 @@ import { createCommand as createRm } from './commands/rm.js';
15
16
  import { createCommand as createRmdir } from './commands/rmdir.js';
16
17
  import { createCommand as createStat } from './commands/stat.js';
17
18
  import { createCommand as createTouch } from './commands/touch.js';
19
+ import { createCommand as createHex } from './commands/hex.js';
20
+ import { createCommand as createLess } from './commands/less.js';
21
+ import { createCommand as createSed } from './commands/sed.js';
22
+ import { createCommand as createTee } from './commands/tee.js';
18
23
  // Export individual command factories
19
24
  export { createCommand as createCat } from './commands/cat.js';
20
25
  export { createCommand as createCd } from './commands/cd.js';
21
26
  export { createCommand as createChmod } from './commands/chmod.js';
22
27
  export { createCommand as createCp } from './commands/cp.js';
23
28
  export { createCommand as createEcho } from './commands/echo.js';
29
+ export { createCommand as createLn } from './commands/ln.js';
24
30
  export { createCommand as createLs } from './commands/ls.js';
25
31
  export { createCommand as createMkdir } from './commands/mkdir.js';
26
32
  export { createCommand as createMv } from './commands/mv.js';
@@ -29,6 +35,10 @@ export { createCommand as createRm } from './commands/rm.js';
29
35
  export { createCommand as createRmdir } from './commands/rmdir.js';
30
36
  export { createCommand as createStat } from './commands/stat.js';
31
37
  export { createCommand as createTouch } from './commands/touch.js';
38
+ export { createCommand as createHex } from './commands/hex.js';
39
+ export { createCommand as createLess } from './commands/less.js';
40
+ export { createCommand as createSed } from './commands/sed.js';
41
+ export { createCommand as createTee } from './commands/tee.js';
32
42
  /**
33
43
  * Creates all coreutils commands.
34
44
  * This function replaces the TerminalCommands function from the kernel.
@@ -40,6 +50,7 @@ export function createAllCommands(kernel, shell, terminal) {
40
50
  chmod: createChmod(kernel, shell, terminal),
41
51
  cp: createCp(kernel, shell, terminal),
42
52
  echo: createEcho(kernel, shell, terminal),
53
+ ln: createLn(kernel, shell, terminal),
43
54
  ls: createLs(kernel, shell, terminal),
44
55
  mkdir: createMkdir(kernel, shell, terminal),
45
56
  mv: createMv(kernel, shell, terminal),
@@ -47,7 +58,11 @@ export function createAllCommands(kernel, shell, terminal) {
47
58
  rm: createRm(kernel, shell, terminal),
48
59
  rmdir: createRmdir(kernel, shell, terminal),
49
60
  stat: createStat(kernel, shell, terminal),
50
- touch: createTouch(kernel, shell, terminal)
61
+ touch: createTouch(kernel, shell, terminal),
62
+ hex: createHex(kernel, shell, terminal),
63
+ less: createLess(kernel, shell, terminal),
64
+ sed: createSed(kernel, shell, terminal),
65
+ tee: createTee(kernel, shell, terminal)
51
66
  };
52
67
  }
53
68
  // For backward compatibility, export as TerminalCommands
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ecmaos/coreutils",
3
3
  "description": "ecmaOS: Core utilities for the ecmaOS framework",
4
- "version": "0.1.1",
4
+ "version": "0.1.3",
5
5
  "license": "MIT+Apache-2.0",
6
6
  "homepage": "https://ecmaos.sh",
7
7
  "repository": "https://github.com/ecmaos/ecmaos",
@@ -24,13 +24,15 @@
24
24
  "dependencies": {
25
25
  "@zenfs/core": "^2.4.2",
26
26
  "@zip.js/zip.js": "^2.7.52",
27
+ "ansi-escape-sequences": "^6.2.4",
27
28
  "chalk": "^5.3.0",
28
29
  "command-line-args": "^6.0.0",
29
30
  "command-line-usage": "^7.0.3",
30
31
  "human-format": "^1.2.1",
31
- "@ecmaos/types": "^0.4.1"
32
+ "@ecmaos/types": "^0.4.2"
32
33
  },
33
34
  "devDependencies": {
35
+ "@types/ansi-escape-sequences": "^4.0.4",
34
36
  "@types/command-line-args": "^5.2.3",
35
37
  "@types/command-line-usage": "^5.0.4"
36
38
  },
@@ -14,14 +14,7 @@ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal):
14
14
  { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the directory to change to' }
15
15
  ],
16
16
  run: async (argv) => {
17
- let destination = (argv.path as string) || shell.cwd
18
- if (destination && destination.startsWith('~')) {
19
- const home = shell.env.get('HOME')
20
- if (home) {
21
- destination = destination.replace(/^~(?=$|\/)/, home)
22
- }
23
- }
24
-
17
+ const destination = (argv.path as string) || shell.cwd
25
18
  const fullPath = destination ? path.resolve(shell.cwd, destination) : shell.cwd
26
19
  await shell.context.fs.promises.access(fullPath)
27
20
  shell.cwd = fullPath
@@ -0,0 +1,92 @@
1
+ import path from 'path'
2
+ import type { CommandLineOptions } from 'command-line-args'
3
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
4
+ import { TerminalCommand } from '../shared/terminal-command.js'
5
+ import { writelnStdout, writelnStderr } from '../shared/helpers.js'
6
+
7
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
8
+ return new TerminalCommand({
9
+ command: 'hex',
10
+ description: 'Display file contents in hexadecimal format',
11
+ kernel,
12
+ shell,
13
+ terminal,
14
+ options: [
15
+ { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
16
+ { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the file to display' }
17
+ ],
18
+ run: async (argv: CommandLineOptions, process?: Process) => {
19
+ const filePath = argv.path as string | undefined
20
+
21
+ if (!filePath) {
22
+ await writelnStderr(process, terminal, 'Usage: hex <file>')
23
+ return 1
24
+ }
25
+
26
+ const fullPath = path.resolve(shell.cwd, filePath)
27
+
28
+ try {
29
+ const exists = await shell.context.fs.promises.exists(fullPath)
30
+ if (!exists) {
31
+ await writelnStderr(process, terminal, `hex: ${filePath}: No such file or directory`)
32
+ return 1
33
+ }
34
+
35
+ const stats = await shell.context.fs.promises.stat(fullPath)
36
+ if (stats.isDirectory()) {
37
+ await writelnStderr(process, terminal, `hex: ${filePath}: Is a directory`)
38
+ return 1
39
+ }
40
+
41
+ const data = await shell.context.fs.promises.readFile(fullPath)
42
+ const bytesPerLine = 16
43
+
44
+ for (let offset = 0; offset < data.length; offset += bytesPerLine) {
45
+ const lineBytes = data.slice(offset, offset + bytesPerLine)
46
+ const offsetHex = offset.toString(16).padStart(8, '0')
47
+
48
+ const hexGroups: string[] = []
49
+ const asciiChars: string[] = []
50
+
51
+ for (let i = 0; i < bytesPerLine; i++) {
52
+ if (i < lineBytes.length) {
53
+ const byte = lineBytes[i]
54
+ if (byte === undefined) continue
55
+
56
+ const hex = byte.toString(16).padStart(2, '0')
57
+
58
+ if (i % 2 === 0) {
59
+ hexGroups.push(hex)
60
+ } else {
61
+ hexGroups[hexGroups.length - 1] += hex
62
+ }
63
+
64
+ if (byte >= 32 && byte <= 126) {
65
+ asciiChars.push(String.fromCharCode(byte))
66
+ } else {
67
+ asciiChars.push('.')
68
+ }
69
+ } else {
70
+ if (i % 2 === 0) {
71
+ hexGroups.push(' ')
72
+ } else {
73
+ hexGroups[hexGroups.length - 1] += ' '
74
+ }
75
+ asciiChars.push(' ')
76
+ }
77
+ }
78
+
79
+ const hexString = hexGroups.join(' ').padEnd(47, ' ')
80
+ const asciiString = asciiChars.join('')
81
+
82
+ await writelnStdout(process, terminal, `${offsetHex}: ${hexString} ${asciiString}`)
83
+ }
84
+
85
+ return 0
86
+ } catch (error) {
87
+ await writelnStderr(process, terminal, `hex: ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`)
88
+ return 1
89
+ }
90
+ }
91
+ })
92
+ }
@@ -0,0 +1,192 @@
1
+ import path from 'path'
2
+ import ansi from 'ansi-escape-sequences'
3
+ import type { CommandLineOptions } from 'command-line-args'
4
+ import type { IDisposable } from '@xterm/xterm'
5
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
6
+ import { TerminalCommand } from '../shared/terminal-command.js'
7
+ import { writelnStderr } from '../shared/helpers.js'
8
+
9
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
10
+ return new TerminalCommand({
11
+ command: 'less',
12
+ description: 'View file contents interactively',
13
+ kernel,
14
+ shell,
15
+ terminal,
16
+ options: [
17
+ { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
18
+ { name: 'path', type: String, typeLabel: '{underline path}', defaultOption: true, description: 'The path to the file to view' }
19
+ ],
20
+ run: async (argv: CommandLineOptions, process?: Process) => {
21
+ if (!process) return 1
22
+
23
+ let lines: string[] = []
24
+ let currentLine = 0
25
+ let keyListener: IDisposable | null = null
26
+ let linesRendered = 0
27
+
28
+ try {
29
+ if (argv.path) {
30
+ const filePath = argv.path as string
31
+ const expandedPath = shell.expandTilde(filePath)
32
+ const fullPath = path.resolve(shell.cwd, expandedPath)
33
+
34
+ const exists = await shell.context.fs.promises.exists(fullPath)
35
+ if (!exists) {
36
+ await writelnStderr(process, terminal, `less: ${filePath}: No such file or directory`)
37
+ return 1
38
+ }
39
+
40
+ const stats = await shell.context.fs.promises.stat(fullPath)
41
+ if (stats.isDirectory()) {
42
+ await writelnStderr(process, terminal, `less: ${filePath}: Is a directory`)
43
+ return 1
44
+ }
45
+
46
+ const content = await shell.context.fs.promises.readFile(fullPath, 'utf-8')
47
+ lines = content.split('\n')
48
+ } else {
49
+ if (!process.stdin) {
50
+ await writelnStderr(process, terminal, 'less: No input provided')
51
+ return 1
52
+ }
53
+
54
+ const reader = process.stdin.getReader()
55
+ const decoder = new TextDecoder()
56
+ const chunks: string[] = []
57
+
58
+ try {
59
+ while (true) {
60
+ const { done, value } = await reader.read()
61
+ if (done) break
62
+ chunks.push(decoder.decode(value, { stream: true }))
63
+ }
64
+ chunks.push(decoder.decode(new Uint8Array(), { stream: false }))
65
+ } finally {
66
+ reader.releaseLock()
67
+ }
68
+
69
+ const content = chunks.join('')
70
+ lines = content.split('\n')
71
+ }
72
+
73
+ if (lines.length === 0) {
74
+ return 0
75
+ }
76
+
77
+ terminal.unlisten()
78
+ terminal.write('\n')
79
+ terminal.write(ansi.cursor.hide)
80
+
81
+ const rows = terminal.rows
82
+ const displayRows = rows - 1
83
+
84
+ const render = () => {
85
+ const maxLine = Math.max(0, lines.length - displayRows)
86
+ if (currentLine > maxLine) {
87
+ currentLine = maxLine
88
+ }
89
+ if (currentLine < 0) {
90
+ currentLine = 0
91
+ }
92
+
93
+ if (linesRendered > 0) {
94
+ terminal.write(ansi.cursor.up(linesRendered))
95
+ }
96
+
97
+ const endLine = Math.min(currentLine + displayRows, lines.length)
98
+ linesRendered = 0
99
+
100
+ for (let i = currentLine; i < endLine; i++) {
101
+ terminal.write(ansi.erase.inLine(2))
102
+ const line = lines[i] || ''
103
+ terminal.write(line)
104
+ linesRendered++
105
+ if (i < endLine - 1) {
106
+ terminal.write('\n')
107
+ }
108
+ }
109
+
110
+ for (let i = endLine - currentLine; i < displayRows; i++) {
111
+ terminal.write('\n')
112
+ terminal.write(ansi.erase.inLine(2))
113
+ linesRendered++
114
+ }
115
+
116
+ const percentage = lines.length > 0 ? Math.round(((endLine / lines.length) * 100)) : 100
117
+ const statusLine = `-- ${currentLine + 1}-${endLine} / ${lines.length} (${percentage}%)`
118
+ terminal.write('\n')
119
+ terminal.write(ansi.erase.inLine(2))
120
+ terminal.write(statusLine)
121
+ linesRendered++
122
+ }
123
+
124
+ render()
125
+
126
+ await new Promise<void>((resolve) => {
127
+ keyListener = terminal.onKey(async ({ domEvent }) => {
128
+ const keyName = domEvent.key
129
+
130
+ switch (keyName) {
131
+ case 'q':
132
+ case 'Q':
133
+ case 'Escape':
134
+ if (keyListener) {
135
+ keyListener.dispose()
136
+ keyListener = null
137
+ }
138
+ terminal.write(ansi.cursor.show)
139
+ terminal.write('\n')
140
+ terminal.listen()
141
+ resolve()
142
+ return
143
+ case 'ArrowUp':
144
+ if (currentLine > 0) {
145
+ currentLine--
146
+ render()
147
+ }
148
+ break
149
+ case 'ArrowDown':
150
+ case 'Enter':
151
+ currentLine++
152
+ render()
153
+ break
154
+ case 'PageDown':
155
+ case ' ':
156
+ currentLine = Math.min(currentLine + displayRows, Math.max(0, lines.length - displayRows))
157
+ render()
158
+ break
159
+ case 'PageUp':
160
+ case 'b':
161
+ case 'B':
162
+ currentLine = Math.max(0, currentLine - displayRows)
163
+ render()
164
+ break
165
+ case 'Home':
166
+ case 'g':
167
+ currentLine = 0
168
+ render()
169
+ break
170
+ case 'End':
171
+ case 'G':
172
+ currentLine = Math.max(0, lines.length - displayRows)
173
+ render()
174
+ break
175
+ }
176
+ })
177
+ })
178
+
179
+ return 0
180
+ } catch (error) {
181
+ if (keyListener) {
182
+ (keyListener as IDisposable).dispose()
183
+ }
184
+ terminal.write(ansi.cursor.show)
185
+ terminal.write('\n')
186
+ terminal.listen()
187
+ await writelnStderr(process, terminal, `less: ${error instanceof Error ? error.message : 'Unknown error'}`)
188
+ return 1
189
+ }
190
+ }
191
+ })
192
+ }
@@ -0,0 +1,108 @@
1
+ import path from 'path'
2
+ import chalk from 'chalk'
3
+ import type { CommandLineOptions } from 'command-line-args'
4
+ import type { Kernel, Process, Shell, Terminal } from '@ecmaos/types'
5
+ import { TerminalCommand } from '../shared/terminal-command.js'
6
+ import { writelnStdout, writelnStderr } from '../shared/helpers.js'
7
+
8
+ export function createCommand(kernel: Kernel, shell: Shell, terminal: Terminal): TerminalCommand {
9
+ return new TerminalCommand({
10
+ command: 'ln',
11
+ description: 'Create links between files',
12
+ kernel,
13
+ shell,
14
+ terminal,
15
+ options: [
16
+ { name: 'help', type: Boolean, description: kernel.i18n.t('Display help') },
17
+ { name: 'symbolic', type: Boolean, alias: 's', description: 'Create symbolic links instead of hard links' },
18
+ { name: 'force', type: Boolean, alias: 'f', description: 'Remove existing destination files' },
19
+ { name: 'verbose', type: Boolean, alias: 'v', description: 'Print name of each linked file' },
20
+ { name: 'args', type: String, multiple: true, defaultOption: true, description: 'The target and optional link name' }
21
+ ],
22
+ run: async (argv: CommandLineOptions, process?: Process) => {
23
+ const args = (argv.args as string[]) || []
24
+ const symbolic = argv.symbolic as boolean || false
25
+ const force = argv.force as boolean || false
26
+ const verbose = argv.verbose as boolean || false
27
+
28
+ if (args.length === 0) {
29
+ await writelnStderr(process, terminal, chalk.red('ln: missing file operand'))
30
+ await writelnStderr(process, terminal, 'Try \'ln --help\' for more information.')
31
+ return 1
32
+ }
33
+
34
+ const target = args[0]
35
+ if (!target) {
36
+ await writelnStderr(process, terminal, chalk.red('ln: missing file operand'))
37
+ return 1
38
+ }
39
+
40
+ const targetPath = path.resolve(shell.cwd, target)
41
+
42
+ let targetStats
43
+ try {
44
+ targetStats = await shell.context.fs.promises.stat(targetPath)
45
+ if (!targetStats.isFile() && !targetStats.isDirectory()) {
46
+ await writelnStderr(process, terminal, chalk.red(`ln: ${target}: invalid target`))
47
+ return 1
48
+ }
49
+ if (!symbolic && targetStats.isDirectory()) {
50
+ await writelnStderr(process, terminal, chalk.red(`ln: ${target}: hard link not allowed for directory`))
51
+ return 1
52
+ }
53
+ } catch (error) {
54
+ await writelnStderr(process, terminal, chalk.red(`ln: ${target}: No such file or directory`))
55
+ return 1
56
+ }
57
+
58
+ let linkName: string
59
+ if (args.length === 1) {
60
+ linkName = path.join(shell.cwd, path.basename(target))
61
+ } else {
62
+ const linkNameInput = args[1]
63
+ if (!linkNameInput) {
64
+ await writelnStderr(process, terminal, chalk.red('ln: missing link name'))
65
+ return 1
66
+ }
67
+ const linkNamePath = path.resolve(shell.cwd, linkNameInput)
68
+ const linkNameStats = await shell.context.fs.promises.stat(linkNamePath).catch(() => null)
69
+ if (linkNameStats?.isDirectory()) {
70
+ linkName = path.join(linkNamePath, path.basename(target))
71
+ } else {
72
+ linkName = linkNamePath
73
+ }
74
+ }
75
+
76
+ try {
77
+ const linkExists = await shell.context.fs.promises.stat(linkName).catch(() => null)
78
+ if (linkExists) {
79
+ if (force) {
80
+ if (linkExists.isDirectory()) {
81
+ await shell.context.fs.promises.rmdir(linkName)
82
+ } else {
83
+ await shell.context.fs.promises.unlink(linkName)
84
+ }
85
+ } else {
86
+ await writelnStderr(process, terminal, chalk.red(`ln: ${linkName}: File exists`))
87
+ return 1
88
+ }
89
+ }
90
+
91
+ if (symbolic) {
92
+ await shell.context.fs.promises.symlink(targetPath, linkName)
93
+ } else {
94
+ await shell.context.fs.promises.link(targetPath, linkName)
95
+ }
96
+
97
+ if (verbose) {
98
+ await writelnStdout(process, terminal, linkName)
99
+ }
100
+
101
+ return 0
102
+ } catch (error) {
103
+ await writelnStderr(process, terminal, chalk.red(`ln: ${(error as Error).message}`))
104
+ return 1
105
+ }
106
+ }
107
+ })
108
+ }