refactor: extract shared runCommand utility and cleanup codebase

- Create shared command utility in ts/utils/command.ts
- Update index.ts, process-manager.ts, system-info.ts to use shared utility
- Remove duplicate runCommand implementations from all files
- Remove legacy Chrome wrapper methods (startChrome, stopChrome, isChromeRunning)
- Consolidate Sway window selectors from 5 to 2
- Remove unused isobuild TypeScript files (mod.ts, deno.json, ts/index.ts)
- Make getStatus() async to properly await system info
- Add disk and system info sections to UI
This commit is contained in:
2026-01-08 16:31:57 +00:00
parent 1435496a1c
commit 3fb8b14e41
9 changed files with 87 additions and 438 deletions

View File

@@ -1,18 +0,0 @@
{
"name": "@ecobridge/eco-os-isobuild",
"version": "0.0.1",
"exports": "./mod.ts",
"tasks": {
"build": "deno run --allow-all mod.ts build",
"clean": "deno run --allow-all mod.ts clean",
"test-qemu": "deno run --allow-all mod.ts test-qemu"
},
"imports": {
"@std/fs": "jsr:@std/fs@^1.0.0",
"@std/path": "jsr:@std/path@^1.0.0",
"@std/async": "jsr:@std/async@^1.0.0"
},
"compilerOptions": {
"strict": true
}
}

View File

@@ -1,70 +0,0 @@
/**
* EcoOS ISO Builder
*
* CLI for building custom Ubuntu ISO with EcoOS daemon
*/
import { build } from './ts/index.ts';
const command = Deno.args[0];
switch (command) {
case 'build':
await build();
break;
case 'clean':
console.log('Cleaning build artifacts...');
try {
await Deno.remove('./output', { recursive: true });
await Deno.remove('./build', { recursive: true });
console.log('Clean complete.');
} catch {
console.log('Nothing to clean.');
}
break;
case 'test-qemu':
console.log('Testing ISO in QEMU...');
const isoPath = './output/ecoos.iso';
try {
await Deno.stat(isoPath);
} catch {
console.error('ISO not found. Run "deno task build" first.');
Deno.exit(1);
}
const qemu = new Deno.Command('qemu-system-x86_64', {
args: [
'-enable-kvm',
'-m', '4G',
'-cpu', 'host',
'-smp', '2',
'-cdrom', isoPath,
'-boot', 'd',
'-vga', 'virtio',
'-display', 'gtk',
'-device', 'usb-tablet',
'-nic', 'user,hostfwd=tcp::3006-:3006',
],
stdout: 'inherit',
stderr: 'inherit',
});
await qemu.spawn().status;
break;
default:
console.log(`
EcoOS ISO Builder
Usage:
deno task build Build the ISO
deno task clean Clean build artifacts
deno task test-qemu Test ISO in QEMU
Requirements:
- Ubuntu 24.04+ host
- live-build, debootstrap, xorriso installed
- sudo access for live-build
`);
}

View File

