# 桌面 API
## 功能
`bt-app` 会向 `static`、`server` 和 `remote` 页面注入 `window.bt`。桌面 API 提供 BT 后端调用、窗口控制、系统对话框、托盘、剪贴板、屏幕吸管、框选截图、全局快捷键、通知、文件拖入、目录文件监听、应用信息,以及有界流式 HTTP、应用凭据、工作区、原生进程和应用自有数据能力。
页面只应使用 `window.bt`。运行器不会公开全局 `window.__TAURI__`,也不会把本地 `fs`、`net`、`process` 能力直接暴露给前端。
## 语法
```text
window.bt.call(name, ...args)
window.bt.window.set_title(title)
window.bt.window.set_size(width, height)
window.bt.window.set_position(x, y)
window.bt.window.placement()
window.bt.window.set_background_color(color)
window.bt.window.set_close_mode(mode)
window.bt.window.on_close_requested(callback)
window.bt.window.close_now()
window.bt.window.open_devtools()
window.bt.dialog.open_file(options)
window.bt.tray.enable(options)
window.bt.clipboard.read_text()
window.bt.screen.pick_color(options)
window.bt.screen.capture_area(options)
window.bt.shortcut.register(shortcut_id, accelerator, callback)
window.bt.shortcut.unregister(shortcut_id)
window.bt.shortcut.unregister_all()
window.bt.notify.show(options)
window.bt.drag.on_files(callback)
window.bt.app.version()
window.bt.app.info(path)
window.bt.app.run(path, args)
window.bt.app.documents_dir()
window.bt.app.watch_path(path, callback, options)
window.bt.credential.store(credential_id, secret)
window.bt.http.stream(options, callback)
window.bt.workspace.open(root)
window.bt.workspace.list(workspace_id, relative, recursive)
window.bt.workspace.read(workspace_id, relative, max_bytes)
window.bt.workspace.atomic_write(workspace_id, relative, content, expected_sha256)
window.bt.process.start(options)
window.bt.process.status(process_id, task_id, identity)
window.bt.process.stop(process_id, task_id, identity)
window.bt.process.stop_task(task_id)
window.bt.data.store(key, value)
window.bt.data.prepare_cleanup()
window.bt.data.confirm_cleanup(confirm_token)
window.bt.events.on_backend(callback)
```
所有异步方法返回 `Promise`。成功时直接返回数据;失败时 Promise 会 reject,错误值是字符串或可转为字符串的错误对象。
## 参数
### bt.call
| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| name | string | 是 | `main.bt` 中的全局函数名 |
| ...args | any | 否 | 传给 BT 函数的参数,会按 JSON 值转换 |
### bt.window
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| set_title | title:string | void | 设置窗口标题 |
| set_size | width:number, height:number | void | 设置窗口逻辑尺寸 |
| set_position | x:number, y:number | void | 设置窗口外框左上角逻辑坐标,支持多显示器负坐标 |
| placement | 无 | object | 读取窗口外框与当前显示器可用工作区的逻辑坐标和尺寸 |
| set_background_color | color:string | void | 设置窗口和 WebView 背景色;`color` 必须是 `#RRGGBB` |
| set_resizable | resizable:boolean | void | 设置是否允许调整大小 |
| minimize / maximize / restore / close / hide / show / focus / center | 无 | void | 常用窗口动作 |
| set_fullscreen | fullscreen:boolean | void | 设置全屏 |
| is_fullscreen / is_maximized / is_minimized / is_visible / is_always_on_top | 无 | boolean | 读取窗口状态 |
| set_always_on_top | enabled:boolean | void | 设置窗口置顶 |
| set_decorations | visible:boolean | void | 设置系统标题栏和边框 |
| set_skip_taskbar | enabled:boolean | void | 设置是否跳过任务栏 |
| set_close_mode | mode:string | void | `exit`、`hide` 或 `tray` |
| on_close_requested | callback:function | function | 接管 `exit` 模式关闭请求,返回取消监听函数 |
| close_now | 无 | void | 页面完成异步收尾后放行并关闭窗口 |
| drag | 无 | void | 自定义标题栏拖动窗口 |
| start_resize | edge:string | void | `top`、`bottom`、`left`、`right`、`top_left`、`top_right`、`bottom_left`、`bottom_right` |
| flash | 无 | void | 请求系统提醒用户关注窗口 |
| open_devtools | 无 | void | 打开 WebView 开发者工具,仅在 `dev.devtools=true` 时可用 |
`set_background_color()` 同时更新原生窗口和 WebView 的不透明底色,适合普通窗口切换亮暗主题。需要 HTML 圆角、PNG Alpha 或透明页面背景时,应在 `app.json` 创建期设置 `window.transparent=true`,不能用该运行期方法代替透明窗口配置。
`set_position(x, y)` 的 `x`、`y` 使用逻辑像素;多显示器位于主屏左侧或上方时可以传负数。`placement()` 返回字段:
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| x | integer | 是 | 无 | 逻辑像素,可为负值 | 窗口外框左上角 X 坐标 |
| y | integer | 是 | 无 | 逻辑像素,可为负值 | 窗口外框左上角 Y 坐标 |
| width | integer | 是 | 无 | 大于 0 | 窗口外框宽度 |
| height | integer | 是 | 无 | 大于 0 | 窗口外框高度 |
| scale_factor | number | 是 | 无 | 大于 0 | 当前显示器从逻辑像素到物理像素的缩放倍率 |
| content_area | object | 是 | 无 | 见下表 | 扣除系统边框和阴影后的 WebView 内容区域 |
| work_area | object | 是 | 无 | 见下表 | 当前显示器扣除任务栏等系统区域后的可用工作区 |
`content_area` 与 `work_area` 都使用相同矩形字段;前者是桌面中的 WebView 内容区域,后者是当前显示器的可用工作区:
| 对象 | 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- | --- |
| content_area | x | integer | 是 | 无 | 逻辑像素,可为负值 | 内容区域左上角 X 坐标 |
| content_area | y | integer | 是 | 无 | 逻辑像素,可为负值 | 内容区域左上角 Y 坐标 |
| content_area | width | integer | 是 | 无 | 大于 0 | 内容区域宽度 |
| content_area | height | integer | 是 | 无 | 大于 0 | 内容区域高度 |
| work_area | x | integer | 是 | 无 | 逻辑像素,可为负值 | 工作区左上角 X 坐标 |
| work_area | y | integer | 是 | 无 | 逻辑像素,可为负值 | 工作区左上角 Y 坐标 |
| work_area | width | integer | 是 | 无 | 大于 0 | 工作区宽度 |
| work_area | height | integer | 是 | 无 | 大于 0 | 工作区高度 |
```javascript
const placement = await window.bt.window.placement()
// 移到当前显示器工作区左上方,并保留 10 逻辑像素间距
await window.bt.window.set_position(
placement.work_area.x + 10,
placement.work_area.y + 10
)
```
### bt.dialog
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| open_file | options | string 或 null | 选择单个文件 |
| open_files | options | string[] | 选择多个文件 |
| open_dir | options | string 或 null | 选择目录 |
| save_file | options | string 或 null | 选择保存路径 |
| message | message:string, options | void | 显示消息框 |
| confirm | message:string, options | boolean | 显示确认框 |
`options.title` 为对话框标题,`options.default_path` 为默认路径,`options.filters` 为文件过滤器数组。消息框 `options.kind` 支持 `info`、`warning`、`error`。
### bt.tray
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| enable | options | void | 启用托盘图标,重复调用会更新现有托盘 |
| disable | 无 | void | 关闭托盘图标 |
| set_icon | icon:string | void | 设置托盘图标路径 |
| set_tooltip | text:string | void | 设置托盘提示 |
| set_menu | menu:array | void | 设置托盘菜单 |
| on_menu_click | callback | function | 监听菜单点击,返回取消监听函数 |
菜单项格式为 `{id:'show', text:'显示窗口', enabled:true}`;分隔线格式为 `{type:'separator'}`。同一应用默认只保留一个托盘图标,重复调用 `window.bt.tray.enable()` 会更新图标、提示文本和菜单,不会新增多个托盘入口。
`window.bt.window.set_close_mode('tray')` 只设置窗口关闭按钮的行为:之后用户点击窗口关闭按钮或代码调用 `window.bt.window.close()` 时,窗口会隐藏到托盘,程序继续常驻运行。如果需要按钮点击后立刻关闭到托盘,应先设置 `tray` 模式,再调用 `window.bt.window.close()`。
`on_close_requested()` 用于必须在退出前等待异步落盘的应用。注册后,`exit` 模式的关闭请求会先被阻止并调用回调;回调完成保存后必须调用 `close_now()`。若保存失败,应用可保留窗口并提示用户,不能调用 `close_now()`。取消监听会恢复默认直接关闭行为。
### bt.clipboard
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| read_text | 无 | string | 读取文本剪贴板 |
| write_text | text:string | void | 写入文本 |
| clear | 无 | void | 清空剪贴板 |
### bt.screen
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| pick_color | options | ColorResult 或 null | 在遮罩出现前捕获各显示器画面,显示 Tauri 透明吸管遮罩;移动时实时预览指针像素,左键返回颜色,Esc 或右键返回 `null` |
| capture_area | options | CaptureResult 或 null | 在遮罩出现前捕获各显示器画面,拖动框选单个显示器内的区域;成功时可直接写入系统图片剪贴板 |
`pick_color(options)` 的 `options` 字段:
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| copy_to_clipboard | boolean | 否 | `true` | `true` 或 `false` | 成功后是否把大写 `#RRGGBB` 写入文本剪贴板 |
`capture_area(options)` 的 `options` 字段:
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| copy_to_clipboard | boolean | 否 | `true` | `true` 或 `false` | 成功后是否把 RGBA 图像直接写入系统图片剪贴板 |
`ColorResult` 字段:
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| x | integer | 是 | 无 | 虚拟桌面物理像素坐标,可为负值 | 取色点 X 坐标 |
| y | integer | 是 | 无 | 虚拟桌面物理像素坐标,可为负值 | 取色点 Y 坐标 |
| r | integer | 是 | 无 | 0~255 | 红色通道 |
| g | integer | 是 | 无 | 0~255 | 绿色通道 |
| b | integer | 是 | 无 | 0~255 | 蓝色通道 |
| a | integer | 是 | 无 | 0~255 | Alpha 通道,屏幕通常为 255 |
| rgb | integer | 是 | 无 | 0~16777215 | 24 位二进制色值,按 `(r << 16) | (g << 8) | b` 计算 |
| hex | string | 是 | 无 | `#RRGGBB` | 固定大写的十六进制色值 |
| rgba | integer[] | 是 | 无 | 4 个 0~255 字节 | 固定顺序 `[r, g, b, a]` |
| clipboard | boolean | 是 | 无 | `true` 或 `false` | 本次是否已写入文本剪贴板 |
`CaptureResult` 字段:
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| x | integer | 是 | 无 | 虚拟桌面物理像素坐标,可为负值 | 选区左上角 X 坐标 |
| y | integer | 是 | 无 | 虚拟桌面物理像素坐标,可为负值 | 选区左上角 Y 坐标 |
| width | integer | 是 | 无 | 至少 2 个物理像素 | 选区宽度 |
| height | integer | 是 | 无 | 至少 2 个物理像素 | 选区高度 |
| clipboard | boolean | 是 | 无 | `true` 或 `false` | 本次是否已写入系统图片剪贴板 |
屏幕图像不经过 Base64 或 JSON 数组返回前端;原生层直接把 RGBA 写入系统图片剪贴板,因此可以在支持图片粘贴的聊天框、富文本编辑器和图像软件中粘贴。纯文本输入框或主动禁用图片粘贴的软件不能接收图片。
### bt.shortcut
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| register | shortcut_id:string, accelerator:string, callback:function | function | 注册或替换一条系统全局快捷键,Promise 完成后返回异步注销函数 |
| unregister | shortcut_id:string | boolean | 注销指定 ID;存在并注销成功返回 `true` |
| unregister_all | 无 | void | 注销当前应用通过 `bt.shortcut` 注册的全部快捷键 |
`register()` 参数:
| 参数 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| shortcut_id | string | 是 | 无 | 1~64 个字母、数字、`-` 或 `_` | 页面使用的稳定快捷键 ID |
| accelerator | string | 是 | 无 | Tauri 快捷键组合,如 `CommandOrControl+Alt+C` | 操作系统全局快捷键 |
| callback | function | 是 | 无 | JavaScript 函数 | 快捷键按下后的回调 |
回调负载字段:
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| shortcut_id | string | 是 | 无 | 注册时的稳定 ID | 触发来源 |
| accelerator | string | 是 | 无 | 注册成功的快捷键文本 | 当前组合键 |
同一应用最多注册 16 条全局快捷键。相同 `shortcut_id` 会原子替换原组合;相同组合不能同时绑定给两个 ID。快捷键由应用进程持有,主窗口隐藏或关闭到托盘后仍可触发;应用退出时会由操作系统释放,页面主动停用时应调用返回的注销函数或 `unregister_all()`。
### bt.notify
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| permission_state | 无 | string | `granted`、`denied` 或 `prompt` |
| request_permission | 无 | string | 请求通知权限 |
| show | options | void | 显示通知 |
`options.title` 为空时使用应用标题,`options.body` 为通知正文。
`request_permission()` 返回的是系统通知权限状态;`show()` 才是真正发送系统通知。Windows 便携 exe 会使用兼容的 toast 发送路径,不依赖安装器创建的应用通知注册。通知是否弹出桌面横幅仍由操作系统通知设置、勿扰模式和应用通知策略决定,未弹出时通常仍可在系统通知中心查看。
### bt.drag
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| on_files | callback | function | 监听拖入文件或目录,回调参数为绝对路径数组 |
### bt.app
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| version | 无 | string | 当前应用版本 |
| engine_version | 无 | string | bt-app 引擎版本 |
| platform | 无 | string | `windows`、`macos`、`linux` 或其他系统名 |
| open_url | url:string | void | 用系统默认浏览器打开 HTTP/HTTPS 地址 |
| open_path | path:string | void | 用系统默认程序打开文件或目录 |
| reveal_path | path:string | void | 在文件管理器中定位路径 |
| quit | 无 | void | 退出程序 |
| args | 无 | string[] | 已剔除 bt-app 命令、BTR 路径和 `--` 的当前软件业务参数 |
| info | path:string | BtrAppInfo | 读取 BTR 软件的 app.json、容器信息和图标,不执行软件代码 |
| run | path:string, args:string[] | BtrRunResult | 使用当前 bt-app 启动一个独立 BTR 软件进程 |
| documents_dir | 无 | string | 操作系统解析后的用户“文档”已知目录绝对路径 |
| watch_path | path:string, callback:function, options | function | 监听目录文件变化,返回取消监听函数 |
| unwatch_path | 无 | void | 停止当前窗口的全部监听 |
`info(path)` 的 `path` 必须是存在的 `.btr` 文件绝对路径或可规范化路径。它会完成 BTR 格式、版本、资源路径和大小上限校验,只读取 `btr.json`、`app.json` 及可选图标,不执行 `app.main`、`server.bt` 或页面脚本,适合工具条安全展示软件信息。`app.icon` 当前只支持 ICO,因此 `icon_data_url` 使用 `data:image/x-icon;base64,...`;未配置或不存在时为 `null`。
`BtrAppInfo` 字段:
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| path | string | 是 | 无 | 规范绝对 `.btr` 路径 | 实际检查的软件文件 |
| id | string | 是 | 无 | 合法 `app.id` | 稳定应用标识 |
| name | string | 是 | 无 | 合法 `app.name` | 应用内部名称 |
| title | string | 是 | 无 | 非空文本 | 默认展示标题 |
| version | string | 是 | 无 | 非空文本 | 应用版本 |
| description | string 或 null | 是 | `null` | 文本或 `null` | 应用说明 |
| mode | string | 是 | 无 | `static`、`server`、`remote` | 运行模式 |
| entry | string | 是 | 无 | 已校验入口 | 页面入口 |
| icon | string 或 null | 是 | `null` | 项目内 ICO 相对路径或 `null` | app.json 原始图标路径 |
| icon_data_url | string 或 null | 是 | `null` | ICO data URL 或 `null`;图标最大 16 MiB | 可直接赋给 `
` 的图标 |
| format_version | integer | 是 | 无 | 当前为 `1` | BTR 容器格式版本 |
| bt_version | string | 是 | 无 | BT 版本 | 生成 BTR 的运行时版本 |
| bt_min_version | string | 是 | 无 | BT 版本 | 软件声明的最低运行时版本 |
| file_count | integer | 是 | 无 | 0~4095 | 不含内部 `btr.json` 的资源数 |
| package_bytes | integer | 是 | 无 | 最大 256 MiB | 压缩后文件字节数 |
| uncompressed_bytes | integer | 是 | 无 | 最大 512 MiB | 全部条目展开后总字节数 |
`run(path, args=[])` 会在启动子进程前再次完整校验 BTR,并以参数数组直接创建进程,不经过 shell。`args` 最多 256 项,单项最长 32768 字节。Promise 完成仅表示操作系统已创建进程,不表示其 WebView 已完成页面加载。
`BtrRunResult` 字段:
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| pid | integer | 是 | 无 | 操作系统进程 ID | 已创建的 BTR 软件进程 |
| path | string | 是 | 无 | 规范绝对 `.btr` 路径 | 实际运行的软件文件 |
| id | string | 是 | 无 | 合法 `app.id` | 已运行软件的稳定标识 |
```javascript
const info = await window.bt.app.info('D:/BT Apps/picker.btr')
// 输出:BTR 软件标题
console.log(info.title)
const started = await window.bt.app.run(info.path, ['--quick'])
// 输出:新软件进程 ID
console.log(started.pid)
```
每次 `run()` 都创建独立软件进程,使 VM、窗口、托盘、快捷键和退出生命周期彼此隔离;它解决的是多个软件重复携带 bt-app exe 的磁盘体积问题,不承诺多个正在运行的软件共用同一个 WebView 进程。像吸管、截图这类工具条内置能力可以继续留在工具条进程中,只有真正启动 BTR 软件时才创建其窗口。
`documents_dir()` 使用系统已知目录 API,不会硬编码 `Documents` 名称,可兼容本地化、OneDrive 和系统重定向。`watch_path()` 使用系统文件事件监听目录变化,不会按固定周期扫描目录;同一窗口最多同时保留 64 个监听器。`options.recursive` 默认为 `true`,表示递归监听子目录。回调参数为 `{watch_id, root, kind, paths}`,`kind` 可能是 `create`、`modify`、`remove`、`rename` 或 `other`。一般应保存 `watch_path()` 返回的取消函数,不需要直接使用内部 `watch_id`。
### bt.credential
应用凭据以明文 JSON 保存到当前用户的隐藏应用目录。显式配置 `app.id` 时使用该稳定标识;未配置时为兼容旧版本继续使用 `app.name`,例如 `%USERPROFILE%\.bt_ai\credentials\`。这是便于迁移和人工维护的明文格式,不使用 Windows DPAPI。接口不会向页面提供读取明文的方法,但能访问该用户目录的本机程序可以读取文件,因此调用方必须在界面中明确提示并保护操作系统账户权限。
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| store | credential_id:string, secret:string | void | 以 JSON 明文原子保存凭据;同 ID 会覆盖旧文件 |
| has | credential_id:string | boolean | 判断凭据 JSON 是否存在、格式有效且 ID 匹配 |
| delete | credential_id:string | boolean | 删除指定凭据;返回是否实际删除 |
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| credential_id | string | 是 | 无 | 1~128 字符,仅字母、数字、`_`、`-`、`.` | 调用方定义的稳定凭据 ID,文件名为 `.json` |
| secret | string | 是 | 无 | 1~4096 个 UTF-8 字符 | 保存到 JSON 的 `api_key` 明文字段,原生请求构造时短暂读取 |
### bt.http
`stream()` 发起真实异步 HTTP 请求,通过回调依次返回响应头、SSE 或普通分块以及最终完整正文。启动前会先安装事件监听;`cancel()` 会先关闭原生事件闸门,再通知网络任务终止,因此取消成功后不会再派发该请求的状态或正文事件。
| `stream options` 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| request_id | string | 否 | 自动生成 UUID | 1~128 字符,仅字母、数字、`_`、`-`、`.` | 调用方需要预先关联事件时可指定的唯一请求 ID |
| url | string | 是 | 无 | `http://` 或 `https://` | 请求地址 |
| method | string | 否 | `POST` | 1~16 个 ASCII 字母 | HTTP 方法 |
| headers | object | 否 | `{}` | 最多 32 项;名称不超过 128 字符,值不超过 8192 字符 | 非敏感请求头;禁止 `authorization`、`proxy-authorization` 和 `cookie` |
| body | string | 否 | `""` | UTF-8 字节数不超过 4 MiB | 请求正文 |
| credential_id | string | 否 | `""` | 空值或合法应用凭据 ID | 非空时由原生层读取 JSON 并注入 `Authorization: Bearer ...`,不会回传页面 |
| timeout_ms | integer | 否 | `120000` | 1000~600000 | 整个请求的超时毫秒数 |
| max_response_bytes | integer | 否 | `4000000` | 1~4000000 | 单个响应允许累计的最大字节数 |
`stream()` 返回控制对象:
原生层会在构造第一个 HTTP client 前幂等初始化 Rustls ring crypto provider。因此,即使应用冷启动后直接调用 `window.bt.http.stream()`,也不依赖 BT 标准库或 Web 服务提前执行网络初始化。
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| request_id | string | 是 | 无 | 本次请求 ID | 与事件对应的稳定 ID |
| active_limit | integer | 是 | 无 | 当前固定为 8 | 当前进程允许的并发流式请求上限 |
| max_response_bytes | integer | 是 | 无 | 1~4000000 | 本次请求实际生效的响应上限 |
| off | function | 是 | 无 | 无参数 | 仅取消页面事件监听,不终止请求 |
| cancel | function | 是 | 无 | 无参数,返回 `Promise` | 原子关闭事件闸门并取消请求 |
流式回调事件字段:
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| request_id | string | 是 | 无 | 本次请求 ID | 请求关联 ID |
| sequence | integer | 是 | 无 | 从 1 严格递增 | 单请求事件顺序 |
| kind | string | 是 | 无 | `start`、`headers`、`chunk`、`done`、`error`、`cancelled` | 事件类型 |
| status | integer | 是 | `0` | 0 或 HTTP 状态码 | 尚未收到响应头时为 0 |
| data | string | 是 | `""` | 单分块文本或最终完整正文 | `chunk` 与 `done` 的正文数据 |
| message | string | 是 | `""` | 脱敏错误文本 | `error` 等事件的状态说明 |
| received_bytes | integer | 是 | `0` | 0~本次响应上限 | 当前累计响应字节数 |
单个响应最多派发 16384 个分块事件;超过字节数或分块数上限会以 `error` 结束并释放请求登记。
### bt.workspace
工作区 API 先登记一个真实目录,再用短期 `workspace_id` 操作相对路径。所有路径都经过规范化和真实路径边界检查,递归列表不跟随符号链接或 junction。
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| open | root:string | WorkspaceOpenResult | 登记已存在的真实目录;最多保留 8 个,超限淘汰最早登记项 |
| close | workspace_id:string | boolean | 关闭登记,不删除任何文件 |
| list | workspace_id:string, relative:string, recursive:boolean | WorkspaceEntry[] | 有界列出目录,最多 4096 项 |
| read | workspace_id:string, relative:string, max_bytes:integer | WorkspaceReadResult | 读取 UTF-8 文本,最大 4 MiB |
| atomic_write | workspace_id:string, relative:string, content:string, expected_sha256:string\|null | WorkspaceWriteResult | 同目录临时文件原子替换,并可按旧摘要拒绝并发覆盖 |
| 调用字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| root | string | 是 | 无 | 已存在的绝对目录 | 工作区真实根目录 |
| workspace_id | string | 是 | 无 | `open()` 返回的 ID | 原生工作区登记 |
| relative | string | list 否;read/write 是 | `""` | 工作区内相对路径;禁止绝对路径和 `..` | 目标目录或文件 |
| recursive | boolean | 否 | `false` | `true` 或 `false` | 是否递归列目录 |
| max_bytes | integer | 否 | `1048576` | 1~4194304 | 读取上限,超过即拒绝而不是截断 |
| content | string | write 是 | 无 | UTF-8 文本 | 原子写入的新内容 |
| expected_sha256 | string 或 null | 否 | `null` | 64 位十六进制 SHA-256 | 非空时只允许覆盖摘要相同的旧文件 |
| 返回对象 | 字段 | 类型 | 含义 |
| --- | --- | --- | --- |
| WorkspaceOpenResult | workspace_id | string | 原生生成的短期工作区 ID |
| WorkspaceOpenResult | root | string | 规范化后的真实根目录 |
| WorkspaceEntry | path | string | 使用 `/` 的工作区相对路径 |
| WorkspaceEntry | kind | string | `file`、`directory` 或 `symlink` |
| WorkspaceEntry | bytes | integer | 文件字节数;目录和链接为 0 |
| WorkspaceReadResult | path | string | 规范相对路径 |
| WorkspaceReadResult | content | string | UTF-8 正文 |
| WorkspaceReadResult | bytes | integer | 原始字节数 |
| WorkspaceReadResult | sha256 | string | 内容 SHA-256 |
| WorkspaceWriteResult | path | string | 规范相对路径 |
| WorkspaceWriteResult | bytes | integer | 新内容字节数 |
| WorkspaceWriteResult | sha256 | string | 新内容 SHA-256 |
| WorkspaceWriteResult | previous_sha256 | string 或 null | 旧内容摘要;新文件为 null |
### bt.process
原生进程使用程序名和参数数组直接启动,不经过 shell。进程必须属于一个调用方任务和已登记工作区;查询、停止时必须同时匹配 `process_id`、`task_id` 与不可伪造的 `identity`。停止会清理该句柄对应的进程树,不按名称批量结束外部进程。
| `start options` 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| task_id | string | 是 | 无 | 合法短期 ID | 调用方任务归属 |
| program | string | 是 | 无 | 非空,最长 32768 字符 | 可执行程序路径或程序名 |
| args | string[] | 否 | `[]` | 最多 256 项,单项最长 32768 字符 | 不经过 shell 的参数数组 |
| workspace_id | string | 是 | 无 | 已登记工作区 ID | 进程目录边界 |
| cwd | string | 否 | `""` | 工作区内已存在的相对目录 | 子进程当前目录 |
| environment | object | 否 | `{}` | 最多 64 项;名称最长 256,值最长 32768 | 只注入该子进程的环境变量,不写入进程快照 |
| timeout_ms | integer | 否 | `0` | 0~86400000 | 0 表示不自动超时;否则到期清理进程树 |
| output_limit | integer | 否 | `65536` | 1~1048576 | stdout、stderr 各自保留的尾部字节上限 |
| ProcessSnapshot 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| id | string | 是 | 无 | 原生记录 ID | `status()` / `stop()` 的 process_id |
| task_id | string | 是 | 无 | 启动时值 | 调用方任务归属 |
| identity | string | 是 | 无 | 64 位摘要 | 精确停止所需的进程身份值 |
| pid | integer | 是 | 无 | 操作系统 PID | 仅用于观察,不可代替 identity |
| program | string | 是 | 无 | 启动时值 | 程序文本 |
| args | string[] | 是 | `[]` | 启动时值 | 参数数组;不得用参数传递敏感凭据 |
| cwd | string | 是 | 无 | 工作区内真实目录 | 实际当前目录 |
| running | boolean | 是 | 无 | `true` 或 `false` | 是否仍在运行 |
| exit_code | integer 或 null | 是 | `null` | 运行中为 null | 退出码 |
| timed_out | boolean | 是 | `false` | `true` 或 `false` | 是否由超时终止 |
| stdout / stderr | string | 是 | `""` | 各自不超过 output_limit | 有界输出尾部 |
| stdout_dropped / stderr_dropped | integer | 是 | `0` | 大于等于 0 | 因上限被丢弃的字节数 |
| started_at | integer | 是 | 无 | Unix 毫秒时间戳 | 启动时间 |
`on_event()` 回调字段为 `process_id:string`、`task_id:string`、`kind:string`、`data:string`、`pid:integer`;`kind` 可为 `start`、`stdout`、`stderr`、`exit`、`timeout` 或 `stopped`。同时最多登记 32 个进程,结束记录会按启动时间淘汰。
### bt.data
应用 JSON 状态默认保存在当前用户的隐藏应用目录下 `sessions` 子目录。显式配置 `app.id` 时使用稳定标识;未配置时为兼容旧版本继续使用 `app.name`。自动化验收可设置 `BT_APP_DATA_HOME`,把隐藏应用目录临时放到指定父目录;正常桌面运行应保持该变量未设置。清理采用“预览—用户二次确认—一次性票据执行”三步边界,只清理该应用数据根的固定子目录,不访问已登记工作区。
| 方法 | 参数 | 返回值 | 说明 |
| --- | --- | --- | --- |
| store | key:string, value:any JSON | void | 原子保存一个 JSON 文件 |
| load | key:string | any JSON 或 null | 文件不存在时返回 null |
| prepare_cleanup | 无 | CleanupPreview | 统计固定类别并生成 120 秒有效的一次性确认票据 |
| confirm_cleanup | confirm_token:string | CleanupResult | 消费票据并实际清理;票据错误、过期或重复使用均拒绝 |
| 调用字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| key | string | store/load 是 | 无 | 1~64 字符,仅字母、数字、`_`、`-` | 应用 JSON 文件键 |
| value | JSON | store 是 | 无 | 序列化后不超过 4 MiB | 应用状态值 |
| confirm_token | string | confirm_cleanup 是 | 无 | prepare_cleanup 返回且未消费、未过期 | 二次确认票据 |
| 返回对象 | 字段 | 类型 | 含义 |
| --- | --- | --- | --- |
| CleanupPreview | confirm_token | string | 120 秒有效的一次性票据 |
| CleanupPreview | categories | CleanupCategory[] | 固定清理类别统计 |
| CleanupPreview | total_files | integer | 总文件数 |
| CleanupPreview | total_bytes | integer | 总字节数 |
| CleanupPreview | boundary | string | 本次清理真实目录边界说明 |
| CleanupCategory | name | string | `knowledge_cache`、`sessions`、`logs`、`temp` 或 `credentials` |
| CleanupCategory | files | integer | 类别文件数 |
| CleanupCategory | bytes | integer | 类别总字节数 |
| CleanupResult | cleared | string[] | 已处理的固定类别 |
| CleanupResult | removed_files | integer | 清理前文件数 |
| CleanupResult | removed_bytes | integer | 清理前字节数 |
| CleanupResult | workspace_untouched | boolean | 当前实现成功时恒为 true,表示未触碰用户工作区 |
### bt.events
`on_backend(callback)` 监听 `bt.call()` 完成事件,返回取消监听函数。回调字段如下;事件不包含 BT 函数返回正文。
| 字段 | 类型 | 必填 | 默认值 | 有效范围或可选值 | 含义 |
| --- | --- | --- | --- | --- | --- |
| name | string | 是 | 无 | 被调用的 BT 全局函数名 | 调用来源 |
| ok | boolean | 是 | 无 | `true` 或 `false` | IPC 调用是否成功 |
| at | integer | 是 | 无 | Unix 毫秒时间戳 | 完成时间 |
## 返回值
`bt.call()` 直接返回 BT 函数的返回值。BT 函数返回对象时,前端收到普通 JSON 对象;返回字符串、数字、布尔或数组时,前端收到对应 JSON 值。
如果 BT 函数不存在、执行报错、VM 调用队列已满或桌面 API 参数无效,Promise 会 reject,不会返回 `{error,message,data}` 包装对象。
## 代码示例
`main.bt`:
```bt
/**
* 返回前端传入的数据。
*
* @param data 前端 JSON 参数。
* @return 响应对象。
*/
fn inspect(data) {
{
ok: true,
message: 'BT 已收到',
data: data
}
}
```
页面调用:
```js
try {
const result = await window.bt.call('inspect', {name: 'BT'})
await window.bt.window.set_title(result.message)
} catch (err) {
console.log('调用失败', String(err))
}
```
屏幕吸管、截图和全局快捷键:
```js
const stopPickColor = await window.bt.shortcut.register(
'pick-color',
'CommandOrControl+Alt+C',
async () => {
const color = await window.bt.screen.pick_color({copy_to_clipboard: true})
if (color) console.log(color.hex, color.rgb, color.rgba)
}
)
const screenshot = await window.bt.screen.capture_area({copy_to_clipboard: true})
if (screenshot) console.log(screenshot.width, screenshot.height)
// 不再需要快捷键时注销
await stopPickColor()
```
托盘菜单:
```js
await window.bt.tray.enable({
tooltip: 'BT 应用',
menu: [
{id: 'show', text: '显示窗口'},
{type: 'separator'},
{id: 'quit', text: '退出程序'}
]
})
window.bt.tray.on_menu_click(async (id) => {
if (id == 'show') {
await window.bt.window.show()
await window.bt.window.focus()
}
if (id == 'quit') {
await window.bt.app.quit()
}
})
await window.bt.window.set_close_mode('tray')
await window.bt.window.close()
```
退出前完成异步快照:
```js
const stopCloseListener = await window.bt.window.on_close_requested(async () => {
try {
await save_session_snapshot()
await window.bt.window.close_now()
} catch (error) {
await window.bt.dialog.message('会话快照保存失败:' + String(error), {
title: '无法安全退出',
kind: 'error'
})
}
})
```
读取用户文档目录:
```js
const documentsDir = await window.bt.app.documents_dir()
console.log(documentsDir)
```
监听目录变化:
```js
const stopWatch = await window.bt.app.watch_path('D:/docs', (event) => {
console.log(event.kind, event.paths)
})
// 不再需要监听时取消
await stopWatch()
```
流式请求与真实取消:
```js
const controller = await window.bt.http.stream({
url: 'https://example.com/events',
method: 'POST',
headers: {'content-type': 'application/json'},
body: JSON.stringify({stream: true}),
credential_id: 'service-primary'
}, (event) => {
if (event.kind == 'chunk') {
console.log(event.data)
}
})
// 用户取消后,原生层不会再派发该请求的事件
await controller.cancel()
controller.off()
```
工作区原子更新:
```js
const workspace = await window.bt.workspace.open('D:/project/demo')
const current = await window.bt.workspace.read(workspace.workspace_id, 'config.json')
await window.bt.workspace.atomic_write(
workspace.workspace_id,
'config.json',
JSON.stringify({enabled: true}),
current.sha256
)
```
## 注意事项
- `bt.call()` 是长期 VM 业务通道;窗口、托盘、剪贴板、通知等桌面能力走独立 command,不占用 BT VM。
- VM 调用队列有上限。前端高频调用时应节流,避免无控增长。
- `remote` 页面同样拥有完整 `window.bt` 能力,只应加载可信地址。
- 同一个窗口最多同时保留 64 个 `watch_path()` 监听;每个返回的取消函数只停止对应监听,`unwatch_path()` 不传参数时停止全部监听。
- `on_close_requested()` 只拦截窗口的 `exit` 关闭流程;回调应防止重复执行,并且只有异步收尾完成后才能调用 `close_now()`。
- 设置 `BT_PERMISSION_DENY=desktop` 后,窗口、对话框、托盘、剪贴板、全局快捷键、通知、拖入文件事件、文件监听事件和应用级桌面命令会被拒绝;屏幕吸管和截图还要求单独的 `screen` 权限。`bt.call()` 本身不受 `desktop` 限制,被调用 BT 函数内部使用的标准库能力仍按各自权限检查。
- Windows、macOS 和 Linux X11 使用同一套 Tauri 自绘吸管/框选遮罩;macOS 首次使用通常需要用户授予屏幕录制权限。原生 Wayland 不支持可靠的全局窗口坐标和普通全局快捷键,当前版本会返回错误,不承诺与 X11 相同的遮罩界面。
- 第一版截图选区限制在单个显示器内;多显示器各自有遮罩,但不能从一块显示器跨越到另一块显示器拖选。HDR、广色域、受保护视频和系统安全桌面可能与肉眼颜色不同或返回黑色内容。
- 通用生产 API 只提供受边界约束的客户端 HTTP、已登记工作区和句柄化子进程;不会暴露任意文件系统、网络监听、shell 字符串或按名称结束进程能力。
- 凭据是当前用户目录中的明文 JSON,不得再放入 URL、普通请求头、请求体、进程参数、日志或页面存储;应使用 `credential_id` 让原生层注入,并限制用户目录访问权限。
- 应用关闭、任务取消或清理数据前,应先取消仍在运行的流式请求并精确停止所属任务的子进程。
- 通用桌面 API 示例位于 `examples/desktop-api`;吸管、截图与全局快捷键示例位于 `examples/desktop-screen-tools`。