Feat : Prio order for auto stream record
All checks were successful
release / build (push) Successful in 27s
release / verify-windows (push) Successful in 1m20s

This commit is contained in:
jeanotx32
2026-08-12 16:01:18 +02:00
parent 96b364bd66
commit eae694d49a
12 changed files with 442 additions and 72 deletions

View File

@@ -884,10 +884,28 @@ export interface WatchTarget {
favorite: boolean;
/**
* Lancer l'enregistrement sans intervention dès que ce profil est en direct
* depuis assez longtemps. Exige un agent assigné, et un seul profil en
* automatisme par agent : une VM n'enregistre qu'un flux à la fois.
* depuis assez longtemps. Exige un agent assigné.
*
* Plusieurs profils peuvent l'activer sur une même VM : elle n'en capture
* qu'un à la fois, et c'est {@link WatchTarget.priority} qui arbitre.
*/
autoRecord: boolean;
/**
* Rang dans la file d'attente d'une VM, le plus élevé l'emportant.
*
* L'échelle est globale pour rester comparable partout, mais elle ne tranche
* qu'entre profils assignés au même agent : deux VM distinctes ne se
* disputent rien.
*/
priority: number;
/**
* Autorise ce profil à couper une capture de priorité inférieure sur sa VM.
*
* Séparé de la priorité, et non déduit d'elle : interrompre un
* enregistrement en cours perd la fin d'un fichier. Cela ne doit pas
* découler d'un simple changement de rang, mais d'un choix explicite.
*/
preempt: boolean;
state: StreamState;
rawStatus: string | null;
/** Depuis quand l'état est stable, selon nos propres observations. */
@@ -963,6 +981,76 @@ export function recordingUsername(agent: Pick<AgentView, 'status' | 'watch'>): s
return agent.watch.username || null;
}
/**
* Rangs proposés par l'interface. La colonne reste un entier libre : affiner
* l'échelle un jour ne demandera pas de migration.
*/
export const PRIORITY_LEVELS = [
{ value: 0, label: 'Basse' },
{ value: 1, label: 'Normale' },
{ value: 2, label: 'Haute' },
{ value: 3, label: 'Critique' },
] as const;
export const DEFAULT_PRIORITY = 1;
export function priorityLabel(value: number): string {
return PRIORITY_LEVELS.find((level) => level.value === value)?.label ?? `Rang ${value}`;
}
export interface PreemptionVerdict {
allowed: boolean;
/** Pourquoi l'interruption est refusée — destiné à être affiché tel quel. */
reason?: string;
}
/**
* `candidate` a-t-il le droit de couper la capture en cours au profit de la sienne ?
*
* Trois refus, dans cet ordre, du plus prudent au plus attendu :
*
* 1. **Capture non identifiée.** Si l'on ne sait pas quel profil la VM
* enregistre, on ne la coupe pas : le risque est de détruire une capture
* dont on ignore la valeur, et l'on n'a de toute façon aucune priorité à
* lui comparer.
* 2. **Interruption non autorisée** sur le profil candidat.
* 3. **Priorité insuffisante** — strictement supérieure exigée, ce qui rend
* les égalités inoffensives : à rang égal, la capture en place l'emporte.
*/
export function preemptionVerdict(
candidate: Pick<WatchTarget, 'id' | 'priority' | 'preempt'>,
current: Pick<WatchTarget, 'id' | 'username' | 'label' | 'priority'> | null,
): PreemptionVerdict {
if (!current) {
return {
allowed: false,
reason: "la capture en cours n'a pas pu être rattachée à un profil suivi",
};
}
// Rien à interrompre : c'est déjà ce profil qui est capturé. Refusé plutôt
// qu'autorisé — l'appelant s'apprêtait à lancer une seconde capture.
if (current.id === candidate.id) {
return { allowed: false, reason: 'sa capture est déjà en cours sur cette VM' };
}
const name = current.label ?? current.username;
if (!candidate.preempt) {
return {
allowed: false,
reason: `« ${name} » est en cours de capture et l'interruption n'est pas autorisée sur ce profil`,
};
}
if (candidate.priority <= current.priority) {
return {
allowed: false,
reason:
`« ${name} » est en cours de capture avec une priorité au moins égale ` +
`(${priorityLabel(current.priority)})`,
};
}
return { allowed: true };
}
/** Cette VM enregistre-t-elle bien *ce* profil, et non un autre qui lui est assigné ? */
export function isRecordingTarget(
agent: Pick<AgentView, 'status' | 'watch'> | null | undefined,