@@ -1,254 +0,0 @@
/**
* ISO Build Orchestration
*
* Builds the EcoOS custom Ubuntu ISO
*/
import * as path from '@std/path';
const ROOT = path.dirname(path.dirname(path.fromFileUrl(import.meta.url)));
const DAEMON_DIR = path.join(path.dirname(ROOT), 'ecoos_daemon');
const BUILD_DIR = path.join(ROOT, 'build');
const OUTPUT_DIR = path.join(ROOT, 'output');
const CONFIG_DIR = path.join(ROOT, 'config');
export async function build(): Promise<void> {
console.log('=== EcoOS ISO Builder ===\n');
// Step 1: Check prerequisites
console.log('[1/7] Checking prerequisites...');
await checkPrerequisites();
// Step 2: Bundle the daemon
console.log('[2/7] Bundling ecoos_daemon...');
await bundleDaemon();
// Step 3: Prepare build directory
console.log('[3/7] Preparing build directory...');
await prepareBuildDir();
// Step 4: Configure live-build
console.log('[4/7] Configuring live-build...');
await configureLiveBuild();
// Step 5: Copy package lists
console.log('[5/7] Copying package lists...');
await copyPackageLists();
// Step 6: Copy daemon and configs to chroot includes
console.log('[6/7] Preparing chroot includes...');
await prepareChrootIncludes();
// Step 7: Build ISO
console.log('[7/7] Building ISO (this may take a while)...');
await buildIso();
console.log('\n=== Build Complete ===');
console.log(`ISO: ${OUTPUT_DIR}/ecoos.iso`);
}
async function checkPrerequisites(): Promise<void> {
const commands = ['lb', 'debootstrap', 'xorriso'];
for (const cmd of commands) {
try {
const result = await run('which', [cmd]);
if (!result.success) {
throw new Error(`${cmd} not found`);
}
} catch {
console.error(`Missing prerequisite: ${cmd}`);
console.error('Install with: sudo apt install live-build debootstrap xorriso');
Deno.exit(1);
}
}
console.log(' All prerequisites found.');
}
async function bundleDaemon(): Promise<void> {
// Compile the daemon to a single executable
const bundleDir = path.join(DAEMON_DIR, 'bundle');
await Deno.mkdir(bundleDir, { recursive: true });
const result = await run('deno', [
'compile',
'--allow-all',
'--output', path.join(bundleDir, 'eco-daemon'),
path.join(DAEMON_DIR, 'mod.ts'),
], { cwd: DAEMON_DIR });
if (!result.success) {
console.error('Failed to bundle daemon');
console.error(result.stderr);
Deno.exit(1);
}
console.log(' Daemon bundled successfully.');
}
async function prepareBuildDir(): Promise<void> {
// Clean and create build directory
try {
await Deno.remove(BUILD_DIR, { recursive: true });
} catch {
// Directory may not exist
}
await Deno.mkdir(BUILD_DIR, { recursive: true });
console.log(' Build directory prepared.');
}
async function configureLiveBuild(): Promise<void> {
// Initialize live-build config
const result = await run('lb', ['config'], { cwd: BUILD_DIR });
if (!result.success) {
console.error('Failed to initialize live-build');
console.error(result.stderr);
Deno.exit(1);
}
// Copy our auto/config
const autoDir = path.join(BUILD_DIR, 'auto');
await Deno.mkdir(autoDir, { recursive: true });
const configSrc = path.join(CONFIG_DIR, 'live-build', 'auto', 'config');
const configDst = path.join(autoDir, 'config');
await Deno.copyFile(configSrc, configDst);
await Deno.chmod(configDst, 0o755);
// Re-run lb config with our settings
const result2 = await run('lb', ['config'], { cwd: BUILD_DIR });
if (!result2.success) {
console.error('Failed to configure live-build');
Deno.exit(1);
}
console.log(' live-build configured.');
}
async function copyPackageLists(): Promise<void> {
const srcDir = path.join(CONFIG_DIR, 'live-build', 'package-lists');
const dstDir = path.join(BUILD_DIR, 'config', 'package-lists');
await Deno.mkdir(dstDir, { recursive: true });
for await (const entry of Deno.readDir(srcDir)) {
if (entry.isFile && entry.name.endsWith('.list.chroot')) {
const src = path.join(srcDir, entry.name);
const dst = path.join(dstDir, entry.name);
await Deno.copyFile(src, dst);
}
}
console.log(' Package lists copied.');
}
async function prepareChrootIncludes(): Promise<void> {
const includesDir = path.join(BUILD_DIR, 'config', 'includes.chroot');
// Create directory structure
await Deno.mkdir(path.join(includesDir, 'opt', 'eco', 'bin'), { recursive: true });
await Deno.mkdir(path.join(includesDir, 'opt', 'eco', 'daemon'), { recursive: true });
await Deno.mkdir(path.join(includesDir, 'etc', 'systemd', 'system'), { recursive: true });
// Copy bundled daemon
const daemonSrc = path.join(DAEMON_DIR, 'bundle', 'eco-daemon');
const daemonDst = path.join(includesDir, 'opt', 'eco', 'bin', 'eco-daemon');
try {
await Deno.copyFile(daemonSrc, daemonDst);
await Deno.chmod(daemonDst, 0o755);
} catch (e) {
console.error('Failed to copy daemon bundle:', e);
// Fall back to copying source files
console.log(' Copying daemon source files instead...');
await copyDir(path.join(DAEMON_DIR, 'ts'), path.join(includesDir, 'opt', 'eco', 'daemon'));
await Deno.copyFile(
path.join(DAEMON_DIR, 'mod.ts'),
path.join(includesDir, 'opt', 'eco', 'daemon', 'mod.ts')
);
}
// Copy systemd service
const serviceSrc = path.join(CONFIG_DIR, 'systemd', 'eco-daemon.service');
const serviceDst = path.join(includesDir, 'etc', 'systemd', 'system', 'eco-daemon.service');
await Deno.copyFile(serviceSrc, serviceDst);
// Copy autoinstall config
const autoinstallDir = path.join(BUILD_DIR, 'config', 'includes.binary');
await Deno.mkdir(autoinstallDir, { recursive: true });
const userDataSrc = path.join(CONFIG_DIR, 'autoinstall', 'user-data');
const userDataDst = path.join(autoinstallDir, 'autoinstall', 'user-data');
await Deno.mkdir(path.dirname(userDataDst), { recursive: true });
await Deno.copyFile(userDataSrc, userDataDst);
// Create empty meta-data file (required for cloud-init)
await Deno.writeTextFile(path.join(path.dirname(userDataDst), 'meta-data'), '');
console.log(' Chroot includes prepared.');
}
async function buildIso(): Promise<void> {
const result = await run('sudo', ['lb', 'build'], {
cwd: BUILD_DIR,
stdout: 'inherit',
stderr: 'inherit',
});
if (!result.success) {
console.error('ISO build failed');
Deno.exit(1);
}
// Move ISO to output directory
await Deno.mkdir(OUTPUT_DIR, { recursive: true });
for await (const entry of Deno.readDir(BUILD_DIR)) {
if (entry.isFile && entry.name.endsWith('.iso')) {
const src = path.join(BUILD_DIR, entry.name);
const dst = path.join(OUTPUT_DIR, 'ecoos.iso');
await Deno.rename(src, dst);
break;
}
}
}
async function run(
cmd: string,
args: string[],
options: { cwd?: string; stdout?: 'piped' | 'inherit'; stderr?: 'piped' | 'inherit' } = {}
): Promise<{ success: boolean; stdout: string; stderr: string }> {
const command = new Deno.Command(cmd, {
args,
cwd: options.cwd,
stdout: options.stdout ?? 'piped',
stderr: options.stderr ?? 'piped',
});
const result = await command.output();
return {
success: result.success,
stdout: new TextDecoder().decode(result.stdout),
stderr: new TextDecoder().decode(result.stderr),
};
}
async function copyDir(src: string, dst: string): Promise<void> {
await Deno.mkdir(dst, { recursive: true });
for await (const entry of Deno.readDir(src)) {
const srcPath = path.join(src, entry.name);
const dstPath = path.join(dst, entry.name);
if (entry.isDirectory) {
await copyDir(srcPath, dstPath);
} else {
await Deno.copyFile(srcPath, dstPath);
}
}
}