# Desktop API
## Function
`bt_app` injects `window.bt` into the `static`, `server` and `remote` pages. The desktop API provides BT backend calls, window control, system dialogs, tray, clipboard, screen straw, frame selection screenshots, global shortcut keys, notifications, file drag-in, directory file monitoring, application information, as well as bounded streaming HTTP, application credentials, workspace, native processes and application-owned data capabilities. The
page should only use `window.bt`. The runner will not expose the global `window.__TAURI__`, nor will it expose the local `fs`, `net`, and `process` capabilities directly to the front end.
## Syntax
```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)
```
All asynchronous methods return `Promise`. Data is returned directly on success; Promise will reject on failure, and the error value is a string or an error object that can be converted to a string.
## Parameters
### bt.call
| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| name | string | Yes | Global function name in `main.bt` |
| ...args | any | No | Parameters passed to the BT function will be converted according to JSON value |
### bt.window
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| set_title | title:string | void | Set window title |
| set_size | width:number, height:number | void | Set the logical size of the window |
| set_position | x:number, y:number | void | Set the logical coordinates of the upper left corner of the window frame, supporting negative coordinates for multiple monitors |
| placement | None | object | Read the logical coordinates and dimensions of the window frame and the available workspace of the current monitor |
| set_background_color | color:string | void | Set the background color of the window and WebView; `color` must be `#RRGGBB` |
| set_resizable | resizable:boolean | void | Set whether resizing is allowed |
| minimize / maximize / restore / close / hide / show / focus / center | None | void | Common window actions |
| set_fullscreen | fullscreen:boolean | void | Set full screen |
| is_fullscreen / is_maximized / is_minimized / is_visible / is_always_on_top | None | boolean | Read window status |
| set_always_on_top | enabled:boolean | void | Set the window to be on top |
| set_decorations | visible:boolean | void | Set the system title bar and border |
| set_skip_taskbar | enabled:boolean | void | Set whether to skip the taskbar |
| set_close_mode | mode:string | void | `exit`, `hide` or `tray` |
| on_close_requested | callback:function | function | Take over the `exit` mode close request and return the cancel listening function |
| close_now | None | void | Release and close the window after the page completes asynchronous closing |
| drag | None | void | Custom title bar drag window |
| start_resize | edge:string | void | `top`、`bottom`、`left`、`right`、`top_left`、`top_right`、`bottom_left`、`bottom_right` |
| flash | None | void | Request the system to remind the user to pay attention to the window |
| open_devtools | None | void | Open WebView developer tools, only available when `dev.devtools=true` |
`set_background_color()` simultaneously updates the opaque background color of the native window and WebView, which is suitable for switching between light and dark themes for ordinary windows. When HTML rounded corners, PNG Alpha, or a transparent page background are required, `window.transparent=true` should be set during `app.json` creation. This runtime method cannot be used in place of a transparent window configuration.
`x` and `y` of `set_position(x, y)` use logical pixels; negative numbers can be passed when multiple monitors are located on the left or above the main screen. `placement()` return fields:
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| x | integer | Yes | None | Logical pixel, can be negative | X coordinate of the upper left corner of the window frame |
| y | integer | yes | none | logical pixel, can be negative | Y coordinate of the upper left corner of the window frame |
| width | integer | yes | none | greater than 0 | window frame width |
| height | integer | yes | none | greater than 0 | window frame height |
| scale_factor | number | Yes | None | Greater than 0 | The scaling factor of the current display from logical pixels to physical pixels |
| content_area | object | Yes | None | See table below | WebView content area after deducting system borders and shadows |
| work_area | object | Yes | None | See the table below | The available work area of the current monitor after deducting the system area such as the taskbar |
Both `content_area` and `work_area` use the same rectangular field; the former is the WebView content area on the desktop, and the latter is the available workspace of the current monitor:
| Object | Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- | --- |
| content_area | x | integer | yes | none | logical pixel, can be negative | X coordinate of the upper left corner of the content area |
| content_area | y | integer | yes | none | logical pixel, can be negative | Y coordinate of the upper left corner of the content area |
| content_area | width | integer | yes | none | greater than 0 | content area width |
| content_area | height | integer | yes | none | greater than 0 | content area height |
| work_area | x | integer | yes | none | logical pixel, can be negative | X coordinate of the upper left corner of the work area |
| work_area | y | integer | yes | none | logical pixel, can be negative | Y coordinate of the upper left corner of the work area |
| work_area | width | integer | yes | none | greater than 0 | work area width |
| work_area | height | integer | yes | none | greater than 0 | work area height |
```javascript
const placement = await window.bt.window.placement()
// Move to the upper left of the current monitor workspace and retain 10 logical pixels
await window.bt.window.set_position(
placement.work_area.x + 10,
placement.work_area.y + 10
)
```
### bt.dialog
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| open_file | options | string or null | select a single file |
| open_files | options | string[] | Select multiple files |
| open_dir | options | string or null | select directory |
| save_file | options | string or null | select save path |
| message | message:string, options | void | display message box |
| confirm | message:string, options | boolean | Show confirmation box |
`options.title` is the dialog box title, `options.default_path` is the default path, and `options.filters` is the file filter array. Message box `options.kind` supports `info`, `warning`, and `error`.
### bt.tray
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| enable | options | void | Enable the tray icon, repeated calls will update the existing tray |
| disable | None | void | Close tray icon |
| set_icon | icon:string | void | Set tray icon path |
| set_tooltip | text:string | void | Set tray tip |
| set_menu | menu:array | void | Set tray menu |
| on_menu_click | callback | function | Monitor menu click and return to cancel listening function |
The menu item format is `{id:'show', text:'Show Window', enabled:true}`; the separator format is `{type:'separator'}`. The same application retains one tray icon by default. Repeated calls to `window.bt.tray.enable()` update the icon, tooltip, and menu without adding duplicate tray entries.
`window.bt.window.set_close_mode('tray')` only sets the behavior of the window close button: when the user clicks the window close button or the code calls `window.bt.window.close()`, the window will be hidden in the tray and the program will continue to run permanently. If you need to close to the tray immediately after clicking the button, you should set the `tray` mode first and then call `window.bt.window.close()`.
`on_close_requested()` is used for applications that must wait for asynchronous disk placement before exiting. After registration, the shutdown request of `exit` mode will be blocked first and the callback will be called; `close_now()` must be called after the callback completes the save. If the save fails, the application can keep the window and prompt the user, but cannot call `close_now()`. Canceling listening will restore the default direct shutdown behavior.
### bt.clipboard
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| read_text | None | string | Read text from clipboard |
| write_text | text:string | void | write text |
| clear | None | void | Clear the clipboard |
### bt.screen
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| pick_color | options | ColorResult or null | Capture each monitor screen before the mask appears, display the Tauri transparent straw mask; preview the pointer pixels in real time when moving, left click to return to color, Esc or right click to return `null` |
| capture_area | options | CaptureResult or null | Capture the screen of each monitor before the mask appears, drag the frame to select the area within a single monitor; if successful, it can be written directly to the system image clipboard |
`options` fields of `pick_color(options)`:
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| copy_to_clipboard | boolean | No | `true` | `true` or `false` | Whether to write uppercase `#RRGGBB` to the text clipboard after success |
`options` fields of `capture_area(options)`:
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| copy_to_clipboard | boolean | No | `true` | `true` or `false` | Whether to write the RGBA image directly to the system picture clipboard after success |
`ColorResult` field:
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| x | integer | Yes | None | Physical pixel coordinates of the virtual desktop, which can be negative | X coordinate of the color point |
| y | integer | Yes | None | Physical pixel coordinates of the virtual desktop, which can be negative | Y coordinate of the color point |
| r | integer | yes | none | 0~255 | red channel |
| g | integer | yes | none | 0~255 | green channel |
| b | integer | yes | none | 0~255 | blue channel |
| a | integer | Yes | None | 0~255 | Alpha channel, the screen is usually 255 |
| rgb | integer | Yes | None | 0~16777215 | 24-bit binary color value, calculated according to `(r << 16) | (g << 8) | b` |
| hex | string | yes | none | `#RRGGBB` | Fixed uppercase hexadecimal color value |
| rgba | integer[] | Yes | None | 4 bytes from 0 to 255 | Fixed order `[r, g, b, a]` |
| clipboard | boolean | yes | none | `true` or `false` | Whether the text has been written to the clipboard this time |
`CaptureResult` field:
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| x | integer | Yes | None | Physical pixel coordinates of the virtual desktop, which can be negative | X coordinate of the upper left corner of the selection |
| y | integer | Yes | None | Physical pixel coordinates of the virtual desktop, which can be negative | Y coordinate of the upper left corner of the selection |
| width | integer | yes | none | at least 2 physical pixels | selection width |
| height | integer | yes | none | at least 2 physical pixels | selection height |
| clipboard | boolean | yes | none | `true` or `false` | Whether the system picture clipboard has been written to this time |
The screen image is returned to the front end without passing through Base64 or JSON array; the native layer directly writes RGBA to the system image clipboard, so it can be pasted in chat boxes, rich text editors and image software that support image pasting. Plain text input boxes or software that actively disables image pasting cannot accept images.
### bt.shortcut
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| register | shortcut_id:string, accelerator:string, callback:function | function | Register or replace a system global shortcut key, return the asynchronous cancellation function after Promise is completed |
| unregister | shortcut_id:string | boolean | Unregister the specified ID; it exists and returns `true` if the cancellation is successful |
| unregister_all | None | void | Unregister all shortcut keys registered by the current application through `bt.shortcut` |
`register()` parameters:
| Parameters | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| shortcut_id | string | Yes | None | 1 to 64 letters, numbers, `-` or `_` | Stable shortcut key ID used by the page |
| accelerator | string | Yes | None | Tauri shortcut key combination, such as `CommandOrControl+Alt+C` | Operating system global shortcut key |
| callback | function | Yes | None | JavaScript function | Callback after shortcut key is pressed |
Callback payload fields:
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| shortcut_id | string | yes | none | stable ID at registration | trigger source |
| accelerator | string | yes | none | accelerator text of successful registration | current key combination |
A maximum of 16 global shortcut keys can be registered for the same application. The same `shortcut_id` will atomically replace the original combination; the same combination cannot be bound to two IDs at the same time. The shortcut key is held by the application process and can still be triggered after the main window is hidden or closed to the tray; it will be released by the operating system when the application exits. When the page is actively deactivated, the returned logout function or `unregister_all()` should be called.
### bt.notify
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| permission_state | None | string | `granted`, `denied`, or `prompt` |
| request_permission | None | string | Request notification permission |
| show | options | void | show notifications |
When `options.title` is empty, the application title is used, and `options.body` is the notification body.
`request_permission()` returns the system notification permission status; `show()` actually sends system notifications. Windows portable exes use a compatible toast delivery path and do not rely on app notification registration created by the installer. Whether a notification pops up on the desktop banner is still determined by the operating system notification settings, Do Not Disturb mode and application notification policy. If it does not pop up, it can usually still be viewed in the system notification center.
### bt.drag
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| on_files | callback | function | Listen for dragged files or directories, the callback parameter is an absolute path array |
### bt.app
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| version | None | string | Current application version |
| engine_version | None | string | bt_app engine version |
| platform | None | string | `windows`, `macos`, `linux` or other system name |
| open_url | url:string | void | Open the HTTP/HTTPS address with the system default browser |
| open_path | path:string | void | Open the file or directory with the system default program |
| reveal_path | path:string | void | Locate path in file manager |
| quit | None | void | Exit program |
| args | None | string[] | The current software business parameters of the bt_app command, BTR path and `--` have been eliminated |
| info | path:string | BtrAppInfo | Read the app.json, container information and icon of BTR software without executing the software code |
| run | path:string, args:string[] | BtrRunResult | Start an independent BTR software process using the current bt_app |
| documents_dir | None | string | The absolute path of the user's "documents" known directory after parsing by the operating system |
| watch_path | path:string, callback:function, options | function | Monitor directory file changes and return to cancel the listening function |
| unwatch_path | None | void | Stop all monitoring of the current window |
`path` of `info(path)` must be an absolute or canonical path to an existing `.btr` file. It will complete the BTR format, version, resource path and size limit verification, only read `btr.json`, `app.json` and optional icons, and will not execute `app.main`, `server.bt` or page scripts. It is suitable for toolbars to safely display software information. `app.icon` currently only supports ICO, so `icon_data_url` uses `data:image/x-icon;base64,...`; when not configured or does not exist, it is `null`.
`BtrAppInfo` field:
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| path | string | yes | none | canonical absolute `.btr` path | actual software file to check |
| id | string | yes | none | legal `app.id` | stable application identification |
| name | string | yes | none | legal `app.name` | application internal name |
| title | string | yes | none | non-empty text | default display title |
| version | string | yes | none | non-empty text | application version |
| description | string or null | yes | `null` | text or `null` | application description |
| mode | string | yes | none | `static`, `server`, `remote` | operating mode |
| entry | string | Yes | None | Verified entry | Page entry |
| icon | string or null | Yes | `null` | ICO relative path within the project or `null` | app.json original icon path |
| icon_data_url | string or null | Yes | `null` | ICO data URL or `null`; maximum icon size 16 MiB | Icon that can be directly assigned to `
` |
| format_version | integer | Yes | None | Currently `1` | BTR container format version |
| bt_version | string | yes | none | BT version | Runtime version to generate BTR |
| bt_min_version | string | yes | none | BT version | Minimum runtime version declared by the software |
| file_count | integer | Yes | None | 0~4095 | Number of resources excluding internal `btr.json` |
| package_bytes | integer | Yes | None | Maximum 256 MiB | Number of bytes in the compressed file |
| uncompressed_bytes | integer | Yes | None | Maximum 512 MiB | Total number of bytes after expansion of all entries |
`run(path, args=[])` will completely verify the BTR again before starting the child process, and create the process directly with the parameter array without going through the shell. `args` has a maximum of 256 entries, and a single entry has a maximum length of 32768 bytes. Promise completion only means that the operating system has created the process, not that its WebView has completed page loading.
`BtrRunResult` field:
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| pid | integer | yes | none | operating system process ID | created BTR software process |
| path | string | yes | none | canonical absolute `.btr` path | actual running software file |
| id | string | yes | none | legal `app.id` | stable identification of the running software |
```javascript
const info = await window.bt.app.info('D:/BT Apps/picker.btr')
// Output: BTR software title
console.log(info.title)
const started = await window.bt.app.run(info.path, ['--quick'])
// Output: new software process ID
console.log(started.pid)
```
Each time `run()` creates an independent software process to isolate the VM, window, tray, shortcut keys and exit life cycle from each other; it solves the disk volume problem of multiple software repeatedly carrying bt_app exe, and does not promise that multiple running software can share the same WebView process. Built-in toolbar capabilities such as eyedroppers and screenshots can remain in the toolbar process, and their windows are only created when the BTR software is actually started.
`documents_dir()` uses the system-known directory API, does not hardcode the `Documents` name, and is compatible with localization, OneDrive, and system redirections. `watch_path()` uses system file events to monitor directory changes and does not scan the directory at a fixed period; the same window can retain up to 64 listeners at the same time. `options.recursive` defaults to `true`, which means recursively listening to subdirectories. The callback parameter is `{watch_id, root, kind, paths}`, `kind` may be `create`, `modify`, `remove`, `rename` or `other`. Generally, the cancellation function returned by `watch_path()` should be saved and there is no need to use the internal `watch_id` directly.
### bt.credential
App credentials are saved as clear text JSON to the current user's hidden app directory. Use this stable identifier when explicitly configuring `app.id`; when not configured, continue to use `app.name` for compatibility with older versions, such as `%USERPROFILE%\.bt_ai\credentials\`. This is a clear text format that facilitates migration and manual maintenance and does not use Windows DPAPI. The interface does not provide the page with a method to read clear text, but native programs with access to the user's directory can read the file, so the caller must explicitly prompt in the interface and protect operating system account permissions.
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| store | credential_id:string, secret:string | void | Save credentials atomically as JSON plaintext; same ID will overwrite old files |
| has | credential_id:string | boolean | Determine whether the credential JSON exists, the format is valid and the ID matches |
| delete | credential_id:string | boolean | Delete the specified credential; returns whether it is actually deleted |
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| credential_id | string | Yes | None | 1~128 characters, only letters, numbers, `_`, `-`, `.` | Stable credential ID defined by the caller, file name is `.json` |
| secret | string | yes | none | 1~4096 UTF-8 characters | `api_key` plain text field saved to JSON, read briefly during native request construction |
### bt.http
`stream()` initiates a real asynchronous HTTP request, and returns response headers, SSE or normal chunks, and finally the complete body through callbacks. Event monitoring will be installed before startup; `cancel()` will first close the native event gate and then notify the network task to terminate, so the status or body events of the request will not be distributed after successful cancellation.
| `stream options` Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| request_id | string | No | Automatically generate UUID | 1~128 characters, only letters, numbers, `_`, `-`, `.` | Unique request ID that can be specified when the caller needs to pre-correlate events |
| url | string | yes | none | `http://` or `https://` | request address |
| method | string | No | `POST` | 1~16 ASCII letters | HTTP method |
| headers | object | No | `{}` | Maximum 32 items; name cannot exceed 128 characters, value cannot exceed 8192 characters | Non-sensitive request headers; `authorization`, `proxy-authorization`, and `cookie` are prohibited |
| body | string | No | `""` | UTF-8 bytes no more than 4 MiB | Request body |
| credential_id | string | No | `""` | Null value or valid application credential ID | If not empty, the native layer will read JSON and inject `Authorization: Bearer ...`, and the page will not be returned |
| timeout_ms | integer | No | `120000` | 1000~600000 | Timeout milliseconds for the entire request |
| max_response_bytes | integer | No | `4000000` | 1~4000000 | The maximum number of accumulated bytes allowed in a single response |
`stream()` returns the control object:
The native layer will initialize the Rustls ring crypto provider idempotently before constructing the first HTTP client. Therefore, even if the application calls `window.bt.http.stream()` directly after a cold start, it does not rely on the BT standard library or web services to perform network initialization in advance.
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| request_id | string | Yes | None | This request ID | The stable ID corresponding to the event |
| active_limit | integer | Yes | None | Currently fixed at 8 | The upper limit of concurrent streaming requests allowed by the current process |
| max_response_bytes | integer | Yes | None | 1~4000000 | The actual upper limit of the response for this request |
| off | function | yes | none | no parameters | only cancels page event monitoring, does not terminate the request |
| cancel | function | yes | none | no parameters, returns `Promise` | atomically closes the event gate and cancels the request |
Streaming callback event fields:
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| request_id | string | Yes | None | This request ID | Request association ID |
| sequence | integer | yes | none | strictly increasing from 1 | single request event sequence |
| kind | string | yes | none | `start`, `headers`, `chunk`, `done`, `error`, `cancelled` | event type |
| status | integer | Yes | `0` | 0 or HTTP status code | 0 if no response headers have been received |
| data | string | Yes | `""` | Single chunked text or final full text | Text data for `chunk` and `done` |
| message | string | Yes | `""` | Desensitization error text | Status description of `error` and other events |
| received_bytes | integer | Yes | `0` | 0~the upper limit of this response | The current cumulative number of response bytes |
A single response can dispatch up to 16384 chunked events; exceeding the upper limit of the number of bytes or chunks will end with `error` and release the request registration.
### bt.workspace
The workspace API first registers a real directory, and then uses the short-term `workspace_id` to operate the relative path. All paths are normalized and true path bounds checked, recursive lists do not follow symbolic links or junctions.
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| open | root:string | WorkspaceOpenResult | Register existing real directories; a maximum of 8 entries will be retained, and the oldest registration entry will be eliminated if the limit is exceeded |
| close | workspace_id:string | boolean | Close registration without deleting any files |
| list | workspace_id:string, relative:string, recursive:boolean | WorkspaceEntry[] | Bounded list of directories, up to 4096 entries |
| read | workspace_id:string, relative:string, max_bytes:integer | WorkspaceReadResult | Read UTF-8 text, max 4 MiB |
| atomic_write | workspace_id:string, relative:string, content:string, expected_sha256:string\|null | WorkspaceWriteResult | Atomic replacement of temporary files in the same directory, and concurrent overwriting can be rejected based on the old summary |
| Calling field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| root | string | Yes | None | Existing absolute directory | The real root directory of the workspace |
| workspace_id | string | Yes | None | ID returned by `open()` | Native workspace registration |
| relative | string | list no; read/write yes | `""` | relative path within the workspace; absolute paths and `..` | target directory or file are prohibited |
| recursive | boolean | No | `false` | `true` or `false` | Whether to list directories recursively |
| max_bytes | integer | No | `1048576` | 1~4194304 | Reading upper limit, if exceeded, it will be rejected instead of truncated |
| content | string | write yes | None | UTF-8 text | New content written atomically |
| expected_sha256 | string or null | no | `null` | 64-bit hexadecimal SHA-256 | When non-null, only old files with the same digest are allowed to be overwritten |
| Return Object | Field | Type | Meaning |
| --- | --- | --- | --- |
| WorkspaceOpenResult | workspace_id | string | Natively generated short-lived workspace ID |
| WorkspaceOpenResult | root | string | normalized real root directory |
| WorkspaceEntry | path | string | Workspace relative path using `/` |
| WorkspaceEntry | kind | string | `file`, `directory` or `symlink` |
| WorkspaceEntry | bytes | integer | Number of bytes in the file; 0 for directories and links |
| WorkspaceReadResult | path | string | canonical relative path |
| WorkspaceReadResult | content | string | UTF-8 text |
| WorkspaceReadResult | bytes | integer | Original number of bytes |
| WorkspaceReadResult | sha256 | string | Content SHA-256 |
| WorkspaceWriteResult | path | string | canonical relative path |
| WorkspaceWriteResult | bytes | integer | Number of bytes of new content |
| WorkspaceWriteResult | sha256 | string | New content SHA-256 |
| WorkspaceWriteResult | previous_sha256 | string or null | Summary of old content; null for new files |
### bt.process
The native process is started directly using the program name and parameter array, without going through the shell. The process must belong to a caller task and registered workspace; when querying and stopping, it must match `process_id`, `task_id` and the unforgeable `identity`. Stopping will clean up the process tree corresponding to the handle and end external processes in batches without naming them.
| `start options` Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| task_id | string | yes | none | legal short-term ID | caller task ownership |
| program | string | yes | none | non-empty, up to 32768 characters | executable program path or program name |
| args | string[] | No | `[]` | Maximum 256 items, maximum single item 32768 characters | Parameter array without shell |
| workspace_id | string | yes | none | registered workspace ID | process directory boundary |
| cwd | string | No | `""` | Relative directory that already exists in the workspace | Current directory of the child process |
| environment | object | No | `{}` | Up to 64 items; the longest name is 256, the longest value is 32768 | Only the environment variables of the child process are injected, and the process snapshot is not written |
| timeout_ms | integer | No | `0` | 0~86400000 | 0 means no automatic timeout; otherwise, the process tree will be cleaned up upon expiration |
| output_limit | integer | No | `65536` | 1~1048576 | The upper limit of tail bytes reserved by stdout and stderr respectively |
| ProcessSnapshot field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| id | string | yes | none | native record ID | process_id of `status()` / `stop()` |
| task_id | string | yes | none | startup value | caller task ownership |
| identity | string | yes | none | 64-bit digest | Process identity value required for exact stop |
| pid | integer | yes | none | operating system PID | for observation only, not a replacement for identity |
| program | string | yes | none | startup value | program text |
| args | string[] | Yes | `[]` | Startup value | Parameter array; sensitive credentials must not be passed as parameters |
| cwd | string | Yes | None | The real directory in the workspace | The actual current directory |
| running | boolean | yes | none | `true` or `false` | Is it still running |
| exit_code | integer or null | yes | `null` | null while running | exit code |
| timed_out | boolean | Yes | `false` | `true` or `false` | Whether terminated by timeout |
| stdout / stderr | string | Yes | `""` | Each does not exceed output_limit | Bounded output tail |
| stdout_dropped / stderr_dropped | integer | yes | `0` | greater than or equal to 0 | number of bytes dropped due to upper limit |
| started_at | integer | yes | none | Unix millisecond timestamp | startup time |
The `on_event()` callback field is `process_id:string`, `task_id:string`, `kind:string`, `data:string`, `pid:integer`; `kind` can be `start`, `stdout`, `stderr`, `exit`, `timeout` or `stopped`. A maximum of 32 processes can be registered at the same time, and the end records will be eliminated according to the startup time.
### bt.data
The application JSON status is saved in the `sessions` subdirectory under the current user's hidden application directory by default. Use the stable flag when explicitly configuring `app.id`; when not configured, continue to use `app.name` for compatibility with older versions. Automated acceptance can set `BT_APP_DATA_HOME` to temporarily place the hidden application directory in the specified parent directory; normal desktop operation should keep this variable unset. Cleaning adopts the three-step boundary of "preview - user secondary confirmation - one-time ticket execution". Only the fixed subdirectory of the application data root is cleaned, and the registered workspace is not accessed.
| Method | Parameters | Return value | Description |
| --- | --- | --- | --- |
| store | key:string, value:any JSON | void | Atomicly save a JSON file |
| load | key:string | any JSON or null | returns null if the file does not exist |
| prepare_cleanup | None | CleanupPreview | Count fixed categories and generate a one-time confirmation ticket valid for 120 seconds |
| confirm_cleanup | confirm_token:string | CleanupResult | Consume the ticket and actually clean it; tickets that are incorrect, expired or reused are rejected |
| Calling field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| key | string | store/load Yes | None | 1~64 characters, only letters, numbers, `_`, `-` | Application JSON file key |
| value | JSON | store yes | none | no more than 4 MiB after serialization | application state value |
| confirm_token | string | confirm_cleanup Yes | None | prepare_cleanup returned and not consumed, not expired | Second confirmation ticket |
| Return Object | Field | Type | Meaning |
| --- | --- | --- | --- |
| CleanupPreview | confirm_token | string | One-time ticket valid for 120 seconds |
| CleanupPreview | categories | CleanupCategory[] | Fixed cleaning category statistics |
| CleanupPreview | total_files | integer | Total number of files |
| CleanupPreview | total_bytes | integer | Total number of bytes |
| CleanupPreview | boundary | string | Description of the boundary of the real directory being cleaned this time |
| CleanupCategory | name | string | `knowledge_cache`, `sessions`, `logs`, `temp` or `credentials` |
| CleanupCategory | files | integer | Number of category files |
| CleanupCategory | bytes | integer | Total number of bytes in category |
| CleanupResult | cleared | string[] | Fixed category processed |
| CleanupResult | removed_files | integer | Number of files before cleaning |
| CleanupResult | removed_bytes | integer | Number of bytes before cleaning |
| CleanupResult | workspace_untouched | boolean | Always true when the current implementation is successful, indicating that the user's workspace has not been touched |
### bt.events
`on_backend(callback)` listens to the `bt.call()` completion event and returns to the cancel listening function. The callback fields are as follows; the event does not contain the BT function return text.
| Field | Type | Required | Default value | Valid range or optional value | Meaning |
| --- | --- | --- | --- | --- | --- |
| name | string | yes | none | called BT global function name | calling source |
| ok | boolean | yes | none | `true` or `false` | Whether the IPC call was successful |
| at | integer | yes | none | Unix millisecond timestamp | completion time |
## Return Value
`bt.call()` directly returns the return value of the BT function. When the BT function returns an object, the front end receives a normal JSON object; when a string, number, Boolean or array is returned, the front end receives the corresponding JSON value.
If the BT function does not exist, an execution error is reported, the VM call queue is full, or the desktop API parameters are invalid, the Promise will reject and the `{error,message,data}` wrapper object will not be returned.
## Code Examples
`main.bt`:
```bt
/**
* Returns the data passed in by the front end.
*
* @param data front-end JSON parameters.
* @return response object.
*/
fn inspect(data) {
{
ok: true,
message: 'BT has received ',
data: data
}
}
```
Page call:
```js
try {
const result = await window.bt.call('inspect', {name: 'BT'})
await window.bt.window.set_title(result.message)
} catch (err) {
console.log(' call failure ', String(err))
}
```
Screendropper, screenshots and global shortcut keys:
```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)
// Log out when shortcut keys are no longer needed
await stopPickColor()
```
Tray menu:
```js
await window.bt.tray.enable({
tooltip: 'BT Application ',
menu: [
{id: 'show', text: ' displays window '},
{type: 'separator'},
{id: 'quit', text: ' exits program '}
]
})
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()
```
Complete an asynchronous snapshot before exiting:
```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(' failed to save session snapshot: ' + String(error), {
title: ' cannot exit safely ',
kind: 'error'
})
}
})
```
Read the user document directory:
```js
const documentsDir = await window.bt.app.documents_dir()
console.log(documentsDir)
```
Listen for directory changes:
```js
const stopWatch = await window.bt.app.watch_path('D:/docs', (event) => {
console.log(event.kind, event.paths)
})
// Cancel when monitoring is no longer needed
await stopWatch()
```
Streaming request and real cancellation:
```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)
}
})
// After the user cancels, the native layer will no longer dispatch the requested event
await controller.cancel()
controller.off()
```
Workspace atomic update:
```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
)
```
## Notes
- `bt.call()` is a long-term VM business channel; desktop capabilities such as windows, trays, clipboards, and notifications are independent command, does not occupy BT VM.
- The VM call queue is capped. The front-end should be throttled when making high-frequency calls to avoid uncontrolled growth.
- The `remote` page also has full `window.bt` capabilities and should only load trusted addresses.
- The same window can retain up to 64 `watch_path()` monitors at the same time; each returned cancellation function only stops the corresponding monitor, and `unwatch_path()` stops all monitors when no parameters are passed.
- `on_close_requested()` only intercepts the `exit` shutdown process of the window; the callback should prevent repeated execution, and `close_now()` can only be called after the asynchronous closing is completed.
- When `BT_PERMISSION_DENY=desktop` is set, windows, dialogs, trays, clipboards, global shortcuts, notifications, drag-in file events, file listen events, and app-level desktop commands are denied; screen eyedroppers and screenshots also require separate `screen` permissions. `bt.call()` itself is not restricted by `desktop`, and the standard library capabilities used internally by the called BT function are still checked according to their respective permissions.
- Windows, macOS and Linux X11 use the same set of Tauri self-drawn eyedroppers/box masks; macOS first use usually requires the user to grant screen recording permissions. Native Wayland does not support reliable global window coordinates and common global shortcut keys, the current version returns errors, and does not promise the same masking interface as X11.
- The first version of screenshot selection is limited to a single monitor; multiple monitors each have a mask, but drag selection cannot be spanned from one monitor to another. HDR, Wide Color Gamut, Protected Video, and System Security Desktop may differ in color from the naked eye or return black content.
- Generic production API only provides boundary-constrained client HTTP, registered workspaces, and handleable subprocesses; does not expose arbitrary filesystem, network listening, shell strings, or kill-process-by-name capabilities.
- Credentials are clear text JSON in the current user directory and must not be placed in URLs, normal request headers, request bodies, process parameters, logs or page storage; `credential_id` should be used to allow native layer injection and restrict user directory access.
- Before closing an app, canceling a task, or clearing data, still-running streaming requests should be canceled and the child process of the task to which they belong should be stopped exactly.
- Common desktop API examples are located at `examples/desktop-api`; eyedropper, screenshot and global shortcut key examples are located at `examples/desktop-screen-tools`.