Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions src/cli/interactive.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
import readline from "readline";

const interactive = () => {
// Write your code here
// Use readline module for interactive CLI
// Support commands: uptime, cwd, date, exit
// Handle Ctrl+C and unknown commands
const currentReadLine = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

currentReadLine.setPrompt("> ");
currentReadLine.prompt();

currentReadLine.on("line", (line) => {
const cmd = line.trim();

if (cmd === "uptime") {
console.log(`Uptime: ${process.uptime().toFixed(2)}s`);
} else if (cmd === "cwd") {
console.log(process.cwd());
} else if (cmd === "date") {
console.log(new Date().toISOString());
} else if (cmd === "exit") {
console.log("Goodbye!");
process.exit(0);
} else {
console.log("Unknown command");
}

currentReadLine.prompt();
});

currentReadLine.on("close", () => {
console.log("Goodbye!");
process.exit(0);
});
};

interactive();
50 changes: 46 additions & 4 deletions src/cli/progress.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,50 @@
const progress = () => {
// Write your code here
// Simulate progress bar from 0% to 100% over ~5 seconds
// Update in place using \r every 100ms
// Format: [████████████████████ ] 67%
const currentArgs = process.argv;

const getArg = (name, def) => {
const i = currentArgs.indexOf(name);
return i !== -1 ? Number(currentArgs[i + 1]) : def;
};

const duration = getArg("--duration", 5000);
const interval = getArg("--interval", 100);
const length = getArg("--length", 30);

const colorIndex = currentArgs.indexOf("--color");
let colorCode = "";

if (colorIndex !== -1) {
const hex = currentArgs[colorIndex + 1];
if (/^#[0-9a-fA-F]{6}$/.test(hex)) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);

colorCode = `\x1b[38;2;${r};${g};${b}m`;
process.stdout.write(`\x1b[38;2;${r};${g};${b}mTEST COLOR\x1b[0m\n`);
}
}

const steps = Math.floor(duration / interval);
let current = 0;

const timer = setInterval(() => {
current++;

const percent = Math.round((current / steps) * 100);

const filled = Math.round((current / steps) * length);
const empty = length - filled;

const bar = colorCode + "█".repeat(filled) + "\x1b[0m" + " ".repeat(empty);

process.stdout.write(`\r[${bar}] ${percent}%`);

if (current >= steps) {
clearInterval(timer);
process.stdout.write("\nDone!\n");
}
}, interval);
};

progress();
22 changes: 16 additions & 6 deletions src/cp/execCommand.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { spawn } from "child_process";

const execCommand = () => {
// Write your code here
// Take command from CLI argument
// Spawn child process
// Pipe child stdout/stderr to parent stdout/stderr
// Pass environment variables
// Exit with same code as child
const input = process.argv[2];

const [cmd, ...args] = input.split(" ");

const child = spawn(cmd, args, {
env: process.env,
});

child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

child.on("close", (code) => {
process.exit(code);
});
};

