mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 06:02:01 +00:00
d85b634f75
- api.bundle.download 取文件和服务端给的文件名(优先 RFC 5987 的 filename*,没有再看 filename),下载照 EvidencePane 的写法 - 按钮只在运行结束且已封存时可用;运行中、等待审批、接着跑(留着旧封存)、没有封存清单时禁用,悬停和读屏都说明原因; 服务端回 409(封存核对不一致等)时提示原因 - 文案进 BUNDLE_TEXT;预览用例 dev/fixtures/bundle.tsx(已封存、运行中、等待审批、未封存、接着跑)给 check-bundle 用
46 lines
2.0 KiB
TypeScript
46 lines
2.0 KiB
TypeScript
/**
|
|
* 可验证报告的接口(/api/runs/{run_id}/…),挂在 api.bundle 上。
|
|
*
|
|
* 请求函数(get / post / del……)和 ApiError 从 ./http 导入,不要从 ./client 导入:client.ts 要导入
|
|
* 这个文件把它挂到 api 上,反过来导入就成了循环(见 http.ts 开头的说明)。
|
|
*/
|
|
import { ApiError, BASE, NETWORK_MESSAGE, actorHeader, failure, report } from './http'
|
|
|
|
/**
|
|
* Content-Disposition 里的文件名:先看 RFC 5987 的 filename*(中文名写在这里),没有或解不开再看 filename。
|
|
* 服务端给的 filename 是纯 ASCII 的退路(agentlab-<运行号>-bundle.html),只在老客户端那边派上用场
|
|
*/
|
|
export function dispositionName(header: string | null): string | null {
|
|
if (!header) return null
|
|
const star = /filename\*\s*=\s*UTF-8''([^;]+)/i.exec(header)?.[1]
|
|
if (star) {
|
|
try {
|
|
return decodeURIComponent(star.trim().replace(/^"|"$/g, ''))
|
|
} catch {
|
|
// 百分号编码坏了:退回 filename
|
|
}
|
|
}
|
|
return /filename\s*=\s*"?([^";]+)"?/i.exec(header)?.[1]?.trim() || null
|
|
}
|
|
|
|
export const bundle = {
|
|
/**
|
|
* 下载可验证报告(GET /runs/{id}/bundle.html,附件):回来的是文件本身和服务端给的文件名,没给就是 null,
|
|
* 由调用方按运行名补。运行未封存、封存核对不一致时服务端回 409,ApiError 的 message 是给人看的原因
|
|
*/
|
|
download: async (runId: string): Promise<{ blob: Blob; name: string | null }> => {
|
|
const path = `/runs/${encodeURIComponent(runId)}/bundle.html`
|
|
let res: Response
|
|
try {
|
|
res = await fetch(BASE + path, { headers: actorHeader() })
|
|
} catch (e: any) {
|
|
const err = new ApiError(0, NETWORK_MESSAGE, { kind: 'network', raw: `${e?.name ?? 'Error'}: ${e?.message ?? e}` })
|
|
report(false, err)
|
|
throw err
|
|
}
|
|
if (!res.ok) throw failure(res.status, res.statusText, await res.text().catch(() => ''), path)
|
|
report(true)
|
|
return { blob: await res.blob(), name: dispositionName(res.headers.get('Content-Disposition')) }
|
|
},
|
|
}
|