feat: initial SleepGuard implementation
Wake-on-demand proxy + agent system with SvelteKit dashboard. Monorepo: shared types, proxy (Hono + http-proxy), agent (monitors + locks), web (SvelteKit SPA). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
5
packages/shared/src/index.ts
Normal file
5
packages/shared/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './types/common.js';
|
||||
export * from './types/agent.js';
|
||||
export * from './types/proxy.js';
|
||||
export * from './types/upsnap.js';
|
||||
export * from './utils/env.js';
|
||||
60
packages/shared/src/types/agent.ts
Normal file
60
packages/shared/src/types/agent.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
export interface CpuStatus {
|
||||
usagePercent: number;
|
||||
loadAvg: [number, number, number];
|
||||
cores: number;
|
||||
}
|
||||
|
||||
export interface GpuStatus {
|
||||
available: boolean;
|
||||
usagePercent: number;
|
||||
memoryUsedMB: number;
|
||||
memoryTotalMB: number;
|
||||
temperature: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface MemoryStatus {
|
||||
usedPercent: number;
|
||||
usedMB: number;
|
||||
totalMB: number;
|
||||
}
|
||||
|
||||
export interface DiskIoStatus {
|
||||
active: boolean;
|
||||
readKBps: number;
|
||||
writeKBps: number;
|
||||
}
|
||||
|
||||
export interface ProcessInfo {
|
||||
name: string;
|
||||
running: boolean;
|
||||
pids: number[];
|
||||
}
|
||||
|
||||
export interface Lock {
|
||||
name: string;
|
||||
reason?: string;
|
||||
createdAt: string;
|
||||
expiresAt?: string;
|
||||
}
|
||||
|
||||
export interface CreateLockRequest {
|
||||
name: string;
|
||||
ttlSeconds?: number;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface AgentStatus {
|
||||
cpu: CpuStatus;
|
||||
gpu: GpuStatus;
|
||||
memory: MemoryStatus;
|
||||
diskIo: DiskIoStatus;
|
||||
processes: ProcessInfo[];
|
||||
locks: Lock[];
|
||||
uptime: number;
|
||||
}
|
||||
|
||||
export interface CanShutdownResponse {
|
||||
canShutdown: boolean;
|
||||
reasons: string[];
|
||||
}
|
||||
20
packages/shared/src/types/common.ts
Normal file
20
packages/shared/src/types/common.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export enum MachineState {
|
||||
OFFLINE = 'OFFLINE',
|
||||
WAKING = 'WAKING',
|
||||
ONLINE = 'ONLINE',
|
||||
IDLE_CHECK = 'IDLE_CHECK',
|
||||
SHUTTING_DOWN = 'SHUTTING_DOWN',
|
||||
}
|
||||
|
||||
export interface ServiceConfig {
|
||||
name: string;
|
||||
host: string;
|
||||
target: string;
|
||||
}
|
||||
|
||||
export interface LogEntry {
|
||||
timestamp: string;
|
||||
level: 'info' | 'warn' | 'error';
|
||||
message: string;
|
||||
details?: string;
|
||||
}
|
||||
41
packages/shared/src/types/proxy.ts
Normal file
41
packages/shared/src/types/proxy.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { MachineState, ServiceConfig, LogEntry } from './common.js';
|
||||
import type { AgentStatus, Lock } from './agent.js';
|
||||
|
||||
export interface ProxyConfig {
|
||||
idleTimeoutMinutes: number;
|
||||
healthCheckIntervalSeconds: number;
|
||||
wakingTimeoutSeconds: number;
|
||||
}
|
||||
|
||||
export interface ServiceHealth {
|
||||
name: string;
|
||||
host: string;
|
||||
target: string;
|
||||
healthy: boolean;
|
||||
lastCheck: string | null;
|
||||
}
|
||||
|
||||
export interface ProxyStatus {
|
||||
state: MachineState;
|
||||
services: ServiceHealth[];
|
||||
config: ProxyConfig;
|
||||
idleTimer: {
|
||||
lastActivity: string;
|
||||
remainingSeconds: number;
|
||||
} | null;
|
||||
agent: AgentStatus | null;
|
||||
locks: Lock[];
|
||||
logs: LogEntry[];
|
||||
}
|
||||
|
||||
export type WsMessageType =
|
||||
| 'state_change'
|
||||
| 'status_update'
|
||||
| 'service_health'
|
||||
| 'log';
|
||||
|
||||
export interface WsMessage {
|
||||
type: WsMessageType;
|
||||
data: unknown;
|
||||
timestamp: string;
|
||||
}
|
||||
18
packages/shared/src/types/upsnap.ts
Normal file
18
packages/shared/src/types/upsnap.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export interface UpSnapAuthResponse {
|
||||
token: string;
|
||||
record: {
|
||||
id: string;
|
||||
email: string;
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface UpSnapDevice {
|
||||
id: string;
|
||||
name: string;
|
||||
ip: string;
|
||||
mac: string;
|
||||
status: string;
|
||||
collectionId: string;
|
||||
collectionName: string;
|
||||
}
|
||||
27
packages/shared/src/utils/env.ts
Normal file
27
packages/shared/src/utils/env.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export function requireEnv(name: string): string {
|
||||
const value = process.env[name];
|
||||
if (!value) {
|
||||
throw new Error(`Missing required environment variable: ${name}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function optionalEnv(name: string, defaultValue: string): string {
|
||||
return process.env[name] ?? defaultValue;
|
||||
}
|
||||
|
||||
export function intEnv(name: string, defaultValue: number): number {
|
||||
const raw = process.env[name];
|
||||
if (!raw) return defaultValue;
|
||||
const parsed = parseInt(raw, 10);
|
||||
if (isNaN(parsed)) {
|
||||
throw new Error(`Environment variable ${name} must be a number, got: ${raw}`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function boolEnv(name: string, defaultValue: boolean): boolean {
|
||||
const raw = process.env[name];
|
||||
if (!raw) return defaultValue;
|
||||
return raw === 'true' || raw === '1';
|
||||
}
|
||||
Reference in New Issue
Block a user