UNPKG

alwaysai

Version:

The alwaysAI command-line interface (CLI)

88 lines (79 loc) 2.17 kB
import { Readable } from 'stream'; import { Spawner } from './types'; import { CodedError } from '@carnesen/coded-error'; export function GnuSpawner(context: { resolvePath: Spawner['resolvePath']; run: Spawner['run']; runForegroundSync: Spawner['runForegroundSync']; runForeground: Spawner['runForeground']; runStreaming: Spawner['runStreaming']; }): Spawner { const { resolvePath, run, runForegroundSync, runForeground, runStreaming } = context; return { run, runForegroundSync, runForeground, runStreaming, resolvePath, readdir, mkdirp, rimraf, tar, untar, exists, }; async function mkdirp(path?: string) { await run({ exe: 'mkdir', args: ['-p', resolvePath(path)] }); } async function readdir(path?: string) { let output: string; const resolvedPath = resolvePath(path); try { output = await run({ exe: 'ls', args: ['-A1', resolvedPath] }); // ^^ The output looks like '/foo/bar.txt' if path is an existing file // Else it looks like 'a b c' if the path is a directory with files/subdirs a, b, c. } catch (ex) { if ( ex && typeof ex.message === 'string' && ex.message.includes('No such file or directory') ) { ex.code = 'ENOENT'; } throw ex; } if (output.startsWith('/')) { throw new CodedError(`ENOTDIR: not a directory "${resolvedPath}"`, 'ENOTDIR'); } return output.length > 0 ? output.split('\n') : []; } async function rimraf(path?: string) { await run({ exe: 'rm', args: ['-rf', resolvePath(path)] }); } async function tar(...paths: string[]) { return await runStreaming({ exe: 'tar', args: ['-cz', ...paths], cwd: resolvePath(), }); } async function untar(input: Readable, cwd = '.') { await run({ exe: 'tar', args: ['-xz'], cwd, input, }); } async function exists(path: string) { if (!path) { throw new Error('"path" is required'); } try { await run({ exe: 'stat', args: [resolvePath(path)] }); return true; } catch (ex) { return false; } } }