diff --git a/packages/cli/cli.js b/packages/cli/cli.js index 39d5ecd..5b2c546 100644 --- a/packages/cli/cli.js +++ b/packages/cli/cli.js @@ -1,82 +1,8 @@ -/* eslint-disable no-await-in-loop */ /* eslint-disable no-restricted-syntax */ const fs = require('fs').promises const { exit } = require('process') -const readline = require('readline') - -const RETURN_SYMBOL = '\u21B5' - -// ANSI color map -const ANSI_COLORS = { - black: 30, - red: 31, - green: 32, - yellow: 33, - blue: 34, - magenta: 35, - cyan: 36, - white: 37, -} - -function colorize(content, color) { - const colorCode = ANSI_COLORS[color] - if (!colorCode) return content - return `\x1b[${colorCode}m${content}\x1b[0m` -} - -process.stdin.setRawMode(true) - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - terminal: false, -}) - -function waitForEnter() { - return new Promise((resolve) => { - rl.once('line', resolve) - }) -} - -function printLine(content, delay, color) { - const printContent = `${colorize(content, color)}${RETURN_SYMBOL}` - return new Promise((resolve) => { - let i = 0 - const timer = setInterval(() => { - process.stdout.write(printContent[i]) - i += 1 - if (i >= printContent.length) { - clearInterval(timer) - waitForEnter().then(resolve) - } - }, delay) - - waitForEnter().then(() => { - clearInterval(timer) - process.stdout.write(printContent.slice(i)) - process.stdout.write('\n') - resolve() - }) - }) -} - -async function printResponses(responses, delay = 100) { - for (const response of responses) { - const { type, content, speaker, color } = response - if (type === 'narrative') { - for (const line of content) { - await printLine(line, delay, color || 'white') - } - } - if (type === 'dialogue') { - console.log(`${speaker}:`) - for (const line of content) { - await printLine(`\t${line}`, delay, color || 'green') - } - } - } - rl.close() -} +const { colorize, printResponses } = require('./print.js') +const discoverFiles = require('./discover.js') async function readOrNone(file) { try { @@ -86,6 +12,25 @@ async function readOrNone(file) { } } +function parseCommand(command) { + const [name, ...args] = command.trim().split(' ') + return { name, args } +} + +const commandListeners = { + ls: [discoverFiles], +} + +async function handleCommand(command) { + const { name, args } = parseCommand(command) + const listeners = commandListeners[name] + if (!listeners) return {} + return listeners.reduce(async (result, listener) => { + const res = await listener(name, args) + return { ...res, ...result } + }, {}) +} + async function main() { const { API_ENDPOINT, @@ -98,6 +43,8 @@ async function main() { } = process.env if (!CMD_NAME) exit(0) + const additionalData = await handleCommand(CMD_NAME) + const output = await readOrNone(CMD_OUTPUT_FILE) const error = await readOrNone(CMD_ERROR_FILE) const payload = { @@ -107,6 +54,7 @@ async function main() { exit_code: CMD_EXIT_CODE, token: TOKEN, pwd: PWD, + ...additionalData, } try { const res = await fetch(`${API_ENDPOINT}/api/v1/commands`, { @@ -133,4 +81,4 @@ async function main() { } } -main().then(exit).catch(console.error) +main().then(exit) diff --git a/packages/cli/discover.js b/packages/cli/discover.js new file mode 100644 index 0000000..06d9d28 --- /dev/null +++ b/packages/cli/discover.js @@ -0,0 +1,38 @@ +const fs = require('fs').promises +const path = require('path') + +async function collectFilesInfo(directoryPath, level = 0) { + const entries = await fs.readdir(directoryPath, { withFileTypes: true }) + + const promises = entries.map(async (entry) => { + const filePath = path.join(directoryPath, entry.name) + const isDirectory = entry.isDirectory() + const discovered = level === 0 && !entry.name.startsWith('.') + + const file = { + path: filePath, + name: entry.name, + type: isDirectory ? 'folder' : 'file', + discovered, + } + + if (isDirectory && level < 2) { + // Get up to two levels deep + const subFilesInfo = await collectFilesInfo(filePath, level + 1) + return [file, ...subFilesInfo] + } + + return file + }) + + const results = await Promise.all(promises) + return results.flat() +} + +async function discoverFiles(name, args) { + let targetPath = args[0] || '.' + targetPath = path.resolve(process.cwd(), targetPath) + return { discover: await collectFilesInfo(targetPath) } +} + +module.exports = discoverFiles diff --git a/packages/cli/package.json b/packages/cli/package.json index d8c4598..0bbe6b2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -3,5 +3,7 @@ "version": "1.0.0", "main": "index.js", "license": "MIT", - "dependencies": {} + "dependencies": { + "chokidar": "^3.5.3" + } } diff --git a/packages/cli/print.js b/packages/cli/print.js new file mode 100644 index 0000000..76a77a5 --- /dev/null +++ b/packages/cli/print.js @@ -0,0 +1,82 @@ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-await-in-loop */ +const readline = require('readline') + +const RETURN_SYMBOL = '\u21B5' + +// ANSI color map +const ANSI_COLORS = { + black: 30, + red: 31, + green: 32, + yellow: 33, + blue: 34, + magenta: 35, + cyan: 36, + white: 37, +} + +function colorize(content, color) { + const colorCode = ANSI_COLORS[color] + if (!colorCode) return content + return `\x1b[${colorCode}m${content}\x1b[0m` +} + +process.stdin.setRawMode(true) + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: false, +}) + +function waitForEnter() { + return new Promise((resolve) => { + rl.once('line', resolve) + }) +} + +function printLine(content, delay, color) { + const printContent = `${colorize(content, color)}${RETURN_SYMBOL}` + return new Promise((resolve) => { + let i = 0 + const timer = setInterval(() => { + process.stdout.write(printContent[i]) + i += 1 + if (i >= printContent.length) { + clearInterval(timer) + waitForEnter().then(resolve) + } + }, delay) + + waitForEnter().then(() => { + clearInterval(timer) + process.stdout.write(printContent.slice(i)) + process.stdout.write('\n') + resolve() + }) + }) +} + +async function printResponses(responses, delay = 100) { + for (const response of responses) { + const { type, content, speaker, color } = response + if (type === 'narrative') { + for (const line of content) { + await printLine(line, delay, color || 'white') + } + } + if (type === 'dialogue') { + console.log(`${speaker}:`) + for (const line of content) { + await printLine(`\t${line}`, delay, color || 'green') + } + } + } + rl.close() +} + +module.exports = { + printResponses, + colorize, +} diff --git a/packages/cli/watch.js b/packages/cli/watch.js new file mode 100644 index 0000000..8288150 --- /dev/null +++ b/packages/cli/watch.js @@ -0,0 +1,6 @@ +const chokidar = require('chokidar') + +// One-liner for current directory +chokidar.watch('.').on('all', (event, path) => { + console.log(event, path) +}) diff --git a/server/quests/helloworld/Dockerfile b/server/quests/helloworld/Dockerfile index 8a8942d..9606231 100644 --- a/server/quests/helloworld/Dockerfile +++ b/server/quests/helloworld/Dockerfile @@ -14,6 +14,6 @@ WORKDIR /home/rudeus COPY ./server/quests/helloworld/home /home/rudeus/ -COPY ./packages/cli/cli.js /usr/local/bin/cli.js +COPY ./packages/cli /usr/local/lib/cli CMD [ "/bin/zsh" ] diff --git a/server/quests/helloworld/home/.zshrc b/server/quests/helloworld/home/.zshrc index d8275fd..29c9f3e 100644 --- a/server/quests/helloworld/home/.zshrc +++ b/server/quests/helloworld/home/.zshrc @@ -29,7 +29,7 @@ precmd() { export CMD_ERROR_FILE=/tmp/cmd.error # Now run your Node.js script with the result as an argument - node /usr/local/bin/cli.js + node /usr/local/lib/cli/cli.js rm /tmp/cmd.out /tmp/cmd.error 2>/dev/null # cleanup } diff --git a/server/src/utils/docker.js b/server/src/utils/docker.js index 730a6e1..fc9bbe4 100644 --- a/server/src/utils/docker.js +++ b/server/src/utils/docker.js @@ -18,7 +18,7 @@ const containerOptions = { ? [] : [ `${projectPath}/server/quests/helloworld/home:/home/rudeus`, - `${projectPath}/packages/cli/cli.js:/usr/local/bin/cli.js`, + `${projectPath}/packages/cli:/usr/local/lib/cli`, ], }, }