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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ src/apps/mobile/**/*.ets text eol=lf
src/apps/mobile/**/*.kt text eol=lf
src/apps/mobile/**/*.swift text eol=lf

# The Product Operation Registry embeds and compares this generated JSON byte for byte.
src/crates/contracts/product-domains/src/generated/remote-surface-registry.json text eol=lf

# models.dev provenance hashes exact redistributed bytes. Keep these assets
# stable across checkout platforms so the offline release check is reproducible.
src/crates/services/services-integrations/assets/models-dev.json text eol=lf
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ jobs:
exit $testExitCode

- name: Run product-control domain and delivery-profile contracts
shell: bash
run: |
cargo test --locked -p openbitfun-product-domains --no-default-features product_control
cargo test --locked -p openbitfun-product-domains --no-default-features remote_surface
Expand Down
26 changes: 25 additions & 1 deletion docs/architecture/remote-workspace-transport.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Remote workspace transport

This document defines the transport boundary for SSH and Docker workspaces.
This document defines the transport boundary for SSH, Docker, and WSL workspaces.
The Agent Runtime stays on the OpenBitFun host. Local and remote workspaces share
file-tool algorithms through Session-bound IO providers; search retains native
acceleration with shared matching and reduction. The convergence section below
Expand Down Expand Up @@ -59,6 +59,30 @@ published `22/tcp` endpoint:
Runtime code only reads `effective_config`. The original `auto` value remains
persisted so a later reconnect can discover that sshd has become available.

## Native WSL

WSL is an independent workspace target. The additive `wsl` profile field stores
an explicit distribution and optional Linux user. Missing `wsl` preserves legacy
SSH/Docker behavior. Reserved `wsl.invalid:0` legacy endpoint fields keep older
readers from treating a WSL profile as a usable SSH endpoint. Connection drift
and saved-profile deduplication include the WSL target.

The Services integration launches `wsl.exe --distribution … --cd ~ --exec
/bin/sh -lc …` on the owning Windows host through the managed command factory.
Each argument stays separate; command stdout and file streams remain binary.
Only Windows-side WSL listing and diagnostic output is decoded as UTF-16LE when
needed. Interactive terminals use the existing local PTY adapter and WSL's
configured default shell, with a POSIX cwd. Non-TTY commands share Docker's
in-target PID/process-group supervision and separate signal channel.

WSL uses shell filesystem operations, never SFTP or Windows-local path IO.
The host advertises `wsl_workspaces_v1`; controllers reject WSL discovery and
profile mutations before RPC if the peer lacks that capability. Discovery then
reports the executing host's platform support. CLI peer setup is explicitly
unsupported by the Product Operation Registry. Existing Remote Control sessions
reuse the bound workspace providers, and Detached Dispatch does not create WSL
profiles. Native WSL has no SSH transport for port forwarding.

## ProxyJump

The comma-separated jump chain is resolved left to right. Each token can be a
Expand Down
12 changes: 10 additions & 2 deletions docs/features/remote-workspaces.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Remote SSH and container workspaces
# Remote SSH, container, and WSL workspaces

OpenBitFun remote workspaces use one saved target for the file explorer, terminal,
Agent commands, and workspace tools. The target can be:
Expand All @@ -7,14 +7,22 @@ Agent commands, and workspace tools. The target can be:
- an SSH host reached through one or more jump hosts;
- a Docker container on an SSH host;
- a Docker container on the local machine; or
- an sshd endpoint running inside a container.
- an sshd endpoint running inside a container; or
- a WSL Linux distribution on the Windows OpenBitFun host.

The local client behavior is supported on macOS, Windows, and Linux. Remote
workspace paths are always interpreted with POSIX `/` separators, independent
of the client OS. Docker workspace commands require a POSIX-compatible
container shell; selecting a Windows container does not silently reinterpret
paths or commands with Windows semantics.

## Windows WSL

