Add btop and pacman terminal commands
This commit is contained in:
1 parent
201a1e0914
commit
dcf27d90b9
3 files changed
+152
-3
No files matched your search
@@ -3,6 +3,7 @@ import { allSitePages } from '#lib/site/pages';
|
|||||||
import { blankLine, errorLine, line, span } from '../output';
|
import { blankLine, errorLine, line, span } from '../output';
|
||||||
import { infoCommands } from './info';
|
import { infoCommands } from './info';
|
||||||
import { systemCommands } from './system';
|
import { systemCommands } from './system';
|
||||||
|
import { pacmanCommand } from './pacman';
|
||||||
import type { Command, CommandContext } from './types';
|
import type { Command, CommandContext } from './types';
|
||||||
|
|
||||||
export type { Command, CommandContext } from './types';
|
export type { Command, CommandContext } from './types';
|
||||||
@@ -38,7 +39,14 @@ const socialShortcuts: Command[] = profile.socials.map((social) => ({
|
|||||||
run: (_args, context) => context.openUrl(social.url)
|
run: (_args, context) => context.openUrl(social.url)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const commands: Command[] = [helpCommand, ...infoCommands, ...systemCommands, ...pageShortcuts, ...socialShortcuts];
|
export const commands: Command[] = [
|
||||||
|
helpCommand,
|
||||||
|
...infoCommands,
|
||||||
|
...systemCommands,
|
||||||
|
pacmanCommand,
|
||||||
|
...pageShortcuts,
|
||||||
|
...socialShortcuts
|
||||||
|
];
|
||||||
|
|
||||||
export function findCommand(name: string): Command | undefined {
|
export function findCommand(name: string): Command | undefined {
|
||||||
const lowerName = name.toLowerCase();
|
const lowerName = name.toLowerCase();
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
import { profile } from '#lib/content';
|
||||||
|
import { blankLine, errorLine, line, span, type OutputBlock } from '../output';
|
||||||
|
import type { Command, CommandContext } from './types';
|
||||||
|
|
||||||
|
const stepDelay = 350;
|
||||||
|
const progressBarWidth = 24;
|
||||||
|
|
||||||
|
const jokePackages: Record<string, OutputBlock[]> = {
|
||||||
|
girlfriend: [errorLine('target not found: girlfriend')],
|
||||||
|
money: [errorLine('target not found: money'), line(span('hint: ', 'yellow'), 'try the Ko-fi icon in the top bar')],
|
||||||
|
sleep: [
|
||||||
|
line(span(':: ', 'blue', { bold: true }), 'sleep and energy-drinks are in conflict. Remove energy-drinks? [y/N] n'),
|
||||||
|
errorLine('unresolvable package conflicts detected')
|
||||||
|
],
|
||||||
|
windows: [errorLine('windows is not available for Arch Linux. Nice try.')],
|
||||||
|
job: [errorLine('job requires entry-level-position>=5-years-experience, which cannot be satisfied')],
|
||||||
|
internship: [errorLine('internship requires internship-experience, which requires internship')]
|
||||||
|
};
|
||||||
|
|
||||||
|
function packageVersion(name: string): string {
|
||||||
|
const seed = [...name].reduce((sum, character) => sum + character.charCodeAt(0), 0);
|
||||||
|
return `${(seed % 9) + 1}.${seed % 13}.${seed % 7}-1`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function packageSize(name: string): string {
|
||||||
|
return `${(name.length * 1.37 + 0.42).toFixed(2)} MiB`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function progressLine(index: number, total: number, action: string, name: string): OutputBlock {
|
||||||
|
const counter = `(${index}/${total})`;
|
||||||
|
return line(
|
||||||
|
span(`${counter} ${action} ${name}`.padEnd(38), 'foreground'),
|
||||||
|
span(`[${'#'.repeat(progressBarWidth)}]`, 'green'),
|
||||||
|
span(' 100%', 'muted')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function printOverTime(context: CommandContext, blocks: OutputBlock[]) {
|
||||||
|
blocks.forEach((block, index) => setTimeout(() => context.print(block), index * stepDelay));
|
||||||
|
}
|
||||||
|
|
||||||
|
function installPackages(context: CommandContext, packages: string[]) {
|
||||||
|
const jokeName = packages.find((name) => name in jokePackages);
|
||||||
|
if (jokeName) {
|
||||||
|
printOverTime(context, [line(span('resolving dependencies...', 'muted')), ...jokePackages[jokeName]]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = packages.length;
|
||||||
|
printOverTime(context, [
|
||||||
|
line(span('resolving dependencies...', 'muted')),
|
||||||
|
line(span('looking for conflicting packages...', 'muted')),
|
||||||
|
blankLine,
|
||||||
|
line(span(`Packages (${total}) `, 'foreground', { bold: true }), packages.map((name) => `${name}-${packageVersion(name)}`).join(' ')),
|
||||||
|
blankLine,
|
||||||
|
line(span('Total Installed Size: ', 'foreground', { bold: true }), packageSize(packages.join(''))),
|
||||||
|
blankLine,
|
||||||
|
line(span(':: ', 'blue', { bold: true }), 'Proceed with installation? [Y/n] y'),
|
||||||
|
...packages.map((name, index) => progressLine(index + 1, total, 'installing', name)),
|
||||||
|
line(span(':: ', 'blue', { bold: true }), 'Running post-transaction hooks...'),
|
||||||
|
progressLine(1, 1, 'Reloading', profile.username),
|
||||||
|
blankLine,
|
||||||
|
line(span('(this is a website, nothing was actually installed, but it felt good right?)', 'muted'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function systemUpgrade(context: CommandContext) {
|
||||||
|
printOverTime(context, [
|
||||||
|
line(span(':: ', 'blue', { bold: true }), 'Synchronizing package databases...'),
|
||||||
|
line(' core is up to date'),
|
||||||
|
line(' extra is up to date'),
|
||||||
|
line(span(':: ', 'blue', { bold: true }), 'Starting full system upgrade...'),
|
||||||
|
line(' there is nothing to do'),
|
||||||
|
line(span('btw, I use Arch.', 'accent'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function listInstalled(context: CommandContext) {
|
||||||
|
const installed = Object.values(profile.skills)
|
||||||
|
.flat()
|
||||||
|
.map((skill) => skill.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''));
|
||||||
|
context.print(...installed.map((name) => line(span(name, 'foreground', { bold: true }), ' ', span(packageVersion(name), 'green'))));
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePackages(context: CommandContext, packages: string[]) {
|
||||||
|
const name = packages[0];
|
||||||
|
context.print(line(span('checking dependencies...', 'muted')), errorLine(`failed to prepare transaction: ${name} is required by ${profile.username}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
function printHelp(context: CommandContext) {
|
||||||
|
context.print(
|
||||||
|
line('usage: pacman <operation> [...]'),
|
||||||
|
line('operations:'),
|
||||||
|
line(' pacman -S <package> install packages'),
|
||||||
|
line(' pacman -Syu upgrade the system'),
|
||||||
|
line(' pacman -Q list installed packages'),
|
||||||
|
line(' pacman -R <package> remove packages')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runPacman(args: string[], context: CommandContext) {
|
||||||
|
const [operation, ...packages] = args;
|
||||||
|
if (!operation) {
|
||||||
|
context.print(errorLine('no operation specified (use -h for help)'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (operation === '-h' || operation === '--help') return printHelp(context);
|
||||||
|
if (operation.startsWith('-Sy') && packages.length === 0) return systemUpgrade(context);
|
||||||
|
if (operation.startsWith('-Q')) return listInstalled(context);
|
||||||
|
|
||||||
|
if (packages.length === 0) {
|
||||||
|
context.print(errorLine('no targets specified (use -h for help)'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (operation.startsWith('-S')) return installPackages(context, packages.map((name) => name.toLowerCase()));
|
||||||
|
if (operation.startsWith('-R')) return removePackages(context, packages);
|
||||||
|
context.print(errorLine(`invalid option '${operation}'`));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pacmanCommand: Command = {
|
||||||
|
name: 'pacman',
|
||||||
|
hidden: true,
|
||||||
|
description: 'Arch Linux package manager',
|
||||||
|
run: runPacman
|
||||||
|
};
|
||||||
|
|
||||||
|
export function runAsRoot(args: string[], context: CommandContext): boolean {
|
||||||
|
if (args[0] !== 'pacman') return false;
|
||||||
|
context.print(line(span(`[sudo] password for ${profile.username}: `, 'muted'), '********'));
|
||||||
|
runPacman(args.slice(1), context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { profile } from '#lib/content';
|
|||||||
import { themes } from '#lib/theme/themes';
|
import { themes } from '#lib/theme/themes';
|
||||||
import { allSitePages, findSitePage, sitePages } from '#lib/site/pages';
|
import { allSitePages, findSitePage, sitePages } from '#lib/site/pages';
|
||||||
import { blankLine, errorLine, line, span } from '../output';
|
import { blankLine, errorLine, line, span } from '../output';
|
||||||
|
import { runAsRoot } from './pacman';
|
||||||
import type { Command } from './types';
|
import type { Command } from './types';
|
||||||
|
|
||||||
function cowsay(message: string): string[] {
|
function cowsay(message: string): string[] {
|
||||||
@@ -122,8 +123,16 @@ export const systemCommands: Command[] = [
|
|||||||
name: 'sudo',
|
name: 'sudo',
|
||||||
hidden: true,
|
hidden: true,
|
||||||
description: 'Elevated privileges',
|
description: 'Elevated privileges',
|
||||||
run: (_args, context) =>
|
run: (args, context) => {
|
||||||
context.print(errorLine(`${profile.username} is not in the sudoers file. This incident will be reported.`))
|
if (runAsRoot(args, context)) return;
|
||||||
|
context.print(errorLine(`${profile.username} is not in the sudoers file. This incident will be reported.`));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'btop',
|
||||||
|
aliases: ['htop', 'top'],
|
||||||
|
description: 'Open the system monitor',
|
||||||
|
run: (_args, context) => context.openApp('btop')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'pwd',
|
name: 'pwd',
|
||||||
|
|||||||
Reference in new issue
Block a user