Compare commits

..

No commits in common. "0bf3560597007706b76704dc5d7abea4cd30f616" and "abe7bc258ef13600e4032762fd627387c4641ef1" have entirely different histories.

2 changed files with 25 additions and 15 deletions

View File

@ -19,11 +19,11 @@
}, },
}, },
"packages": { "packages": {
"@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="], "@types/bun": ["@types/bun@1.3.7", "", { "dependencies": { "bun-types": "1.3.7" } }, "sha512-lmNuMda+Z9b7tmhA0tohwy8ZWFSnmQm1UDWXtH5r9F7wZCfkeO3Jx7wKQ1EOiKq43yHts7ky6r8SDJQWRNupkA=="],
"@types/node": ["@types/node@25.1.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA=="], "@types/node": ["@types/node@25.1.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA=="],
"bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="], "bun-types": ["bun-types@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-qyschsA03Qz+gou+apt6HNl6HnI+sJJLL4wLDke4iugsE6584CMupOtTY1n+2YC9nGVrEKUlTs99jjRLKgWnjQ=="],
"commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="], "commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="],

View File

@ -55,7 +55,7 @@ export function stopApp(dir: string) {
const app = _apps.get(dir) const app = _apps.get(dir)
if (!app || app.state !== 'running') return if (!app || app.state !== 'running') return
info(app, 'Stopping...') info(dir, 'Stopping...')
app.state = 'stopping' app.state = 'stopping'
update() update()
app.proc?.kill() app.proc?.kill()
@ -70,16 +70,20 @@ export function updateAppIcon(dir: string, icon: string) {
saveApp(dir, pkg) saveApp(dir, pkg)
} }
const err = (app: string, ...msg: string[]) =>
console.error('🐾', `${app}:`, ...msg)
const getPort = () => NEXT_PORT++ const getPort = () => NEXT_PORT++
const info = (app: App, ...msg: string[]) => { const info = (app: string, ...msg: string[]) =>
console.log('🐾', `[${app.name}]`, ...msg) console.log('🐾', `${app}:`, ...msg)
app.logs?.push({ time: Date.now(), text: msg.join(' ') })
}
const isApp = (dir: string): boolean => const isApp = (dir: string): boolean =>
!loadApp(dir).error !loadApp(dir).error
const log = (app: string, ...msg: string[]) =>
console.log(`<${app}>`, ...msg)
const update = () => _listeners.forEach(cb => cb()) const update = () => _listeners.forEach(cb => cb())
function allAppDirs() { function allAppDirs() {
@ -116,14 +120,19 @@ function loadApp(dir: string): LoadResult {
if (json.scripts?.toes) { if (json.scripts?.toes) {
return { pkg: json } return { pkg: json }
} else { } else {
return { pkg: json, error: 'Missing scripts.toes in package.json' } const error = 'Missing scripts.toes in package.json'
err(dir, error)
return { pkg: json, error }
} }
} catch (e) { } catch (e) {
const error = `Invalid JSON in package.json: ${e instanceof Error ? e.message : String(e)}` const error = `Invalid JSON in package.json: ${e instanceof Error ? e.message : String(e)}`
err(dir, error)
return { pkg: {}, error } return { pkg: {}, error }
} }
} catch (e) { } catch (e) {
return { pkg: {}, error: 'Missing package.json' } const error = 'Missing package.json'
err(dir, error)
return { pkg: {}, error }
} }
} }
@ -143,11 +152,11 @@ async function runApp(dir: string, port: number) {
const cwd = join(APPS_DIR, dir) const cwd = join(APPS_DIR, dir)
const needsInstall = !existsSync(join(cwd, 'node_modules')) const needsInstall = !existsSync(join(cwd, 'node_modules'))
if (needsInstall) info(app, 'Installing dependencies...') if (needsInstall) info(dir, 'Installing dependencies...')
const install = Bun.spawn(['bun', 'install'], { cwd, stdout: 'pipe', stderr: 'pipe' }) const install = Bun.spawn(['bun', 'install'], { cwd, stdout: 'pipe', stderr: 'pipe' })
await install.exited await install.exited
info(app, `Starting on port ${port}...`) info(dir, `Starting on port ${port}...`)
const proc = Bun.spawn(['bun', 'run', 'toes'], { const proc = Bun.spawn(['bun', 'run', 'toes'], {
cwd, cwd,
@ -172,8 +181,9 @@ async function runApp(dir: string, port: number) {
const chunk = decoder.decode(value) const chunk = decoder.decode(value)
const lines = chunk.split('\n').map(l => l.trimEnd()).filter(Boolean) const lines = chunk.split('\n').map(l => l.trimEnd()).filter(Boolean)
for (const text of lines) { for (const text of lines) {
info(app, text) log(dir, text)
app.logs = (app.logs ?? []).slice(-MAX_LOGS) const line: LogLine = { time: Date.now(), text }
app.logs = [...(app.logs ?? []).slice(-(MAX_LOGS - 1)), line]
} }
if (lines.length) update() if (lines.length) update()
} }
@ -185,9 +195,9 @@ async function runApp(dir: string, port: number) {
// Handle process exit // Handle process exit
proc.exited.then(code => { proc.exited.then(code => {
if (code !== 0) if (code !== 0)
app.logs?.push({ time: Date.now(), text: `Exited with code ${code}` }) err(dir, `Exited with code ${code}`)
else else
app.logs?.push({ time: Date.now(), text: 'Stopped' }) info(dir, 'Stopped')
// Reset to stopped state (or invalid if no longer valid) // Reset to stopped state (or invalid if no longer valid)
app.state = isApp(dir) ? 'stopped' : 'invalid' app.state = isApp(dir) ? 'stopped' : 'invalid'