toes/src/cli/index.ts
2026-01-28 22:36:23 -08:00

105 lines
2.2 KiB
TypeScript

import { program } from 'commander'
import { join } from 'path'
import type { App } from '@types'
import { APPS_DIR } from '$apps'
const HOST = `http://localhost:${process.env.PORT ?? 3000}`
const STATE_ICONS: Record<string, string> = {
running: '●',
starting: '◎',
stopped: '◯',
invalid: '◌',
}
async function get<T>(url: string): Promise<T | undefined> {
try {
const res = await fetch(join(HOST, url))
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
return await res.json()
} catch (error) {
console.error(error)
}
}
async function post<T, B = unknown>(url: string, body?: B): Promise<T | undefined> {
try {
const res = await fetch(join(HOST, url), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
return await res.json()
} catch (error) {
console.error(error)
}
}
async function listApps() {
const apps: App[] | undefined = await get('/api/apps')
if (!apps) return
for (const app of apps) {
console.log(`${STATE_ICONS[app.state] ?? '◯'} ${app.name}`)
}
}
const startApp = async (app: string) => {
await post(`/api/apps/${app}/start`)
}
const stopApp = async (app: string) => {
await post(`/api/apps/${app}/stop`)
}
const restartApp = async (app: string) => {
await post(`/api/apps/${app}/restart`)
}
program
.name('toes')
.version('0.0.1', '-v, --version')
program
.command('list')
.description('List all apps')
.action(listApps)
program
.command('start')
.description('Start an app')
.argument('<name>', 'app name')
.action(startApp)
program
.command('stop')
.description('Stop an app')
.argument('<name>', 'app name')
.action(stopApp)
program
.command('restart')
.description('Restart an app')
.argument('<name>', 'app name')
.action(restartApp)
program
.command('new')
.description('Create a new app')
.argument('<name>', 'app name')
.action(name => {
// ...
})
program
.command('push')
.description('Push app to server')
.option('-f, --force', 'force overwrite')
.action(options => {
// ...
})
program.parse()