FP
This commit is contained in:
22
packages/shared/package.json
Normal file
22
packages/shared/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@stream-control/shared",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"watch": "tsc -b --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
257
packages/shared/src/index.ts
Normal file
257
packages/shared/src/index.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* Protocole partagé entre le serveur de contrôle, les agents et le dashboard.
|
||||
*
|
||||
* Transport : WebSocket, messages JSON, un champ `type` discriminant.
|
||||
* - agent <-> serveur : /ws/agent (l'agent initie la connexion sortante)
|
||||
* - browser <- serveur : /ws/dashboard (flux de statut temps réel)
|
||||
*/
|
||||
|
||||
export const PROTOCOL_VERSION = 1;
|
||||
|
||||
export type Platform = 'windows' | 'linux' | 'darwin' | 'unknown';
|
||||
|
||||
/** Paramètres de connexion à obs-websocket (plugin intégré à OBS >= 28). */
|
||||
export interface ObsSettings {
|
||||
host: string;
|
||||
port: number;
|
||||
/** Mot de passe obs-websocket ; chaîne vide si l'authentification est désactivée. */
|
||||
password: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_OBS_SETTINGS: ObsSettings = {
|
||||
host: '127.0.0.1',
|
||||
port: 4455,
|
||||
password: '',
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Actions pilotables sur un agent
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const AGENT_ACTIONS = [
|
||||
'obs.connect',
|
||||
'obs.disconnect',
|
||||
'obs.refresh',
|
||||
'record.start',
|
||||
'record.stop',
|
||||
'record.pause',
|
||||
'record.resume',
|
||||
'record.split',
|
||||
'stream.start',
|
||||
'stream.stop',
|
||||
'scene.set',
|
||||
'profile.set',
|
||||
'collection.set',
|
||||
'recordDirectory.set',
|
||||
'agent.ping',
|
||||
] as const;
|
||||
|
||||
export type AgentAction = (typeof AGENT_ACTIONS)[number];
|
||||
|
||||
export function isAgentAction(value: unknown): value is AgentAction {
|
||||
return typeof value === 'string' && (AGENT_ACTIONS as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
/** Paramètres attendus par action (les autres actions n'en prennent aucun). */
|
||||
export interface AgentActionParams {
|
||||
'scene.set': { scene: string };
|
||||
'profile.set': { profile: string };
|
||||
'collection.set': { collection: string };
|
||||
'recordDirectory.set': { directory: string };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Statut remonté par un agent
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface AgentStatus {
|
||||
/** L'agent a-t-il une session obs-websocket établie ? */
|
||||
obsConnected: boolean;
|
||||
obsVersion?: string;
|
||||
obsError?: string;
|
||||
|
||||
recording: boolean;
|
||||
recordPaused: boolean;
|
||||
/** Durée d'enregistrement au format HH:MM:SS.mmm renvoyé par OBS. */
|
||||
recordTimecode?: string;
|
||||
recordBytes?: number;
|
||||
/** Chemin du dernier fichier écrit (renseigné à l'arrêt de l'enregistrement). */
|
||||
lastRecordingPath?: string;
|
||||
recordDirectory?: string;
|
||||
|
||||
streaming: boolean;
|
||||
streamTimecode?: string;
|
||||
|
||||
currentScene?: string;
|
||||
scenes: string[];
|
||||
currentProfile?: string;
|
||||
profiles: string[];
|
||||
currentCollection?: string;
|
||||
collections: string[];
|
||||
|
||||
/** Statistiques OBS. */
|
||||
cpuUsage?: number;
|
||||
fps?: number;
|
||||
droppedFrames?: number;
|
||||
renderSkippedFrames?: number;
|
||||
|
||||
/** Statistiques machine (collectées par l'agent, pas par OBS). */
|
||||
systemCpu?: number;
|
||||
systemMemoryUsed?: number;
|
||||
systemMemoryTotal?: number;
|
||||
diskFreeBytes?: number;
|
||||
diskTotalBytes?: number;
|
||||
|
||||
updatedAt: number;
|
||||
}
|
||||
|
||||
export function emptyStatus(): AgentStatus {
|
||||
return {
|
||||
obsConnected: false,
|
||||
recording: false,
|
||||
recordPaused: false,
|
||||
streaming: false,
|
||||
scenes: [],
|
||||
profiles: [],
|
||||
collections: [],
|
||||
updatedAt: 0,
|
||||
};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Messages agent -> serveur
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
||||
|
||||
export interface HelloMessage {
|
||||
type: 'hello';
|
||||
protocol: number;
|
||||
/** Absent lors du tout premier enrôlement : le serveur en attribue un. */
|
||||
agentId?: string;
|
||||
name: string;
|
||||
hostname: string;
|
||||
platform: Platform;
|
||||
agentVersion: string;
|
||||
}
|
||||
|
||||
export interface StatusMessage {
|
||||
type: 'status';
|
||||
status: AgentStatus;
|
||||
}
|
||||
|
||||
export interface ResultMessage {
|
||||
type: 'result';
|
||||
requestId: string;
|
||||
ok: boolean;
|
||||
data?: unknown;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface LogMessage {
|
||||
type: 'log';
|
||||
level: LogLevel;
|
||||
message: string;
|
||||
ts: number;
|
||||
}
|
||||
|
||||
export interface PongMessage {
|
||||
type: 'pong';
|
||||
ts: number;
|
||||
}
|
||||
|
||||
export type AgentToServer =
|
||||
| HelloMessage
|
||||
| StatusMessage
|
||||
| ResultMessage
|
||||
| LogMessage
|
||||
| PongMessage;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Messages serveur -> agent
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface WelcomeMessage {
|
||||
type: 'welcome';
|
||||
agentId: string;
|
||||
/** Fourni uniquement lors de l'enrôlement : l'agent doit le persister. */
|
||||
token?: string;
|
||||
obs: ObsSettings;
|
||||
/** Fréquence de remontée de statut demandée. */
|
||||
statusIntervalMs: number;
|
||||
/** Si vrai, l'agent tente de se connecter à OBS dès le démarrage. */
|
||||
autoConnectObs: boolean;
|
||||
}
|
||||
|
||||
export interface CommandMessage {
|
||||
type: 'command';
|
||||
requestId: string;
|
||||
action: AgentAction;
|
||||
params?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface ConfigMessage {
|
||||
type: 'config';
|
||||
obs: ObsSettings;
|
||||
autoConnectObs: boolean;
|
||||
}
|
||||
|
||||
export interface PingMessage {
|
||||
type: 'ping';
|
||||
ts: number;
|
||||
}
|
||||
|
||||
export type ServerToAgent = WelcomeMessage | CommandMessage | ConfigMessage | PingMessage;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Vue agrégée exposée au dashboard
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface AgentView {
|
||||
id: string;
|
||||
name: string;
|
||||
hostname: string | null;
|
||||
platform: Platform;
|
||||
agentVersion: string | null;
|
||||
online: boolean;
|
||||
lastSeenAt: number | null;
|
||||
createdAt: number;
|
||||
obs: ObsSettings;
|
||||
autoConnectObs: boolean;
|
||||
notes: string | null;
|
||||
status: AgentStatus;
|
||||
}
|
||||
|
||||
export interface LogEntry {
|
||||
id: number;
|
||||
agentId: string | null;
|
||||
agentName: string | null;
|
||||
level: LogLevel;
|
||||
message: string;
|
||||
ts: number;
|
||||
}
|
||||
|
||||
export type ServerToDashboard =
|
||||
| { type: 'snapshot'; agents: AgentView[]; logs: LogEntry[] }
|
||||
| { type: 'agent'; agent: AgentView }
|
||||
| { type: 'agent.removed'; agentId: string }
|
||||
| { type: 'log'; entry: LogEntry };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function detectPlatform(raw: string): Platform {
|
||||
if (raw === 'win32') return 'windows';
|
||||
if (raw === 'linux') return 'linux';
|
||||
if (raw === 'darwin') return 'darwin';
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
export function safeJsonParse<T>(raw: string): T | null {
|
||||
try {
|
||||
return JSON.parse(raw) as T;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
8
packages/shared/tsconfig.json
Normal file
8
packages/shared/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user