docs: add public API engineering docs

This commit is contained in:
Haitao Pan 2026-04-14 16:20:45 +08:00
parent ad6adca3fd
commit 78d59292a6
10 changed files with 9590 additions and 1 deletions

View File

@ -20,7 +20,7 @@ APP_DART_DEFINE_BUILD ?= --dart-define=XWORKMATE_BUILD_NUMBER=$(APP_BUILD_NUMBER
APP_DART_DEFINE_BUILD_DATE ?= --dart-define=XWORKMATE_BUILD_DATE=$(APP_BUILD_DATE)
APP_DART_DEFINE_BUILD_COMMIT ?= --dart-define=XWORKMATE_BUILD_COMMIT=$(APP_BUILD_COMMIT)
.PHONY: help deps analyze test test-all test-flutter test-golden test-integration test-integration-macos test-patrol test-go test-ci check format run open-macos-xcode sync-version build-linux build-macos build-ios-sim package-deb package-rpm package-linux package-mac install-mac clean build-go-core render-release-docs check-export-compliance test-real-env-login-chain inspect-xworkmate-bridge-service
.PHONY: help deps analyze test test-all test-flutter test-golden test-integration test-integration-macos test-patrol test-go test-ci check format run open-macos-xcode sync-version build-linux build-macos build-ios-sim package-deb package-rpm package-linux package-mac install-mac clean build-go-core render-release-docs docs-public-api check-export-compliance test-real-env-login-chain inspect-xworkmate-bridge-service
help: ## Show available targets
@grep -E '^[a-zA-Z0-9_.-]+:.*?## ' Makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-18s %s\n", $$1, $$2}'
@ -72,6 +72,9 @@ format: ## Format Dart sources
render-release-docs: ## Render feature matrix, roadmap, release notes, and changelog docs
$(DART) run tool/render_release_docs.dart
docs-public-api: ## Generate the public API inventory docs payload
python3 scripts/docs/extract_public_api_inventory.py
sync-version: ## Show the version/build metadata sourced from pubspec.yaml
@echo "version=$(APP_VERSION)"
@echo "build=$(APP_BUILD_NUMBER)"

View File

@ -0,0 +1,135 @@
# XWorkmate Public API Engineering Docs
Last Updated: 2026-04-14
## Purpose
本目录为 `xworkmate-app` 的公开接口工程文档下钻层,目标不是替代已有架构文档,而是把“当前主链真正暴露出来的库 / 类 / 函数 / 接口”系统化整理出来。
这一层文档固定采用两层产物:
- `机器清单层`:由脚本自动提取的公开符号与签名,解决“覆盖完整性”
- `设计解释层`:人工编写的职责、参数语义、返回值语义、主调用链、外部副作用,解决“可读性和工程决策语义”
## Reading Order
建议按下面顺序阅读:
1. [生成清单](_generated/public-symbol-inventory.md)
2. [App Orchestration](app-orchestration.md)
3. [Runtime Contracts](runtime-contracts.md)
4. [Feature Surfaces](feature-surfaces.md)
5. [Models And Config](models-and-config.md)
6. [FFI And Rust](ffi-and-rust.md)
再回看这些背景文档补架构上下文:
1. [XWorkmate Layered Architecture](../xworkmate-layered-architecture.md)
2. [XWorkmate Core Module Inventory](../xworkmate-core-module-inventory-2026-04-13.md)
3. [Task Control Plane Unification](../task-control-plane-unification.md)
4. [Settings Integration Configuration Model](../settings-integration-configuration-model.md)
## Scope Rule
纳入范围:
- `lib/app`
- `lib/runtime`
- `lib/models`
- `lib/features/assistant`
- `lib/features/settings`
- `lib/features/mobile`
- `lib/theme`
- `rust/src`
文档粒度:
- 公开顶层符号:非 `_` 开头的 `class`、`abstract class`、`enum`、`typedef`、`extension`
- 公开顶层函数
- Rust `pub struct` / `pub enum`
- Rust `pub unsafe extern "C"` FFI 函数
人工解释层的额外约束:
- 页面层只覆盖“业务 + 关键页面”
- 纯展示型 leaf widget 不逐条展开
- 私有 `_` 符号不作为正式 API 条目
- 低价值 DTO/辅助函数默认留在生成清单,不强行全部补人工说明
## Coverage Summary
当前生成清单覆盖 `130` 个源码文件、`614` 个公开符号。
| Scope | Files | Public Symbols | Detailed Design Entries | Notes |
| --- | ---: | ---: | ---: | --- |
| `lib/app` | 30 | 68 | 10 | 主写桌面编排入口、扩展、registry 与 shell |
| `lib/runtime` | 67 | 377 | 18 | 主写 bridge contract、runtime client、controller、bootstrap |
| `lib/models` | 1 | 34 | 13 | 主写 settings / execution / provider / snapshot 主模型 |
| `lib/features/assistant` | 16 | 80 | 1 | 只展开页面入口与业务挂点 |
| `lib/features/settings` | 4 | 4 | 1 | 只展开设置主入口 |
| `lib/features/mobile` | 6 | 19 | 1 | 只展开移动端 shell 主入口 |
| `lib/theme` | 2 | 13 | 2 | 只展开工程上影响 API 的 theme/palette 入口 |
| `rust/src` | 4 | 19 | 17 | 结构体与 FFI 函数全部展开 |
说明:
- “Public Symbols” 以生成清单 JSON 为准
- “Detailed Design Entries” 是人工解释层条目数,不等于全量公开符号数
- 剩余未逐条解释的公开符号,视为“已被生成清单覆盖,但尚未进入高价值解释层”
## Explicit Exclusions
下面这些内容被明确排除在“人工解释层”之外,但不代表它们不存在:
| Excluded Scope | Files | Public Symbols | Reason |
| --- | ---: | ---: | --- |
| `lib/widgets` | 21 | 51 | 纯展示 leaf widget 为主,不作为本次设计文档主对象 |
| `lib/data` | 1 | 1 | mock/sample 数据,不是运行时公开 contract |
| `lib/i18n` | 1 | 4 | 文案与语言辅助,不是业务接口主链 |
| `lib/main.dart` | 1 | 0 | 启动封装,无独立公开符号 |
## File Map
- [_generated/public-symbol-inventory.md](_generated/public-symbol-inventory.md)
公开符号清单,适合做覆盖检查、文件定位、签名对照
- [_generated/public-symbol-inventory.json](_generated/public-symbol-inventory.json)
机器可校验版本,适合后续脚本和 CI 使用
- [app-orchestration.md](app-orchestration.md)
`AppController`、导航、设置保存、线程/会话/执行主链
- [runtime-contracts.md](runtime-contracts.md)
ACP、gateway runtime、settings controller、session client、bootstrap、registry
- [feature-surfaces.md](feature-surfaces.md)
`AssistantPage`、`SettingsPage`、`MobileShell`、`WorkspacePageSpec`
- [models-and-config.md](models-and-config.md)
`SettingsSnapshot`、连接配置、provider/catalog、gateway/runtime snapshot、多 agent 配置
- [ffi-and-rust.md](ffi-and-rust.md)
Rust 公开结构体与 C ABI 面
## Validation Workflow
生成清单:
```bash
make docs-public-api
```
最低校验:
```bash
python3 scripts/docs/extract_public_api_inventory.py
rg -n '`_' docs/architecture/public-api/*.md
```
人工核对优先文件:
- `lib/runtime/gateway_acp_client.dart`
- `lib/runtime/go_task_service_client.dart`
- `lib/runtime/codex_runtime.dart`
- `lib/app/app_controller_desktop_settings_runtime.dart`
- `rust/src/lib.rs`
## Maintenance Rule
- 新增公开顶层符号后,先运行 `make docs-public-api`
- 如果新增符号属于主链 contract、页面入口、配置主模型、桥接接口或 FFI 面,则必须补到人工解释层
- 如果只是低价值 DTO、单向 helper、视觉组件或简单枚举允许仅由生成清单覆盖

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,263 @@
# App Orchestration
## Module Purpose
`lib/app` 负责把 `feature_flags -> shell/registry -> AppController -> runtime/controller` 这条主链收口到统一桌面/移动编排层。这里的公开接口重点不在“UI 细节”,而在:
- 顶层状态与依赖装配
- 页面 destination 与 registry
- 设置、线程、provider、执行目标的落盘与切换
- 运行时能力刷新与线程上下文重算
## Key Files
| File | Role |
| --- | --- |
| `lib/app/app_controller_desktop_core.dart` | `AppController` 主对象、依赖装配与状态总线 |
| `lib/app/app_controller_desktop_navigation.dart` | 页面切换、settings/detail 导航、语言/主题切换 |
| `lib/app/app_controller_desktop_settings.dart` | settings draft、保存、落盘与本地状态清理 |
| `lib/app/app_controller_desktop_settings_runtime.dart` | settings 与 runtime 之间的副作用桥接 |
| `lib/app/app_controller_desktop_thread_sessions.dart` | 会话/线程、artifact、多 agent 协作入口 |
| `lib/app/app_controller_desktop_workspace_execution.dart` | 执行目标、provider、thread context 主链 |
| `lib/app/app_controller_desktop_runtime_coordination_impl.dart` | runtime 能力刷新与任务重算函数 |
| `lib/app/workspace_page_registry.dart` | destination 到 page builder 的唯一映射 |
| `lib/app/app_shell_desktop.dart` | 桌面/移动入口壳层 |
## `AppController`
- Source: `lib/app/app_controller_desktop_core.dart`
- Type: `class`
- Responsibility:
统一装配 `SecureConfigStore`、`RuntimeCoordinator`、`GatewayRuntime`、`CodexRuntime`、`SettingsController`、各类 gateway controller、task thread 状态、UI feature manifest。
### Constructor Parameters
| Param | Type | Required | Default | Meaning |
| --- | --- | --- | --- | --- |
| `store` | `SecureConfigStore?` | No | `SecureConfigStore()` | 设置与 secret 持久化入口 |
| `runtimeCoordinator` | `RuntimeCoordinator?` | No | 内建 coordinator | 收口 gateway/codex/config bridge |
| `desktopPlatformService` | `DesktopPlatformService?` | No | `createDesktopPlatformService()` | 平台 VPN/集成能力桥 |
| `uiFeatureManifest` | `UiFeatureManifest?` | No | repo manifest | 顶层 surface 能力声明源 |
| `initialBridgeProviderCatalog` | `List<SingleAgentProvider>?` | No | empty | 初始单 agent provider catalog |
| `initialGatewayProviderCatalog` | `List<SingleAgentProvider>?` | No | empty | 初始 gateway provider catalog |
| `initialAvailableExecutionTargets` | `List<AssistantExecutionTarget>?` | No | empty | 初始可见执行目标 |
| `skillDirectoryAccessService` | `SkillDirectoryAccessService?` | No | platform factory | 技能目录授权能力 |
| `accountClientFactory` | `AccountRuntimeClient Function(String)?` | No | default impl | account runtime client 构造 |
| `environmentOverride` | `Map<String, String>?` | No | `null` | 测试/运行时环境覆盖 |
| `singleAgentSharedSkillScanRootOverrides` | `List<String>?` | No | `null` | 共享 skill 扫描根覆写 |
| `arisBundleRepository` | `ArisBundleRepository?` | No | default impl | ARIS bundle 发现仓库 |
| `goTaskServiceClient` | `GoTaskServiceClient?` | No | `DesktopGoTaskService` | 外部 ACP / gateway 任务入口 |
| `multiAgentMountManager` | `MultiAgentMountManager?` | No | default impl | 多 agent mount 管理器 |
### Returns
| Constructor / API | Returns | Meaning |
| --- | --- | --- |
| `AppController(...)` | `AppController` | 初始化后的应用总控制器 |
### Main Call Chain
- `XWorkmateApp` / `AppShell` 持有 `AppController`
- `AppController` 组合 `SettingsController`、`GatewayRuntime`、`GatewaySessionsController`、`GatewayChatController`
- `AssistantPage` / `SettingsPage` 所有关键操作最终都回到 `AppController` 扩展层
### Side Effects
- 初始化 runtime、settings、account client、desktop service
- 维护 task thread、assistant session、本地 UI 状态
- 向 `GatewayAcpClient``GoTaskServiceClient` 注入 endpoint / auth 解析
### Notes
- 这里是 app 侧“唯一大脑”,但业务拆分主要通过 extension 文件完成
- 当前仓库已经收敛到 `assistant + settings`,不再承载旧模块壳
## `AppControllerDesktopNavigation`
- Source: `lib/app/app_controller_desktop_navigation.dart`
- Type: `extension`
- Responsibility:
提供 destination 切换、settings/detail 打开关闭、sidebar 状态、语言与主题切换入口。
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `navigateTo` | `WorkspaceDestination destination` | `void` | 切到顶层页面 |
| `openSettings` | `{SettingsTab? tab, SettingsDetailPage? detail, SettingsNavigationContext? navigationContext}` | `void` | 打开 settings 并可直达 detail |
| `setSettingsTab` | `SettingsTab tab, {bool clearDetail=true}` | `void` | 切换 settings tab |
| `toggleAppLanguage` | none | `Future<void>` | 中英切换 |
| `setAppLanguage` | `AppLanguage language` | `Future<void>` | 显式保存语言 |
| `setThemeMode` | `ThemeMode mode` | `void` | 切换主题模式 |
### Main Call Chain
- `Sidebar` / `MobileShell` / focus panel -> navigation extension
- navigation extension -> `destinationInternal`, `settingsTabInternal`, `settingsDetailInternal`
- 状态更新后由 `AppShell``WorkspacePageSpec` 驱动实际页面构建
## `AppControllerDesktopSettings`
- Source: `lib/app/app_controller_desktop_settings.dart`
- Type: `extension`
- Responsibility:
管理 settings draft、立即保存、apply、workspace path 更新,以及 assistant 本地状态清理。
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `saveSettingsDraft` | `SettingsSnapshot snapshot` | `Future<void>` | 更新草稿但不立即全局 apply |
| `persistSettingsDraft` | none | `Future<void>` | 把 draft 落到持久层 |
| `applySettingsDraft` | none | `Future<void>` | 将 draft 视为当前设置并触发副作用 |
| `saveSettings` | `SettingsSnapshot snapshot, {bool refreshAfterSave=true}` | `Future<void>` | 直接保存并按需刷新 |
| `saveWorkspacePath` | `String value` | `Future<void>` | 写工作区根路径 |
| `clearAssistantLocalState` | none | `Future<void>` | 清理本地 assistant/thread 相关状态 |
### Side Effects
- 写 `SecureConfigStore`
- 触发 runtime / gateway / provider catalog / thread persistence 的后续副作用
- 管理 token/password 等 secret draft
## `AppControllerDesktopSettingsRuntime`
- Source: `lib/app/app_controller_desktop_settings_runtime.dart`
- Type: `extension`
- Responsibility:
把 settings 保存与 desktop/runtime 副作用连起来,包括 gateway catalog、VPN mode、授权目录、connection test。
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `updateAiGatewaySelection` | `List<String> selectedModels` | `Future<void>` | 归一化 AI gateway 选中模型并同步默认模型 |
| `syncAiGatewayCatalog` | `AiGatewayProfile profile, {String apiKeyOverride=''}` | `Future<AiGatewayProfile>` | 通过 settings controller 拉 catalog回写 models controller |
| `refreshDesktopIntegration` | none | `Future<void>` | 刷新 desktop platform 状态 |
| `saveLinuxDesktopConfig` | `LinuxDesktopConfig config` | `Future<void>` | 保存 Linux tunnel/proxy 偏好 |
| `setDesktopVpnMode` | `VpnMode mode` | `Future<void>` | 持久化并切换 tunnel/proxy mode |
| `connectDesktopTunnel` | none | `Future<void>` | 发起 tunnel 连接 |
| `disconnectDesktopTunnel` | none | `Future<void>` | 断开 tunnel |
| `authorizeSkillDirectory` | `{String suggestedPath=''}` | `Future<AuthorizedSkillDirectory?>` | 授权单个 skill 目录 |
| `authorizeSkillDirectories` | `{List<String> suggestedPaths=const []}` | `Future<List<AuthorizedSkillDirectory>>` | 批量授权目录 |
| `testOllamaConnectionDraft` | `{required bool cloud, required SettingsSnapshot snapshot, String apiKeyOverride=''}` | `Future<String>` | 使用草稿参数测试连接 |
### Main Call Chain
- `SettingsPage` / quick actions -> settings runtime extension
- extension -> `SettingsController` / `DesktopPlatformService` / `SkillDirectoryAccessService`
- 成功后回写 `SettingsSnapshot`、`ModelsController`、task recompute
## `AppControllerDesktopThreadSessions`
- Source: `lib/app/app_controller_desktop_thread_sessions.dart`
- Type: `extension`
- Responsibility:
承担 thread/session 级能力artifact 读取、多 agent 协作启动、mount 刷新、online workspace 打开。
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `loadAssistantArtifactSnapshot` | `{String? sessionKey}` | `Future<AssistantArtifactSnapshot>` | 加载某会话 artifact 清单快照 |
| `loadAssistantArtifactPreview` | `AssistantArtifactEntry artifact, {String? sessionKey}` | `Future<AssistantArtifactPreview>` | 读取 artifact 预览内容 |
| `saveMultiAgentConfig` | `MultiAgentConfig config` | `Future<void>` | 持久化多 agent 配置 |
| `refreshMultiAgentMounts` | `{bool sync=false}` | `Future<void>` | 刷新或同步 mount |
| `runMultiAgentCollaboration` | named args | `Future<void>` | 触发多 agent 协作执行 |
| `openOnlineWorkspace` | none | `Future<void>` | 打开在线工作区入口 |
## `AppControllerDesktopWorkspaceExecution`
- Source: `lib/app/app_controller_desktop_workspace_execution.dart`
- Type: `extension`
- Responsibility:
承担 assistant thread 的执行目标、provider、模型、权限、thread context、技能选择、任务归档主链。
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `setAssistantExecutionTarget` | `AssistantExecutionTarget target` | `Future<void>` | 切换当前 thread 的执行目标 |
| `setAssistantProvider` | `SingleAgentProvider provider` | `Future<void>` | 设置当前 thread 的 provider |
| `setAssistantMessageViewMode` | `AssistantMessageViewMode mode` | `Future<void>` | 切换 raw/rendered 视图 |
| `setAssistantPermissionLevel` | `AssistantPermissionLevel level` | `Future<void>` | 更新 assistant 权限档位 |
| `applyAssistantExecutionTargetInternal` | named args | `Future<void>` | 执行目标切换后的 thread/persistence 主逻辑 |
| `selectDefaultModel` | `String modelId` | `Future<void>` | 更新 settings 默认模型 |
| `selectAssistantModelForSession` | named args | `Future<void>` | 按 session 绑定模型 |
| `initializeAssistantThreadContext` | named args | `void` | 创建 thread 上下文骨架 |
| `toggleAssistantSkillForSession` | named args | `Future<void>` | 切换 thread 绑定技能 |
| `saveAssistantTaskArchived` | named args | `Future<void>` | 标记任务归档状态 |
### Main Call Chain
- `AssistantPage` composer / task dialog -> execution extension
- extension -> `ensureDesktopTaskThreadBindingInternal` / `upsertTaskThreadInternal`
- 最终通过 `GoTaskServiceClient`、`GatewayAcpClient`、gateway runtime 进入 bridge
## Runtime Coordination Helpers
- Source: `lib/app/app_controller_desktop_runtime_coordination_impl.dart`
- Type: `top-level functions`
- Responsibility:
这些函数不是页面入口,但它们决定了 app 如何刷新 bridge 能力和重算任务视图。
### Key Functions
| Function | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `refreshAcpCapabilitiesRuntimeInternal` | `AppController controller, {bool forceRefresh=false, bool persistMountTargets=false}` | `Future<void>` | 刷新 ACP capability snapshot |
| `refreshSingleAgentCapabilitiesRuntimeInternal` | `AppController controller, {bool forceRefresh=false}` | `Future<void>` | 刷新单 agent/provider catalog |
| `assistantWorkingDirectoryForSessionRuntimeInternal` | `AppController controller, String sessionKey` | `String?` | 求 session 工作目录 |
| `resolveLocalAssistantWorkingDirectoryForSessionRuntimeInternal` | `AppController controller, String sessionKey, {bool requireLocalExistence=true}` | `String?` | 返回本地且可用的工作目录 |
| `recomputeTasksRuntimeInternal` | `AppController controller` | `void` | 基于当前 session/thread/runtime 状态重建 task 列表 |
## `WorkspacePageSpec` and `buildWorkspacePage`
- Source: `lib/app/workspace_page_registry.dart`
- Type: `class` + `top-level function`
- Responsibility:
这是 app 顶层 destination 到页面实现的唯一映射表。
### `WorkspacePageSpec` Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `destination` | `WorkspaceDestination` | Yes | page 所属目标 |
| `desktopBuilder` | `WorkspacePageBuilder` | Yes | 桌面构建器 |
| `mobileBuilder` | `WorkspacePageBuilder` | Yes | 移动端构建器 |
### `buildWorkspacePage`
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `destination`, `controller`, `onOpenDetail`, `surface` | `Widget` | 根据 surface 选择 desktop/mobile page builder |
### Call Chain
- `AppShell` / `MobileShell` -> `buildWorkspacePage`
- `buildWorkspacePage` -> `AssistantPage` or `SettingsPage`
## `AppShell`
- Source: `lib/app/app_shell_desktop.dart`
- Type: `class`
- Responsibility:
统一承载 desktop/mobile 分支、sidebar、detail drawer、destination rendering 和移动端 fallback 到 `MobileShell` 的切换。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `controller` | `AppController` | Yes | 应用总控制器 |
### Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `AppShell(...)` | `AppShell` | 顶层壳组件 |
### Main Call Chain
- `XWorkmateApp` -> `AppShell`
- `AppShell` -> `buildWorkspacePage` / `MobileShell`
- `AppShell` 同时消费 `SidebarTaskItem`、detail sheet、feature manifest 和 execution target 可见性

View File

@ -0,0 +1,188 @@
# Feature Surfaces
## Purpose
本文件只写“关键页面和业务 surface”不逐条解释视觉组件。页面层的原则是
- `WorkspaceDestination` 是一级入口真源
- `WorkspacePageSpec` 是 destination 到页面实现的唯一映射
- 页面公开 API 只关注 controller 注入、detail 打开和业务入口参数
## `WorkspaceDestination`
- Source: `lib/models/app_models.dart`
- Type: `enum`
- Responsibility:
当前顶层 surface 只有 `assistant``settings`
### Returns
| Getter | Returns | Meaning |
| --- | --- | --- |
| `label` | `String` | 本地化标签 |
| `icon` | `IconData` | 导航图标 |
| `description` | `String` | 页面职责描述 |
| `fromJsonValue` | `WorkspaceDestination?` | 从持久化值恢复枚举 |
## `AssistantFocusEntry`
- Source: `lib/models/app_models.dart`
- Type: `enum`
- Responsibility:
表示 Assistant 内可 pin 的 focus 入口,目前是 `settings / language / theme`
### Returns
| Getter / API | Returns | Meaning |
| --- | --- | --- |
| `label` / `icon` / `description` | view metadata | focus 展示元数据 |
| `destination` | `WorkspaceDestination?` | 是否映射到真实 workspace 页面 |
| `opensSettingsPage` | `bool` | 是否最终进入 settings |
| `fromDestination` | `AssistantFocusEntry` | 从 destination 反解 focus entry |
## `SettingsNavigationContext`
- Source: `lib/models/app_models.dart`
- Type: `class`
- Responsibility:
承担“从哪里进入 settings/detail”的 breadcrumb 和上下文信息。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `rootLabel` | `String` | Yes | 顶层来源标签 |
| `destination` | `WorkspaceDestination` | Yes | 来源 destination |
| `sectionLabel` | `String?` | No | 中间 section 标签 |
| `settingsTab` | `SettingsTab?` | No | 目标 tab |
| `gatewayProfileIndex` | `int?` | No | 目标 gateway profile |
| `prefersGatewaySetupCode` | `bool?` | No | 是否优先 setup-code 流程 |
## `WorkspacePageSurface`
- Source: `lib/app/workspace_page_registry.dart`
- Type: `enum`
- Responsibility:
区分 page builder 的 `desktop``mobile` surface。
## `WorkspacePageSpec`
- Source: `lib/app/workspace_page_registry.dart`
- Type: `class`
- Responsibility:
把一个 destination 的桌面与移动 builder 绑定在一起。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `destination` | `WorkspaceDestination` | Yes | 页面目标 |
| `desktopBuilder` | `WorkspacePageBuilder` | Yes | 桌面 builder |
| `mobileBuilder` | `WorkspacePageBuilder` | Yes | 移动 builder |
## `buildWorkspacePage`
- Source: `lib/app/workspace_page_registry.dart`
- Type: `top-level function`
- Responsibility:
根据 destination 与 surface 实际实例化 `AssistantPage` / `SettingsPage`
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `destination`, `controller`, `onOpenDetail`, `surface` | `Widget` | 根据 `WorkspacePageSpec` 路由到具体页面 |
## `AssistantPage`
- Source: `lib/features/assistant/assistant_page_main.dart`
- Type: `class`
- Responsibility:
承担桌面主工作台、timeline、composer、task rail、artifact pane、focus panel以及所有 assistant 发起动作的 UI 主入口。
### Constructor Parameters
| Param | Type | Required | Default | Meaning |
| --- | --- | --- | --- | --- |
| `controller` | `AppController` | Yes | none | 统一业务控制器 |
| `onOpenDetail` | `ValueChanged<DetailPanelData>` | Yes | none | 打开 detail panel 的回调 |
| `navigationPanelBuilder` | `Widget Function(double contentWidth)?` | No | `null` | 定制导航区 |
| `showStandaloneTaskRail` | `bool` | No | `true` | 是否显示独立 task rail |
| `unifiedPaneStartsCollapsed` | `bool` | No | `false` | 初始化时 unified pane 是否折叠 |
| `clipboardImageReader` | `AssistantClipboardImageReader?` | No | `null` | 自定义剪贴板图像读取器 |
### Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `AssistantPage(...)` | `AssistantPage` | assistant 顶层页面 |
### Main Call Chain
- `AppShell` / `WorkspacePageSpec` -> `AssistantPage`
- 页面内部通过 `AppControllerDesktopWorkspaceExecution`、`AppControllerDesktopThreadSessions` 发起执行、切换 provider、加载 artifact、多 agent 协作
### Notes
- 这里是关键页面,但其大量内部组件和 internal 状态机不作为本次文档展开对象
## `SettingsPage`
- Source: `lib/features/settings/settings_page_core.dart`
- Type: `class`
- Responsibility:
承担 gateway、account、about 等配置型入口,并在保存后尝试刷新 bridge capabilities。
### Constructor Parameters
| Param | Type | Required | Default | Meaning |
| --- | --- | --- | --- | --- |
| `controller` | `AppController` | Yes | none | 应用控制器 |
| `initialTab` | `SettingsTab` | No | `SettingsTab.gateway` | 初始 tab |
| `initialDetail` | `SettingsDetailPage?` | No | `null` | 初始 detail |
| `navigationContext` | `SettingsNavigationContext?` | No | `null` | 导航上下文 |
### Main Call Chain
- `AppControllerDesktopNavigation.openSettings` -> `SettingsPage`
- `SettingsPage` -> `SettingsController.saveSnapshot/loginAccount/syncAccountSettings/verifyAccountMfa`
- 保存后最佳努力刷新 `refreshSingleAgentCapabilitiesInternal``refreshAcpCapabilitiesInternal`
## `MobileShell`
- Source: `lib/features/mobile/mobile_shell_core.dart`
- Type: `class`
- Responsibility:
移动端的统一入口壳层,负责 tab 切换、pairing guide、setup code 连接流和 mobile-safe sheet。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `controller` | `AppController` | Yes | 全局控制器 |
### Key Internal Business Entrypoints
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `showConnectSheetInternal` | none | `void` | 打开 gateway connection detail |
| `openGatewaySetupCodeEntryInternal` | `{String? prefilledSetupCode}` | `Future<void>` | 进入 setup-code 输入流 |
| `connectWithScannedSetupCodeInternal` | `String setupCode` | `Future<void>` | 用扫码结果触发连接 |
| `showPairingGuidePageFlowInternal` | none | `Future<void>` | 打开 pairing guide 页面 |
### Notes
- `mobile_shell_strip.dart`、`mobile_shell_nav.dart`、`mobile_shell_sheet.dart` 等 leaf 组件不逐条展开
## `AppShell`
- Source: `lib/app/app_shell_desktop.dart`
- Type: `class`
- Responsibility:
是 desktop/mobile surface 的统一宿主;在宽度不足时自动切到 `MobileShell`,在桌面场景下渲染 sidebar、detail 与 workspace 页面。
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `controller: AppController` | `AppShell` | 顶层应用壳层 |

View File

@ -0,0 +1,283 @@
# FFI And Rust
## Purpose
`rust/src` 当前是一组相对独立、边界清晰的 Codex FFI 草图实现。这里的重点是:
- Rust 公开结构体与状态模型
- C ABI 函数签名
- Dart / Flutter 侧应该如何理解这些函数的参数与返回值
- 当前实现仍然是 stub 的地方在哪里
## Crate Layout
| File | Responsibility |
| --- | --- |
| `rust/src/lib.rs` | 对外 `pub use``#[no_mangle] extern "C"` 导出 |
| `rust/src/runtime.rs` | Rust-native runtime/config/state |
| `rust/src/types.rs` | FFI-safe message/result/event/account/model 结构 |
| `rust/src/error.rs` | `CodexError` 错误类型 |
## `CodexConfig`
- Source: `rust/src/runtime.rs`
- Type: `struct`
- Responsibility:
C ABI 输入配置,描述 Codex 二进制、工作目录、sandbox/approval policy、model、gateway 和 debug。
### Fields
| Field | Type | Meaning |
| --- | --- | --- |
| `codex_path` | `*const c_char` | Codex 可执行文件路径 |
| `working_directory` | `*const c_char` | 工作目录 |
| `sandbox_mode` | `i32` | `0=read-only`, `1=workspace-write`, `2=danger-full-access` |
| `approval_policy` | `i32` | `0=suggest`, `1=auto-edit`, `2=full-auto` |
| `model` | `*const c_char` | 模型标识 |
| `api_key` | `*const c_char` | gateway API key |
| `gateway_url` | `*const c_char` | gateway URL |
| `debug` | `bool` | debug logging 开关 |
### Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `Default::default()` | `CodexConfig` | 默认 FFI config |
| `to_rust(&self)` | `Result<CodexConfigRust, CodexError>` | 转成 Rust-native 配置 |
## `CodexConfigRust`
- Source: `rust/src/runtime.rs`
- Type: `struct`
- Responsibility:
Rust-native 配置对象,所有指针型字符串都已经转成 `Option<String>`
## `ThreadHandle`
- Source: `rust/src/runtime.rs`
- Type: `struct`
- Responsibility:
作为 C ABI 的 thread opaque handle。
### Fields and Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `id` | `u64` | 线程句柄 ID |
| `new(id)` | `ThreadHandle` | 构造有效句柄 |
| `null()` | `ThreadHandle` | 零值句柄 |
| `is_null()` | `bool` | 判空 |
## `RuntimeState`
- Source: `rust/src/runtime.rs`
- Type: `enum`
- Responsibility:
Rust runtime 的内部状态机:`Disconnected / Connecting / Connected / Ready / Error`。
## `CodexRuntime`
- Source: `rust/src/runtime.rs`
- Type: `struct`
- Responsibility:
Rust 侧管理 Codex 进程与错误状态的核心对象。
### Fields
| Field | Type | Meaning |
| --- | --- | --- |
| `config` | `CodexConfigRust` | Rust-native 配置 |
| `state` | `RuntimeState` | 当前状态 |
| `last_error` | `CString` | 最近错误信息 |
### Key Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `new(config: CodexConfig)` | `CodexRuntime` | 由 FFI config 构造 |
| `with_config(config: CodexConfigRust)` | `CodexRuntime` | 由 Rust-native config 构造 |
| `state(&self)` | `RuntimeState` | 当前状态 |
| `set_error(&mut self, message: &str)` | `()` | 更新错误并切 `Error` |
| `find_codex_binary(&self)` | `Option<PathBuf>` | 查找 codex 二进制 |
| `start(&mut self)` | `Result<(), CodexError>` | 启动 runtime |
| `stop(&mut self)` | `Result<(), CodexError>` | 停止 runtime |
### Notes
- 当前 `start/stop` 仍然是 stub 型实现,尚未真正管理外部进程生命周期
## `CodexResult`
- Source: `rust/src/types.rs`
- Type: `struct`
- Responsibility:
最通用的 FFI-safe 成败返回值。
### Fields
| Field | Type | Meaning |
| --- | --- | --- |
| `success` | `bool` | 是否成功 |
| `error_code` | `i32` | 错误码 |
| `error_message` | `*const c_char` | 错误消息指针 |
### Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `ok()` | `CodexResult` | 成功结果 |
| `err(code, message)` | `CodexResult` | 失败结果 |
## `CodexMessage`
- Source: `rust/src/types.rs`
- Type: `struct`
- Responsibility:
FFI-safe 消息载体。
### Fields
| Field | Type | Meaning |
| --- | --- | --- |
| `message_type` | `*const c_char` | text/code/tool_call 等类型 |
| `content` | `*const c_char` | 消息文本 |
| `thread_id` | `*const c_char` | thread ID |
| `turn_id` | `*const c_char` | turn ID |
## `CodexEvent`
- Source: `rust/src/types.rs`
- Type: `struct`
- Responsibility:
FFI-safe 事件载体。
### Fields
| Field | Type | Meaning |
| --- | --- | --- |
| `event_type` | `*const c_char` | started/delta/completed/error |
| `thread_id` | `*const c_char` | thread ID |
| `turn_id` | `*const c_char` | turn ID |
| `data` | `*const c_char` | JSON 负载 |
| `timestamp` | `i64` | Unix millis |
## `CodexError`
- Source: `rust/src/error.rs`
- Type: `enum`
- Responsibility:
Rust 侧统一错误表示,供 runtime 和 FFI 转换使用。
## Exported FFI Functions
下面这些函数定义在 `rust/src/lib.rs`,是 Flutter / Dart 侧真正可调用的 C ABI 面。
## `codex_init`
- Type: `FFI function`
- Signature role:
初始化入口,必须先于其它 FFI 调用。
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| none | `i32` | `0` 表示成功 |
## `codex_runtime_create`
- Type: `FFI function`
- Responsibility:
创建 `CodexRuntime` 并返回原始指针。
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `config: *const CodexConfig` | `*mut CodexRuntime` | 成功返回 runtime 指针,空指针表示失败 |
### Notes
- 调用方负责后续 `codex_runtime_destroy`
## `codex_runtime_destroy`
- Type: `FFI function`
- Responsibility:
释放由 `codex_runtime_create` 返回的 runtime 指针。
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `runtime: *mut CodexRuntime` | `void` | 空指针时 no-op |
## `codex_start_thread`
- Type: `FFI function`
- Responsibility:
`cwd` 启动 thread目前返回 stub `ThreadHandle::new(0)`
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `runtime: *mut CodexRuntime`, `cwd: *const c_char` | `ThreadHandle` | cwd 为空时返回 null handle |
## `codex_send_message`
- Type: `FFI function`
- Responsibility:
向指定 thread 发送消息,目前仍是 stub。
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `runtime`, `thread`, `message` | `i32` | `0` 成功,`-1` 表示参数非法 |
## `codex_poll_events`
- Type: `FFI function`
- Responsibility:
从 runtime 读取事件数组,目前仍返回 `0`
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `runtime`, `events`, `max_events` | `usize` | 实际写入事件数量 |
## `codex_shutdown`
- Type: `FFI function`
- Responsibility:
优雅关闭 runtime目前仍是 stub。
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `runtime: *mut CodexRuntime` | `i32` | `0` 成功,空指针返回 `-1` |
## `codex_last_error`
- Type: `FFI function`
- Responsibility:
返回最近错误的 C 字符串指针。
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `runtime: *mut CodexRuntime` | `*const c_char` | 指向静态有效到下次 FFI 调用前的错误文本 |
## Dart Integration Notes
- Dart 侧对应阅读顺序:
1. `lib/runtime/codex_runtime.dart`
2. `lib/runtime/codex_config_bridge.dart`
3. `rust/src/lib.rs`
4. `rust/src/runtime.rs`
5. `rust/src/types.rs`
- 当前 FFI 面已经具备“结构体/函数签名骨架”但消息收发、event polling、thread lifecycle 仍未完整实现

View File

@ -0,0 +1,240 @@
# Models And Config
## Purpose
这一层覆盖“状态结构”和“配置 contract”重点在
- 什么对象被持久化
- 什么对象描述当前连接/执行/provider/catalog
- 哪些 helper 会影响默认值、归一化和展示语义
## `SettingsSnapshot`
- Source: `lib/runtime/runtime_models_settings_snapshot.dart`
- Type: `class`
- Responsibility:
是当前 app settings 的唯一主快照对象,也是 settings 持久化与恢复的核心 contract。
### Constructor Parameters
| Param Group | Meaning |
| --- | --- |
| app/UI fields | `appLanguage`、`appActive`、`launchAtLogin`、`showDockIcon` |
| workspace/runtime fields | `workspacePath`、`remoteProjectRoot`、`cliPath`、`codeAgentRuntimeMode` |
| execution/provider fields | `defaultModel`、`defaultProvider`、`assistantExecutionTarget`、`assistantPermissionLevel` |
| connection fields | `gatewayProfiles`、`webSessionPersistence` |
| integration fields | `ollamaLocal`、`ollamaCloud`、`vault`、`aiGateway` |
| multi-agent fields | `multiAgent`、`authorizedSkillDirectories` |
| account fields | `accountBaseUrl`、`accountUsername`、`accountWorkspace`、`accountWorkspaceFollowed` |
| desktop/server fields | `acpBridgeServerModeConfig`、`linuxDesktop` |
### Key Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `SettingsSnapshot.defaults()` | `SettingsSnapshot` | 当前 schema 的默认配置 |
| `copyWith(...)` | `SettingsSnapshot` | 产生归一化后的新快照 |
| `toJson()` | `Map<String, dynamic>` | 序列化 |
| `fromJson(Map<String, dynamic>)` | `SettingsSnapshot` | 反序列化并校验 schema |
### Notes
- `schemaVersion` 当前固定为 `2`
- 这里是 settings 持久化 contract不应该承载临时 UI-only 状态
## `GatewayConnectionProfile`
- Source: `lib/runtime/runtime_models_configs.dart`
- Type: `class`
- Responsibility:
描述单个 gateway 连接槽位,支持 setup code 与手工 host/port/tls 两种配置形态。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `mode` | `RuntimeConnectionMode` | Yes | unconfigured / remote |
| `useSetupCode` | `bool` | Yes | 是否使用 setup code |
| `setupCode` | `String` | Yes | setup code 内容 |
| `host` | `String` | Yes | 远端 host |
| `port` | `int` | Yes | 端口 |
| `tls` | `bool` | Yes | 是否 TLS |
| `tokenRef` | `String` | Yes | token secret ref |
| `passwordRef` | `String` | Yes | password secret ref |
| `selectedAgentId` | `String` | Yes | 当前 profile 默认 agent |
### Key Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `defaults()` / `defaultsGateway()` | `GatewayConnectionProfile` | 默认 gateway profile |
| `emptySlot({required int index})` | `GatewayConnectionProfile` | 非主 profile 的空槽位 |
| `copyWith(...)` | `GatewayConnectionProfile` | 自动做 endpoint 归一化 |
| `toJson()` / `fromJson(...)` | contract mapping | 序列化/恢复 |
## `normalizeGatewayProfiles`
- Source: `lib/runtime/runtime_models_configs.dart`
- Type: `top-level function`
- Responsibility:
对 gateway profile 列表做长度补齐、主槽位 remote 判定、loopback 剔除、token/password ref 修补。
### Parameters and Returns
| Parameters | Returns | Meaning |
| --- | --- | --- |
| `{Iterable<GatewayConnectionProfile>? profiles}` | `List<GatewayConnectionProfile>` | 固定长度、可持久化的 profile 列表 |
## `RuntimeConnectionMode` / `RuntimeConnectionStatus`
- Source: `lib/runtime/runtime_models_connection.dart`
- Type: `enum`
- Responsibility:
区分“配置形态”和“实时连接状态”。
### Returns
| Enum | Key Returns |
| --- | --- |
| `RuntimeConnectionMode` | `label`, `fromJsonValue` |
| `RuntimeConnectionStatus` | `label` |
## `AssistantExecutionTarget`
- Source: `lib/runtime/runtime_models_connection.dart`
- Type: `enum`
- Responsibility:
表示当前 thread 最终是落到 `agent` 还是 `gateway`
### Returns
| Getter / API | Returns | Meaning |
| --- | --- | --- |
| `label` / `compactLabel` | `String` | 展示标签 |
| `promptValue` | `String` | 发给 routing / prompt 的规范值 |
| `isAgent` / `isGateway` | `bool` | 类型判断 |
| `fromJsonValue` | `AssistantExecutionTarget` | 持久化恢复 |
## `SingleAgentProvider`
- Source: `lib/runtime/runtime_models_connection.dart`
- Type: `class`
- Responsibility:
是 app 侧统一 provider 模型,同时承载 agent provider 与 gateway provider。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `providerId` | `String` | Yes | 归一化 provider ID |
| `label` | `String` | Yes | 展示名 |
| `badge` | `String` | Yes | 短徽标 |
| `logoEmoji` | `String` | No | 可选 emoji |
| `supportedTargets` | `List<AssistantExecutionTarget>` | No | 支持的执行目标 |
| `enabled` | `bool` | No | 是否可用 |
| `unavailableReason` | `String` | No | 不可用说明 |
| `source` | `SingleAgentProviderSource` | No | 来源 |
### Key Returns
| API / Getter | Returns | Meaning |
| --- | --- | --- |
| `isUnspecified` | `bool` | 是否空 provider |
| `copyWith(...)` | `SingleAgentProvider` | 生成归一化副本 |
| `codex` / `opencode` / `claude` / `gemini` / `openclaw` | constants | 预置 provider 常量 |
## `GatewayConnectionSnapshot`
- Source: `lib/runtime/runtime_models_runtime_payloads.dart`
- Type: `class`
- Responsibility:
描述 `GatewayRuntime` 当前连接快照,包括 status、auth mode、health/status 原始负载、错误码和 main session key。
### Constructor Parameters
| Param Group | Meaning |
| --- | --- |
| connectivity | `status`, `mode`, `statusText`, `remoteAddress`, `lastConnectedAtMs` |
| identity/auth | `deviceId`, `authRole`, `authScopes`, `connectAuthMode`, `connectAuthFields`, `connectAuthSources` |
| error | `lastError`, `lastErrorCode`, `lastErrorDetailCode` |
| payloads | `healthPayload`, `statusPayload` |
### Key Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `initial(...)` | `GatewayConnectionSnapshot` | 初始离线快照 |
| `copyWith(...)` | `GatewayConnectionSnapshot` | 生成新快照 |
| `normalizedForConnectedState()` | `GatewayConnectionSnapshot` | connected 时清理历史错误 |
| `gatewayTokenMissing` | `bool` | 识别 AUTH_TOKEN_MISSING 场景 |
| `connectAuthSummary` | `String` | 汇总当前鉴权来源 |
## `AiGatewayProfile`
- Source: `lib/runtime/runtime_models_configs.dart`
- Type: `class`
- Responsibility:
描述 AI gateway 地址、模型选择、密钥 ref、catalog 及联通性状态。
### Key Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `defaults()` | `AiGatewayProfile` | 默认 profile |
| `copyWith(...)` | `AiGatewayProfile` | 更新 profile |
| `toJson()` / `fromJson(...)` | contract mapping | 序列化 / 恢复 |
## `MultiAgentConfig`
- Source: `lib/runtime/runtime_models_multi_agent.dart`
- Type: `class`
- Responsibility:
描述 native / ARIS 多 agent 协作的总配置,包括角色 worker、迭代次数、超时、AI gateway 注入策略、managed skills/MCP/mount targets。
### Constructor Parameters
| Param Group | Meaning |
| --- | --- |
| enable/framework | `enabled`, `autoSync`, `framework`, `arisEnabled`, `arisMode`, `arisBundleVersion`, `arisCompatStatus` |
| worker roles | `architect`, `engineer`, `tester` |
| execution policy | `ollamaEndpoint`, `maxIterations`, `minAcceptableScore`, `timeoutSeconds`, `aiGatewayInjectionPolicy` |
| managed assets | `managedSkills`, `managedMcpServers`, `mountTargets` |
### Key Returns
| API / Getter | Returns | Meaning |
| --- | --- | --- |
| `defaults()` | `MultiAgentConfig` | 默认协作配置 |
| `architectEnabled` / `architectTool` / `architectModel` | primitive | Architect 快捷访问 |
| `engineerTool` / `engineerModel` | primitive | Engineer 快捷访问 |
| `testerTool` / `testerModel` | primitive | Tester 快捷访问 |
| `usesAris` | `bool` | 当前是否落到 ARIS |
## `AgentWorkerConfig`
- Source: `lib/runtime/runtime_models_multi_agent.dart`
- Type: `class`
- Responsibility:
描述单个角色 worker 的 CLI、模型与重试策略。
### Constructor Parameters
| Param | Type | Required | Default | Meaning |
| --- | --- | --- | --- | --- |
| `role` | `MultiAgentRole` | Yes | none | 角色 |
| `cliTool` | `String` | Yes | none | `claude/codex/opencode/...` |
| `model` | `String` | Yes | none | 对应模型 |
| `enabled` | `bool` | Yes | none | 是否启用 |
| `maxRetries` | `int` | No | `2` | 最大重试次数 |
## `AppTheme` / `AppPalette`
- Source: `lib/theme/app_theme.dart`, `lib/theme/app_palette.dart`
- Type: `class`
- Responsibility:
虽然属于 UI 层,但它们是工程上可复用的主题入口,因此保留在公开接口层。
### Notes
- `AppTheme` 负责 spacing/radius/typography/sizes 与 `ThemeData` 组合
- `AppPalette``ThemeExtension`,为业务页面和 shell 提供调色板入口

View File

@ -0,0 +1,441 @@
# Runtime Contracts
## Module Purpose
`lib/runtime` 是当前仓库的最大公开接口面。这里承接的不是“视觉状态”,而是:
- bridge ACP / gateway runtime 合同
- task request / routing request / provider catalog
- settings 与 secret 持久化访问
- gateway session / agent / chat 控制器
- desktop 平台能力、skill 目录授权、多 agent mount
- Codex CLI 与 config bridge
## `GatewayAcpException`
- Source: `lib/runtime/gateway_acp_client.dart`
- Type: `class`
- Responsibility:
表示 ACP JSON-RPC 失败、能力缺失或协议错误。
### Constructor Parameters
| Param | Type | Required | Default | Meaning |
| --- | --- | --- | --- | --- |
| `message` | `String` | Yes | none | 错误摘要 |
| `code` | `String?` | No | `null` | 协议级错误码 |
| `details` | `Object?` | No | `null` | 原始错误负载 |
### Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `toString()` | `String` | 输出 `code: message` 或纯 message |
## `GatewayAcpCapabilities`
- Source: `lib/runtime/gateway_acp_client.dart`
- Type: `class`
- Responsibility:
保存 `acp.capabilities` 的解析结果,是 app 侧 single-agent / multi-agent / execution-target / provider catalog 的只读快照。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `singleAgent` | `bool` | Yes | 是否支持单 agent |
| `multiAgent` | `bool` | Yes | 是否支持多 agent |
| `availableExecutionTargets` | `List<AssistantExecutionTarget>` | Yes | 当前 bridge 允许的执行目标 |
| `providerCatalog` | `List<SingleAgentProvider>` | Yes | agent 侧 provider catalog |
| `gatewayProviderCatalog` | `List<SingleAgentProvider>` | Yes | gateway 侧 provider catalog |
| `raw` | `Map<String, dynamic>` | Yes | 原始 capability 负载 |
| `diagnostics` | `Map<String, dynamic>` | No | 可选诊断信息 |
### Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `GatewayAcpCapabilities.empty()` | `GatewayAcpCapabilities` | 空 capability 占位 |
## `GatewayAcpMultiAgentRequest`
- Source: `lib/runtime/gateway_acp_client.dart`
- Type: `class`
- Responsibility:
描述一次 multi-agent session.start / session.message 请求。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `sessionId` | `String` | Yes | 协作会话 ID |
| `threadId` | `String` | Yes | thread ID |
| `prompt` | `String` | Yes | 主任务提示词 |
| `workingDirectory` | `String` | Yes | 工作目录 |
| `attachments` | `List<CollaborationAttachment>` | Yes | 本地文件附件 |
| `selectedSkills` | `List<String>` | Yes | 显式选中的技能键 |
| `resumeSession` | `bool` | Yes | `false` 时发 `session.start``true` 时发 `session.message` |
## `GatewayAcpClient`
- Source: `lib/runtime/gateway_acp_client.dart`
- Type: `class`
- Responsibility:
XWorkmate 对 ACP JSON-RPC 的 app-side client负责 endpoint 解析、auth header 注入、capability 拉取、多 agent 事件流转发。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `endpointResolver` | `Uri? Function()` | Yes | 当前 ACP endpoint 解析器 |
| `authorizationResolver` | `Future<String?> Function(Uri endpoint)?` | No | endpoint 对应的授权头解析器 |
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `loadCapabilities` | `{bool forceRefresh=false, Uri? endpointOverride, String authorizationOverride=''}` | `Future<GatewayAcpCapabilities>` | 拉取并缓存 15 秒 capability 快照 |
| `runMultiAgent` | `GatewayAcpMultiAgentRequest request` | `Stream<MultiAgentRunEvent>` | 打开 ACP multi-agent 事件流 |
### Main Call Chain
- `AppController.refreshAcpCapabilitiesRuntimeInternal` -> `loadCapabilities`
- `GoRuntimeDispatchDesktopClient` / multi-agent flows -> `GatewayAcpClient`
- `GatewayAcpClient` -> ACP JSON-RPC -> bridge
### Side Effects
- 网络请求
- 本地 capability 缓存
- 将 ACP 通知转换成 `MultiAgentRunEvent`
## `ExternalCodeAgentAcpCapabilities`
- Source: `lib/runtime/go_task_service_client.dart`
- Type: `class`
- Responsibility:
表示 Go task service 视角下的外部 ACP 能力镜像,主要用于任务路由与执行目标可见性判断。
## `ExternalCodeAgentAcpRoutingResolution`
- Source: `lib/runtime/go_task_service_client.dart`
- Type: `class`
- Responsibility:
包装 bridge 返回的 routing resolution给调用方提供 `resolvedExecutionTarget`、`resolvedProviderId`、`resolvedSkills`、`unavailable*` 等只读 getter。
### Returns
| Getter | Returns | Meaning |
| --- | --- | --- |
| `resolvedExecutionTarget` | `String` | 归一化后的执行目标 |
| `resolvedProviderId` | `String` | 解析后的 provider |
| `resolvedGatewayProviderId` | `String` | 解析后的 gateway provider |
| `resolvedModel` | `String` | 解析后的模型 |
| `resolvedSkills` | `List<String>` | 解析后的技能列表 |
| `unavailable` | `bool` | 路由是否不可用 |
| `unavailableCode` / `unavailableMessage` | `String` | 不可用原因 |
## `ExternalCodeAgentAcpRoutingConfig`
- Source: `lib/runtime/go_task_service_client.dart`
- Type: `class`
- Responsibility:
描述一次任务执行时 app 侧给 bridge 的 routing 约束,包括 auto/explicit 模式、执行目标、provider、模型、技能、自动安装许可。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `mode` | `ExternalCodeAgentAcpRoutingMode` | Yes | `auto` or `explicit` |
| `preferredGatewayTarget` | `String` | Yes | 自动模式下偏好的 gateway 目标 |
| `explicitExecutionTarget` | `String` | Yes | 显式执行目标 |
| `explicitProviderId` | `String` | Yes | 显式 provider |
| `explicitModel` | `String` | Yes | 显式模型 |
| `explicitSkills` | `List<String>` | Yes | 显式技能列表 |
| `allowSkillInstall` | `bool` | Yes | 是否允许安装缺失技能 |
| `availableSkills` | `List<ExternalCodeAgentAcpAvailableSkill>` | Yes | 当前可见技能清单 |
| `installApproval` | `ExternalCodeAgentAcpSkillInstallApproval?` | No | 安装批准信息 |
### Returns
| API | Returns | Meaning |
| --- | --- | --- |
| `isAuto` | `bool` | 当前是否自动路由 |
| `toJson()` | `Map<String, dynamic>` | 生成 bridge 请求负载 |
## `GoTaskServiceRequest`
- Source: `lib/runtime/go_task_service_client.dart`
- Type: `class`
- Responsibility:
是 desktop task execution 的统一请求模型。无论最终落到 external ACP single、external ACP multi 还是 gateway mode都由它统一描述。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `sessionId` / `threadId` | `String` | Yes | 会话与线程标识 |
| `target` | `AssistantExecutionTarget` | Yes | 目标执行面 |
| `prompt` | `String` | Yes | 用户任务文本 |
| `workingDirectory` | `String` | Yes | 本地工作目录 |
| `model` / `thinking` | `String` | Yes | 模型与 thinking 档位 |
| `selectedSkills` | `List<String>` | Yes | 技能键 |
| `inlineAttachments` | `List<GatewayChatAttachmentPayload>` | Yes | base64 内联附件 |
| `localAttachments` | `List<CollaborationAttachment>` | Yes | 本地文件附件 |
| `agentId` | `String` | Yes | 目标 agent ID |
| `metadata` | `Map<String, dynamic>` | Yes | 额外元数据 |
| `routing` | `ExternalCodeAgentAcpRoutingConfig?` | No | 可选显式 routing |
| `provider` | `SingleAgentProvider` | No | 当前 provider |
| `remoteWorkingDirectoryHint` | `String` | No | 远端工作目录 hint |
| `resumeSession` | `bool` | No | 是否续跑 |
| `collaborationMode` | `GoTaskServiceCollaborationMode` | No | standard / multiAgent |
| `multiAgent` | `bool` | No | 是否强制多 agent |
### Returns
| Getter / Method | Returns | Meaning |
| --- | --- | --- |
| `isMultiAgentRequest` | `bool` | 是否走多 agent route |
| `route` | `GoTaskServiceRoute` | 计算出的实际 route |
| `acpMode` | `String` | ACP session mode |
| `routingExecutionTarget` | `String` | 发送给 routing 的目标值 |
| `effectiveRouting` | `ExternalCodeAgentAcpRoutingConfig` | 若未传入则自动合成 |
| `toExternalAcpParams()` | `Map<String, dynamic>` | 生成 external ACP 请求参数 |
### Main Call Chain
- `AppControllerDesktopWorkspaceExecution` -> `GoTaskServiceRequest`
- `DesktopGoTaskService.execute*` -> `toExternalAcpParams()`
- bridge 根据 routing 再决定 gateway / agent provider 落点
## `SettingsController`
- Source: `lib/runtime/runtime_controllers_settings.dart`
- Type: `class`
- Responsibility:
统一管理 `SettingsSnapshot`、secret refs、audit trail、account state、settings 文件监控和测试连接入口。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `storeInternal` | `SecureConfigStore` | Yes | 底层持久化与 secret store |
| `accountClientFactory` | `AccountRuntimeClient Function(String baseUrl)?` | No | 自定义 account client 构造器 |
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `initialize` | none | `Future<void>` | 读取 snapshot、启动 watcher、加载派生状态 |
| `refreshDerivedState` | none | `Future<void>` | 仅刷新派生状态 |
| `saveSnapshot` | `SettingsSnapshot snapshot` | `Future<void>` | 落盘 settings |
| `saveGatewaySecrets` | `{int? profileIndex, required String token, required String password}` | `Future<void>` | 保存 gateway secret |
| `loadGatewayToken` / `loadGatewayPassword` | `{int? profileIndex}` | `Future<String>` | 读取 gateway 凭据 |
| `testOllamaConnection` | `{required bool cloud}` | `Future<String>` | 测试 Ollama 连接 |
| `testVaultConnectionDraft` | `VaultConfig profile, {String tokenOverride=''}` | `Future<String>` | 使用草稿配置测试 vault |
### Side Effects
- 文件系统 watcher
- secure storage 与 settings JSON 文件
- account session / sync / MFA
## Gateway Runtime Controllers
### `GatewayAgentsController`
- Source: `lib/runtime/runtime_controllers_gateway.dart`
- Responsibility:
管理 gateway agent 列表、当前选中 agent 与 refresh 状态。
### `GatewaySessionsController`
- Source: `lib/runtime/runtime_controllers_gateway.dart`
- Responsibility:
管理 session summary 列表、当前 session key、agent-base session 推导。
### `GatewayChatController`
- Source: `lib/runtime/runtime_controllers_gateway.dart`
- Responsibility:
管理当前会话的消息历史、streaming 文本、pending run、chat/agent 事件消费。
### Common API Shape
| Controller | Key Parameters | Returns |
| --- | --- | --- |
| `refresh()` | none | `Future<void>` |
| `switchSession(String sessionKey)` | `String` | `Future<void>` |
| `loadSession(String sessionKey)` | `String` | `Future<void>` |
| `handleEvent(GatewayPushEvent event)` | `GatewayPushEvent` | `void` |
## `GatewayRuntime`
- Source: `lib/runtime/gateway_runtime_core.dart`
- Type: `class`
- Responsibility:
对 websocket gateway 的连接、鉴权、请求、事件订阅、session client 更新、device identity 和日志做统一管理。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `store` | `SecureConfigStore` | Yes | settings/secret/device token 存储 |
| `identityStore` | `DeviceIdentityStore` | Yes | 设备身份持久化 |
| `sessionClient` | `GatewayRuntimeSessionClient?` | No | 直接 session client |
| `runtimeId` | `String` | No | runtime 实例 ID空时自动生成 |
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `initialize` | none | `Future<void>` | 初始化 package/device/session updates |
| `connectProfile` | `GatewayConnectionProfile profile, {int? profileIndex, String authTokenOverride='', String authPasswordOverride=''}` | `Future<void>` | 按 profile 建立连接 |
| `request` | RPC params | `Future<Map<String, dynamic>>` | 发 websocket RPC |
| `listAgents` | none | `Future<List<GatewayAgentSummary>>` | 取 agent 清单 |
| `listSessions` | `{String? agentId, int limit=24}` | `Future<List<GatewaySessionSummary>>` | 取 session 清单 |
| `loadHistory` | `String sessionKey, {int limit=120}` | `Future<List<GatewayChatMessage>>` | 取会话历史 |
| `sendChat` | named args | `Future<String>` | 发消息并返回 runId |
| `abortChat` | `{required String sessionKey, required String runId}` | `Future<void>` | 终止 run |
| `stop` | none | `Future<void>` | 断连并清理资源 |
### Main Call Chain
- `AppController` / `Gateway*Controller` -> `GatewayRuntime`
- `GatewayRuntime` -> websocket RPC / `GatewayRuntimeSessionClient`
- 返回 `GatewayPushEvent` 与 snapshot 更新给上层 controller
### Side Effects
- 网络 websocket
- device token / shared token / password 鉴权
- runtime logs 与 reconnect timer
## `GatewayRuntimeSessionClient`
- Source: `lib/runtime/gateway_runtime_session_client.dart`
- Type: `abstract class`
- Responsibility:
描述 session connect/update 的外部实现边界,允许 runtime 接入不同 session 数据源。
### Key Types
| Type | Role |
| --- | --- |
| `GatewayRuntimeSessionConnectRequest` | 连接 session 的入参 |
| `GatewayRuntimeSessionConnectResult` | 连接结果 |
| `GatewayRuntimeSessionUpdate` | 增量更新负载 |
## `RuntimeBootstrapConfig`
- Source: `lib/runtime/runtime_bootstrap.dart`
- Type: `class`
- Responsibility:
从 repo / `.env` / OpenClaw 邻接路径中解析启动期默认值,并把“仅用于预填”的 bootstrap 值合并到 `SettingsSnapshot`
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `load` | `{String? workspacePathHint, String? cliPathHint}` | `Future<RuntimeBootstrapConfig>` | 扫描 workspace/OpenClaw/.env 得到 bootstrap 配置 |
| `mergeIntoSettings` | `SettingsSnapshot snapshot` | `SettingsSnapshot` | 只在默认/瞬时路径场景下注入预填值 |
| `preferredGatewayFor` | `RuntimeConnectionMode mode` | `GatewayBootstrapTarget?` | 给连接模式选择 bootstrap gateway |
### Notes
- 这里遵循仓库安全规则:`.env` 只做预填,不做自动连接真源
## `GatewayBootstrapTarget`
- Source: `lib/runtime/runtime_bootstrap.dart`
- Type: `class`
- Responsibility:
描述 bootstrap 阶段解析出来的远程 gateway 目标。
### Constructor Parameters
| Param | Type | Required | Meaning |
| --- | --- | --- | --- |
| `mode` | `RuntimeConnectionMode` | Yes | 当前只用于 remote |
| `url` | `String` | Yes | 原始 URL |
| `host` | `String` | Yes | 解析后的 host |
| `port` | `int` | Yes | 解析后的端口 |
| `tls` | `bool` | Yes | 是否 TLS |
| `token` | `String` | Yes | 可选 bootstrap token |
## `AgentRegistry`
- Source: `lib/runtime/agent_registry.dart`
- Type: `class`
- Responsibility:
聚合 gateway 暴露的 agent capability / registration / response给 app 侧 provider 与 bridge 节点注册使用。
## `SkillDirectoryAccessService`
- Source: `lib/runtime/skill_directory_access.dart`
- Type: `abstract class`
- Responsibility:
抽象 skill 目录授权入口。平台实现包括文件选择器和 macOS bookmark 能力。
### Main Implementations
| Type | Meaning |
| --- | --- |
| `UnsupportedSkillDirectoryAccessService` | 平台不支持时的空实现 |
| `FileSelectorSkillDirectoryAccessService` | 通用文件选择器实现 |
| `MacOsSkillDirectoryAccessService` | macOS bookmark/持久授权实现 |
## `DesktopPlatformService`
- Source: `lib/runtime/desktop_platform_service.dart`
- Type: `abstract class`
- Responsibility:
抽象 tunnel/proxy/VPN 模式、系统平台集成刷新与切换能力。
### Main Implementations
| Type | Meaning |
| --- | --- |
| `UnsupportedDesktopPlatformService` | 无平台支持时的降级实现 |
| `MethodChannelDesktopPlatformService` | 通过 method channel 驱动平台能力 |
## `CodexRuntime`
- Source: `lib/runtime/codex_runtime.dart`
- Type: `class`
- Responsibility:
管理本地 Codex CLI stdio 进程、线程/turn RPC、事件流和账户查询。
### Key Methods
| Method | Parameters | Returns | Meaning |
| --- | --- | --- | --- |
| `findCodexBinary` | none | `Future<String?>` | 解析 codex 二进制路径 |
| `startStdio` | named args | `Future<void>` | 以 stdio 模式启动 Codex CLI |
| `request` | RPC request params | `Future<Map<String, dynamic>>` | 发 Codex RPC 请求 |
| `startThread` | `{required String cwd, bool ephemeral=false}` | `Future<CodexThread>` | 新建 thread |
| `resumeThread` | `{required String threadId}` | `Future<CodexThread>` | 恢复 thread |
| `sendMessage` | named args | `Stream<CodexTurnEvent>` | 对 thread 发消息并返回事件流 |
| `interrupt` | `{required String threadId}` | `Future<void>` | 中断运行 |
| `getAccount` | none | `Future<CodexAccount>` | 拉账户信息 |
| `listModels` | named args | `Future<List<Map<String, dynamic>>>` | 列模型 |
| `listSkills` | `{required String cwd}` | `Future<List<Map<String, dynamic>>>` | 列技能 |
| `stop` | none | `Future<void>` | 停止进程与清理 |
### Key Companion Types
| Type | Role |
| --- | --- |
| `CodexThread` | thread 元数据 |
| `CodexTurn` | turn 元数据 |
| `CodexAccount` / `CodexRateLimit` | 账户与限额 |
| `CodexUserInput` / `CodexAttachment` | 入参模型 |
| `CodexRpcError` | RPC 错误 |
| `CodexLaunchConfiguration` | 嵌入式启动配置 |
## `CodexConfigBridge`
- Source: `lib/runtime/codex_config_bridge.dart`
- Type: `class`
- Responsibility:
负责把 app settings 与 Codex 配置文件 / MCP server 配置桥接起来,是 `CodexRuntime` 之外的“配置写入侧”。

View File

@ -0,0 +1,423 @@
#!/usr/bin/env python3
"""Extract public API inventory for XWorkmate engineering docs.
This script intentionally stays lightweight:
- It only scans source files that define the public engineering surface.
- It extracts top-level public symbols and compact signatures.
- It does not attempt semantic explanation or method-level expansion.
"""
from __future__ import annotations
import json
import re
from collections import defaultdict
from dataclasses import dataclass, asdict
from datetime import datetime, timezone
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[2]
OUTPUT_DIR = REPO_ROOT / "docs" / "architecture" / "public-api" / "_generated"
TARGETS = (
"lib/app",
"lib/runtime",
"lib/models",
"lib/features",
"lib/theme",
"rust/src",
)
COVERAGE_PREFIXES = (
"lib/app",
"lib/runtime",
"lib/models",
"lib/features/assistant",
"lib/features/settings",
"lib/features/mobile",
"lib/theme",
"rust/src",
)
DART_GLOB = "*.dart"
RUST_GLOB = "*.rs"
@dataclass(frozen=True)
class SymbolRecord:
language: str
path: str
line: int
kind: str
name: str
signature: str
def repo_relative(path: Path) -> str:
return path.relative_to(REPO_ROOT).as_posix()
def normalize_signature(value: str) -> str:
return re.sub(r"\s+", " ", value.strip())
def iter_target_files() -> list[Path]:
files: list[Path] = []
for target in TARGETS:
root = REPO_ROOT / target
if not root.exists():
continue
pattern = DART_GLOB if root.parts[-1] != "src" else RUST_GLOB
files.extend(sorted(root.rglob(pattern)))
return files
def extract_symbols(path: Path) -> list[SymbolRecord]:
if path.suffix == ".dart":
return extract_dart_symbols(path)
if path.suffix == ".rs":
return extract_rust_symbols(path)
return []
def extract_dart_symbols(path: Path) -> list[SymbolRecord]:
lines = path.read_text(encoding="utf-8").splitlines()
symbols: list[SymbolRecord] = []
index = 0
type_patterns = (
("abstract interface", re.compile(r"^abstract\s+class\s+([A-Za-z]\w*)\b")),
("class", re.compile(r"^class\s+([A-Za-z]\w*)\b")),
("mixin", re.compile(r"^mixin\s+([A-Za-z]\w*)\b")),
("enum", re.compile(r"^enum\s+([A-Za-z]\w*)\b")),
("typedef", re.compile(r"^typedef\s+([A-Za-z]\w*)\b")),
("extension", re.compile(r"^extension\s+([A-Za-z]\w*)\b")),
)
function_pattern = re.compile(
r"^(?:[A-Za-z_<>{}\[\]\?.,\s]+)\s+([A-Za-z]\w*)\s*\("
)
while index < len(lines):
raw_line = lines[index]
stripped = raw_line.strip()
if not stripped or raw_line.startswith((" ", "\t")):
index += 1
continue
if stripped.startswith(("//", "/*", "*", "@", "import ", "export ", "part ")):
index += 1
continue
if stripped.startswith(("const ", "final ", "var ")):
index += 1
continue
matched = False
for kind, pattern in type_patterns:
result = pattern.match(stripped)
if not result:
continue
name = result.group(1)
if name.startswith("_"):
matched = True
break
signature, consumed = collect_signature(lines, index)
symbols.append(
SymbolRecord(
language="dart",
path=repo_relative(path),
line=index + 1,
kind=kind,
name=name,
signature=signature,
)
)
index += consumed
matched = True
break
if matched:
continue
if stripped.startswith(
(
"if ",
"for ",
"while ",
"switch ",
"return ",
"assert ",
"throw ",
"try ",
)
):
index += 1
continue
function_match = function_pattern.match(stripped)
if function_match:
name = function_match.group(1)
if not name.startswith("_"):
signature, consumed = collect_signature(lines, index)
symbols.append(
SymbolRecord(
language="dart",
path=repo_relative(path),
line=index + 1,
kind="top-level function",
name=name,
signature=signature,
)
)
index += consumed
continue
index += 1
return symbols
def collect_signature(lines: list[str], start: int) -> tuple[str, int]:
chunk: list[str] = []
paren_balance = 0
consumed = 0
while start + consumed < len(lines):
line = lines[start + consumed].strip()
chunk.append(line)
paren_balance += line.count("(") - line.count(")")
consumed += 1
if paren_balance <= 0 and (
"{" in line or line.endswith(";") or "=>" in line
):
break
return normalize_signature(" ".join(chunk)), consumed
def extract_rust_symbols(path: Path) -> list[SymbolRecord]:
lines = path.read_text(encoding="utf-8").splitlines()
symbols: list[SymbolRecord] = []
type_patterns = (
("struct", re.compile(r"^pub\s+struct\s+([A-Za-z]\w*)\b")),
("enum", re.compile(r"^pub\s+enum\s+([A-Za-z]\w*)\b")),
)
ffi_pattern = re.compile(r'^pub\s+unsafe\s+extern\s+"C"\s+fn\s+([A-Za-z]\w*)\s*\(')
index = 0
while index < len(lines):
stripped = lines[index].strip()
if not stripped or lines[index].startswith((" ", "\t")):
index += 1
continue
matched = False
for kind, pattern in type_patterns:
result = pattern.match(stripped)
if result:
signature, consumed = collect_signature(lines, index)
symbols.append(
SymbolRecord(
language="rust",
path=repo_relative(path),
line=index + 1,
kind=kind,
name=result.group(1),
signature=signature,
)
)
index += consumed
matched = True
break
if matched:
continue
ffi_match = ffi_pattern.match(stripped)
if ffi_match:
signature, consumed = collect_signature(lines, index)
symbols.append(
SymbolRecord(
language="rust",
path=repo_relative(path),
line=index + 1,
kind="FFI function",
name=ffi_match.group(1),
signature=signature,
)
)
index += consumed
continue
index += 1
return symbols
def build_inventory(symbols: list[SymbolRecord]) -> dict:
files = iter_target_files()
files_by_group: dict[str, list[dict]] = defaultdict(list)
symbol_counts_by_group: dict[str, int] = defaultdict(int)
file_counts_by_group: dict[str, int] = defaultdict(int)
symbols_by_path: dict[str, list[SymbolRecord]] = defaultdict(list)
for symbol in symbols:
symbols_by_path[symbol.path].append(symbol)
for file_path in files:
relative = repo_relative(file_path)
parts = relative.split("/")
group = "/".join(parts[:2]) if len(parts) >= 2 else relative
file_counts_by_group[group] += 1
file_symbols = sorted(symbols_by_path[relative], key=lambda item: item.line)
symbol_counts_by_group[group] += len(file_symbols)
files_by_group[group].append(
{
"path": relative,
"language": "dart" if file_path.suffix == ".dart" else "rust",
"symbolCount": len(file_symbols),
"symbols": [asdict(item) for item in file_symbols],
}
)
groups = []
for group in sorted(files_by_group):
groups.append(
{
"group": group,
"fileCount": file_counts_by_group[group],
"symbolCount": symbol_counts_by_group[group],
"files": files_by_group[group],
}
)
scope_summaries = []
all_paths = [repo_relative(path) for path in files]
for prefix in COVERAGE_PREFIXES:
scope_files = [
item
for item in all_paths
if item == prefix or item.startswith(f"{prefix}/")
]
scope_symbol_count = sum(
1
for symbol in symbols
if symbol.path == prefix or symbol.path.startswith(f"{prefix}/")
)
scope_summaries.append(
{
"scope": prefix,
"fileCount": len(scope_files),
"symbolCount": scope_symbol_count,
}
)
return {
"generatedAt": datetime.now(timezone.utc).isoformat(),
"targets": list(TARGETS),
"coverageScopes": scope_summaries,
"totals": {
"fileCount": len(files),
"symbolCount": len(symbols),
},
"groups": groups,
}
def render_markdown(inventory: dict) -> str:
lines: list[str] = [
"# Public Symbol Inventory",
"",
"> Auto-generated by `scripts/docs/extract_public_api_inventory.py`.",
">",
"> Scope: `lib/app`, `lib/runtime`, `lib/models`, `lib/features/**`, `lib/theme`, `rust/src`.",
"> Excludes private `_` symbols and non-top-level Dart members.",
"",
f"- Generated at: `{inventory['generatedAt']}`",
f"- Files scanned: `{inventory['totals']['fileCount']}`",
f"- Public symbols extracted: `{inventory['totals']['symbolCount']}`",
"",
"## Group Summary",
"",
"| Group | Files | Public Symbols |",
"| --- | ---: | ---: |",
]
for group in inventory["groups"]:
lines.append(
f"| `{group['group']}` | {group['fileCount']} | {group['symbolCount']} |"
)
lines.extend(
[
"",
"## Coverage Scope Summary",
"",
"| Scope | Files | Public Symbols |",
"| --- | ---: | ---: |",
]
)
for scope in inventory["coverageScopes"]:
lines.append(
f"| `{scope['scope']}` | {scope['fileCount']} | {scope['symbolCount']} |"
)
for group in inventory["groups"]:
lines.extend(
[
"",
f"## {group['group']}",
"",
f"- Files: `{group['fileCount']}`",
f"- Public symbols: `{group['symbolCount']}`",
]
)
for file_entry in group["files"]:
lines.extend(
[
"",
f"### `{file_entry['path']}`",
"",
f"- Language: `{file_entry['language']}`",
f"- Public symbols: `{file_entry['symbolCount']}`",
]
)
if not file_entry["symbols"]:
lines.extend(["", "_No extracted public top-level symbols._"])
continue
lines.extend(
[
"",
"| Line | Kind | Name | Signature |",
"| ---: | --- | --- | --- |",
]
)
for symbol in file_entry["symbols"]:
signature = symbol["signature"].replace("|", "\\|")
lines.append(
f"| {symbol['line']} | `{symbol['kind']}` | `{symbol['name']}` | `{signature}` |"
)
lines.append("")
return "\n".join(lines)
def main() -> None:
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
files = iter_target_files()
symbols: list[SymbolRecord] = []
for file_path in files:
symbols.extend(extract_symbols(file_path))
inventory = build_inventory(symbols)
markdown = render_markdown(inventory)
json_path = OUTPUT_DIR / "public-symbol-inventory.json"
md_path = OUTPUT_DIR / "public-symbol-inventory.md"
json_path.write_text(
json.dumps(inventory, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)
md_path.write_text(markdown, encoding="utf-8")
print(f"Wrote {repo_relative(json_path)}")
print(f"Wrote {repo_relative(md_path)}")
print(
f"Scanned {inventory['totals']['fileCount']} files, extracted {inventory['totals']['symbolCount']} public symbols."
)
if __name__ == "__main__":
main()