Compare commits

...

2 Commits

Author SHA1 Message Date
071f1a02b5 install toes cli 2026-02-18 20:46:56 -08:00
7b12dc9a9b ignore sandlot 2026-02-18 16:05:59 -08:00
6 changed files with 79 additions and 2 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.sandlot/
# dependencies (bun install) # dependencies (bun install)
node_modules node_modules
pub/client/index.js pub/client/index.js

View File

@ -5,6 +5,7 @@ import {
AppSelectorChevron, AppSelectorChevron,
DashboardContainer, DashboardContainer,
DashboardHeader, DashboardHeader,
DashboardInstallCmd,
DashboardTitle, DashboardTitle,
StatusDot, StatusDot,
StatusDotLink, StatusDotLink,
@ -35,7 +36,9 @@ export function DashboardLanding({ render }: { render: () => void }) {
</AppSelectorChevron> </AppSelectorChevron>
)} )}
</DashboardTitle> </DashboardTitle>
{/*<DashboardSubtitle>Your personal web appliance</DashboardSubtitle>*/} <DashboardInstallCmd>
curl -fsSL {location.origin}/install | bash
</DashboardInstallCmd>
</DashboardHeader> </DashboardHeader>
<StatusDotsRow> <StatusDotsRow>

View File

@ -27,6 +27,7 @@ export {
ClickableAppName, ClickableAppName,
DashboardContainer, DashboardContainer,
DashboardHeader, DashboardHeader,
DashboardInstallCmd,
DashboardSubtitle, DashboardSubtitle,
DashboardTitle, DashboardTitle,
HamburgerButton, HamburgerButton,

View File

@ -229,6 +229,14 @@ export const DashboardTitle = define('DashboardTitle', {
}, },
}) })
export const DashboardInstallCmd = define('DashboardInstallCmd', {
base: 'code',
fontSize: 13,
opacity: 0.6,
userSelect: 'all',
cursor: 'text',
})
export const DashboardSubtitle = define('DashboardSubtitle', { export const DashboardSubtitle = define('DashboardSubtitle', {
fontSize: 16, fontSize: 16,
color: theme('colors-textMuted'), color: theme('colors-textMuted'),

View File

@ -6,9 +6,9 @@ import syncRouter from './api/sync'
import systemRouter from './api/system' import systemRouter from './api/system'
import { Hype } from '@because/hype' import { Hype } from '@because/hype'
import { cleanupStalePublishers } from './mdns' import { cleanupStalePublishers } from './mdns'
import { extractSubdomain, proxySubdomain, proxyWebSocket, websocket } from './proxy'
import type { Server } from 'bun' import type { Server } from 'bun'
import type { WsData } from './proxy' import type { WsData } from './proxy'
import { extractSubdomain, proxySubdomain, proxyWebSocket, websocket } from './proxy'
const app = new Hype({ layout: false, logging: !!process.env.DEBUG }) const app = new Hype({ layout: false, logging: !!process.env.DEBUG })
@ -59,6 +59,31 @@ app.all('/api/tools/:tool/:path{.+}', async c => {
}) })
}) })
const DIST_DIR = import.meta.dir + '/../../dist'
const INSTALL_SCRIPT = await Bun.file(import.meta.dir + '/install.sh').text()
// Install script: curl -fsSL http://toes.local/install | bash
app.get('/install', c => {
if (!TOES_URL) return c.text('TOES_URL is not configured', 500)
const script = INSTALL_SCRIPT.replace('__TOES_URL__', TOES_URL)
return c.text(script, 200, { 'content-type': 'text/plain' })
})
// Serve built CLI binaries from dist/
app.get('/dist/:file', async c => {
const file = c.req.param('file')
if (!file || file.includes('/') || file.includes('..')) {
return c.text('Not found', 404)
}
const bunFile = Bun.file(`${DIST_DIR}/${file}`)
if (!(await bunFile.exists())) {
return c.text(`Binary "${file}" not found — run cli:build:all on the server`, 404)
}
return new Response(bunFile, {
headers: { 'content-type': 'application/octet-stream' },
})
})
cleanupStalePublishers() cleanupStalePublishers()
await initApps() await initApps()

38
src/server/install.sh Normal file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -e
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case "$OS" in
darwin) OS=macos ;;
linux) OS=linux ;;
*) echo "Unsupported OS: $OS" >&2; exit 1 ;;
esac
case "$ARCH" in
x86_64) ARCH=x64 ;;
arm64|aarch64) ARCH=arm64 ;;
*) echo "Unsupported arch: $ARCH" >&2; exit 1 ;;
esac
BINARY="toes-${OS}-${ARCH}"
URL="__TOES_URL__/dist/${BINARY}"
if [ -n "$TOES_INSTALL_DIR" ]; then
INSTALL_DIR="$TOES_INSTALL_DIR"
elif [ -d /usr/local/bin ] && [ -w /usr/local/bin ]; then
INSTALL_DIR=/usr/local/bin
else
INSTALL_DIR="$HOME/.local/bin"
fi
mkdir -p "$INSTALL_DIR"
echo "Downloading toes CLI (${OS}/${ARCH})..."
curl -fsSL "$URL" -o "$INSTALL_DIR/toes"
chmod +x "$INSTALL_DIR/toes"
echo "Installed toes to $INSTALL_DIR/toes"
if ! echo "$PATH" | tr ':' '\n' | grep -qx "$INSTALL_DIR"; then
echo "Add $INSTALL_DIR to your PATH to use toes globally"
fi