terminal-quest 1.0.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 (131) hide show
  1. package/LICENSE +21 -0
  2. package/bin/terminal-quest.js +2 -0
  3. package/dist/App.d.ts +2 -0
  4. package/dist/App.js +33 -0
  5. package/dist/components/HintBar.d.ts +9 -0
  6. package/dist/components/HintBar.js +11 -0
  7. package/dist/components/MenuItem.d.ts +9 -0
  8. package/dist/components/MenuItem.js +9 -0
  9. package/dist/components/ObjectivePanel.d.ts +8 -0
  10. package/dist/components/ObjectivePanel.js +10 -0
  11. package/dist/components/ProgressBar.d.ts +8 -0
  12. package/dist/components/ProgressBar.js +10 -0
  13. package/dist/components/TerminalOutput.d.ts +11 -0
  14. package/dist/components/TerminalOutput.js +25 -0
  15. package/dist/components/TerminalPrompt.d.ts +10 -0
  16. package/dist/components/TerminalPrompt.js +46 -0
  17. package/dist/data/commands-meta.d.ts +17 -0
  18. package/dist/data/commands-meta.js +256 -0
  19. package/dist/data/stories/00-beginner-pc.d.ts +3 -0
  20. package/dist/data/stories/00-beginner-pc.js +841 -0
  21. package/dist/data/stories/01-first-server.d.ts +3 -0
  22. package/dist/data/stories/01-first-server.js +364 -0
  23. package/dist/data/stories/02-messy-project.d.ts +3 -0
  24. package/dist/data/stories/02-messy-project.js +433 -0
  25. package/dist/data/stories/03-log-detective.d.ts +3 -0
  26. package/dist/data/stories/03-log-detective.js +291 -0
  27. package/dist/data/stories/04-deploy-day.d.ts +3 -0
  28. package/dist/data/stories/04-deploy-day.js +337 -0
  29. package/dist/data/stories/05-git-incident.d.ts +3 -0
  30. package/dist/data/stories/05-git-incident.js +534 -0
  31. package/dist/data/stories/06-pipe-master.d.ts +3 -0
  32. package/dist/data/stories/06-pipe-master.js +377 -0
  33. package/dist/data/stories/07-dangerous-commands.d.ts +3 -0
  34. package/dist/data/stories/07-dangerous-commands.js +411 -0
  35. package/dist/data/stories/index.d.ts +4 -0
  36. package/dist/data/stories/index.js +14 -0
  37. package/dist/data/stories/k1-treasure-hunt.d.ts +3 -0
  38. package/dist/data/stories/k1-treasure-hunt.js +815 -0
  39. package/dist/data/types.d.ts +97 -0
  40. package/dist/data/types.js +2 -0
  41. package/dist/engine/Achievements.d.ts +5 -0
  42. package/dist/engine/Achievements.js +93 -0
  43. package/dist/engine/CommandHandler.d.ts +17 -0
  44. package/dist/engine/CommandHandler.js +177 -0
  45. package/dist/engine/HintEngine.d.ts +10 -0
  46. package/dist/engine/HintEngine.js +26 -0
  47. package/dist/engine/MissionEngine.d.ts +17 -0
  48. package/dist/engine/MissionEngine.js +84 -0
  49. package/dist/engine/TabCompletion.d.ts +14 -0
  50. package/dist/engine/TabCompletion.js +93 -0
  51. package/dist/engine/VirtualFS.d.ts +33 -0
  52. package/dist/engine/VirtualFS.js +276 -0
  53. package/dist/engine/commands/cat.d.ts +4 -0
  54. package/dist/engine/commands/cat.js +18 -0
  55. package/dist/engine/commands/cd.d.ts +4 -0
  56. package/dist/engine/commands/cd.js +12 -0
  57. package/dist/engine/commands/chmod.d.ts +4 -0
  58. package/dist/engine/commands/chmod.js +98 -0
  59. package/dist/engine/commands/clear.d.ts +4 -0
  60. package/dist/engine/commands/clear.js +4 -0
  61. package/dist/engine/commands/cp.d.ts +4 -0
  62. package/dist/engine/commands/cp.js +26 -0
  63. package/dist/engine/commands/cut.d.ts +4 -0
  64. package/dist/engine/commands/cut.js +76 -0
  65. package/dist/engine/commands/echo.d.ts +4 -0
  66. package/dist/engine/commands/echo.js +4 -0
  67. package/dist/engine/commands/find.d.ts +4 -0
  68. package/dist/engine/commands/find.js +60 -0
  69. package/dist/engine/commands/git.d.ts +4 -0
  70. package/dist/engine/commands/git.js +510 -0
  71. package/dist/engine/commands/grep.d.ts +4 -0
  72. package/dist/engine/commands/grep.js +127 -0
  73. package/dist/engine/commands/head.d.ts +4 -0
  74. package/dist/engine/commands/head.js +59 -0
  75. package/dist/engine/commands/help.d.ts +4 -0
  76. package/dist/engine/commands/help.js +32 -0
  77. package/dist/engine/commands/hint.d.ts +4 -0
  78. package/dist/engine/commands/hint.js +4 -0
  79. package/dist/engine/commands/index.d.ts +8 -0
  80. package/dist/engine/commands/index.js +51 -0
  81. package/dist/engine/commands/ls.d.ts +4 -0
  82. package/dist/engine/commands/ls.js +50 -0
  83. package/dist/engine/commands/man.d.ts +4 -0
  84. package/dist/engine/commands/man.js +51 -0
  85. package/dist/engine/commands/mkdir.d.ts +4 -0
  86. package/dist/engine/commands/mkdir.js +31 -0
  87. package/dist/engine/commands/mv.d.ts +4 -0
  88. package/dist/engine/commands/mv.js +15 -0
  89. package/dist/engine/commands/pwd.d.ts +4 -0
  90. package/dist/engine/commands/pwd.js +4 -0
  91. package/dist/engine/commands/rm.d.ts +4 -0
  92. package/dist/engine/commands/rm.js +49 -0
  93. package/dist/engine/commands/sort.d.ts +4 -0
  94. package/dist/engine/commands/sort.js +100 -0
  95. package/dist/engine/commands/tail.d.ts +4 -0
  96. package/dist/engine/commands/tail.js +59 -0
  97. package/dist/engine/commands/touch.d.ts +4 -0
  98. package/dist/engine/commands/touch.js +18 -0
  99. package/dist/engine/commands/uniq.d.ts +4 -0
  100. package/dist/engine/commands/uniq.js +61 -0
  101. package/dist/engine/commands/wc.d.ts +4 -0
  102. package/dist/engine/commands/wc.js +67 -0
  103. package/dist/index.d.ts +3 -0
  104. package/dist/index.js +6 -0
  105. package/dist/screens/MissionBriefScreen.d.ts +9 -0
  106. package/dist/screens/MissionBriefScreen.js +27 -0
  107. package/dist/screens/MissionCompleteScreen.d.ts +9 -0
  108. package/dist/screens/MissionCompleteScreen.js +30 -0
  109. package/dist/screens/ProgressScreen.d.ts +8 -0
  110. package/dist/screens/ProgressScreen.js +24 -0
  111. package/dist/screens/SettingsScreen.d.ts +8 -0
  112. package/dist/screens/SettingsScreen.js +45 -0
  113. package/dist/screens/StorySelectScreen.d.ts +8 -0
  114. package/dist/screens/StorySelectScreen.js +81 -0
  115. package/dist/screens/TerminalScreen.d.ts +12 -0
  116. package/dist/screens/TerminalScreen.js +150 -0
  117. package/dist/screens/TitleScreen.d.ts +7 -0
  118. package/dist/screens/TitleScreen.js +27 -0
  119. package/dist/state/GameState.d.ts +8 -0
  120. package/dist/state/GameState.js +12 -0
  121. package/dist/state/ProgressStore.d.ts +9 -0
  122. package/dist/state/ProgressStore.js +45 -0
  123. package/dist/state/useGameState.d.ts +11 -0
  124. package/dist/state/useGameState.js +92 -0
  125. package/dist/utils/ascii-art.d.ts +4 -0
  126. package/dist/utils/ascii-art.js +22 -0
  127. package/dist/utils/colors.d.ts +17 -0
  128. package/dist/utils/colors.js +17 -0
  129. package/dist/utils/text.d.ts +4 -0
  130. package/dist/utils/text.js +28 -0
  131. package/package.json +58 -0
