diff --git a/packages/container/discover.js b/packages/container/discover.js index 67fd286..5955bb7 100644 --- a/packages/container/discover.js +++ b/packages/container/discover.js @@ -3,7 +3,15 @@ const path = require('path') const { resolvePath } = require('./utils') async function collectFilesInfo(inputPath, level = 0, hiddenFiles = false) { - const stats = await fs.stat(inputPath) + let stats + try { + stats = await fs.stat(inputPath) + } catch (e) { + if (e.code === 'ENOENT') { + return [] + } + throw e + } const name = path.basename(inputPath) const discovered = level <= 1 && (hiddenFiles || !name.startsWith('.')) const self = { @@ -39,12 +47,17 @@ async function collectFilesInfo(inputPath, level = 0, hiddenFiles = false) { async function discoverFiles(argv) { const targetPath = argv._.length < 2 ? ['.'] : argv._.slice(1) try { - const result = await Promise.all( - targetPath - .map((p) => resolvePath(p)) - .map((p) => collectFilesInfo(p, 0, argv.a || argv.all)) - ) - return { discover: result.flat() } + const result = ( + await Promise.all( + targetPath + .map((p) => resolvePath(p)) + .map((p) => collectFilesInfo(p, 0, argv.a || argv.all)) + ) + ).flat() + if (result.length === 0) { + return {} + } + return { discover: result } } catch (_) { return {} } diff --git a/packages/container/handleCommand.js b/packages/container/handleCommand.js index fa8af10..7c8ad45 100644 --- a/packages/container/handleCommand.js +++ b/packages/container/handleCommand.js @@ -1,4 +1,5 @@ const fs = require('fs').promises +const path = require('path') const minimist = require('minimist') const { discoverFiles, collectFilesInfo } = require('./discover') const { resolvePath } = require('./utils') @@ -43,6 +44,9 @@ const commandListeners = { mv: { handler: moveFiles, }, + pwd: { + handler: showCurrentPath, + }, } async function handleCommand(command) { @@ -149,4 +153,22 @@ async function moveFiles(argv) { return results } +function showCurrentPath() { + const pwd = process.cwd() + const dirs = pwd.split('/') + let currentPath = '/' + const result = [] + for (const dir of dirs) { + currentPath = path.join(currentPath, dir) + result.push({ + path: currentPath, + type: 'directory', + discovered: true, + }) + } + return { + add: result, + } +} + module.exports = handleCommand diff --git a/packages/file-graph/fileGraph.ts b/packages/file-graph/fileGraph.ts index 1332868..cf32dbc 100644 --- a/packages/file-graph/fileGraph.ts +++ b/packages/file-graph/fileGraph.ts @@ -34,6 +34,10 @@ export default class FileGraph extends FileNode { // Overwrite the file graph with the provided files // Remove file nodes that don't exist anymore and add new ones + if (eventFiles.length === 0) { + return + } + const newNode = new FileNode(eventFiles[0]) eventFiles.slice(1).forEach((file) => { diff --git a/quests/copy/game.yml b/quests/copy/game.yml index e517005..39e495d 100644 --- a/quests/copy/game.yml +++ b/quests/copy/game.yml @@ -78,6 +78,10 @@ stages: response: content: | 你成功查看到 `hello.txt` 的內容了! + 內容是: + ``` + Hello + ``` - id: cat-world name: 查看 `world.txt` 的內容 @@ -113,6 +117,10 @@ stages: response: content: | 你成功查看到 `world.txt` 的內容了! + 內容是: + ``` + World + ``` - id: cat-next name: 輸入 `echo next` 來繼續 @@ -271,30 +279,9 @@ stages: response: content: | 你成功複製 `notes` 資料夾到 `notes2` 了! - 我們打開來確認一下 `notes2` 資料夾的內容吧! - hint: 使用 `ls notes2` 來查看 `notes2` 資料夾的內容 - - - id: ls-notes2 - name: 查看 `notes2` 資料夾的內容 - requirements: - - cp-notes - condition: - pwd: /home/commander - command: ls notes2 - output: note.txt - exceptions: - - id: wrong_directory - condition: - not: - pwd: /home/commander - response: - content: | - 你不在 `/home/commander` 資料夾裡。 - 請使用 `cd /home/commander` 來返回 `/home/commander` 資料夾。 - response: - content: | - 你成功查看到 `notes2` 資料夾的內容了! 我們可以看到,整個 `notes` 資料夾包含裡面的檔案,都被複製到 `notes2` 資料夾了。 + 使用 `cp -r` 複製資料夾,不論資料夾中有多少檔案,多少層目錄,都可以一次複製過去。 + 那麼最後我們要來試試看,如果複製一個檔案到已經存在的檔案會發生什麼事。 請把 `hello.txt` 複製到 `world.txt` 看看會發生什麼事。 hint: 使用 `cp hello.txt world.txt` 來複製 `hello.txt` 到 `world.txt` @@ -302,7 +289,7 @@ stages: - id: cp-overwrite name: 複製 `hello.txt` 到 `world.txt` requirements: - - ls-notes2 + - cp-notes condition: pwd: /home/commander command: cp hello.txt world.txt diff --git a/quests/create/game.yml b/quests/create/game.yml index e9ec693..520cf2f 100644 --- a/quests/create/game.yml +++ b/quests/create/game.yml @@ -57,20 +57,7 @@ stages: response: content: | 你應該可以看到終端機上沒有任何回應。 - 然而實際上 `touch` 指令已經成功建立了一個檔案。 - 請你用之前學過的 `ls` 指令來查看目前所在資料夾中有哪些檔案。 - hint: 輸入 `ls` 指令來查看目前所在資料夾中有哪些檔案 - - - id: ls-touch - name: 使用 `ls` 指令來查看新建立的檔案 - requirements: - - touch - condition: - command: ls - output: hello.txt - response: - content: | - 你應該可以看到終端機上顯示了 `hello.txt` 這個檔案。 + 而右邊的檔案樹可以看到已經建立了 `hello.txt` 這個檔案。 我們來看看 `hello.txt` 檔案的內容吧! 我們可以使用之前學過的 `cat` 指令來查看檔案內容。 hint: 輸入 `cat hello.txt` 來查看 `hello.txt` 檔案的內容 @@ -78,7 +65,7 @@ stages: - id: cat name: 使用 `cat` 指令來查看 `hello.txt` 檔案的內容 requirements: - - ls-touch + - touch condition: command: cat hello.txt response: @@ -113,19 +100,7 @@ stages: response: content: | 非常好,你已經成功建立了第二個檔案。 - 接下來,請你用之前學過的 `ls` 指令來查看剛剛建立的兩個檔案。 - hint: 輸入 `ls` 指令來查看目前所在資料夾中有哪些檔案 - - - id: ls-2 - name: 使用 `ls` 指令來查看剛剛建立的兩個檔案 - requirements: - - touch-2 - condition: - command: ls - output: hello.txt\nworld.txt - response: - content: | - 你應該可以看到終端機上顯示了 `hello.txt` 和 `world.txt` 這兩個檔案。 + 你可以看到檔案樹上顯示了 `hello.txt` 和 `world.txt` 這兩個檔案。 接下來,我們來試試看建立資料夾吧! `mkdir` 指令可以建立一個空的資料夾。 使用方式是 `mkdir [資料夾名稱]`。 @@ -135,7 +110,7 @@ stages: - id: mkdir name: 使用 `mkdir` 指令建立 `notes` 資料夾 requirements: - - ls-2 + - touch-2 condition: files: - path: /home/commander/notes @@ -144,19 +119,6 @@ stages: response: content: | 非常好,你已經成功建立了 `notes` 資料夾。 - 接下來,請你用 `ls` 指令來查看剛剛建立的資料夾。 - hint: 輸入 `ls` 指令來查看目前所在資料夾中有哪些檔案 - - - id: ls-mkdir - name: 使用 `ls` 查看剛建立的資料夾 - requirements: - - mkdir - condition: - command: ls - output: notes - response: - content: | - 你應該可以看到終端機上顯示了 `notes` 這個資料夾。 接下來,我們要在 `notes` 資料夾中建立一個 `note.txt` 的檔案。 首先,我們要先進入 `notes` 資料夾。 還記得之前的 `cd` 指令嗎?請你試著使用 `cd` 指令來進入 `notes` 資料夾。 @@ -167,7 +129,7 @@ stages: - id: cd-notes name: 移動到 `notes` 資料夾 requirements: - - ls-mkdir + - mkdir condition: pwd: /home/commander/notes response: @@ -188,19 +150,16 @@ stages: response: content: | 非常好,你已經成功建立了 `note.txt` 檔案。 - 跟之前一樣,你可以使用 `ls` 指令來查看目前所在資料夾中有哪些檔案。 - hint: 輸入 `ls` 指令來查看目前所在資料夾中有哪些檔案 + 恭喜你完成了這個關卡! + 在下一個關卡中,你將學習如何複製檔案與資料夾。 + 輸入 `echo finish` 來完成這個關卡。 - id: END - name: 使用 `ls` 指令來查看目前所在資料夾中有哪些檔案 + name: 完成關卡 requirements: - touch-note condition: - command: ls - output: note.txt + command: echo finish response: content: | - 你應該可以看到終端機上顯示了 `note.txt` 這個檔案。 恭喜你完成了這個關卡! - 在下一個關卡中,你將學習如何複製檔案與資料夾。 - hint: 恭喜你完成了所有任務! diff --git a/quests/execute/game.yml b/quests/execute/game.yml index e75b580..86156bb 100644 --- a/quests/execute/game.yml +++ b/quests/execute/game.yml @@ -33,7 +33,9 @@ stages: response: content: | 你看到終端機顯示出 `/usr/bin/ls` 這個路徑,它其實就是 `ls` 指令它檔案存在的位置。 - `/usr/bin` 是一個系統資料夾,裡面會儲存許多系統內建的程式,像是 `cat`, `cp` 等指令也都被存在這裡。 + `/usr/bin` 是一個系統資料夾,裡面會儲存許多系統內建的程式。 + + 接下來我們來看看 `cat` 指令的路徑。 hint: 使用 `which cat` 查看 `cat` 的路徑 - id: which-cat @@ -44,7 +46,7 @@ stages: command: which cat response: content: | - 你看到了 `/usr/bin/cat` 這個路徑,它就是 `cat` 指令它檔案存在的位置。 + 你看到了 `/usr/bin/cat` 這個路徑,它也是存在 `/usr/bin` 資料夾裡。 我們要執行一個可執行檔時,只要 **直接輸入它的路徑** 就可以執行它。 我們來試看看使用執行檔的路徑來執行指令。 @@ -60,7 +62,7 @@ stages: response: content: | 你看到了當前的資料夾內有 `commands` 資料夾和 `dog`, `hello.txt` 這兩個檔案。 - 我們再來試看看用 `ls` 的路徑來執行它。 + 我們再來試看看用 `/usr/bin/ls` 這個絕對路徑來執行。 hint: 輸入 `/usr/bin/ls` 來查看當前的資料夾 - id: bin-ls @@ -69,9 +71,18 @@ stages: - ls condition: command: /usr/bin/ls + exceptions: + - id: exception-ls + condition: + command: ls + response: + content: | + 在這邊,我們要練習使用 `ls` 的絕對路徑來執行它。 + 請輸入 `/usr/bin/ls` 來查看當前的資料夾。 response: content: | - 你會看到它列出了我們資料夾內的檔案,你會發現它的輸出跟單純打 `ls` 的結果一模一樣。 + 你會看到它把我們資料夾裡的檔案都列出來了,跟你直接打 `ls` 的結果一樣。這是因為 `ls` 這個程式其實就放在 `/usr/bin/ls`,所以不管你怎麼執行,結果都不會變。 + 它一樣可以使用參數。我們來用 `/usr/bin/cat` 試試看顯示 `hello.txt` 的內容。 hint: 輸入 `/usr/bin/cat hello.txt` @@ -90,7 +101,7 @@ stages: `程式名稱` 就是我們要執行的程式,比如 `ls`, `cat`, `which` 等。 `參數` 是我們要給程式的資訊,比如在 `cat` 後面加上 `hello.txt`。 - 我們之所以不用輸入 `/usr/bin/ls`,而只要輸入 `ls` 就能執行,是因為有一個叫 `PATH` 的設定,幫我們設定好電腦要去哪裡找這些程式。 + 我們之所以不用輸入 `/usr/bin/ls`,而只要輸入 `ls` 就能執行,是因為有一個叫 `PATH` 的設定,幫我們設定好電腦要去哪些資料夾找這些程式。 不過這個 `PATH` 的內容比較複雜,我們以後再學。 現在,我們來試試看執行一個不是內建的指令。 @@ -126,6 +137,16 @@ stages: or: - command: /home/commander/dog - command: ~/dog + exceptions: + - id: exception-dog + condition: + command: dog + error: 'command not found: dog' + response: + content: | + `dog` 不是內建的指令,不能直接打名稱執行。它是個可執行檔,需要指定它的路徑才能執行。 + 在這裡,我們要練習使用 `dog` 的絕對路徑來執行。 + 請輸入 `/home/commander/dog` 來執行 `dog` 檔案。 response: content: | 你看到了終端機輸出這隻傲嬌的狗。 @@ -142,6 +163,16 @@ stages: condition: pwd: /home/commander command: ./dog + exceptions: + - id: exception-dog-2 + condition: + command: dog + error: 'command not found: dog' + response: + content: | + `dog` 不是內建的指令,不能直接打名稱執行。它是個可執行檔,需要指定它的路徑才能執行。 + 在這裡,我們要練習使用 `dog` 的相對路徑來執行。 + 請輸入 `./dog` 來執行 `dog` 檔案。 response: content: | 你看到這隻狗執行了跟剛才一樣的結果。 @@ -168,6 +199,23 @@ stages: 恭喜你,你已經完成了這個課程! 請輸入 `echo finish` 來完成這個關卡。 exceptions: + - id: exception-lucky + condition: + command: lucky + error: 'command not found: lucky' + response: + content: | + `lucky` 不是內建的指令,不能直接打名稱執行。它是個可執行檔,需要指定它的路徑才能執行。 + + - id: exception-lucky-2 + condition: + pwd: /home/commander + command: ./lucky + error: 'no such file or directory' + response: + content: | + 你還沒有進入 `commands` 資料夾,所以不能使用相對路徑來執行 `lucky` 檔案。 + 請先輸入 `cd commands` 來移動到 `commands` 資料夾,再使用相對路徑 `./lucky` 來執行 `lucky` 檔案。 - id: help condition: command: echo help diff --git a/quests/move/game.yml b/quests/move/game.yml index 93e979f..5f02ee0 100644 --- a/quests/move/game.yml +++ b/quests/move/game.yml @@ -54,39 +54,16 @@ stages: response: content: | 你做得很好! - 我們用 `ls` 跟 `ls notes` 來查看一下資料夾內的檔案吧! - hint: 使用 `ls`、`ls notes` 來查看資料夾內的檔案 + `hello.txt` 已經移動到 `notes` 資料夾中了! - - id: ls-no-hello - requirements: - - mv-hello - name: 檢查 `hello.txt` 是否還在資料夾中 - condition: - pwd: /home/commander - command: ls - response: - content: | - `hello.txt` 已經不在資料夾中了! - - - id: ls-notes-hello - requirements: - - mv-hello - name: 檢查 `hello.txt` 是否在 `notes` 資料夾中 - condition: - pwd: /home/commander - command: ls notes - output: hello.txt - response: - content: | - `hello.txt` 出現在 `notes` 資料夾中了! - `mv` 指令也可以用來更改檔案名稱。 - 只要將目的地改成新的檔案名稱就可以了。 - 現在我們要將 `world.txt` 更名為 `wow.txt`。 + `mv` 指令不只可以移動檔案,還可以改檔案的名字哦! + 只要把目標改成你想要的新名字就行了。 + 現在,我們要把 `world.txt` 改名成 `wow.txt`。 hint: 使用 `mv world.txt wow.txt` 來將 `world.txt` 更名為 `wow.txt` - id: mv-world requirements: - - ls-notes-hello + - mv-hello name: 將 `world.txt` 更名為 `wow.txt` condition: command: mv world.txt wow.txt @@ -97,32 +74,19 @@ stages: - path: /home/commander/world.txt type: file exists: false - response: - content: | - 你做得很好! - 我們用 `ls` 來確認一下資料夾內的檔案吧! - hint: 使用 `ls` 來查看資料夾內的檔案 - - - id: ls-wow - requirements: - - mv-world - name: 檢查 `wow.txt` 是否存在 - condition: - pwd: /home/commander - command: ls - output: wow.txt response: content: | `world.txt` 現在已經變成 `wow.txt` 了。 + 接下來我們來學習如何移動資料夾吧! - 移動資料夾非常簡單,方法跟移動檔案一樣。 + 移動資料夾跟移動檔案一樣。 `mv` 指令會自動將資料夾內的所有檔案一起移動 我們先來將 `notes` 資料夾改名叫作 `docs` 吧! hint: 使用 `mv notes docs` 來將 `notes` 資料夾改名叫作 `docs` - id: mv-notes requirements: - - ls-wow + - mv-world name: 將 `notes` 資料夾改名叫作 `docs` condition: command: mv notes docs @@ -133,30 +97,16 @@ stages: - path: /home/commander/notes type: directory exists: false - response: - content: | - 你做得很好! - 我們用 `ls` 來確認一下資料夾內的檔案吧! - hint: 使用 `ls` 來查看資料夾內的檔案 - - - id: ls-docs - requirements: - - mv-notes - name: 檢查 `docs` 資料夾是否存在 - condition: - pwd: /home/commander - command: ls - output: docs response: content: | `notes` 資料夾已經變成 `docs` 了。 那麼最後來考考你: 試著將 `docs/hello.txt` 移動到家目錄底下的 `hi.txt`。 - 如果你不知道怎麼做,可以輸入 `echo hint` 來取得提示。 + hint: 使用 `mv docs/hello.txt hi.txt` 來將 `docs/hello.txt` 移動到家目錄底下的 `hi.txt` - id: mv-docs-hello requirements: - - ls-docs + - mv-notes name: 將 `docs/hello.txt` 移動到家目錄底下的 `hi.txt` condition: command: mv docs/hello.txt hi.txt @@ -167,24 +117,17 @@ stages: - path: /home/commander/docs/hello.txt type: file exists: false - exceptions: - - id: hint - condition: - command: echo hint - response: - content: | - 你可以使用 `mv docs/hello.txt hi.txt` 來完成這個任務。 - hint: 使用 `mv docs/hello.txt hi.txt` 來將 `docs/hello.txt` 移動到家目錄底下的 `hi.txt` response: content: | 你做得很好! 你已經學會如何移動檔案與資料夾了! + 請輸入 `echo finish` 來完成這個關卡。 - id: END requirements: - mv-docs-hello - name: 完成任務 + name: 完成關卡 condition: command: echo finish response: diff --git a/quests/remove/game.yml b/quests/remove/game.yml index 54ba38a..d6a5ac3 100644 --- a/quests/remove/game.yml +++ b/quests/remove/game.yml @@ -50,23 +50,10 @@ stages: - path: /home/commander/hello.txt type: file exists: false - response: - content: | - 你做得很好! - 我們用 `ls` 來查看一下資料夾內的檔案吧! - hint: 使用 `ls` 來查看資料夾內的檔案 - - - id: ls-no-hello - requirements: - - rm-hello - name: 檢查 `hello.txt` 是否還在資料夾中 - condition: - pwd: /home/commander - command: ls response: content: | `hello.txt` 已經不在資料夾中了! - 你會發現 `world.txt` 與 `wow.txt` 還在。 + `rm` 也可以同時刪除多個檔案,只要把檔案名稱用空白隔開就可以了。 使用方式為 `rm [檔案1] [檔案2] ...`。 現在請你使用 `rm` 刪除 `world.txt` 與 `wow.txt`。 @@ -77,10 +64,9 @@ stages: - id: rm-world-wow requirements: - - ls-no-hello + - rm-hello name: 刪除 `world.txt` 與 `wow.txt` condition: - command: rm world.txt wow.txt files: - path: /home/commander/world.txt type: file @@ -88,23 +74,11 @@ stages: - path: /home/commander/wow.txt type: file exists: false - response: - content: | - 你做得很好! - 我們用 `ls` 來查看一下資料夾內的檔案吧! - hint: 使用 `ls` 來查看資料夾內的檔案 - - - id: ls-no-world-wow - requirements: - - rm-world-wow - name: 檢查 `world.txt` 與 `wow.txt` 是否還在資料夾中 - condition: - pwd: /home/commander - command: ls response: content: | 你會發現 `world.txt` 與 `wow.txt` 已經不見了。 - 現在我們來學習 `rmdir` 指令。 + + 現在我們來學習如何刪除資料夾。 `rmdir` 指令可以用來刪除空的資料夾。 使用方式為 `rmdir [資料夾名稱]`。 我們先來刪除 `empty` 資料夾吧! @@ -112,7 +86,7 @@ stages: - id: rm-empty requirements: - - ls-no-world-wow + - rm-world-wow name: 刪除 `empty` 資料夾 condition: command: rmdir empty @@ -122,26 +96,13 @@ stages: exists: false response: content: | - 你做得很好! - 我們用 `ls` 來查看一下資料夾內的檔案吧! - hint: 使用 `ls` 來查看資料夾內的檔案 - - - id: ls-no-empty - requirements: - - rm-empty - name: 檢查 `empty` 資料夾是否還在 - condition: - pwd: /home/commander - command: ls - response: - content: | - 你會發現 `empty` 資料夾已經不見了。 + `empty` 資料夾已經不見了。 現在請你試試看 `rmdir` 指令刪除 `notes` 資料夾。 hint: 使用 `rmdir notes` 來刪除 `notes` 資料夾 - id: rm-notes-failed requirements: - - ls-no-empty + - rm-empty name: 嘗試刪除 `notes` 資料夾 condition: command: rmdir notes @@ -156,6 +117,7 @@ stages: hint: | 使用 `rm notes/[檔案名稱]` 來刪除 `notes` 資料夾內的檔案 你可能需要使用 `ls notes` 來查看 `notes` 資料夾內有哪些檔案 + 或是利用 Tab 來自動補齊檔案名稱 - id: rm-notes-content requirements: @@ -224,26 +186,14 @@ stages: exists: false response: content: | - 很好!現在請你使用 `ls` 指令查看資料夾內容。 - hint: 使用 `ls` 來查看資料夾內容 - - - id: ls-no-trash - requirements: - - rm-trash - name: 檢查 `trash` 資料夾是否還在 - condition: - pwd: /home/commander - command: ls - response: - content: | - `trash` 資料夾已經不見了。 + `trash` 資料夾跟它的所有檔案都不見了。 恭喜你!你已經學會如何刪除檔案與資料夾了! 請輸入 `echo finish` 來完成這個關卡。 - id: END requirements: - - ls-no-trash - name: 完成任務 + - rm-trash + name: 完成關卡 condition: command: echo finish response: diff --git a/server/src/api/controllers/commandController.ts b/server/src/api/controllers/commandController.ts index f4fbdaf..d41afac 100644 --- a/server/src/api/controllers/commandController.ts +++ b/server/src/api/controllers/commandController.ts @@ -6,8 +6,12 @@ import { finishSession } from '../../models/sessionManager' import { asyncHandler } from '../../middleware/error' import { questManager } from '../../models/quest' import { CLIFileExistenceChecker } from '../../containers/cli' +import config from '../../config' export const newCommand = asyncHandler(async (req: Request, res: Response) => { + if (!config.isProduction) { + console.log('newCommand', JSON.stringify(req.body, null, 2)) + } const body = commandSchema.safeParse(req.body) if (!body.success) { res.status(400).json({ message: 'Invalid command', errors: body.error })