Files
hermes-agent/apps/shared/src/cron-trigger-controller.ts
Evgenii f9d64b9a9d fix(cron): add reliable trigger feedback in Web and Desktop clients
- Shared per-job trigger controller (apps/shared) coalesces duplicate
  clicks for the same profile+job inside a mounted client while letting
  unrelated jobs run independently; the backend durable claim remains
  authoritative across windows/processes.
- Two-phase feedback everywhere: the action stays disabled/spinning
  while the request is in flight and the terminal success/error is
  reported once, after the HTTP response — no premature success toast
  (Web), matching the Desktop info notification.
- Desktop keeps the 24h trigger timeout for the synchronous long
  operation and fences stale profile/list responses and unmounted
  surfaces; the sidebar trigger button shows a spinner while busy.
2026-08-14 20:46:50 -07:00

41 lines
1.1 KiB
TypeScript

export interface CronTriggerRunResult<T> {
started: boolean
value: T | null
}
export interface CronTriggerController {
isRunning(key: string): boolean
run<T>(key: string, action: () => Promise<T>, onStarted?: () => void): Promise<CronTriggerRunResult<T>>
}
// This is an interaction guard for one mounted UI surface. Cross-window and
// cross-process exclusion remains the backend's responsibility via its durable
// cron claim; a renderer-local Set must never be treated as the execution lock.
export function createCronTriggerController(
onRunningChange: (key: string, running: boolean) => void = () => undefined
): CronTriggerController {
const running = new Set<string>()
return {
isRunning: key => running.has(key),
async run<T>(key: string, action: () => Promise<T>, onStarted?: () => void) {
if (running.has(key)) {
return { started: false, value: null }
}
running.add(key)
try {
onRunningChange(key, true)
onStarted?.()
return { started: true, value: await action() }
} finally {
running.delete(key)
onRunningChange(key, false)
}
}
}
}