| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 |
- // Bot API 客户端(/bot/v1,docs/design/13-bot-teams.md §9)。使用 Bot Key 或 Bot 浏览器令牌,与 AgentClient 的 Key 互不通用
- import type {
- BotCard,
- BrowserTokenDto,
- CreateTeamBody,
- FileDto,
- InteractionResponse,
- PatchTeamBody,
- SendTeamMessageBody,
- StubTaskBody,
- TeamDetailDto,
- TeamDto,
- TeamEvent,
- TeamJobsDto,
- TeamMessage,
- TeamStatus,
- TeamUsageDto,
- } from '@agent-server/shared'
- import { AgentApiError, HttpClient, query, type StreamHandlers } from './http'
- export class BotClient extends HttpClient {
- /** 平台后端调用(需要 Bot Key):签发交给浏览器使用的短期令牌 */
- issueBrowserToken = (ttlSeconds?: number) => this.request<BrowserTokenDto>('POST', '/bot/v1/browser-tokens', ttlSeconds === undefined ? {} : { ttlSeconds })
- /** 这个 Key 可以使用的机器人及其能力卡片 */
- bots = () => this.request<BotCard[]>('GET', '/bot/v1/bots')
- teams = {
- create: (body: CreateTeamBody) => this.request<TeamDetailDto>('POST', '/bot/v1/teams', body),
- list: (q: { scope?: 'all'; status?: TeamStatus; limit?: number } = {}) => this.request<TeamDto[]>('GET', `/bot/v1/teams${query(q)}`),
- get: (id: string) => this.request<TeamDetailDto>('GET', `/bot/v1/teams/${id}`),
- /** 标题、限额;debug 只有后台可以设置 */
- update: (id: string, body: PatchTeamBody) => this.request<TeamDetailDto>('PATCH', `/bot/v1/teams/${id}`, body),
- remove: (id: string) => this.request<void>('DELETE', `/bot/v1/teams/${id}`),
- pause: (id: string) => this.request<TeamDetailDto>('POST', `/bot/v1/teams/${id}/pause`),
- resume: (id: string) => this.request<TeamDetailDto>('POST', `/bot/v1/teams/${id}/resume`),
- close: (id: string) => this.request<TeamDetailDto>('POST', `/bot/v1/teams/${id}/close`),
- reopen: (id: string) => this.request<TeamDetailDto>('POST', `/bot/v1/teams/${id}/reopen`),
- addBot: (id: string, botId: string) => this.request<TeamDetailDto>('POST', `/bot/v1/teams/${id}/bots`, { botId }),
- removeBot: (id: string, botId: string) => this.request<TeamDetailDto>('DELETE', `/bot/v1/teams/${id}/bots/${botId}`),
- interruptBot: (id: string, botId: string) => this.request<{ ok: true }>('POST', `/bot/v1/teams/${id}/bots/${botId}/interrupt`),
- /** 让机器人开始新的会话(组长在当前任务结束后) */
- resetSession: (id: string, botId: string) => this.request<{ ok: true }>('POST', `/bot/v1/teams/${id}/bots/${botId}/reset-session`, {}),
- send: (id: string, body: SendTeamMessageBody) => this.request<TeamMessage>('POST', `/bot/v1/teams/${id}/messages`, body),
- messages: (id: string, after = 0) => this.request<{ items: TeamEvent[]; lastSeq: number }>('GET', `/bot/v1/teams/${id}/messages?after=${after}`),
- events: (id: string, after = 0) => this.request<{ items: TeamEvent[]; lastSeq: number }>('GET', `/bot/v1/teams/${id}/events?after=${after}`),
- files: (id: string) => this.request<FileDto[]>('GET', `/bot/v1/teams/${id}/files`),
- upload: (id: string, files: Blob[] | File[]) => {
- const form = new FormData()
- for (const f of files) form.append('file', f, (f as File).name ?? 'upload')
- return this.request<FileDto[]>('POST', `/bot/v1/teams/${id}/files`, form)
- },
- reply: (id: string, interactionId: string, body: InteractionResponse) =>
- this.request<{ ok: true }>('POST', `/bot/v1/teams/${id}/interactions/${interactionId}`, body),
- jobs: (id: string) => this.request<TeamJobsDto>('GET', `/bot/v1/teams/${id}/jobs`),
- cancelJob: (id: string, jobId: string) => this.request<{ ok: true }>('POST', `/bot/v1/teams/${id}/jobs/${jobId}/cancel`),
- continueJob: (id: string, jobId: string) => this.request<{ ok: true }>('POST', `/bot/v1/teams/${id}/jobs/${jobId}/continue`),
- usage: (id: string) => this.request<TeamUsageDto>('GET', `/bot/v1/teams/${id}/usage`),
- /** 仅后台:替身组员的子任务结果 */
- stubResult: (id: string, taskId: string, body: StubTaskBody) => this.request<{ ok: true }>('POST', `/bot/v1/teams/${id}/tasks/${taskId}/stub`, body),
- /** 仅后台:逐步执行时放行一步 */
- continueStep: (id: string, stepId: string) => this.request<{ ok: true }>('POST', `/bot/v1/teams/${id}/steps/${stepId}/continue`),
- }
- /** 文件下载地址(同源 Cookie 鉴权时可直接用于 <img>/<a>) */
- fileUrl(fileId: string, inline = false) {
- return `${this.opts.baseUrl}/bot/v1/files/${fileId}${inline ? '?inline=1' : ''}`
- }
- /** 带鉴权头下载文件为 Blob */
- async fileBlob(fileId: string): Promise<Blob> {
- const res = await this.send(`/bot/v1/files/${fileId}?inline=1`, {})
- if (!res.ok) throw new AgentApiError(res.status, 'http_error', `HTTP ${res.status}`)
- return res.blob()
- }
- /** 订阅工作组事件流;断线自动重连并从最后收到的 seq 续传。返回取消函数 */
- subscribe(teamId: string, handlers: StreamHandlers<TeamEvent>, opts: { after?: number } = {}): () => void {
- return this.stream(`/bot/v1/teams/${teamId}/stream`, handlers, opts.after ?? 0)
- }
- }
|