#!/usr/bin/env node
/**
* Mini "echo service" demo for hyper-svc.
*
* Demonstrates the daemon + client lifecycle in two modes:
*
* node examples/cli.js daemon Start the echo daemon (UDS+HTTP)
* node examples/cli.js call <method> [json] Connect to the daemon and call a method
* node examples/cli.js demo Run an in-process round-trip end-to-end
*
* The daemon registers two methods:
* - "echo" → returns whatever you send
* - "uppercase" → returns the upper-cased string
* - "info" → returns service metadata (name, handlers, transports)
*
* Storage: HYPER_SVC_STORE env var (default: ./.hyper-svc-cli)
* Socket: HYPER_SVC_SOCK env var (default: ./.hyper-svc-cli/svc.sock)
*
* NOTE: storage path is provided for parity with other modules' examples; the
* echo demo doesn't actually use it. Real consumers (pear-git-v3) would pass
* a corestore for the swarm transport to replicate.
*/
import { Service, Client } from '../index.js'
import { mkdir } from '#node:fs/promises'
import { join, dirname } from '#node:path'
import process from '#node:process'
const storagePath = process.env.HYPER_SVC_STORE || './.hyper-svc-cli'
const sockPath = process.env.HYPER_SVC_SOCK || join(storagePath, 'svc.sock')
const httpPort = parseInt(process.env.HYPER_SVC_HTTP || '4090')
const args = process.argv.slice(2)
const cmd = args[0]
function buildService () {
const svc = new Service({
name: 'echo',
uds: sockPath,
http: { port: httpPort }
})
svc.handle('echo', async (ctx, params) => {
return { from: ctx.transport, params }
})
svc.handle('uppercase', async (ctx, params) => {
return String(params.text || '').toUpperCase()
})
svc.handle('info', async (ctx) => {
return {
name: svc.name,
transport: ctx.transport,
identity: ctx.identity,
handlers: svc.listHandlers(),
mounts: svc.listMounts()
}
})
return svc
}
async function main () {
if (cmd === 'daemon') {
await mkdir(dirname(sockPath), { recursive: true })
const svc = buildService()
await svc.listen()
console.log('hyper-svc echo daemon')
console.log(' uds: %s', sockPath)
console.log(' http: http://127.0.0.1:%d', svc.http.port)
console.log(' handlers: %s', svc.listHandlers().join(', '))
console.log(' (Ctrl-C to stop)')
process.on('SIGINT', async () => {
console.log('\nshutting down...')
try { await svc.close() } catch {}
process.exit(0)
})
return
}
if (cmd === 'call') {
const method = args[1]
if (!method) { console.error('Usage: cli.js call <method> [json-params]'); process.exit(1) }
let params = {}
if (args[2]) {
try { params = JSON.parse(args[2]) } catch (e) {
console.error('bad JSON params:', e.message)
process.exit(1)
}
}
const client = await Client.discover({
uds: sockPath,
http: `http://127.0.0.1:${httpPort}`
})
console.log('connected via', client.kind)
try {
const result = await client.call(method, params)
console.log(JSON.stringify(result, null, 2))
} finally {
await client.close()
}
return
}
if (cmd === 'demo') {
// In-process: spin up a daemon, connect to it, make calls, shut it down
await mkdir(dirname(sockPath), { recursive: true })
const svc = buildService()
await svc.listen()
console.log('--- hyper-svc demo ---\n')
console.log('Started service "echo"')
console.log(' uds: %s', sockPath)
console.log(' http: http://127.0.0.1:%d\n', svc.http.port)
console.log('1. Discover (prefers UDS)')
const client = await Client.discover({
uds: sockPath,
http: `http://127.0.0.1:${svc.http.port}`
})
console.log(' → connected via', client.kind, '\n')
console.log('2. Call echo({ hello: "world" })')
const a = await client.call('echo', { hello: 'world' })
console.log(' →', JSON.stringify(a), '\n')
console.log('3. Call uppercase({ text: "hyper-svc" })')
const b = await client.call('uppercase', { text: 'hyper-svc' })
console.log(' →', JSON.stringify(b), '\n')
console.log('4. Call info()')
const c = await client.call('info', {})
console.log(' →', JSON.stringify(c, null, 2), '\n')
console.log('5. Force HTTP transport')
await client.close()
const httpClient = await Client.discover({
http: `http://127.0.0.1:${svc.http.port}`,
prefer: 'http'
})
console.log(' → connected via', httpClient.kind)
const d = await httpClient.call('echo', { from: 'http' })
console.log(' →', JSON.stringify(d), '\n')
await httpClient.close()
await svc.close()
console.log('shutdown complete.')
return
}
console.log('hyper-svc echo service example')
console.log(' daemon Start the daemon (UDS + HTTP)')
console.log(' call <method> [json-params] Call a method on a running daemon')
console.log(' demo Run an in-process round-trip')
console.log('')
console.log('Storage: HYPER_SVC_STORE env var (default: ./.hyper-svc-cli)')
console.log('Socket: HYPER_SVC_SOCK env var (default: <storage>/svc.sock)')
console.log('HTTP: HYPER_SVC_HTTP env var (default: 4090)')
console.log('')
console.log('Note: this CLI does NOT share state with examples/web.js — they use')
console.log('different storage paths because the corestore single-writer lock applies')
console.log('once you start mounting real modules.')
}
main().catch((err) => { console.error(err.message); process.exit(1) })