update
This commit is contained in:
@@ -31,6 +31,7 @@ export class EcoDaemon {
|
|||||||
private logs: string[] = [];
|
private logs: string[] = [];
|
||||||
private swayStatus: ServiceStatus = { state: 'stopped' };
|
private swayStatus: ServiceStatus = { state: 'stopped' };
|
||||||
private chromiumStatus: ServiceStatus = { state: 'stopped' };
|
private chromiumStatus: ServiceStatus = { state: 'stopped' };
|
||||||
|
private manualRestartUntil: number = 0; // Timestamp until which auto-restart is disabled
|
||||||
|
|
||||||
constructor(config?: Partial<DaemonConfig>) {
|
constructor(config?: Partial<DaemonConfig>) {
|
||||||
this.config = {
|
this.config = {
|
||||||
@@ -94,6 +95,9 @@ export class EcoDaemon {
|
|||||||
return { success: false, message: 'Cannot restart Chromium: Sway is not running' };
|
return { success: false, message: 'Cannot restart Chromium: Sway is not running' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable auto-restart for 15 seconds to prevent restart loop
|
||||||
|
this.manualRestartUntil = Date.now() + 15000;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Stop existing Chromium
|
// Stop existing Chromium
|
||||||
await this.processManager.stopBrowser();
|
await this.processManager.stopBrowser();
|
||||||
@@ -313,8 +317,9 @@ export class EcoDaemon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If Sway is running but Chromium died, restart Chromium
|
// If Sway is running but Chromium died, restart Chromium
|
||||||
|
// Skip if manual restart is in progress (prevents restart loop)
|
||||||
if (this.swayStatus.state === 'running' && this.chromiumStatus.state === 'running'
|
if (this.swayStatus.state === 'running' && this.chromiumStatus.state === 'running'
|
||||||
&& !this.processManager.isBrowserRunning()) {
|
&& !(await this.processManager.isBrowserRunning()) && Date.now() > this.manualRestartUntil) {
|
||||||
this.log('Chromium process died, attempting restart...');
|
this.log('Chromium process died, attempting restart...');
|
||||||
this.chromiumStatus = { state: 'starting', lastAttempt: new Date().toISOString() };
|
this.chromiumStatus = { state: 'starting', lastAttempt: new Date().toISOString() };
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -193,33 +193,23 @@ for_window [app_id="chromium-browser"] fullscreen enable
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Chromium arguments for kiosk mode on Wayland
|
// Chromium arguments for kiosk mode on Wayland
|
||||||
|
// Hardware acceleration is enabled where available but falls back gracefully
|
||||||
const browserArgs = [
|
const browserArgs = [
|
||||||
|
// Wayland/Ozone configuration
|
||||||
'--ozone-platform=wayland',
|
'--ozone-platform=wayland',
|
||||||
'--enable-features=UseOzonePlatform',
|
'--enable-features=UseOzonePlatform',
|
||||||
|
// Kiosk mode settings
|
||||||
'--kiosk',
|
'--kiosk',
|
||||||
'--no-first-run',
|
'--no-first-run',
|
||||||
'--disable-infobars',
|
'--disable-infobars',
|
||||||
'--disable-session-crashed-bubble',
|
'--disable-session-crashed-bubble',
|
||||||
'--disable-restore-session-state',
|
'--disable-restore-session-state',
|
||||||
|
'--noerrdialogs',
|
||||||
|
// Disable unnecessary features for kiosk
|
||||||
'--disable-background-networking',
|
'--disable-background-networking',
|
||||||
'--disable-sync',
|
'--disable-sync',
|
||||||
'--disable-translate',
|
'--disable-translate',
|
||||||
'--noerrdialogs',
|
'--disable-features=TranslateUI',
|
||||||
// Required for VM/headless/sandboxed environments
|
|
||||||
'--no-sandbox',
|
|
||||||
'--disable-setuid-sandbox',
|
|
||||||
'--disable-dev-shm-usage',
|
|
||||||
// GPU/rendering flags for VM environments
|
|
||||||
'--disable-gpu',
|
|
||||||
'--disable-gpu-compositing',
|
|
||||||
'--disable-gpu-sandbox',
|
|
||||||
'--disable-software-rasterizer',
|
|
||||||
'--disable-accelerated-2d-canvas',
|
|
||||||
'--disable-accelerated-video-decode',
|
|
||||||
'--use-gl=swiftshader',
|
|
||||||
'--in-process-gpu',
|
|
||||||
// Disable features that may cause issues in kiosk mode
|
|
||||||
'--disable-features=TranslateUI,VizDisplayCompositor',
|
|
||||||
'--disable-hang-monitor',
|
'--disable-hang-monitor',
|
||||||
'--disable-breakpad',
|
'--disable-breakpad',
|
||||||
'--disable-component-update',
|
'--disable-component-update',
|
||||||
@@ -281,8 +271,15 @@ for_window [app_id="chromium-browser"] fullscreen enable
|
|||||||
return this.swayProcess !== null;
|
return this.swayProcess !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
isBrowserRunning(): boolean {
|
async isBrowserRunning(): Promise<boolean> {
|
||||||
return this.browserProcess !== null;
|
// Check if any chromium process is running (Chromium forks, so we can't just track the parent)
|
||||||
|
try {
|
||||||
|
const cmd = new Deno.Command('pgrep', { args: ['-f', 'chromium'], stdout: 'null', stderr: 'null' });
|
||||||
|
const result = await cmd.output();
|
||||||
|
return result.success;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async stopSway(): Promise<void> {
|
async stopSway(): Promise<void> {
|
||||||
@@ -335,12 +332,12 @@ for_window [app_id="chromium-browser"] fullscreen enable
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Monitor process exit
|
// Monitor process exit - only nullify if still the same process (prevents race condition on restart)
|
||||||
process.status.then((status) => {
|
process.status.then((status) => {
|
||||||
console.log(`[${name}] Process exited with code ${status.code}`);
|
console.log(`[${name}] Process exited with code ${status.code}`);
|
||||||
if (name === 'sway') {
|
if (name === 'sway' && this.swayProcess === process) {
|
||||||
this.swayProcess = null;
|
this.swayProcess = null;
|
||||||
} else if (name === 'chromium') {
|
} else if (name === 'chromium' && this.browserProcess === process) {
|
||||||
this.browserProcess = null;
|
this.browserProcess = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { EcoDaemon } from '../daemon/index.ts';
|
import type { EcoDaemon } from '../daemon/index.ts';
|
||||||
|
import { VERSION } from '../version.ts';
|
||||||
|
|
||||||
export class UIServer {
|
export class UIServer {
|
||||||
private port: number;
|
private port: number;
|
||||||
@@ -137,7 +138,19 @@ export class UIServer {
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
.container { max-width: 1200px; margin: 0 auto; }
|
.container { max-width: 1200px; margin: 0 auto; }
|
||||||
h1 { font-size: 24px; margin-bottom: 20px; }
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
h1 { font-size: 24px; margin: 0; }
|
||||||
|
.clock {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
.grid {
|
.grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
@@ -240,7 +253,10 @@ export class UIServer {
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>EcoOS Management</h1>
|
<div class="header">
|
||||||
|
<h1>EcoOS Management <span style="font-size: 12px; color: var(--text-dim); font-weight: normal;">v${VERSION}</span></h1>
|
||||||
|
<div class="clock" id="clock"></div>
|
||||||
|
</div>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>Services</h2>
|
<h2>Services</h2>
|
||||||
@@ -528,6 +544,24 @@ export class UIServer {
|
|||||||
updateStatus(JSON.parse(e.data));
|
updateStatus(JSON.parse(e.data));
|
||||||
} catch {}
|
} catch {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Clock update
|
||||||
|
function updateClock() {
|
||||||
|
const now = new Date();
|
||||||
|
const options = {
|
||||||
|
weekday: 'short',
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
};
|
||||||
|
document.getElementById('clock').textContent = now.toLocaleString('en-US', options);
|
||||||
|
}
|
||||||
|
updateClock();
|
||||||
|
setInterval(updateClock, 1000);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
|
|||||||
1
ecoos_daemon/ts/version.ts
Normal file
1
ecoos_daemon/ts/version.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const VERSION = "0.1.1";
|
||||||
Binary file not shown.
@@ -48,28 +48,28 @@ else
|
|||||||
echo "KVM not available, using software emulation (slower)"
|
echo "KVM not available, using software emulation (slower)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Start QEMU headless with VNC and serial console
|
# Start QEMU with VirtIO-GPU (VirGL OpenGL acceleration) and serial console
|
||||||
> "$SERIAL_LOG" # Clear old log
|
> "$SERIAL_LOG" # Clear old log
|
||||||
qemu-system-x86_64 \
|
qemu-system-x86_64 \
|
||||||
$KVM_OPTS \
|
$KVM_OPTS \
|
||||||
-m 4G \
|
-m 4G \
|
||||||
-smp 2 \
|
-smp 4 \
|
||||||
-bios /usr/share/qemu/OVMF.fd \
|
-bios /usr/share/qemu/OVMF.fd \
|
||||||
-drive file="$ISO_PATH",media=cdrom \
|
-drive file="$ISO_PATH",media=cdrom \
|
||||||
-drive file="$DISK_PATH",format=qcow2,if=virtio \
|
-drive file="$DISK_PATH",format=qcow2,if=virtio \
|
||||||
-vga qxl \
|
-device virtio-vga \
|
||||||
-display none \
|
-display none \
|
||||||
-vnc :0 \
|
-spice port=5930,disable-ticketing=on \
|
||||||
-serial unix:"$SERIAL_SOCK",server,nowait \
|
-serial unix:"$SERIAL_SOCK",server,nowait \
|
||||||
-monitor unix:"$MONITOR_SOCK",server,nowait \
|
-monitor unix:"$MONITOR_SOCK",server,nowait \
|
||||||
-nic user,model=virtio-net-pci,hostfwd=tcp::3006-:3006,hostfwd=tcp::2222-:22 \
|
-nic user,model=virtio-net-pci,hostfwd=tcp::3006-:3006,hostfwd=tcp::2222-:22 \
|
||||||
-daemonize \
|
-pidfile "$PID_FILE" &
|
||||||
-pidfile "$PID_FILE"
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
sleep 1
|
||||||
echo "=== EcoOS Test VM Started ==="
|
echo "=== EcoOS Test VM Started ==="
|
||||||
echo "PID: $(cat $PID_FILE)"
|
echo "PID: $(cat $PID_FILE 2>/dev/null || echo 'running')"
|
||||||
echo "VNC: localhost:5900"
|
echo "SPICE: spicy -h localhost -p 5930"
|
||||||
echo "Serial Log: $SERIAL_LOG"
|
echo "Serial Log: $SERIAL_LOG"
|
||||||
echo "Management UI: http://localhost:3006"
|
echo "Management UI: http://localhost:3006"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "@ecobridge/eco-os",
|
"name": "@ecobridge/eco-os",
|
||||||
|
"version": "0.1.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm run daemon:bundle && cp ecoos_daemon/bundle/eco-daemon isobuild/config/includes.chroot/opt/eco/bin/ && mkdir -p .nogit/iso && docker build --no-cache -t ecoos-builder -f isobuild/Dockerfile . && docker run --rm --privileged -v $(pwd)/.nogit/iso:/output ecoos-builder",
|
"build": "npm version patch --no-git-tag-version && node -e \"const v=require('./package.json').version; require('fs').writeFileSync('ecoos_daemon/ts/version.ts', 'export const VERSION = \\\"'+v+'\\\";\\n');\" && pnpm run daemon:bundle && cp ecoos_daemon/bundle/eco-daemon isobuild/config/includes.chroot/opt/eco/bin/ && mkdir -p .nogit/iso && docker build --no-cache -t ecoos-builder -f isobuild/Dockerfile . && docker run --rm --privileged -v $(pwd)/.nogit/iso:/output ecoos-builder",
|
||||||
"daemon:dev": "cd ecoos_daemon && deno run --allow-all --watch mod.ts",
|
"daemon:dev": "cd ecoos_daemon && deno run --allow-all --watch mod.ts",
|
||||||
"daemon:start": "cd ecoos_daemon && deno run --allow-all mod.ts",
|
"daemon:start": "cd ecoos_daemon && deno run --allow-all mod.ts",
|
||||||
"daemon:bundle": "cd ecoos_daemon && deno compile --allow-all --output bundle/eco-daemon mod.ts",
|
"daemon:bundle": "cd ecoos_daemon && deno compile --allow-all --output bundle/eco-daemon mod.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user