feat(daemon): add serial console reader and UI tab for serial logs; add version propagation and CI/release workflows

This commit is contained in:
2026-01-09 14:34:51 +00:00
parent 5234411c9d
commit 6dd6ead1c9
8 changed files with 262 additions and 7 deletions

View File

@@ -8,6 +8,7 @@ import { ProcessManager } from './process-manager.ts';
import { SystemInfo } from './system-info.ts';
import { UIServer } from '../ui/server.ts';
import { runCommand } from '../utils/command.ts';
import { VERSION } from '../version.ts';
export interface DaemonConfig {
uiPort: number;
@@ -29,6 +30,7 @@ export class EcoDaemon {
private systemInfo: SystemInfo;
private uiServer: UIServer;
private logs: string[] = [];
private serialLogs: string[] = [];
private swayStatus: ServiceStatus = { state: 'stopped' };
private chromiumStatus: ServiceStatus = { state: 'stopped' };
private manualRestartUntil: number = 0; // Timestamp until which auto-restart is disabled
@@ -62,15 +64,21 @@ export class EcoDaemon {
return [...this.logs];
}
getSerialLogs(): string[] {
return [...this.serialLogs];
}
async getStatus(): Promise<Record<string, unknown>> {
const systemInfo = await this.systemInfo.getInfo();
return {
version: VERSION,
sway: this.swayStatus.state === 'running',
swayStatus: this.swayStatus,
chromium: this.chromiumStatus.state === 'running',
chromiumStatus: this.chromiumStatus,
systemInfo,
logs: this.logs.slice(-50),
serialLogs: this.serialLogs.slice(-50),
};
}
@@ -131,6 +139,9 @@ export class EcoDaemon {
await this.uiServer.start();
this.log('Management UI started successfully');
// Start serial console reader in the background
this.startSerialReader();
// Start the Sway/Chromium initialization in the background
// This allows the UI server to remain responsive even if Sway fails
this.startServicesInBackground();
@@ -302,6 +313,31 @@ export class EcoDaemon {
return parseInt(result.stdout.trim(), 10);
}
private startSerialReader(): void {
(async () => {
try {
const file = await Deno.open('/dev/ttyS0', { read: true });
this.log('Serial console reader started on /dev/ttyS0');
const reader = file.readable.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
const text = decoder.decode(value);
for (const line of text.split('\n').filter((l) => l.trim())) {
this.serialLogs.push(line);
if (this.serialLogs.length > 1000) {
this.serialLogs = this.serialLogs.slice(-1000);
}
}
}
} catch (error) {
this.log(`Serial reader not available: ${error}`);
}
})();
}
private async runForever(): Promise<void> {
// Monitor processes and restart if needed
while (true) {