52 lines
2.4 KiB
TypeScript
52 lines
2.4 KiB
TypeScript
/** Desktop installation admission and task inspection for the shared Web Host. */
|
|
|
|
import type { Context } from '@deepseek-ai/cordis'
|
|
import type {} from '@deepseek-ai/dsh-agent'
|
|
import type {} from '@deepseek-ai/dsh-jobs'
|
|
import type {} from '@deepseek-ai/dsh-client-connection'
|
|
|
|
/**
|
|
* Register update admission on the owning Host context.
|
|
* @param ctx - Booted Desktop profile context; disposal removes the request listener.
|
|
* @returns Task inspector whose lock refuses new API requests, drains admitted requests, and rechecks work.
|
|
*/
|
|
export function installDesktopUpdateTaskControl(ctx: Context): (action: 'inspect' | 'lock' | 'unlock') => Promise<boolean> {
|
|
let locked = false
|
|
let lockGeneration = 0
|
|
let stopped = false
|
|
ctx.effect(() => () => { stopped = true })
|
|
const pendingRequests = new Set<Promise<void>>()
|
|
ctx.on('connection/request', async (_request, response, next) => {
|
|
if (locked) {
|
|
response.writeHead(503)
|
|
response.end()
|
|
return
|
|
}
|
|
const finished = Promise.withResolvers<void>()
|
|
pendingRequests.add(finished.promise)
|
|
try { await next() }
|
|
finally { pendingRequests.delete(finished.promise); finished.resolve() }
|
|
})
|
|
return async (action) => {
|
|
if (stopped) throw new Error('desktop update: Host is stopping')
|
|
if (action === 'unlock') { locked = false; lockGeneration++ }
|
|
const agents = ctx.get('agents')
|
|
const jobs = ctx.get('jobs')
|
|
if (agents === undefined || jobs === undefined) throw new Error('desktop update: task services are unavailable')
|
|
if (action === 'lock') {
|
|
locked = true
|
|
const generation = ++lockGeneration
|
|
// Read requests are not tasks; admitted writes must finish before the final work check.
|
|
await Promise.all(pendingRequests)
|
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Disposal can run while admitted requests drain.
|
|
if (stopped) throw new Error('desktop update: Host is stopping')
|
|
if (generation !== lockGeneration) throw new Error('desktop update: admission lock was superseded')
|
|
}
|
|
const liveAgents = agents.list()
|
|
return liveAgents.some(agent => agent.status === 'running'
|
|
|| agent.inbox.nextTurn.length > 0 || agent.inbox.nextStep.length > 0)
|
|
|| [undefined, ...liveAgents].some(agent => jobs.list(agent?.id)
|
|
.some(job => job.status === 'running' || job.status === 'stopping'))
|
|
}
|
|
}
|