@@ -0,0 +1,59 @@
1
+ export function head(fs, args) {
2
+ // Extract stdin
3
+ let stdin;
4
+ const stdinIdx = args.findIndex(a => a.startsWith('__stdin__:'));
5
+ if (stdinIdx !== -1) {
6
+ stdin = args[stdinIdx].slice('__stdin__:'.length);
7
+ args = [...args.slice(0, stdinIdx), ...args.slice(stdinIdx + 1)];
8
+ }
9
+ let lineCount = 10;
10
+ const files = [];
11
+ for (let i = 0; i < args.length; i++) {
12
+ const arg = args[i];
13
+ if (arg === '-n') {
14
+ i++;
15
+ if (i >= args.length) {
16
+ return { output: '', error: 'head: option requires an argument -- n' };
17
+ }
18
+ const n = parseInt(args[i], 10);
19
+ if (isNaN(n)) {
20
+ return { output: '', error: `head: invalid number of lines: '${args[i]}'` };
21
+ }
22
+ lineCount = n;
23
+ }
24
+ else if (arg.startsWith('-n')) {
25
+ const n = parseInt(arg.slice(2), 10);
26
+ if (isNaN(n)) {
27
+ return { output: '', error: `head: invalid number of lines: '${arg.slice(2)}'` };
28
+ }
29
+ lineCount = n;
30
+ }
31
+ else if (arg.startsWith('-')) {
32
+ return { output: '', error: `head: invalid option -- '${arg}'` };
33
+ }
34
+ else {
35
+ files.push(arg);
36
+ }
37
+ }
38
+ let content;
39
+ if (files.length > 0) {
40
+ try {
41
+ content = fs.readFile(files[0]);
42
+ }
43
+ catch (e) {
44
+ return { output: '', error: `head: ${e.message}` };
45
+ }
46
+ }
47
+ else if (stdin !== undefined) {
48
+ content = stdin;
49
+ }
50
+ else {
51
+ return { output: '', error: 'head: missing file operand' };
52
+ }
53
+ if (content === '')
54
+ return { output: '' };
55
+ const lines = content.split('\n');
56
+ const selected = lines.slice(0, lineCount);
57
+ return { output: selected.join('\n') };
58
+ }
59
+ //# sourceMappingURL=head.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function help(_fs: VirtualFS, _args: string[]): CommandResult;
4
+ //# sourceMappingURL=help.d.ts.map
@@ -0,0 +1,32 @@
1
+ const HELP_TEXT = `使用可能なコマンド:
2
+ pwd 現在のディレクトリを表示
3
+ ls [path] ファイル一覧 (-l 詳細, -a 全表示)
4
+ cd [path] ディレクトリ移動
5
+ cat file... ファイル内容を表示
6
+ grep [opts] pattern file パターン検索 (-i, -n, -r)
7
+ cp [-r] src dest コピー
8
+ mv src dest 移動・名前変更
9
+ mkdir [-p] path ディレクトリ作成
10
+ rm [-r] [-f] path 削除
11
+ touch file 空ファイル作成
12
+ find [path] [-name p] [-type t] ファイル検索
13
+ head [-n N] file 先頭N行を表示
14
+ tail [-n N] file 末尾N行を表示
15
+ wc [-l] [-w] [-c] file 行数/単語数/バイト数
16
+ sort [-r] [-n] file ソート
17
+ uniq [-c] file 重複除去
18
+ cut -d delim -f N file フィールド切り出し
19
+ chmod mode file 権限変更
20
+ echo text [> file] テキスト出力/ファイル書き込み
21
+ git <subcmd> Gitコマンド (status/log/diff/branch/checkout/merge/stash)
22
+ clear 画面クリア
23
+ help このヘルプを表示
24
+ hint ヒントを表示
25
+ objectives / obj 現在の目標を表示
26
+
27
+ ヒント: コマンドは | (パイプ) で繋げられます
28
+ 例: cat file.txt | grep ERROR | wc -l`;
29
+ export function help(_fs, _args) {
30
+ return { output: HELP_TEXT };
31
+ }
32
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function hint(_fs: VirtualFS, _args: string[]): CommandResult;
4
+ //# sourceMappingURL=hint.d.ts.map
@@ -0,0 +1,4 @@
1
+ export function hint(_fs, _args) {
2
+ return { output: 'HINT_REQUEST' };
3
+ }
4
+ //# sourceMappingURL=hint.js.map
@@ -0,0 +1,8 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ export interface CommandResult {
3
+ output: string;
4
+ error?: string;
5
+ }
6
+ export type CommandFn = (fs: VirtualFS, args: string[]) => CommandResult;
7
+ export declare const commandRegistry: Record<string, CommandFn>;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,51 @@
1
+ import { pwd } from './pwd.js';
2
+ import { ls } from './ls.js';
3
+ import { cd } from './cd.js';
4
+ import { cat } from './cat.js';
5
+ import { grep } from './grep.js';
6
+ import { cp } from './cp.js';
7
+ import { echo } from './echo.js';
8
+ import { help } from './help.js';
9
+ import { hint } from './hint.js';
10
+ import { clear } from './clear.js';
11
+ import { git } from './git.js';
12
+ import { mkdir } from './mkdir.js';
13
+ import { mv } from './mv.js';
14
+ import { rm } from './rm.js';
15
+ import { find } from './find.js';
16
+ import { touch } from './touch.js';
17
+ import { head } from './head.js';
18
+ import { tail } from './tail.js';
19
+ import { wc } from './wc.js';
20
+ import { sort } from './sort.js';
21
+ import { uniq } from './uniq.js';
22
+ import { cut } from './cut.js';
23
+ import { chmod } from './chmod.js';
24
+ import { man } from './man.js';
25
+ export const commandRegistry = {
26
+ pwd,
27
+ ls,
28
+ cd,
29
+ cat,
30
+ grep,
31
+ cp,
32
+ echo,
33
+ help,
34
+ hint,
35
+ clear,
36
+ git,
37
+ mkdir,
38
+ mv,
39
+ rm,
40
+ find,
41
+ touch,
42
+ head,
43
+ tail,
44
+ wc,
45
+ sort,
46
+ uniq,
47
+ cut,
48
+ chmod,
49
+ man,
50
+ };
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function ls(fs: VirtualFS, args: string[]): CommandResult;
4
+ //# sourceMappingURL=ls.d.ts.map
@@ -0,0 +1,50 @@
1
+ export function ls(fs, args) {
2
+ let showAll = false;
3
+ let longFormat = false;
4
+ const paths = [];
5
+ for (const arg of args) {
6
+ if (arg.startsWith('-')) {
7
+ for (const ch of arg.slice(1)) {
8
+ if (ch === 'a')
9
+ showAll = true;
10
+ else if (ch === 'l')
11
+ longFormat = true;
12
+ else
13
+ return { output: '', error: `ls: invalid option -- '${ch}'` };
14
+ }
15
+ }
16
+ else {
17
+ paths.push(arg);
18
+ }
19
+ }
20
+ const targetPath = paths.length > 0 ? paths[0] : '.';
21
+ if (!fs.exists(targetPath)) {
22
+ return { output: '', error: `ls: cannot access '${targetPath}': No such file or directory` };
23
+ }
24
+ if (fs.isFile(targetPath)) {
25
+ const resolved = fs.resolvePath(targetPath);
26
+ const name = resolved.split('/').filter(Boolean).pop() ?? resolved;
27
+ if (longFormat) {
28
+ const perms = fs.getPermissions(targetPath);
29
+ return { output: `${perms} ${name}` };
30
+ }
31
+ return { output: name };
32
+ }
33
+ if (longFormat) {
34
+ const entries = fs.listDirDetailed(targetPath);
35
+ const lines = entries
36
+ .filter(e => showAll || !e.name.startsWith('.'))
37
+ .map(e => {
38
+ const resolvedDir = fs.resolvePath(targetPath);
39
+ const fullPath = resolvedDir === '/' ? `/${e.name}` : `${resolvedDir}/${e.name}`;
40
+ const perms = fs.getPermissions(fullPath);
41
+ const suffix = e.type === 'directory' ? '/' : '';
42
+ return `${perms} ${e.name}${suffix}`;
43
+ });
44
+ return { output: lines.join('\n') };
45
+ }
46
+ const entries = fs.listDir(targetPath);
47
+ const filtered = entries.filter(e => showAll || !e.startsWith('.'));
48
+ return { output: filtered.join(' ') };
49
+ }
50
+ //# sourceMappingURL=ls.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function man(fs: VirtualFS, args: string[]): CommandResult;
4
+ //# sourceMappingURL=man.d.ts.map
@@ -0,0 +1,51 @@
1
+ import { commandsMeta } from '../../data/commands-meta.js';
2
+ const categories = {
3
+ 'Navigation': ['pwd', 'cd', 'ls'],
4
+ 'File Operations': ['cat', 'cp', 'mv', 'rm', 'touch', 'mkdir', 'echo'],
5
+ 'Text Processing': ['grep', 'head', 'tail', 'wc', 'sort', 'uniq', 'cut'],
6
+ 'Search': ['find'],
7
+ 'Permissions': ['chmod'],
8
+ 'Version Control': ['git'],
9
+ 'Help': ['help', 'hint', 'man'],
10
+ };
11
+ export function man(fs, args) {
12
+ if (args.length === 0) {
13
+ const lines = ['Terminal Quest コマンドリファレンス', ''];
14
+ for (const [category, cmds] of Object.entries(categories)) {
15
+ lines.push(` ${category}:`);
16
+ for (const cmd of cmds) {
17
+ const meta = commandsMeta.find(m => m.name === cmd);
18
+ lines.push(` ${cmd.padEnd(10)} ${meta?.description ?? ''}`);
19
+ }
20
+ lines.push('');
21
+ }
22
+ lines.push('詳細: man <コマンド名>');
23
+ return { output: lines.join('\n') };
24
+ }
25
+ const cmdName = args[0];
26
+ const meta = commandsMeta.find(m => m.name === cmdName);
27
+ if (!meta) {
28
+ return { output: '', error: `man: ${cmdName} のマニュアルはありません` };
29
+ }
30
+ const lines = [
31
+ `NAME`,
32
+ ` ${meta.name} - ${meta.description}`,
33
+ '',
34
+ `USAGE`,
35
+ ` ${meta.usage}`,
36
+ '',
37
+ ];
38
+ if (meta.options && meta.options.length > 0) {
39
+ lines.push('OPTIONS');
40
+ for (const opt of meta.options) {
41
+ lines.push(` ${opt.flag.padEnd(15)} ${opt.description}`);
42
+ }
43
+ lines.push('');
44
+ }
45
+ lines.push('EXAMPLES');
46
+ for (const ex of meta.examples) {
47
+ lines.push(` $ ${ex.cmd.padEnd(28)} ${ex.desc}`);
48
+ }
49
+ return { output: lines.join('\n') };
50
+ }
51
+ //# sourceMappingURL=man.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function mkdir(fs: VirtualFS, args: string[]): CommandResult;
4
+ //# sourceMappingURL=mkdir.d.ts.map
@@ -0,0 +1,31 @@
1
+ export function mkdir(fs, args) {
2
+ let recursive = false;
3
+ const paths = [];
4
+ for (const arg of args) {
5
+ if (arg === '-p') {
6
+ recursive = true;
7
+ }
8
+ else if (arg.startsWith('-')) {
9
+ return { output: '', error: `mkdir: invalid option -- '${arg}'` };
10
+ }
11
+ else {
12
+ paths.push(arg);
13
+ }
14
+ }
15
+ if (paths.length === 0) {
16
+ return { output: '', error: 'mkdir: missing operand' };
17
+ }
18
+ for (const path of paths) {
19
+ if (!recursive && fs.exists(path)) {
20
+ return { output: '', error: `mkdir: cannot create directory '${path}': File exists` };
21
+ }
22
+ try {
23
+ fs.mkdir(path, recursive);
24
+ }
25
+ catch (e) {
26
+ return { output: '', error: `mkdir: ${e.message}` };
27
+ }
28
+ }
29
+ return { output: '' };
30
+ }
31
+ //# sourceMappingURL=mkdir.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function mv(fs: VirtualFS, args: string[]): CommandResult;
4
+ //# sourceMappingURL=mv.d.ts.map
@@ -0,0 +1,15 @@
1
+ export function mv(fs, args) {
2
+ if (args.length < 2) {
3
+ return { output: '', error: 'mv: missing operand' };
4
+ }
5
+ const source = args[0];
6
+ const dest = args[1];
7
+ try {
8
+ fs.move(source, dest);
9
+ }
10
+ catch (e) {
11
+ return { output: '', error: `mv: ${e.message}` };
12
+ }
13
+ return { output: '' };
14
+ }
15
+ //# sourceMappingURL=mv.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function pwd(fs: VirtualFS, _args: string[]): CommandResult;
4
+ //# sourceMappingURL=pwd.d.ts.map
@@ -0,0 +1,4 @@
1
+ export function pwd(fs, _args) {
2
+ return { output: fs.getCwd() };
3
+ }
4
+ //# sourceMappingURL=pwd.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function rm(fs: VirtualFS, args: string[]): CommandResult;
4
+ //# sourceMappingURL=rm.d.ts.map
@@ -0,0 +1,49 @@
1
+ export function rm(fs, args) {
2
+ let recursive = false;
3
+ let force = false;
4
+ const paths = [];
5
+ for (const arg of args) {
6
+ if (arg === '--recursive') {
7
+ recursive = true;
8
+ }
9
+ else if (arg === '--force') {
10
+ force = true;
11
+ }
12
+ else if (arg.startsWith('-') && arg.length > 1 && !arg.startsWith('--')) {
13
+ for (const ch of arg.slice(1)) {
14
+ if (ch === 'r' || ch === 'R')
15
+ recursive = true;
16
+ else if (ch === 'f')
17
+ force = true;
18
+ else
19
+ return { output: '', error: `rm: invalid option -- '${ch}'` };
20
+ }
21
+ }
22
+ else {
23
+ paths.push(arg);
24
+ }
25
+ }
26
+ if (paths.length === 0) {
27
+ if (force)
28
+ return { output: '' };
29
+ return { output: '', error: 'rm: missing operand' };
30
+ }
31
+ for (const path of paths) {
32
+ try {
33
+ if (!fs.exists(path)) {
34
+ if (!force) {
35
+ return { output: '', error: `rm: cannot remove '${path}': No such file or directory` };
36
+ }
37
+ continue;
38
+ }
39
+ fs.remove(path, recursive);
40
+ }
41
+ catch (e) {
42
+ if (!force) {
43
+ return { output: '', error: `rm: ${e.message}` };
44
+ }
45
+ }
46
+ }
47
+ return { output: '' };
48
+ }
49
+ //# sourceMappingURL=rm.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function sort(fs: VirtualFS, args: string[]): CommandResult;
4
+ //# sourceMappingURL=sort.d.ts.map
@@ -0,0 +1,100 @@
1
+ export function sort(fs, args) {
2
+ // Extract stdin
3
+ let stdin;
4
+ const stdinIdx = args.findIndex(a => a.startsWith('__stdin__:'));
5
+ if (stdinIdx !== -1) {
6
+ stdin = args[stdinIdx].slice('__stdin__:'.length);
7
+ args = [...args.slice(0, stdinIdx), ...args.slice(stdinIdx + 1)];
8
+ }
9
+ let reverse = false;
10
+ let numeric = false;
11
+ let delimiter;
12
+ let keyField;
13
+ const files = [];
14
+ for (let i = 0; i < args.length; i++) {
15
+ const arg = args[i];
16
+ if (arg === '-t') {
17
+ i++;
18
+ if (i >= args.length) {
19
+ return { output: '', error: 'sort: option requires an argument -- t' };
20
+ }
21
+ delimiter = args[i];
22
+ }
23
+ else if (arg.startsWith('-t') && arg.length > 2) {
24
+ delimiter = arg.slice(2);
25
+ }
26
+ else if (arg === '-k') {
27
+ i++;
28
+ if (i >= args.length) {
29
+ return { output: '', error: 'sort: option requires an argument -- k' };
30
+ }
31
+ keyField = parseInt(args[i], 10);
32
+ if (isNaN(keyField)) {
33
+ return { output: '', error: `sort: invalid number at field start: '${args[i]}'` };
34
+ }
35
+ }
36
+ else if (arg.startsWith('-k') && arg.length > 2) {
37
+ keyField = parseInt(arg.slice(2), 10);
38
+ if (isNaN(keyField)) {
39
+ return { output: '', error: `sort: invalid number at field start: '${arg.slice(2)}'` };
40
+ }
41
+ }
42
+ else if (arg.startsWith('-') && arg.length > 1 && !arg.startsWith('--')) {
43
+ for (const ch of arg.slice(1)) {
44
+ if (ch === 'r')
45
+ reverse = true;
46
+ else if (ch === 'n')
47
+ numeric = true;
48
+ else
49
+ return { output: '', error: `sort: invalid option -- '${ch}'` };
50
+ }
51
+ }
52
+ else {
53
+ files.push(arg);
54
+ }
55
+ }
56
+ let content;
57
+ if (files.length > 0) {
58
+ try {
59
+ content = fs.readFile(files[0]);
60
+ }
61
+ catch (e) {
62
+ return { output: '', error: `sort: ${e.message}` };
63
+ }
64
+ }
65
+ else if (stdin !== undefined) {
66
+ content = stdin;
67
+ }
68
+ else {
69
+ return { output: '', error: 'sort: missing file operand' };
70
+ }
71
+ const lines = content.split('\n');
72
+ const getKey = (line) => {
73
+ if (delimiter !== undefined && keyField !== undefined) {
74
+ const fields = line.split(delimiter);
75
+ return fields[keyField - 1] ?? '';
76
+ }
77
+ return line;
78
+ };
79
+ if (numeric) {
80
+ lines.sort((a, b) => {
81
+ const ka = getKey(a);
82
+ const kb = getKey(b);
83
+ const na = parseFloat(ka) || 0;
84
+ const nb = parseFloat(kb) || 0;
85
+ return na - nb;
86
+ });
87
+ }
88
+ else {
89
+ lines.sort((a, b) => {
90
+ const ka = getKey(a);
91
+ const kb = getKey(b);
92
+ return ka.localeCompare(kb);
93
+ });
94
+ }
95
+ if (reverse) {
96
+ lines.reverse();
97
+ }
98
+ return { output: lines.join('\n') };
99
+ }
100
+ //# sourceMappingURL=sort.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function tail(fs: VirtualFS, args: string[]): CommandResult;
4
+ //# sourceMappingURL=tail.d.ts.map
@@ -0,0 +1,59 @@
1
+ export function tail(fs, args) {
2
+ // Extract stdin
3
+ let stdin;
4
+ const stdinIdx = args.findIndex(a => a.startsWith('__stdin__:'));
5
+ if (stdinIdx !== -1) {
6
+ stdin = args[stdinIdx].slice('__stdin__:'.length);
7
+ args = [...args.slice(0, stdinIdx), ...args.slice(stdinIdx + 1)];
8
+ }
9
+ let lineCount = 10;
10
+ const files = [];
11
+ for (let i = 0; i < args.length; i++) {
12
+ const arg = args[i];
13
+ if (arg === '-n') {
14
+ i++;
15
+ if (i >= args.length) {
16
+ return { output: '', error: 'tail: option requires an argument -- n' };
17
+ }
18
+ const n = parseInt(args[i], 10);
19
+ if (isNaN(n)) {
20
+ return { output: '', error: `tail: invalid number of lines: '${args[i]}'` };
21
+ }
22
+ lineCount = n;
23
+ }
24
+ else if (arg.startsWith('-n')) {
25
+ const n = parseInt(arg.slice(2), 10);
26
+ if (isNaN(n)) {
27
+ return { output: '', error: `tail: invalid number of lines: '${arg.slice(2)}'` };
28
+ }
29
+ lineCount = n;
30
+ }
31
+ else if (arg.startsWith('-')) {
32
+ return { output: '', error: `tail: invalid option -- '${arg}'` };
33
+ }
34
+ else {
35
+ files.push(arg);
36
+ }
37
+ }
38
+ let content;
39
+ if (files.length > 0) {
40
+ try {
41
+ content = fs.readFile(files[0]);
42
+ }
43
+ catch (e) {
44
+ return { output: '', error: `tail: ${e.message}` };
45
+ }
46
+ }
47
+ else if (stdin !== undefined) {
48
+ content = stdin;
49
+ }
50
+ else {
51
+ return { output: '', error: 'tail: missing file operand' };
52
+ }
53
+ if (content === '')
54
+ return { output: '' };
55
+ const lines = content.split('\n');
56
+ const selected = lines.slice(-lineCount);
57
+ return { output: selected.join('\n') };
58
+ }
59
+ //# sourceMappingURL=tail.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function touch(fs: VirtualFS, args: string[]): CommandResult;
4
+ //# sourceMappingURL=touch.d.ts.map
@@ -0,0 +1,18 @@
1
+ export function touch(fs, args) {
2
+ if (args.length === 0) {
3
+ return { output: '', error: 'touch: missing file operand' };
4
+ }
5
+ const filename = args[0];
6
+ if (fs.exists(filename)) {
7
+ // File or directory already exists, do nothing (success)
8
+ return { output: '' };
9
+ }
10
+ try {
11
+ fs.writeFile(filename, '');
12
+ }
13
+ catch (e) {
14
+ return { output: '', error: `touch: ${e.message}` };
15
+ }
16
+ return { output: '' };
17
+ }
18
+ //# sourceMappingURL=touch.js.map
@@ -0,0 +1,4 @@
1
+ import { VirtualFS } from '../VirtualFS.js';
2
+ import type { CommandResult } from './index.js';
3
+ export declare function uniq(fs: VirtualFS, args: string[]): CommandResult;
4
+ //# sourceMappingURL=uniq.d.ts.map