Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/interactive-capabilities/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ OpenBitFun Playbook currently contains **22 features**, **21 settings pages**, a
- Generated per-item interaction audit: `docs/interactive-capabilities/technical/product-control-open-audit.json`
- Generated low-level audit map: `docs/interactive-capabilities/technical/tauri-command-map.json`

说明书、网站、搜索和智能体只看“功能 + 设置 + 子能力”。每项子能力都必须引用已注册 Tauri Command 或可解析的源码标记;这些证据不会进入公开目录。当前 **660** 个 Tauri 命令只用于实现覆盖审计。产品 UI 交互源码会在生成和检查时扫描并校验,但不会保存成随普通 UI 改动频繁变化的版本化快照。
说明书、网站、搜索和智能体只看“功能 + 设置 + 子能力”。每项子能力都必须引用已注册 Tauri Command 或可解析的源码标记;这些证据不会进入公开目录。当前 **662** 个 Tauri 命令只用于实现覆盖审计。产品 UI 交互源码会在生成和检查时扫描并校验,但不会保存成随普通 UI 改动频繁变化的版本化快照。

Docs, website, search, and agents see only features, settings, and documented sub-capabilities. Every sub-capability must reference a registered Tauri command or a resolvable source marker; evidence is stripped from public projections. The **660** Tauri commands remain implementation-audit evidence only. Product UI interaction sources are scanned and validated during generation and checks, but are not stored as a versioned snapshot that churns with ordinary UI changes.
Docs, website, search, and agents see only features, settings, and documented sub-capabilities. Every sub-capability must reference a registered Tauri command or a resolvable source marker; evidence is stripped from public projections. The **662** Tauri commands remain implementation-audit evidence only. Product UI interaction sources are scanned and validated during generation and checks, but are not stored as a versioned snapshot that churns with ordinary UI changes.

## 控制边界 / Control boundary

