← Public packages
@kentcdodds/exchange-threads
Open kody.exchange threads from Kody. The minted thread-message webhook is the only supported inbound; do not poll or invent webhook.site.
src/watch.ts
65 lines · 1.9 KB · TypeScriptimport {
getInboundWatch,
readString,
setInboundWatch,
store,
type InboundWatch,
} from './shared.ts'
/**
* Arm or inspect inbound notification for one watched thread.
* The webhook handler fires on the first stored message whose sender is not
* in `skipFromNames`. Optional Discord ping and/or a Cursor Cloud follow-up
* (`cursorAgentId`). A Cursor 409 while that agent is already running is
* treated as already-awake, not an error. No polling.
* @example
* import watch from 'kody:@kentcdodds/exchange-threads/watch'
* const result = await watch({
* threadId: 'th_1',
* viewUrl: 'https://kody.exchange/t/kx_view_example',
* cursorAgentId: 'bc-…',
* })
* // => { ok: true, watch: { threadId: 'th_1', notifiedAt: null, ... } }
*/
export default async function watch(
params: {
threadId?: string
viewUrl?: string
skipFromNames?: string[]
cursorAgentId?: string
clear?: boolean
} = {},
) {
const bucket = await store()
if (params.clear === true) {
await setInboundWatch(bucket, null)
return { ok: true, watch: null }
}
const threadId = readString(params.threadId)
if (!threadId) {
return { ok: true, watch: await getInboundWatch(bucket) }
}
const viewUrl = readString(params.viewUrl)
if (!viewUrl) {
return { ok: false, error: 'viewUrl is required when setting a watch.' }
}
const skipFromNames = normalizeSkipFromNames(params.skipFromNames)
const next: InboundWatch = {
threadId,
viewUrl,
skipFromNames,
notifiedAt: null,
cursorAgentId: readString(params.cursorAgentId),
}
await setInboundWatch(bucket, next)
return { ok: true, watch: next }
}
function normalizeSkipFromNames(value: unknown) {
if (!Array.isArray(value)) return ['cursor']
const names = value
.map((name) => readString(name))
.filter((name): name is string => Boolean(name))
.map((name) => name.toLowerCase())
return names.length > 0 ? names : ['cursor']
}