From 1da7e77f005e1e7d2bb30840f2ff6233342add25 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath <2+defunkt@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:51:36 -0800 Subject: [PATCH] TOES_URL and TOES_HOST support --- README.md | 13 +++++++++++ src/cli/commands/index.ts | 1 + src/cli/commands/logs.ts | 46 +++++++++++++++++++++----------------- src/cli/commands/manage.ts | 29 +++++++++++++++++++++++- src/cli/http.ts | 14 +++++++++++- src/cli/setup.ts | 6 +++++ 6 files changed, 86 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 81b3957..eff1f27 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,19 @@ Plug it in, turn it on, and forget about the cloud. - https://toes.local web UI for managing your projects. - Per-branch staging environments for Claude. +## cli configuration + +by default, the CLI connects to `localhost:3000` in dev and `toes.local:80` in production. + +```bash +toes config # show current host +TOES_URL=http://192.168.1.50:3000 toes list # full URL +TOES_HOST=mypi.local toes list # hostname (port 80) +TOES_HOST=mypi.local PORT=3000 toes list # hostname + port +``` + +set `NODE_ENV=production` to default to `toes.local:80`. + ## fun stuff - textOS (TODO, more?) diff --git a/src/cli/commands/index.ts b/src/cli/commands/index.ts index d844467..fe687ce 100644 --- a/src/cli/commands/index.ts +++ b/src/cli/commands/index.ts @@ -1,5 +1,6 @@ export { logApp } from './logs' export { + configShow, infoApp, listApps, newApp, diff --git a/src/cli/commands/logs.ts b/src/cli/commands/logs.ts index a341abb..0b5f075 100644 --- a/src/cli/commands/logs.ts +++ b/src/cli/commands/logs.ts @@ -1,5 +1,5 @@ import type { LogLine } from '@types' -import { get, makeUrl } from '../http' +import { get, handleError, makeUrl } from '../http' import { resolveAppName } from '../name' export const printLog = (line: LogLine) => @@ -29,31 +29,35 @@ export async function logApp(arg: string | undefined, options: { follow?: boolea } export async function tailLogs(name: string) { - const url = makeUrl(`/api/apps/${name}/logs/stream`) - const res = await fetch(url) - if (!res.ok) { - console.error(`App not found: ${name}`) - return - } - if (!res.body) return + try { + const url = makeUrl(`/api/apps/${name}/logs/stream`) + const res = await fetch(url) + if (!res.ok) { + console.error(`App not found: ${name}`) + return + } + if (!res.body) return - const reader = res.body.getReader() - const decoder = new TextDecoder() - let buffer = '' + const reader = res.body.getReader() + const decoder = new TextDecoder() + let buffer = '' - while (true) { - const { done, value } = await reader.read() - if (done) break + while (true) { + const { done, value } = await reader.read() + if (done) break - buffer += decoder.decode(value, { stream: true }) - const lines = buffer.split('\n\n') - buffer = lines.pop() ?? '' + buffer += decoder.decode(value, { stream: true }) + const lines = buffer.split('\n\n') + buffer = lines.pop() ?? '' - for (const line of lines) { - if (line.startsWith('data: ')) { - const data = JSON.parse(line.slice(6)) as LogLine - printLog(data) + for (const line of lines) { + if (line.startsWith('data: ')) { + const data = JSON.parse(line.slice(6)) as LogLine + printLog(data) + } } } + } catch (error) { + handleError(error) } } diff --git a/src/cli/commands/manage.ts b/src/cli/commands/manage.ts index 16e2bb1..bbfe4d9 100644 --- a/src/cli/commands/manage.ts +++ b/src/cli/commands/manage.ts @@ -3,7 +3,7 @@ import { generateTemplates, type TemplateType } from '%templates' import color from 'kleur' import { existsSync, mkdirSync, writeFileSync } from 'fs' import { basename, join } from 'path' -import { del, get, getManifest, post } from '../http' +import { del, get, getManifest, HOST, post } from '../http' import { confirm, prompt } from '../prompts' import { resolveAppName } from '../name' import { pushApp } from './sync' @@ -15,6 +15,33 @@ export const STATE_ICONS: Record = { invalid: color.red('◌'), } +export async function configShow() { + console.log(`Host: ${color.bold(HOST)}`) + + const source = process.env.TOES_URL + ? 'TOES_URL' + : process.env.TOES_HOST + ? 'TOES_HOST' + (process.env.PORT ? ' + PORT' : '') + : process.env.NODE_ENV === 'production' + ? 'default (production)' + : 'default (development)' + + console.log(`Source: ${color.gray(source)}`) + + if (process.env.TOES_URL) { + console.log(` TOES_URL=${process.env.TOES_URL}`) + } + if (process.env.TOES_HOST) { + console.log(` TOES_HOST=${process.env.TOES_HOST}`) + } + if (process.env.PORT) { + console.log(` PORT=${process.env.PORT}`) + } + if (process.env.NODE_ENV) { + console.log(` NODE_ENV=${process.env.NODE_ENV}`) + } +} + export async function infoApp(arg?: string) { const name = resolveAppName(arg) if (!name) return diff --git a/src/cli/http.ts b/src/cli/http.ts index b681b29..fd83fcc 100644 --- a/src/cli/http.ts +++ b/src/cli/http.ts @@ -1,6 +1,17 @@ import type { Manifest } from '@types' -export const HOST = `http://localhost:${process.env.PORT ?? 3000}` +function getDefaultHost(): string { + if (process.env.NODE_ENV === 'production') { + return `http://toes.local:${process.env.PORT ?? 80}` + } + return `http://localhost:${process.env.PORT ?? 3000}` +} + +const defaultPort = process.env.NODE_ENV === 'production' ? 80 : 3000 + +export const HOST = process.env.TOES_URL + ?? (process.env.TOES_HOST ? `http://${process.env.TOES_HOST}:${process.env.PORT ?? defaultPort}` : undefined) + ?? getDefaultHost() export function makeUrl(path: string): string { return `${HOST}${path}` @@ -9,6 +20,7 @@ export function makeUrl(path: string): string { export function handleError(error: unknown): void { if (error instanceof Error && 'code' in error && error.code === 'ConnectionRefused') { console.error(`🐾 Can't connect to toes server at ${HOST}`) + console.error(` Set TOES_URL or TOES_HOST to connect to a different host`) return } console.error(error) diff --git a/src/cli/setup.ts b/src/cli/setup.ts index 0fcc722..f22fbed 100644 --- a/src/cli/setup.ts +++ b/src/cli/setup.ts @@ -3,6 +3,7 @@ import { readFileSync } from 'fs' import color from 'kleur' import { + configShow, diffApp, getApp, infoApp, @@ -45,6 +46,11 @@ program .command('version', { hidden: true }) .action(() => console.log(program.version())) +program + .command('config') + .description('Show current host configuration') + .action(configShow) + program .command('info') .description('Show info for an app')