Expand Down
14 changes: 7 additions & 7 deletions docs/interactive-capabilities/capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "OpenBitFun Playbook",
"origin": "https://playbook.openbitfun.com",
"source": "src/shared/interactive-capabilities/catalog.json",
"digest": "5473331e06ecd4bfafbf9b547d48b4fe1561ccf69d86fdfde51ea10dc919531c",
"digest": "6587344a6b5e75a80457ea4ba2670bf37cf3038bdb436089ae402eb7b5a5a025",
"ownerDigest": "c0e5c187cf62bc6ed06196ce8520b3eb427bf268cf24659b72d2552fb1d99c54",
"searchAcceptance": [
{
Expand Down Expand Up @@ -23834,13 +23834,13 @@
},
{
"id": "devices",
"titleZh": "列出、连接、查看在线状态和删除同账户设备",
"titleEn": "List, connect, inspect online status, and remove same-account devices",
"titleZh": "列出、连接、查看设备型号与系统、设置别名和删除同账户设备",
"titleEn": "List, connect, inspect model and OS, set aliases, and remove same-account devices",
"control": {
"kind": "open",
"reasonCode": "unstructuredInteraction",
"reasonZh": "“列出、连接、查看在线状态和删除同账户设备”由多个实时状态相关步骤组成,目前没有一个能确定完成整个流程的单一结构化 Command;智能体会打开精确入口,并把后续交互保留在用户可见界面。",
"reasonEn": "“List, connect, inspect online status, and remove same-account devices” spans multiple live-state-dependent steps and currently has no single structured Command that can deterministically complete the whole workflow; the Agent opens the exact entry and keeps the remaining interaction visible to the user."
"reasonZh": "“列出、连接、查看设备型号与系统、设置别名和删除同账户设备”需要选择设备并确认操作。设备目录使用控制端的账户身份,不随 Peer Device 切换到目标主机。别名优先于技术名称显示,但不修改技术名称;型号与系统信息由设备自身上报。仅支持对应能力的 Relay 可更新或清除别名,旧 Relay 明确显示不支持。智能体打开设备管理入口,由用户确认后续交互。",
"reasonEn": "“List, connect, inspect model and OS, set aliases, and remove same-account devices” requires device selection and confirmation. The device directory uses the controller's account identity, not the selected Peer host. Aliases take display priority without changing technical names; model and OS metadata come from the device itself. Alias updates and clearing require Relay capability support; older Relays explicitly remain unsupported. The Agent opens device management and leaves subsequent interaction to the user."
}
},
{
Expand Down Expand Up @@ -23919,8 +23919,8 @@
"Configure Feishu, Telegram, WeChat, and other bots, and stop a bot independently",
"使用 GitHub 登录、退出并查看身份状态",
"Sign in with GitHub, sign out, and inspect identity status",
"列出、连接、查看在线状态和删除同账户设备",
"List, connect, inspect online status, and remove same-account devices",
"列出、连接、查看设备型号与系统、设置别名和删除同账户设备",
"List, connect, inspect model and OS, set aliases, and remove same-account devices",
"进入 Peer Device Mode,把另一台 OpenBitFun 设备作为命令与事件数据面",
"Enter Peer Device Mode and use another OpenBitFun device as the command and event data plane",
"打开 Remote Connect",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Connect to this host from mobile, a bot, or another OpenBitFun device to inspect
- Configure Feishu, Telegram, WeChat, and other bots, and stop a bot independently
- **智能体可定位入口,需交互完成 / Agent opens; interaction required** · 使用 GitHub 登录、退出并查看身份状态
- Sign in with GitHub, sign out, and inspect identity status
- **智能体可定位入口,需交互完成 / Agent opens; interaction required** · 列出、连接、查看在线状态和删除同账户设备
- List, connect, inspect online status, and remove same-account devices
- **智能体可定位入口,需交互完成 / Agent opens; interaction required** · 列出、连接、查看设备型号与系统、设置别名和删除同账户设备
- List, connect, inspect model and OS, set aliases, and remove same-account devices
- **智能体可定位入口,需交互完成 / Agent opens; interaction required** · 进入 Peer Device Mode,把另一台 OpenBitFun 设备作为命令与事件数据面
- Enter Peer Device Mode and use another OpenBitFun device as the command and event data plane

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"generatedFrom": "src/shared/interactive-capabilities/catalog.json",
"catalogDigest": "5473331e06ecd4bfafbf9b547d48b4fe1561ccf69d86fdfde51ea10dc919531c",
"catalogDigest": "6587344a6b5e75a80457ea4ba2670bf37cf3038bdb436089ae402eb7b5a5a025",
"count": 211,
"reasonCounts": {
"externalAuth": 4,
Expand Down Expand Up @@ -1965,17 +1965,19 @@
{
"capabilityId": "feature.remote-connect",
"itemId": "devices",
"titleZh": "列出、连接、查看在线状态和删除同账户设备",
"titleEn": "List, connect, inspect online status, and remove same-account devices",
"titleZh": "列出、连接、查看设备型号与系统、设置别名和删除同账户设备",
"titleEn": "List, connect, inspect model and OS, set aliases, and remove same-account devices",
"reasonCode": "unstructuredInteraction",
"reasonZh": "“列出、连接、查看在线状态和删除同账户设备”由多个实时状态相关步骤组成,目前没有一个能确定完成整个流程的单一结构化 Command;智能体会打开精确入口,并把后续交互保留在用户可见界面。",
"reasonEn": "“List, connect, inspect online status, and remove same-account devices” spans multiple live-state-dependent steps and currently has no single structured Command that can deterministically complete the whole workflow; the Agent opens the exact entry and keeps the remaining interaction visible to the user.",
"reasonZh": "“列出、连接、查看设备型号与系统、设置别名和删除同账户设备”需要选择设备并确认操作。设备目录使用控制端的账户身份,不随 Peer Device 切换到目标主机。别名优先于技术名称显示,但不修改技术名称;型号与系统信息由设备自身上报。仅支持对应能力的 Relay 可更新或清除别名,旧 Relay 明确显示不支持。智能体打开设备管理入口,由用户确认后续交互。",
"reasonEn": "“List, connect, inspect model and OS, set aliases, and remove same-account devices” requires device selection and confirmation. The device directory uses the controller's account identity, not the selected Peer host. Aliases take display priority without changing technical names; model and OS metadata come from the device itself. Alias updates and clearing require Relay capability support; older Relays explicitly remain unsupported. The Agent opens device management and leaves subsequent interaction to the user.",
"presentationTarget": {
"kind": "event",
"eventName": "openbitfun:open-remote-connect"
},
"evidence": [
"command:account_list_devices",
"command:account_relay_capabilities",
"command:account_update_device_alias",
"command:account_connect_devices",
"command:account_online_devices",
"command:account_delete_device",
Expand Down
40 changes: 36 additions & 4 deletions docs/interactive-capabilities/technical/tauri-command-map.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"schemaVersion": 2,
"generatedFrom": "src/shared/interactive-capabilities/catalog.json",
"catalogDigest": "5473331e06ecd4bfafbf9b547d48b4fe1561ccf69d86fdfde51ea10dc919531c",
"commandCount": 660,
"catalogDigest": "6587344a6b5e75a80457ea4ba2670bf37cf3038bdb436089ae402eb7b5a5a025",
"commandCount": 662,
"coverage": {
"commandCount": 660,
"documentedCommandCount": 613,
"commandCount": 662,
"documentedCommandCount": 615,
"implementationCommandCount": 47,
"implementationDigest": "401fd0a5f64377e65573d1b0883fbfa844570096adfafd4c11a999d749ba5859"
},
Expand Down Expand Up @@ -268,6 +268,22 @@
"signature": "fn account_online_devices() -> Result<Vec<OnlineDeviceInfo>, String>",
"remoteWorkspacePolicy": "WorkspaceAgnostic"
},
{
"id": "account_relay_capabilities",
"moduleId": "remote_connect",
"capabilityId": "feature.remote-connect",
"capabilityIds": [
"feature.remote-connect"
],
"documentedItemIds": [
"feature.remote-connect:devices"
],
"visibility": "documented",
"rustPath": "api::remote_connect_api::account_relay_capabilities",
"sourceFile": "src/apps/desktop/src/api/remote_connect_api.rs",
"signature": "fn account_relay_capabilities() -> Result<Vec<String>, String>",
"remoteWorkspacePolicy": "WorkspaceAgnostic"
},
{
"id": "account_status",
"moduleId": "remote_connect",
Expand Down Expand Up @@ -326,6 +342,22 @@
"signature": "fn account_unsubscribe_session(request: UnsubscribeSessionRequest) -> Result<(), String>",
"remoteWorkspacePolicy": "WorkspaceAgnostic"
},
{
"id": "account_update_device_alias",
"moduleId": "remote_connect",
"capabilityId": "feature.remote-connect",
"capabilityIds": [
"feature.remote-connect"
],
"documentedItemIds": [
"feature.remote-connect:devices"
],
"visibility": "documented",
"rustPath": "api::remote_connect_api::account_update_device_alias",
"sourceFile": "src/apps/desktop/src/api/remote_connect_api.rs",
"signature": "fn account_update_device_alias( request: AccountUpdateDeviceAliasRequest, ) -> Result<(), String>",
"remoteWorkspacePolicy": "WorkspaceAgnostic"
},
{
"id": "acknowledge_external_ecosystems_command",
"moduleId": "external_sources",
Expand Down
24 changes: 23 additions & 1 deletion src/apps/cli/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ pub(crate) fn account_snapshot_projection(
.map(|device| AccountDevice {
device_id: device.device_id,
device_name: device.device_name,
device_alias: device.device_alias,
device_model: device.device_model,
device_os: device.device_os,
device_os_version: device.device_os_version,
device_client_version: device.device_client_version,
device_client_protocol: device.device_client_protocol,
compatible: device.compatible,
online: device.online,
})
.collect(),
Expand Down Expand Up @@ -355,6 +362,17 @@ impl CliAccountRoutingHost {
.read_account_context_for_generation(account_generation)
.await
{
// AuthOk is emitted on initial connection and every reconnect.
// Use its authenticated id, never a controller's target id.
if session.token != expected_token {
return;
}
if let Err(error) = runtime
.report_local_device_metadata(account_generation, &device_id)
.await
{
tracing::warn!("Failed to report device metadata on connection: {error}");
}
if session.token == expected_token
&& self
.routing_loop_is_current(account_generation, relay_client)
Expand Down Expand Up @@ -519,7 +537,11 @@ impl CliAccountRoutingHost {
tracing::info!("Device routing disconnected");
crate::peer_host::update_controller_presence(Vec::new()).await;
}
RelayEvent::Reconnected => tracing::info!("Device routing reconnected"),
RelayEvent::Reconnected => {
tracing::info!("Device routing reconnected");
// The following AuthOk reports metadata with the server-confirmed
// device id. Do not race it with a second PATCH using cached identity.
}
RelayEvent::Error { message } => {
tracing::warn!("Device routing error: {message}")
}
Expand Down
22 changes: 19 additions & 3 deletions src/apps/cli/src/ui/login_form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,13 +297,29 @@ impl LoginFormState {
let is_local = local_id == Some(d.device_id.as_str());
let status = if d.online { "online" } else { "offline" };
let badge = if is_local { " [this device]" } else { "" };
// A missing flag (older Relay) is unknown and shows nothing.
let compat = if d.is_compatible() {
""
} else {
" · incompatible"
};
device_lines.push(Line::from(Span::styled(
format!(
" {}{} {} · {}",
d.device_name,
" {}{} {} · {}{} {}",
d.display_name(),
badge,
truncate_id(&d.device_id),
status
status,
compat,
[
d.device_model.as_deref(),
d.device_os.as_deref(),
d.device_os_version.as_deref()
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
.join(" ")
),
if d.online {
Style::default().fg(Color::White)
Expand Down
Loading
Loading