Written on 29 May 2023

GUIs for CLIs?

Programs that are only available as command line tools are often powerful but out of reach for less technical users. Maybe they should have configurable UIs.

Command line tools are super useful. They often do one thing and do it very well. But the lack of a point-and-click style interface means that people who don't feel comfortable on the terminal can't use the program.

But what if the CLI tools had graphic user interfaces? GUIs for CLIs - that has a nice ring to it.

I'm not saying those developers should instead build web apps. I'm just wondering if there's a way we can bundle command line tools with something that allows more people to use them by generating something they're more familiar with: a website with form fields.

I've been wondering this for a while, since at least September 2019 when I created cli-gui, a project that has just sat on my Github accuring Digital Ocean bills since then though I never wrote about it until now.

And I started thinking about it again this week when two things happened:

  1. A colleague needed some help downloading a copy of a website.
  2. I came across textual, a very cool Python library for building "text user interfaces", through one of the many Changelog podcast feeds.

Together, those two events reminded me that there's still a need for this kind of thing. There are so many tools that are only available as CLI programs and they're only available to people who brave the shell prompt. Here's a short, and obviously unexhaustive, list of the ones that come to mind:

  1. ffmpeg - All purpose video manipulation
  2. sherlock - Searching user names across many social networks
  3. youtube-dl - Download videos from YouTube
  4. mapshaper - All purpose geographic data manipulation
  5. wget - Download a copy of a website

So is there is a way we can create a configuration file that would take any of these tools and allow an interface to be scaffolded? Even though these tools do widely different things they all work on similar principles: they take options and arguments and then output something, usually to standard out or to a file.

The cli-gui project was my first go at a framework for wrapping command-line tools in a web page with form fields. It used a config.js file to determine what fields were created for each tool and attempted to use Docker as a way to reliably and safely run scripts. I haven't really touched Docker since and I'm not completely sure it's all that "safe" - I just make websites and do data journalism, I don't really know how computers work. From my limited perspective, we tricked rocks into thinking and, honestly, maybe it was a mistake.

The sherlock tool takes a single argument with no flag, just the username to search. Here's what the generated interface looks:

A website comprised of an "Input" section with a text input field with a value of "sherlock" and a "Run" button and an "Output" section where the results of running "sherlock sherlock" are streamed from the Docker container with the checked social networks and if the username "sherlock" exists there.

Animated gif showing the cli-gui interface running the sherlock CLI to look for the username "sherlock"

As you can see in the animated GIF above, cli-gui was set up to allow a user to provide a single parameter to sherlock with the label "Enter username for search query". Here's what the configuration looks like to create that UI and the corresponding documentation:

   id: 'sherlock',
   image: 'sherlock:latest',
   description: 'Sherlock is a tool for searching for a username across many social networks (<a href="https://github.com/sherlock-project/sherlock">website</a>)',
   fields: [
       name: 'username',
       label: 'Enter username for search query',
       defaultValue: '',
       type: 'string',
   format: (fields) => {
     const { username } = fields
     return username

The idea is that you can have a set of fields, defined in the fields array and in this case just a string called username, along with a function that accepts all of those fields to return the text formatted to pass to the command-line tool. Specifically, the command that gets executed for a username of "jeremiak" is "sherlock jeremiak". The function provides flexibility to take any number of fields and format them appropriately for the underlying tool.

JavaScript is my go to language and the major reason is because I know it. Usage begets usage. But a feature that keeps me hooked is that you can express yourself in many different patterns: objects, functions, promises, streams, and more. And that was useful for cli-gui because I can stream the results of the tool straight to the browser as the program executes in Docker. Neat.

Reading through the code for cli-gui has me scratching my head and I'm shaking it looking at the interface. I definitely don't think this is the thing that's going to make command line utilities easier to run. But I still like some of the core ideas four (long) years later and I still have a use case for it.