diff --git a/TODO.txt b/TODO.txt index 6761318..6fcb212 100644 --- a/TODO.txt +++ b/TODO.txt @@ -24,7 +24,7 @@ [x] `toes start ` [x] `toes stop ` [x] `toes restart ` -[ ] `toes open ` +[x] `toes open ` [ ] `toes logs ` [ ] `toes new` [ ] `toes pull` diff --git a/src/cli/index.ts b/src/cli/index.ts index dc02928..d334077 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -57,6 +57,20 @@ const restartApp = async (app: string) => { await post(`/api/apps/${app}/restart`) } +async function openApp(name: string) { + const app: App | undefined = await get(`/api/apps/${name}`) + if (!app) { + console.error(`App not found: ${name}`) + return + } + if (app.state !== 'running') { + console.error(`App is not running: ${name}`) + return + } + const url = `http://localhost:${app.port}` + console.log(`Opening ${url}`) + Bun.spawn(['open', url]) +} program .name('toes') @@ -79,13 +93,18 @@ program .argument('', 'app name') .action(stopApp) - program .command('restart') .description('Restart an app') .argument('', 'app name') .action(restartApp) +program + .command('open') + .description('Open an app in browser') + .argument('', 'app name') + .action(openApp) + program .command('new') .description('Create a new app') diff --git a/src/server/index.tsx b/src/server/index.tsx index 92e84cf..9220597 100644 --- a/src/server/index.tsx +++ b/src/server/index.tsx @@ -1,7 +1,16 @@ import { allApps, initApps, onChange, startApp, stopApp, updateAppIcon } from '$apps' import type { App as SharedApp } from '@types' +import type { App as BackendApp } from '$apps' import { Hype } from 'hype' +// BackendApp -> SharedApp +function convert(app: BackendApp): SharedApp { + const clone = { ...app } + delete clone.proc + delete clone.logs + return clone +} + const app = new Hype({ layout: false }) // SSE endpoint for real-time app state updates @@ -47,14 +56,18 @@ app.get('/api/apps/stream', c => { }) }) -app.get('/api/apps', c => { - const apps = allApps().map(app => { - const clone = { ...app } - delete clone.proc - delete clone.logs - return clone - }) - return c.json(apps) +app.get('/api/apps', c => + c.json(allApps().map(convert)) +) + +app.get('/api/apps/:app', c => { + const appName = c.req.param('app') + if (!appName) return c.json({ error: 'App not found' }, 404) + + const app = allApps().find(a => a.name === appName) + if (!app) return c.json({ error: 'App not found' }, 404) + + return c.json(convert(app)) }) app.post('/api/apps/:app/start', c => {