ob
Version:
OB (The Second Terminal Printer) - A lightweight server-client tool for real-time terminal logging and progress display via Unix sockets. Ideal for CLI apps needing dynamic output management.
108 lines (97 loc) • 2.53 kB
JavaScript
/*
.-. .-')
\ ( OO )
.-'),-----. ;-----.\
( OO' .-. '| .-. |
/ | | | || '-' /_)
\_) | |\| || .-. `.
\ | | | || | \ |
`' '-' '| '--' / 2025.07.14
`-----' `------' The second terminal printer
*/
import net from 'net'
import fs from 'fs'
import os from 'os'
import util from 'util'
const SOCKET_PATH = os.platform() === 'win32' ? '\\\\.\\pipe\\log-pipe' : '/tmp/log.sock'
function styleText(format, text) {
const codes = {
red: '\x1b[31m',
bgBlue: '\x1b[44m',
bold: '\x1b[1m',
reset: '\x1b[0m',
}
const styles = Array.isArray(format) ? format : [format]
return styles.map((f) => codes[f]).join('') + text + codes.reset
}
function parseStr(string) {
try {
const arr = JSON.parse(string)
if (!Array.isArray(arr)) {
return 'Error: Invalid string.'
}
return arr
} catch (error) {
return new Error(error).toString()
}
}
function logger(string) {
if (string.startsWith('Error')) {
console.log(styleText('red', string))
return
}
const result = parseStr(string)
if (typeof result == 'string') {
console.log(styleText('red', result))
return
}
const args = result.map((item) =>
util.inspect(item, {
depth: null,
colors: true,
compact: false,
}),
)
console.log(...args)
}
function startLogServer() {
if (os.platform() !== 'win32') {
try {
if (fs.existsSync(SOCKET_PATH)) {
fs.unlinkSync(SOCKET_PATH)
console.log(`Cleaned up existing socket file: ${SOCKET_PATH}`)
}
} catch (err) {
console.error(`Failed to clean up socket file: ${err.message}`)
}
}
const server = net.createServer((socket) => {
socket.on('data', (data) => logger(data.toString()))
socket.on('end', () => {})
})
server.listen(SOCKET_PATH, () => {
console.log(`Log server listening on ${SOCKET_PATH}`)
console.log('Press Ctrl+C to stop.')
})
server.on('error', (err) => {
console.error(`Server error: ${err.message}`)
process.exit(1)
})
process.on('SIGINT', () => {
server.close()
if (os.platform() !== 'win32') {
try {
if (fs.existsSync(SOCKET_PATH)) {
fs.unlinkSync(SOCKET_PATH)
console.log(`Cleaned up socket file: ${SOCKET_PATH}`)
}
} catch (err) {
console.error(`Failed to clean up socket file on exit: ${err.message}`)
}
}
console.log('Log server stopped.')
process.exit()
})
}
startLogServer()