first commit
This commit is contained in:
691
claude-code-中文Wiki/11-Swarm系统.md
Normal file
691
claude-code-中文Wiki/11-Swarm系统.md
Normal file
@@ -0,0 +1,691 @@
|
||||
# Swarm 系统
|
||||
|
||||
Claude Code 的 Swarm 系统是一个多智能体协作框架,支持团队模式、进程内队友和多种终端后端实现。
|
||||
|
||||
## 1. Swarm 系统概述
|
||||
|
||||
### 1.1 核心目录结构
|
||||
|
||||
**位置:** `/src/utils/swarm/`
|
||||
|
||||
```
|
||||
swarm/
|
||||
├── backends/
|
||||
│ ├── types.ts # 后端类型定义
|
||||
│ ├── registry.ts # 后端注册表
|
||||
│ ├── detection.ts # 环境检测
|
||||
│ ├── TmuxBackend.ts # Tmux 后端
|
||||
│ ├── ITermBackend.ts # iTerm2 后端
|
||||
│ ├── InProcessBackend.ts # 进程内后端
|
||||
│ ├── PaneBackendExecutor.ts # 面板执行器
|
||||
│ └── teammateModeSnapshot.ts # 团队模式快照
|
||||
├── teamHelpers.ts # 团队辅助函数
|
||||
├── teammateInit.ts # 队友初始化
|
||||
├── teammateLayoutManager.ts # 布局管理
|
||||
├── teammateModel.ts # 队友模型
|
||||
├── teammatePromptAddendum.ts # 提示补充
|
||||
├── permissionSync.ts # 权限同步
|
||||
├── reconnection.ts # 重连机制
|
||||
├── spawnUtils.ts # 生成工具
|
||||
├── spawnInProcess.ts # 进程内生成
|
||||
├── inProcessRunner.ts # 进程内运行器
|
||||
├── constants.ts # 常量定义
|
||||
└── leaderPermissionBridge.ts # 权限桥接
|
||||
```
|
||||
|
||||
## 2. 团队模式快照 (teammateModeSnapshot.ts)
|
||||
|
||||
### 2.1 快照机制
|
||||
|
||||
```typescript
|
||||
// 位置:/src/utils/swarm/backends/teammateModeSnapshot.ts
|
||||
|
||||
export type TeammateMode = 'auto' | 'tmux' | 'in-process'
|
||||
|
||||
// CLI 覆盖
|
||||
let cliTeammateModeOverride: TeammateMode | null = null
|
||||
|
||||
// 捕获启动时的团队模式
|
||||
export function captureTeammateModeSnapshot(): void {
|
||||
if (cliTeammateModeOverride) {
|
||||
initialTeammateMode = cliTeammateModeOverride
|
||||
} else {
|
||||
const config = getGlobalConfig()
|
||||
initialTeammateMode = config.teammateMode ?? 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
// 获取快照模式
|
||||
export function getTeammateModeFromSnapshot(): TeammateMode
|
||||
```
|
||||
|
||||
### 2.2 模式类型
|
||||
|
||||
| 模式 | 描述 |
|
||||
|------|------|
|
||||
| `auto` | 自动检测可用后端 |
|
||||
| `tmux` | 强制使用 Tmux 后端 |
|
||||
| `in-process` | 使用进程内后端 |
|
||||
|
||||
## 3. 团队辅助函数 (teamHelpers.ts)
|
||||
|
||||
### 3.1 团队文件操作
|
||||
|
||||
```typescript
|
||||
// 获取团队目录
|
||||
export function getTeamDir(teamName: string): string
|
||||
|
||||
// 获取团队配置文件路径
|
||||
export function getTeamFilePath(teamName: string): string
|
||||
|
||||
// 读取团队文件(同步)
|
||||
export function readTeamFile(teamName: string): TeamFile | null
|
||||
|
||||
// 读取团队文件(异步)
|
||||
export async function readTeamFileAsync(teamName: string): Promise<TeamFile | null>
|
||||
|
||||
// 写入团队文件(同步)
|
||||
function writeTeamFile(teamName: string, teamFile: TeamFile): void
|
||||
|
||||
// 写入团队文件(异步)
|
||||
export async function writeTeamFileAsync(teamName: string, teamFile: TeamFile): Promise<void>
|
||||
```
|
||||
|
||||
### 3.2 团队成员管理
|
||||
|
||||
```typescript
|
||||
// 从团队文件移除队友
|
||||
export function removeTeammateFromTeamFile(
|
||||
teamName: string,
|
||||
identifier: { agentId?: string; name?: string }
|
||||
): boolean
|
||||
|
||||
// 移除成员
|
||||
export function removeMemberFromTeam(
|
||||
teamName: string,
|
||||
tmuxPaneId: string
|
||||
): boolean
|
||||
|
||||
// 按 Agent ID 移除成员
|
||||
export function removeMemberByAgentId(
|
||||
teamName: string,
|
||||
agentId: string
|
||||
): boolean
|
||||
|
||||
// 设置成员权限模式
|
||||
export function setMemberMode(
|
||||
teamName: string,
|
||||
memberName: string,
|
||||
mode: PermissionMode
|
||||
): boolean
|
||||
|
||||
// 同步队友模式
|
||||
export function syncTeammateMode(mode: PermissionMode, teamNameOverride?: string): void
|
||||
|
||||
// 设置成员活跃状态
|
||||
export async function setMemberActive(
|
||||
teamName: string,
|
||||
memberName: string,
|
||||
isActive: boolean
|
||||
): Promise<void>
|
||||
```
|
||||
|
||||
### 3.3 团队清理
|
||||
|
||||
```typescript
|
||||
// 清理会话团队
|
||||
export async function cleanupSessionTeams(): Promise<void>
|
||||
|
||||
// 清理团队目录
|
||||
export async function cleanupTeamDirectories(teamName: string): Promise<void>
|
||||
|
||||
// 注册团队会话清理
|
||||
export function registerTeamForSessionCleanup(teamName: string): void
|
||||
|
||||
// 注销团队会话清理
|
||||
export function unregisterTeamForSessionCleanup(teamName: string): void
|
||||
```
|
||||
|
||||
## 4. 队友初始化 (teammateInit.ts)
|
||||
|
||||
### 4.1 初始化流程
|
||||
|
||||
```typescript
|
||||
// 位置:/src/utils/swarm/teammateInit.ts
|
||||
|
||||
export function initializeTeammateHooks(
|
||||
setAppState: (updater: (prev: AppState) => AppState) => void,
|
||||
sessionId: string,
|
||||
teamInfo: { teamName: string; agentId: string; agentName: string }
|
||||
): void
|
||||
```
|
||||
|
||||
### 4.2 团队权限应用
|
||||
|
||||
```typescript
|
||||
// 应用团队范围的允许路径
|
||||
if (teamFile.teamAllowedPaths && teamFile.teamAllowedPaths.length > 0) {
|
||||
for (const allowedPath of teamFile.teamAllowedPaths) {
|
||||
setAppState(prev => ({
|
||||
...prev,
|
||||
toolPermissionContext: applyPermissionUpdate(
|
||||
prev.toolPermissionContext,
|
||||
{
|
||||
type: 'addRules',
|
||||
rules: [{ toolName: allowedPath.toolName, ruleContent }],
|
||||
behavior: 'allow',
|
||||
destination: 'session'
|
||||
}
|
||||
)
|
||||
}))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 停止钩子注册
|
||||
|
||||
```typescript
|
||||
// 注册停止钩子以通知团队领导
|
||||
addFunctionHook(
|
||||
setAppState,
|
||||
sessionId,
|
||||
'Stop',
|
||||
'',
|
||||
async (messages, _signal) => {
|
||||
// 标记为空闲
|
||||
void setMemberActive(teamName, agentName, false)
|
||||
|
||||
// 发送空闲通知
|
||||
const notification = createIdleNotification(agentName, {...})
|
||||
await writeToMailbox(leadAgentName, {...})
|
||||
|
||||
return true
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## 5. 布局管理 (teammateLayoutManager.ts)
|
||||
|
||||
### 5.1 队友颜色分配
|
||||
|
||||
```typescript
|
||||
// 分配队友颜色
|
||||
export function assignTeammateColor(teammateId: string): AgentColorName
|
||||
|
||||
// 获取队友颜色
|
||||
export function getTeammateColor(teammateId: string): AgentColorName | undefined
|
||||
|
||||
// 清除所有颜色分配
|
||||
export function clearTeammateColors(): void
|
||||
```
|
||||
|
||||
### 5.2 后端选择
|
||||
|
||||
```typescript
|
||||
async function getBackend(): Promise<PaneBackend> {
|
||||
return (await detectAndGetBackend()).backend
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 面板操作
|
||||
|
||||
```typescript
|
||||
// 在 Swarm 视图中创建队友面板
|
||||
export async function createTeammatePaneInSwarmView(
|
||||
teammateName: string,
|
||||
teammateColor: AgentColorName
|
||||
): Promise<{ paneId: string; isFirstTeammate: boolean }>
|
||||
|
||||
// 启用面板边框状态
|
||||
export async function enablePaneBorderStatus(
|
||||
windowTarget?: string,
|
||||
useSwarmSocket?: boolean
|
||||
): Promise<void>
|
||||
|
||||
// 发送命令到面板
|
||||
export async function sendCommandToPane(
|
||||
paneId: string,
|
||||
command: string,
|
||||
useSwarmSocket?: boolean
|
||||
): Promise<void>
|
||||
```
|
||||
|
||||
## 6. 后端系统 (backends/)
|
||||
|
||||
### 6.1 后端类型
|
||||
|
||||
**位置:** `/src/utils/swarm/backends/types.ts`
|
||||
|
||||
```typescript
|
||||
export type BackendType = 'tmux' | 'iterm2' | 'in-process'
|
||||
|
||||
export type PaneBackendType = 'tmux' | 'iterm2'
|
||||
|
||||
export type PaneId = string
|
||||
|
||||
export type CreatePaneResult = {
|
||||
paneId: PaneId
|
||||
isFirstTeammate: boolean
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 PaneBackend 接口
|
||||
|
||||
```typescript
|
||||
export type PaneBackend = {
|
||||
readonly type: BackendType
|
||||
readonly displayName: string
|
||||
readonly supportsHideShow: boolean
|
||||
|
||||
isAvailable(): Promise<boolean>
|
||||
isRunningInside(): Promise<boolean>
|
||||
|
||||
createTeammatePaneInSwarmView(
|
||||
name: string,
|
||||
color: AgentColorName
|
||||
): Promise<CreatePaneResult>
|
||||
|
||||
sendCommandToPane(
|
||||
paneId: PaneId,
|
||||
command: string,
|
||||
useExternalSession?: boolean
|
||||
): Promise<void>
|
||||
|
||||
setPaneBorderColor(
|
||||
paneId: PaneId,
|
||||
color: AgentColorName,
|
||||
useExternalSession?: boolean
|
||||
): Promise<void>
|
||||
|
||||
setPaneTitle(
|
||||
paneId: PaneId,
|
||||
name: string,
|
||||
color: AgentColorName,
|
||||
useExternalSession?: boolean
|
||||
): Promise<void>
|
||||
|
||||
enablePaneBorderStatus(
|
||||
windowTarget?: string,
|
||||
useExternalSession?: boolean
|
||||
): Promise<void>
|
||||
|
||||
rebalancePanes(windowTarget: string, hasLeader: boolean): Promise<void>
|
||||
|
||||
killPane(paneId: PaneId, useExternalSession?: boolean): Promise<boolean>
|
||||
|
||||
hidePane(paneId: PaneId, useExternalSession?: boolean): Promise<boolean>
|
||||
|
||||
showPane(
|
||||
paneId: PaneId,
|
||||
targetWindowOrPane: string,
|
||||
useExternalSession?: boolean
|
||||
): Promise<boolean>
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 TmuxBackend
|
||||
|
||||
**位置:** `/src/utils/swarm/backends/TmuxBackend.ts`
|
||||
|
||||
使用 tmux 进行面板管理的后端实现。
|
||||
|
||||
**功能:**
|
||||
- 创建 tmux 窗口和面板
|
||||
- 发送命令到指定面板
|
||||
- 设置面板颜色和标题
|
||||
- 管理面板布局
|
||||
|
||||
### 6.4 ITermBackend
|
||||
|
||||
**位置:** `/src/utils/swarm/backends/ITermBackend.ts`
|
||||
|
||||
使用 iTerm2 原生分割面板的后端实现。
|
||||
|
||||
**功能:**
|
||||
- 通过 it2 CLI 与 iTerm2 通信
|
||||
- 创建垂直/水平分割
|
||||
- 设置 profile 和颜色
|
||||
|
||||
### 6.5 InProcessBackend
|
||||
|
||||
**位置:** `/src/utils/swarm/backends/InProcessBackend.ts`
|
||||
|
||||
在当前 Node.js 进程中运行队友的后端。
|
||||
|
||||
**特性:**
|
||||
- 共享 API 客户端和 MCP 连接
|
||||
- 通过文件邮箱通信
|
||||
- 使用 AbortController 终止
|
||||
- 始终可用(无外部依赖)
|
||||
|
||||
```typescript
|
||||
export class InProcessBackend implements TeammateExecutor {
|
||||
readonly type = 'in-process' as const
|
||||
|
||||
async spawn(config: TeammateSpawnConfig): Promise<TeammateSpawnResult>
|
||||
async sendMessage(agentId: string, message: TeammateMessage): Promise<void>
|
||||
async terminate(agentId: string, reason?: string): Promise<boolean>
|
||||
async kill(agentId: string): Promise<boolean>
|
||||
async isActive(agentId: string): Promise<boolean>
|
||||
}
|
||||
```
|
||||
|
||||
### 6.6 后端注册表
|
||||
|
||||
**位置:** `/src/utils/swarm/backends/registry.ts`
|
||||
|
||||
```typescript
|
||||
// 检测并获取合适的后端
|
||||
export async function detectAndGetBackend(): Promise<BackendDetectionResult>
|
||||
|
||||
// 注册所有后端
|
||||
export async function ensureBackendsRegistered(): Promise<void>
|
||||
|
||||
// 按类型获取后端
|
||||
export async function getBackendByType(type: BackendType): Promise<PaneBackend>
|
||||
```
|
||||
|
||||
## 7. 权限同步 (permissionSync.ts)
|
||||
|
||||
### 7.1 权限请求流程
|
||||
|
||||
```
|
||||
Worker Agent → 权限请求 → Leader Mailbox
|
||||
Leader Agent ← 轮询 ← 权限队列
|
||||
↓
|
||||
用户批准/拒绝
|
||||
↓
|
||||
Leader → 权限响应 → Worker Mailbox
|
||||
Worker ← 处理响应 ←
|
||||
```
|
||||
|
||||
### 7.2 权限请求结构
|
||||
|
||||
```typescript
|
||||
export type SwarmPermissionRequest = {
|
||||
id: string
|
||||
workerId: string
|
||||
workerName: string
|
||||
workerColor?: string
|
||||
teamName: string
|
||||
toolName: string
|
||||
toolUseId: string
|
||||
description: string
|
||||
input: Record<string, unknown>
|
||||
permissionSuggestions: unknown[]
|
||||
status: 'pending' | 'approved' | 'rejected'
|
||||
resolvedBy?: 'worker' | 'leader'
|
||||
resolvedAt?: number
|
||||
feedback?: string
|
||||
updatedInput?: Record<string, unknown>
|
||||
permissionUpdates?: PermissionUpdate[]
|
||||
createdAt: number
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 权限操作
|
||||
|
||||
```typescript
|
||||
// 创建权限请求
|
||||
export function createPermissionRequest(params: {...}): SwarmPermissionRequest
|
||||
|
||||
// 写入待处理权限请求
|
||||
export async function writePermissionRequest(
|
||||
request: SwarmPermissionRequest
|
||||
): Promise<SwarmPermissionRequest>
|
||||
|
||||
// 读取待处理权限
|
||||
export async function readPendingPermissions(
|
||||
teamName?: string
|
||||
): Promise<SwarmPermissionRequest[]>
|
||||
|
||||
// 解决权限请求
|
||||
export async function resolvePermission(
|
||||
requestId: string,
|
||||
resolution: PermissionResolution,
|
||||
teamName?: string
|
||||
): Promise<boolean>
|
||||
|
||||
// 通过邮箱发送权限请求
|
||||
export async function sendPermissionRequestViaMailbox(
|
||||
request: SwarmPermissionRequest
|
||||
): Promise<boolean>
|
||||
|
||||
// 通过邮箱发送权限响应
|
||||
export async function sendPermissionResponseViaMailbox(
|
||||
workerName: string,
|
||||
resolution: PermissionResolution,
|
||||
requestId: string,
|
||||
teamName?: string
|
||||
): Promise<boolean>
|
||||
```
|
||||
|
||||
### 7.4 沙箱权限
|
||||
|
||||
```typescript
|
||||
// 发送沙箱权限请求
|
||||
export async function sendSandboxPermissionRequestViaMailbox(
|
||||
host: string,
|
||||
requestId: string,
|
||||
teamName?: string
|
||||
): Promise<boolean>
|
||||
|
||||
// 发送沙箱权限响应
|
||||
export async function sendSandboxPermissionResponseViaMailbox(
|
||||
workerName: string,
|
||||
requestId: string,
|
||||
host: string,
|
||||
allow: boolean,
|
||||
teamName?: string
|
||||
): Promise<boolean>
|
||||
```
|
||||
|
||||
### 7.5 团队领导检查
|
||||
|
||||
```typescript
|
||||
// 检查是否为团队领导
|
||||
export function isTeamLeader(teamName?: string): boolean
|
||||
|
||||
// 检查是否为 Swarm Worker
|
||||
export function isSwarmWorker(): boolean
|
||||
```
|
||||
|
||||
## 8. 重连机制 (reconnection.ts)
|
||||
|
||||
### 8.1 初始团队上下文计算
|
||||
|
||||
```typescript
|
||||
export function computeInitialTeamContext():
|
||||
| AppState['teamContext']
|
||||
| undefined {
|
||||
// 从 CLI 参数获取动态团队上下文
|
||||
const context = getDynamicTeamContext()
|
||||
|
||||
if (!context?.teamName || !context?.agentName) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// 读取团队文件获取领导 Agent ID
|
||||
const teamFile = readTeamFile(teamName)
|
||||
|
||||
return {
|
||||
teamName,
|
||||
teamFilePath,
|
||||
leadAgentId: teamFile.leadAgentId,
|
||||
selfAgentId: agentId,
|
||||
selfAgentName: agentName,
|
||||
isLeader,
|
||||
teammates: {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 从会话恢复团队上下文
|
||||
|
||||
```typescript
|
||||
export function initializeTeammateContextFromSession(
|
||||
setAppState: (updater: (prev: AppState) => AppState) => void,
|
||||
teamName: string,
|
||||
agentName: string
|
||||
): void {
|
||||
// 从团队文件读取成员信息
|
||||
const teamFile = readTeamFile(teamName)
|
||||
const member = teamFile.members.find(m => m.name === agentName)
|
||||
const agentId = member?.agentId
|
||||
|
||||
// 设置 AppState 中的团队上下文
|
||||
setAppState(prev => ({
|
||||
...prev,
|
||||
teamContext: {
|
||||
teamName,
|
||||
teamFilePath,
|
||||
leadAgentId: teamFile.leadAgentId,
|
||||
selfAgentId: agentId,
|
||||
selfAgentName: agentName,
|
||||
isLeader: false,
|
||||
teammates: {}
|
||||
}
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
## 9. 队友生成 (spawnUtils.ts, spawnInProcess.ts)
|
||||
|
||||
### 9.1 进程内队友生成
|
||||
|
||||
```typescript
|
||||
// 位置:/src/utils/swarm/spawnInProcess.ts
|
||||
|
||||
export async function spawnInProcessTeammate(
|
||||
identity: TeammateIdentity,
|
||||
context: ToolUseContext
|
||||
): Promise<{
|
||||
success: boolean
|
||||
agentId: string
|
||||
taskId?: string
|
||||
teammateContext?: TeammateContext
|
||||
abortController?: AbortController
|
||||
error?: string
|
||||
}>
|
||||
|
||||
export function killInProcessTeammate(
|
||||
taskId: string,
|
||||
setAppState: (updater: (prev: AppState) => AppState) => void
|
||||
): boolean
|
||||
```
|
||||
|
||||
### 9.2 进程内运行器
|
||||
|
||||
```typescript
|
||||
// 位置:/src/utils/swarm/inProcessRunner.ts
|
||||
|
||||
export async function startInProcessTeammate(params: {
|
||||
identity: TeammateIdentity
|
||||
taskId: string
|
||||
prompt: string
|
||||
teammateContext: TeammateContext
|
||||
toolUseContext: ToolUseContext
|
||||
abortController: AbortController
|
||||
model?: string
|
||||
systemPrompt?: string
|
||||
systemPromptMode?: 'default' | 'replace' | 'append'
|
||||
allowedTools?: string[]
|
||||
allowPermissionPrompts?: boolean
|
||||
}): Promise<void>
|
||||
```
|
||||
|
||||
## 10. TeammateExecutor 接口
|
||||
|
||||
### 10.1 接口定义
|
||||
|
||||
```typescript
|
||||
export type TeammateExecutor = {
|
||||
readonly type: BackendType
|
||||
|
||||
isAvailable(): Promise<boolean>
|
||||
spawn(config: TeammateSpawnConfig): Promise<TeammateSpawnResult>
|
||||
sendMessage(agentId: string, message: TeammateMessage): Promise<void>
|
||||
terminate(agentId: string, reason?: string): Promise<boolean>
|
||||
kill(agentId: string): Promise<boolean>
|
||||
isActive(agentId: string): Promise<boolean>
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 TeammateSpawnConfig
|
||||
|
||||
```typescript
|
||||
export type TeammateSpawnConfig = TeammateIdentity & {
|
||||
prompt: string
|
||||
cwd: string
|
||||
model?: string
|
||||
systemPrompt?: string
|
||||
systemPromptMode?: 'default' | 'replace' | 'append'
|
||||
worktreePath?: string
|
||||
parentSessionId: string
|
||||
permissions?: string[]
|
||||
allowPermissionPrompts?: boolean
|
||||
}
|
||||
```
|
||||
|
||||
### 10.3 TeammateSpawnResult
|
||||
|
||||
```typescript
|
||||
export type TeammateSpawnResult = {
|
||||
success: boolean
|
||||
agentId: string // 格式: agentName@teamName
|
||||
error?: string
|
||||
abortController?: AbortController // 进程内队友使用
|
||||
taskId?: string // AppState.tasks 中的任务 ID
|
||||
paneId?: PaneId // 基于面板的后端使用
|
||||
}
|
||||
```
|
||||
|
||||
## 11. 消息类型
|
||||
|
||||
### 11.1 TeammateMessage
|
||||
|
||||
```typescript
|
||||
export type TeammateMessage = {
|
||||
text: string
|
||||
from: string
|
||||
color?: string
|
||||
timestamp?: string
|
||||
summary?: string // 5-10 字预览
|
||||
}
|
||||
```
|
||||
|
||||
## 12. 类型守卫
|
||||
|
||||
### 12.1 面板后端检查
|
||||
|
||||
```typescript
|
||||
export function isPaneBackend(type: BackendType): type is 'tmux' | 'iterm2' {
|
||||
return type === 'tmux' || type === 'iterm2'
|
||||
}
|
||||
```
|
||||
|
||||
## 13. 环境检测
|
||||
|
||||
### 13.1 tmux 检测
|
||||
|
||||
```typescript
|
||||
// 检测是否在 tmux 会话内运行
|
||||
export async function isInsideTmux(): Promise<boolean>
|
||||
```
|
||||
|
||||
### 13.2 iTerm2 检测
|
||||
|
||||
```typescript
|
||||
// 检测 iTerm2 是否可用
|
||||
export async function isITerm2Available(): Promise<boolean>
|
||||
```
|
||||
|
||||
## 14. 团队常量
|
||||
|
||||
**位置:** `/src/utils/swarm/constants.ts`
|
||||
|
||||
```typescript
|
||||
export const TEAM_LEAD_NAME = 'team-lead'
|
||||
export const DEFAULT_TEAM_PANE_WIDTH = '30%'
|
||||
export const DEFAULT_TEAM_PANE_HEIGHT = '50%'
|
||||
```
|
||||
Reference in New Issue
Block a user