BREAKING CHANGE(daemon): Introduce persistent log storage, numeric ProcessId type, and improved process monitoring / IPC handling

This commit is contained in:
2025-08-30 13:47:14 +00:00
parent e507b75c40
commit 538f282b62
16 changed files with 589 additions and 167 deletions

56
ts/shared/protocol/id.ts Normal file
View File

@@ -0,0 +1,56 @@
/**
* Branded type for process IDs to ensure type safety
*/
export type ProcessId = number & { readonly __brand: 'ProcessId' };
/**
* Input type that accepts various ID formats for backward compatibility
*/
export type ProcessIdInput = ProcessId | number | string;
/**
* Normalizes various ID input formats to a ProcessId
* @param input - The ID in various formats (string, number, or ProcessId)
* @returns A normalized ProcessId
* @throws Error if the input is not a valid process ID
*/
export function toProcessId(input: ProcessIdInput): ProcessId {
let num: number;
if (typeof input === 'string') {
const trimmed = input.trim();
if (!/^\d+$/.test(trimmed)) {
throw new Error(`Invalid process ID: "${input}" is not a numeric string`);
}
num = parseInt(trimmed, 10);
} else if (typeof input === 'number') {
num = input;
} else {
// Already a ProcessId
return input;
}
if (!Number.isInteger(num) || num < 1) {
throw new Error(`Invalid process ID: ${input} must be a positive integer`);
}
return num as ProcessId;
}
/**
* Type guard to check if a value is a ProcessId
*/
export function isProcessId(value: unknown): value is ProcessId {
return typeof value === 'number' && Number.isInteger(value) && value >= 1;
}
/**
* Gets the next sequential ID given existing IDs
*/
export function getNextProcessId(existingIds: Iterable<ProcessId>): ProcessId {
let maxId = 0;
for (const id of existingIds) {
maxId = Math.max(maxId, id);
}
return (maxId + 1) as ProcessId;
}

View File

@@ -1,3 +1,5 @@
import type { ProcessId } from './id.js';
// Process-related interfaces (used in IPC communication)
export interface IMonitorConfig {
name?: string; // Optional name to identify the instance
@@ -11,14 +13,14 @@ export interface IMonitorConfig {
}
export interface IProcessConfig extends IMonitorConfig {
id: string; // Unique identifier for the process
id: ProcessId; // Unique identifier for the process
autorestart: boolean; // Whether to restart the process automatically on crash
watch?: boolean; // Whether to watch for file changes and restart
watchPaths?: string[]; // Paths to watch for changes
}
export interface IProcessInfo {
id: string;
id: ProcessId;
pid?: number;
status: 'online' | 'stopped' | 'errored';
memory: number;
@@ -61,25 +63,25 @@ export interface StartRequest {
}
export interface StartResponse {
processId: string;
processId: ProcessId;
pid?: number;
status: 'online' | 'stopped' | 'errored';
}
// Start by id (server resolves config)
export interface StartByIdRequest {
id: string;
id: ProcessId;
}
export interface StartByIdResponse {
processId: string;
processId: ProcessId;
pid?: number;
status: 'online' | 'stopped' | 'errored';
}
// Stop command
export interface StopRequest {
id: string;
id: ProcessId;
}
export interface StopResponse {
@@ -89,18 +91,18 @@ export interface StopResponse {
// Restart command
export interface RestartRequest {
id: string;
id: ProcessId;
}
export interface RestartResponse {
processId: string;
processId: ProcessId;
pid?: number;
status: 'online' | 'stopped' | 'errored';
}
// Delete command
export interface DeleteRequest {
id: string;
id: ProcessId;
}
export interface DeleteResponse {
@@ -119,7 +121,7 @@ export interface ListResponse {
// Describe command
export interface DescribeRequest {
id: string;
id: ProcessId;
}
export interface DescribeResponse {
@@ -129,7 +131,7 @@ export interface DescribeResponse {
// Get logs command
export interface GetLogsRequest {
id: string;
id: ProcessId;
lines?: number;
}
@@ -143,9 +145,9 @@ export interface StartAllRequest {
}
export interface StartAllResponse {
started: string[];
started: ProcessId[];
failed: Array<{
id: string;
id: ProcessId;
error: string;
}>;
}
@@ -156,9 +158,9 @@ export interface StopAllRequest {
}
export interface StopAllResponse {
stopped: string[];
stopped: ProcessId[];
failed: Array<{
id: string;
id: ProcessId;
error: string;
}>;
}
@@ -169,9 +171,9 @@ export interface RestartAllRequest {
}
export interface RestartAllResponse {
restarted: string[];
restarted: ProcessId[];
failed: Array<{
id: string;
id: ProcessId;
error: string;
}>;
}
@@ -182,10 +184,10 @@ export interface ResetRequest {
}
export interface ResetResponse {
stopped: string[];
removed: string[];
stopped: ProcessId[];
removed: ProcessId[];
failed: Array<{
id: string;
id: ProcessId;
error: string;
}>;
}
@@ -229,17 +231,17 @@ export interface HeartbeatResponse {
// Add (register config without starting)
export interface AddRequest {
// Optional id is ignored server-side if present; server assigns sequential id
config: Omit<IProcessConfig, 'id'> & { id?: string };
config: Omit<IProcessConfig, 'id'> & { id?: ProcessId };
}
export interface AddResponse {
id: string;
id: ProcessId;
config: IProcessConfig;
}
// Remove (delete config and stop if running)
export interface RemoveRequest {
id: string;
id: ProcessId;
}
export interface RemoveResponse {