feat: mosaic gateway CLI daemon management + admin token auth (#369)
Some checks failed
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline failed

This commit was merged in pull request #369.
This commit is contained in:
2026-04-04 18:03:12 +00:00
parent 202e375f41
commit 39ccba95d0
16 changed files with 1325 additions and 188 deletions

View File

@@ -0,0 +1,115 @@
import { getDaemonPid, readMeta, LOG_FILE, GATEWAY_HOME } from './daemon.js';
interface GatewayOpts {
host: string;
port: number;
token?: string;
}
interface ServiceStatus {
name: string;
status: string;
latency?: string;
}
interface AdminHealth {
status: string;
services: {
database: { status: string; latencyMs: number };
cache: { status: string; latencyMs: number };
};
agentPool?: { active: number };
providers?: Array<{ name: string; available: boolean; models: number }>;
}
export async function runStatus(opts: GatewayOpts): Promise<void> {
const meta = readMeta();
const pid = getDaemonPid();
console.log('Mosaic Gateway Status');
console.log('─────────────────────');
// Daemon status
if (pid !== null) {
console.log(` Status: running (PID ${pid.toString()})`);
} else {
console.log(' Status: stopped');
}
// Version
console.log(` Version: ${meta?.version ?? 'unknown'}`);
// Endpoint
const host = opts.host;
const port = opts.port;
console.log(` Endpoint: http://${host}:${port.toString()}`);
console.log(` Config: ${GATEWAY_HOME}`);
console.log(` Logs: ${LOG_FILE}`);
if (pid === null) return;
// Health check
try {
const healthRes = await fetch(`http://${host}:${port.toString()}/health`);
if (!healthRes.ok) {
console.log('\n Health: unreachable');
return;
}
} catch {
console.log('\n Health: unreachable');
return;
}
// Admin health (requires token)
const token = opts.token ?? meta?.adminToken;
if (!token) {
console.log(
'\n (No admin token — run `mosaic gateway config` to set one for detailed status)',
);
return;
}
try {
const res = await fetch(`http://${host}:${port.toString()}/api/admin/health`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) {
console.log('\n Admin health: unauthorized or unavailable');
return;
}
const health = (await res.json()) as AdminHealth;
console.log('\n Services:');
const services: ServiceStatus[] = [
{
name: 'Database',
status: health.services.database.status,
latency: `${health.services.database.latencyMs.toString()}ms`,
},
{
name: 'Cache',
status: health.services.cache.status,
latency: `${health.services.cache.latencyMs.toString()}ms`,
},
];
for (const svc of services) {
const latStr = svc.latency ? ` (${svc.latency})` : '';
console.log(` ${svc.name}:${' '.repeat(10 - svc.name.length)}${svc.status}${latStr}`);
}
if (health.providers && health.providers.length > 0) {
const available = health.providers.filter((p) => p.available);
const names = available.map((p) => p.name).join(', ');
console.log(`\n Providers: ${available.length.toString()} active (${names})`);
}
if (health.agentPool) {
console.log(` Sessions: ${health.agentPool.active.toString()} active`);
}
} catch {
console.log('\n Admin health: connection error');
}
}