Choose **Windows WSL** as its own workspace target. OpenBitFun discovers installed
distributions on the executing Windows host and connects through `wsl.exe`.
See [Desktop WSL setup](../../src/apps/desktop/README.md#windows-wsl-workspaces)
for prerequisites, user selection, and remote-surface support.

## Jump hosts

`ProxyJump` accepts a comma-separated chain such as `jump1,jump2` or
Expand Down
16 changes: 8 additions & 8 deletions docs/interactive-capabilities/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!-- generated by scripts/generate-interactive-capabilities.mjs; do not edit -->
# OpenBitFun 功能与设置目录 / OpenBitFun Features & Settings

OpenBitFun Playbook 当前包含 **22 个功能**和 **21 个设置页**,共 **43 个**用户可理解的条目、**320 项**有源码证据的子能力。每个条目有独立 Markdown,并直接服务于说明书网站、OpenBitFun 全局搜索和 `OpenBitFunControl` Agent 工具。
OpenBitFun Playbook 当前包含 **22 个功能**和 **21 个设置页**,共 **43 个**用户可理解的条目、**321 项**有源码证据的子能力。每个条目有独立 Markdown,并直接服务于说明书网站、OpenBitFun 全局搜索和 `OpenBitFunControl` Agent 工具。

OpenBitFun Playbook currently contains **22 features**, **21 settings pages**, and **320** source-backed sub-capabilities across **43** user-facing entries. Every entry has its own Markdown page and directly powers the website, in-app global search, and the `OpenBitFunControl` agent tool.
OpenBitFun Playbook currently contains **22 features**, **21 settings pages**, and **321** source-backed sub-capabilities across **43** user-facing entries. Every entry has its own Markdown page and directly powers the website, in-app global search, and the `OpenBitFunControl` agent tool.

## 唯一事实源 / Single source of truth

Expand All @@ -27,20 +27,20 @@ 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`

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

- 每个子能力都明确标记为直接控制、委托给专用 Agent 工具、需交互打开或不支持;“打开页面”不会再被统计成“Agent 已控制”。当前覆盖:直接 **48**、委托 **61**、需交互 **211**、不支持 **0**。
- 每个子能力都明确标记为直接控制、委托给专用 Agent 工具、需交互打开或不支持;“打开页面”不会再被统计成“Agent 已控制”。当前覆盖:直接 **48**、委托 **61**、需交互 **212**、不支持 **0**。
- 稳定行为声明为带 JSON 输入契约的 `operations` 或 `options`,并绑定原生产品控制 Provider;Agent 不接触原始 Tauri Command。
- `OpenBitFunControl list` 和 `search` 都返回带 `nextCursor` 的精简分页结果;目录可持续增长,不靠固定总量上限。完整目录和 320 项子能力都不会写入 system prompt。
- `OpenBitFunControl list` 和 `search` 都返回带 `nextCursor` 的精简分页结果;目录可持续增长,不靠固定总量上限。完整目录和 321 项子能力都不会写入 system prompt。
- 目录发现与契约读取不依赖 React 或可见窗口。普通配置型 option 统一由 Product Assembly 的共享 ConfigService 执行器读、写并回读,因此 Desktop、CLI 与 Headless 表面走同一份实现;只有宿主原生 operation/provider option 和界面导航按表面注册适配器,缺失时必须明确返回不可用,禁止静默回退本机。只读 Agent 只能发现和读取目录。

- Every documented item is classified as direct control, delegated Agent control, interactive opening, or unsupported; opening a page is never counted as direct control. Current coverage is **48 direct**, **61 delegated**, **211 interactive**, and **0 unsupported**.
- Every documented item is classified as direct control, delegated Agent control, interactive opening, or unsupported; opening a page is never counted as direct control. Current coverage is **48 direct**, **61 delegated**, **212 interactive**, and **0 unsupported**.
- Stable behavior becomes a typed `operation` or `option` with a JSON input contract and a native product-control provider. Agents never receive raw Tauri commands.
- `OpenBitFunControl list` and `search` return compact pages with a `nextCursor`; the catalog can grow without a fixed total-size ceiling. Neither the full catalog nor its 320 documented items enters the system prompt.
- `OpenBitFunControl list` and `search` return compact pages with a `nextCursor`; the catalog can grow without a fixed total-size ceiling. Neither the full catalog nor its 321 documented items enters the system prompt.
- Discovery and contract lookup do not depend on React or a visible window. Ordinary config-backed options are read, written, and read back by one Product Assembly ConfigService executor shared by Desktop, CLI, and headless surfaces. Only host-native operations/provider options and presentation routes install surface adapters; missing adapters return explicit unavailability without local fallback. Read-only agents may only discover and inspect entries.

## 防腐化门禁 / Anti-drift gates
Expand Down
66 changes: 63 additions & 3 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": "9973af8307e72206bb5c4af5dfb3249fef2def6a27925a13aca9a25ac62ef8a3",
"digest": "d7a7419ddd673eb733ae8bd33dbb3dcd3b4d1ce0acfc953067a3403fe26d6699",
"ownerDigest": "c0e5c187cf62bc6ed06196ce8520b3eb427bf268cf24659b72d2552fb1d99c54",
"searchAcceptance": [
{
Expand Down Expand Up @@ -138,11 +138,11 @@
"features": 22,
"settings": 21,
"userFacing": 43,
"documentedItems": 320,
"documentedItems": 321,
"controlCoverage": {
"direct": 48,
"delegated": 61,
"interactive": 211,
"interactive": 212,
"unsupported": 0
}
},
Expand Down Expand Up @@ -8145,6 +8145,7 @@
"ssh-profiles",
"ssh-auth",
"docker",
"wsl",
"remote-open",
"remote-files",
"transfer",
Expand Down Expand Up @@ -8329,6 +8330,52 @@
"actionId": "project.new"
}
},
{
"id": "feature.remote-workspaces:open:wsl",
"capabilityId": "feature.remote-workspaces",
"itemIds": [
"wsl"
],
"kind": "open",
"risk": "ui",
"executionHost": "presentationSurface",
"availability": {
"desktop": {
"available": true
},
"cli": {
"available": false,
"reason": "This delivery profile has no live presentation surface"
},
"peer": {
"available": true,
"requiredCapabilities": [
"product_control_v1",
"product_control_presentation_v1"
]
},
"remoteControl": {
"available": true
},
"detachedDispatch": {
"available": false,
"reason": "This delivery profile has no live presentation surface"
}
},
"inputSchema": {
"type": "object",
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"additionalProperties": true
},
"openReason": "unstructuredInteraction",
"presentationTarget": {
"kind": "action",
"actionId": "project.new"
}
},
{
"id": "feature.remote-workspaces:open:remote-open",
"capabilityId": "feature.remote-workspaces",
Expand Down Expand Up @@ -23508,6 +23555,17 @@
"reasonEn": "“Discover remote Docker containers and use a container as the work environment” 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."
}
},
{
"id": "wsl",
"titleZh": "选择 Windows 主机上的 WSL 发行版作为工作区",
"titleEn": "Choose a WSL distribution on the Windows host as a workspace",
"control": {
"kind": "open",
"reasonCode": "unstructuredInteraction",
"reasonZh": "“选择 Windows 主机上的 WSL 发行版作为工作区”需要读取执行主机的发行版列表,并由用户选择 Linux 用户和工作区目录;Agent 可打开连接入口,但当前没有覆盖完整配置流程的结构化 Command。",
"reasonEn": "“Choose a WSL distribution on the Windows host as a workspace” requires live distribution discovery on the executing host and user selection of a Linux user and workspace directory; the Agent can open the connection dialog, but no structured Command covers the complete setup workflow."
}
},
{
"id": "remote-open",
"titleZh": "打开、关闭和移除远程工作区,并读取服务器信息",
Expand Down Expand Up @@ -23660,6 +23718,8 @@
"Connect with passwords, private keys, certificates, SSH config hosts, and jump hosts",
"发现远程 Docker 容器并把容器作为工作环境",
"Discover remote Docker containers and use a container as the work environment",
"选择 Windows 主机上的 WSL 发行版作为工作区",
"Choose a WSL distribution on the Windows host as a workspace",
"打开、关闭和移除远程工作区,并读取服务器信息",
"Open, close, and remove remote workspaces, and inspect server information",
"浏览、读取、写入、创建、重命名和删除远程文件与目录",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Open projects over SSH or in containers so files, search, terminal, and agents a
- Connect with passwords, private keys, certificates, SSH config hosts, and jump hosts
- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 发现远程 Docker 容器并把容器作为工作环境
- Discover remote Docker containers and use a container as the work environment
- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 选择 Windows 主机上的 WSL 发行版作为工作区
- Choose a WSL distribution on the Windows host as a workspace
- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 打开、关闭和移除远程工作区,并读取服务器信息
- Open, close, and remove remote workspaces, and inspect server information
- **由专用 Agent 工具控制 / Delegated Agent tool** · `LS` / `Read` / `Write` / `Edit` / `Delete` / `Glob` / `Grep` / `ExecCommand` · 浏览、读取、写入、创建、重命名和删除远程文件与目录
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"schemaVersion": 1,
"generatedFrom": "src/shared/interactive-capabilities/catalog.json",
"catalogDigest": "9973af8307e72206bb5c4af5dfb3249fef2def6a27925a13aca9a25ac62ef8a3",
"count": 211,
"catalogDigest": "d7a7419ddd673eb733ae8bd33dbb3dcd3b4d1ce0acfc953067a3403fe26d6699",
"count": 212,
"reasonCounts": {
"externalAuth": 4,
"secretEntry": 5,
"unstructuredInteraction": 184,
"unstructuredInteraction": 185,
"visualSelection": 18
},
"entries": [
Expand Down Expand Up @@ -1771,6 +1771,22 @@
"command:ssh_list_docker_containers"
]
},
{
"capabilityId": "feature.remote-workspaces",
"itemId": "wsl",
"titleZh": "选择 Windows 主机上的 WSL 发行版作为工作区",
"titleEn": "Choose a WSL distribution on the Windows host as a workspace",
"reasonCode": "unstructuredInteraction",
"reasonZh": "“选择 Windows 主机上的 WSL 发行版作为工作区”需要读取执行主机的发行版列表,并由用户选择 Linux 用户和工作区目录;Agent 可打开连接入口,但当前没有覆盖完整配置流程的结构化 Command。",
"reasonEn": "“Choose a WSL distribution on the Windows host as a workspace” requires live distribution discovery on the executing host and user selection of a Linux user and workspace directory; the Agent can open the connection dialog, but no structured Command covers the complete setup workflow.",
"presentationTarget": {
"kind": "action",
"actionId": "project.new"
},
"evidence": [
"command:ssh_list_wsl_distributions"
]
},
{
"capabilityId": "feature.remote-workspaces",
"itemId": "remote-open",
Expand Down
24 changes: 20 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": "9973af8307e72206bb5c4af5dfb3249fef2def6a27925a13aca9a25ac62ef8a3",
"commandCount": 665,
"catalogDigest": "d7a7419ddd673eb733ae8bd33dbb3dcd3b4d1ce0acfc953067a3403fe26d6699",
"commandCount": 666,
"coverage": {
"commandCount": 665,
"documentedCommandCount": 632,
"commandCount": 666,
"documentedCommandCount": 633,
"implementationCommandCount": 33,
"implementationDigest": "35539d9c1510287cb47f4a68fe35859b78f93bb06cd66b86e58d878a48d8c509"
},
Expand Down Expand Up @@ -9546,6 +9546,22 @@
"signature": "fn ssh_list_saved_connections( state: State<'_, AppState>, ) -> Result<Vec<SavedConnection>, String>",
"remoteWorkspacePolicy": "WorkspaceAgnostic"
},
{
"id": "ssh_list_wsl_distributions",
"moduleId": "ssh",
"capabilityId": "feature.remote-workspaces",
"capabilityIds": [
"feature.remote-workspaces"
],
"documentedItemIds": [
"feature.remote-workspaces:wsl"
],
"visibility": "documented",
"rustPath": "api::ssh_api::ssh_list_wsl_distributions",
"sourceFile": "src/apps/desktop/src/api/ssh_api.rs",
"signature": "fn ssh_list_wsl_distributions( ) -> Result<openbitfun_core::service::remote_ssh::WslDistributions, String>",
"remoteWorkspacePolicy": "WorkspaceAgnostic"
},
{
"id": "ssh_save_connection",
"moduleId": "ssh",
Expand Down
Loading
Loading