execCommand();
47 changes: 43 additions & 4 deletions src/fs/findByExt.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,46 @@
const findByExt = async () => {
// Write your code here
// Recursively find all files with specific extension
// Parse --ext CLI argument (default: .txt)
import { readdir, stat } from "fs/promises";
import { join, relative, extname } from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";

const directoryName = dirname(fileURLToPath(import.meta.url));

export const findByExt = async () => {
const args = process.argv;
const extIndex = args.indexOf("--ext");

const ext = extIndex !== -1 ? args[extIndex + 1] : "txt";

const dotExt = ext.startsWith(".") ? ext : `.${ext}`;

const workspacePath = join(directoryName, "workspace");

await stat(workspacePath).catch(() => {
throw new Error("FS operation failed");
});

const found = [];

async function scan(dir) {
const items = await readdir(dir);

for (const item of items) {
const fullPath = join(dir, item);
const info = await stat(fullPath);

if (info.isDirectory()) {
await scan(fullPath);
} else {
if (extname(item) === dotExt) {
found.push(relative(workspacePath, fullPath));
}
}
}
}

await scan(workspacePath);

found.sort().forEach((p) => console.log(p));
};

await findByExt();
53 changes: 48 additions & 5 deletions src/fs/merge.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,51 @@
const merge = async () => {
// Write your code here
// Default: read all .txt files from workspace/parts in alphabetical order
// Optional: support --files filename1,filename2,... to merge specific files in provided order
// Concatenate content and write to workspace/merged.txt
import { readdir, readFile, writeFile, stat } from "fs/promises";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

const directoryName = dirname(fileURLToPath(import.meta.url));

export const merge = async () => {
const workspacePath = join(directoryName, "workspace");
const partsPath = join(workspacePath, "parts");

await stat(partsPath).catch(() => {
throw new Error("FS operation failed");
});

const args = process.argv;
const filesIndex = args.indexOf("--files");

let filesToMerge = [];

if (filesIndex !== -1) {
const fileNames = args[filesIndex + 1].split(",");

for (const name of fileNames) {
const fullPath = join(partsPath, name);
await stat(fullPath).catch(() => {
throw new Error("FS operation failed");
});
filesToMerge.push(fullPath);
}
} else {
const items = await readdir(partsPath);
filesToMerge = items
.filter((item) => item.endsWith(".txt"))
.sort()
.map((item) => join(partsPath, item));

if (filesToMerge.length === 0) {
throw new Error("FS operation failed");
}
}

const currentChunks = [];
for (const filePath of filesToMerge) {
const text = await readFile(filePath, "utf-8");
currentChunks.push(text);
}

await writeFile(join(workspacePath, "merged.txt"), currentChunks.join(""));
};

await merge();
44 changes: 39 additions & 5 deletions src/fs/restore.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,42 @@
const restore = async () => {
// Write your code here
// Read snapshot.json
// Treat snapshot.rootPath as metadata only
// Recreate directory/file structure in workspace_restored
import { mkdir, readFile, writeFile, access } from "fs/promises";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

const directoryName = dirname(fileURLToPath(import.meta.url));

export const restore = async () => {
const snapshotPath = join(directoryName, "snapshot.json");
const restorePath = join(directoryName, "workspace_restored");

await access(snapshotPath).catch(() => {
throw new Error("FS operation failed");
});

await access(restorePath)
.then(() => {
throw new Error("FS operation failed");
})
.catch((err) => {
if (err.message === "FS operation failed") {
throw err;
}
});

const raw = await readFile(snapshotPath, "utf-8");
const snapshot = JSON.parse(raw);

await mkdir(restorePath);

for (const entry of snapshot.entries) {
const fullPath = join(restorePath, entry.path);

if (entry.type === "directory") {
await mkdir(fullPath, { recursive: true });
} else {
const fileBytes = Buffer.from(entry.content, "base64");
await writeFile(fullPath, fileBytes);
}
}
};

await restore();
47 changes: 40 additions & 7 deletions src/fs/snapshot.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
const snapshot = async () => {
// Write your code here
// Recursively scan workspace directory
// Write snapshot.json with:
// - rootPath: absolute path to workspace
// - entries: flat array of relative paths and metadata
import { readdir, readFile, stat, writeFile } from "fs/promises";
import { join, relative } from "path";

export const snapshot = async (workspacePath) => {
await stat(workspacePath).catch(() => {
throw new Error("FS operation failed");
});

const entries = [];

async function scan(dir) {
const items = await readdir(dir);

for (const item of items) {
const fullPath = join(dir, item);
const relPath = relative(workspacePath, fullPath);
const info = await stat(fullPath);

if (info.isDirectory()) {
entries.push({ path: relPath, type: "directory" });
await scan(fullPath);
} else {
const fileBytes = await readFile(fullPath);
entries.push({
path: relPath,
type: "file",
size: info.size,
content: fileBytes.toString("base64"),
});
}
}
}

await scan(workspacePath);

const result = { rootPath: workspacePath, entries };
const saveTo = join(workspacePath, "..", "snapshot.json");

await writeFile(saveTo, JSON.stringify(result, null, 2));
};

await snapshot();
await snapshot(new URL("workspace", import.meta.url).pathname);
1 change: 1 addition & 0 deletions src/hash/file1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello
36 changes: 32 additions & 4 deletions src/hash/verify.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
import { createReadStream, createWriteStream } from "fs";
import { readFile, access } from "fs/promises";
import { createHash } from "crypto";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

const directoryName = dirname(fileURLToPath(import.meta.url));

const verify = async () => {
// Write your code here
// Read checksums.json
// Calculate SHA256 hash using Streams API
// Print result: filename — OK/FAIL
const checksumsPath = join(directoryName, "checksums.json");

await access(checksumsPath).catch(() => {
throw new Error("FS operation failed");
});

const raw = await readFile(checksumsPath, "utf-8");
const checksums = JSON.parse(raw);

for (const [filename, expectedHash] of Object.entries(checksums)) {
const filePath = join(directoryName, filename);

const actualHash = await new Promise((resolve, reject) => {
const hash = createHash("sha256");
const stream = createReadStream(filePath);

stream.on("data", (chunk) => hash.update(chunk));
stream.on("end", () => resolve(hash.digest("hex")));
stream.on("error", reject);
});

const status = actualHash === expectedHash ? "OK" : "FAIL";
console.log(`${filename} — ${status}`);
}
};

await verify();
21 changes: 16 additions & 5 deletions src/modules/dynamic.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { join, dirname } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));

const dynamic = async () => {
// Write your code here
// Accept plugin name as CLI argument
// Dynamically import plugin from plugins/ directory
// Call run() function and print result
// Handle missing plugin case
const pluginName = process.argv[2];

const pluginPath = join(__dirname, "plugins", `${pluginName}.js`);

try {
const plugin = await import(pluginPath);
console.log(plugin.run());
} catch {
console.log("Plugin not found");
process.exit(1);
}
};

await dynamic();
Loading