44 lines
2.9 KiB
JavaScript
44 lines
2.9 KiB
JavaScript
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import os from 'node:os'
|
|
import crypto from 'node:crypto'
|
|
const createMessage = (input) => ({ ...input, id: crypto.randomUUID(), source: { kind: 'user', ...(input.source || {}) } })
|
|
export const name = 'dsh-queue-steer'
|
|
export const inject = { commands: { required: true }, webServer: { required: true }, connection: { required: true } }
|
|
const file = path.join(process.env.DSH_PROFILE_DIR || path.join(process.env.DSH_HOME || path.join(os.homedir(), '.dsh'), 'profiles', 'web'), 'dsh-queue-steer', 'settings.json')
|
|
const defaults = { defaultMode: 'queue', defaultQueueList: 'batch', batchHeaderTemplate: 'Queued messages batch ({count}):\n\n{messages}' }
|
|
let settings = { ...defaults }
|
|
try { settings = { ...defaults, ...JSON.parse(fs.readFileSync(file, 'utf8')) } } catch {}
|
|
const save = (next) => { settings = { ...settings, ...next }; fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, JSON.stringify(settings, null, 2)) }
|
|
const message = (value, via) => createMessage({ role: 'user', content: [{ type: 'text', text: value }], source: { kind: 'queue-steer', via } })
|
|
|
|
export function apply(ctx) {
|
|
const add = (name, mode) => ctx.commands.register({
|
|
definitionId: CommandDefinitionId(`@local/dsh-queue-steer#${name}`), name,
|
|
description: mode === 'steer' ? 'Steer the running agent with a message' : 'Queue a message for the agent',
|
|
input: { hint: '[message]' },
|
|
handler: ({ rawInput, agent }) => {
|
|
const value = rawInput.trim()
|
|
if (!value) return { kind: 'error', text: `Usage: /${name} <message>` }
|
|
const m = message(value, `command-${name}`)
|
|
if (mode === 'steer' && agent.status === 'running') agent.steer(m)
|
|
else if (mode === 'steer') agent.followup(m)
|
|
else agent.inbox.append('next-turn', m)
|
|
return { kind: 'success', text: `${mode === 'steer' ? 'Sent' : 'Queued'}: ${value.slice(0, 120)}` }
|
|
},
|
|
})
|
|
add('queue', 'queue'); add('steer', 'steer')
|
|
ctx.inject(['webServer', 'connection'], (wctx) => wctx.effect(() => wctx.webServer.register({
|
|
kind: 'exact', path: '/dsh-queue-steer/settings',
|
|
handler: async (req, res) => {
|
|
const denied = wctx.connection.requestRejection(req)
|
|
res.setHeader('content-type', 'application/json; charset=utf-8'); res.setHeader('cache-control', 'no-store')
|
|
if (denied !== undefined) { res.statusCode = denied; return res.end(JSON.stringify({ error: 'rejected' })) }
|
|
if (req.method === 'GET') { res.statusCode = 200; return res.end(JSON.stringify(settings)) }
|
|
if (req.method !== 'POST') { res.statusCode = 405; return res.end() }
|
|
let body = ''; for await (const chunk of req) body += chunk
|
|
try { save(JSON.parse(body || '{}')); res.statusCode = 200; res.end(JSON.stringify(settings)) } catch (e) { res.statusCode = 400; res.end(JSON.stringify({ error: String(e.message || e) })) }
|
|
},
|
|
}), 'dsh-queue-steer: settings'))
|
|
}
|