chore: remove stale Flutter code
This commit is contained in:
parent
017216e812
commit
d23bd2708c
1
.gitignore
vendored
1
.gitignore
vendored
@ -20,6 +20,7 @@ migrate_working_dir/
|
||||
tmp/*
|
||||
!tmp/.gitkeep
|
||||
.worktrees/
|
||||
_codex_worktrees/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
|
||||
@ -38,7 +38,9 @@ class DesktopTaskThreadRepository {
|
||||
_records
|
||||
..clear()
|
||||
..addEntries(
|
||||
records.map((record) => MapEntry<String, TaskThread>(record.threadId, record)),
|
||||
records.map(
|
||||
(record) => MapEntry<String, TaskThread>(record.threadId, record),
|
||||
),
|
||||
);
|
||||
if (persist) {
|
||||
_schedulePersist();
|
||||
@ -74,44 +76,3 @@ class DesktopTaskThreadRepository {
|
||||
unawaited(_persistQueue);
|
||||
}
|
||||
}
|
||||
|
||||
class WebTaskThreadRepository {
|
||||
final Map<String, TaskThread> _records = <String, TaskThread>{};
|
||||
|
||||
Map<String, TaskThread> get recordsView => UnmodifiableMapView(_records);
|
||||
Iterable<TaskThread> get values => _records.values;
|
||||
|
||||
bool containsKey(String sessionKey) => _records.containsKey(sessionKey);
|
||||
|
||||
TaskThread? taskThreadForSession(String sessionKey) => _records[sessionKey];
|
||||
|
||||
TaskThread requireTaskThreadForSession(String sessionKey) {
|
||||
final record = taskThreadForSession(sessionKey);
|
||||
if (record == null) {
|
||||
throw StateError('Missing TaskThread for session $sessionKey.');
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
void replace(TaskThread record) {
|
||||
_records[record.threadId] = record;
|
||||
}
|
||||
|
||||
void replaceAll(Iterable<TaskThread> records) {
|
||||
_records
|
||||
..clear()
|
||||
..addEntries(
|
||||
records.map((record) => MapEntry<String, TaskThread>(record.threadId, record)),
|
||||
);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_records.clear();
|
||||
}
|
||||
|
||||
void removeWhere(bool Function(String sessionKey, TaskThread record) predicate) {
|
||||
_records.removeWhere(predicate);
|
||||
}
|
||||
|
||||
List<TaskThread> snapshot() => values.toList(growable: false);
|
||||
}
|
||||
|
||||
@ -1,773 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../app/app_metadata.dart';
|
||||
import '../models/app_models.dart';
|
||||
|
||||
class MockData {
|
||||
static const quickActions = [
|
||||
QuickAction(
|
||||
title: '写代码',
|
||||
icon: Icons.code_rounded,
|
||||
caption: '生成组件、修复 bug、搭建原型',
|
||||
),
|
||||
QuickAction(
|
||||
title: '分析文档',
|
||||
icon: Icons.description_rounded,
|
||||
caption: '长文总结、要点抽取、风险提示',
|
||||
),
|
||||
QuickAction(
|
||||
title: '生成 PPT',
|
||||
icon: Icons.slideshow_rounded,
|
||||
caption: '提纲生成、页面结构、讲述逻辑',
|
||||
),
|
||||
QuickAction(
|
||||
title: '数据分析',
|
||||
icon: Icons.analytics_rounded,
|
||||
caption: '表格解读、趋势拆解、指标洞察',
|
||||
),
|
||||
QuickAction(
|
||||
title: '设计方案',
|
||||
icon: Icons.draw_rounded,
|
||||
caption: '产品方案、交互思路、界面方向',
|
||||
),
|
||||
QuickAction(
|
||||
title: '邮件处理',
|
||||
icon: Icons.mail_outline_rounded,
|
||||
caption: '总结线程、起草回复、提炼行动项',
|
||||
),
|
||||
QuickAction(
|
||||
title: '自动化任务',
|
||||
icon: Icons.auto_mode_rounded,
|
||||
caption: '定时执行、跨工具协同、持续追踪',
|
||||
),
|
||||
];
|
||||
|
||||
static const recentSessions = [
|
||||
RecentSession(
|
||||
title: '设计新的桌面 IA',
|
||||
timestamp: '12 分钟前',
|
||||
summary: '重排主导航,压平二级结构,保持 Assistant 为首页。',
|
||||
),
|
||||
RecentSession(
|
||||
title: '梳理任务执行历史',
|
||||
timestamp: '今天 14:20',
|
||||
summary: '合并 Queue / Running / Failed 的追踪口径。',
|
||||
),
|
||||
RecentSession(
|
||||
title: '检查 Gateway 轻量控制面',
|
||||
timestamp: '昨天',
|
||||
summary: '减少运维感,更多强调 AI 控制平面状态。',
|
||||
),
|
||||
];
|
||||
|
||||
static const taskMetrics = [
|
||||
MetricSummary(
|
||||
label: 'Total',
|
||||
value: '128',
|
||||
caption: '总任务数',
|
||||
icon: Icons.layers_rounded,
|
||||
),
|
||||
MetricSummary(
|
||||
label: 'Running',
|
||||
value: '12',
|
||||
caption: '当前运行中',
|
||||
icon: Icons.play_circle_outline_rounded,
|
||||
status: StatusInfo('Stable', StatusTone.success),
|
||||
),
|
||||
MetricSummary(
|
||||
label: 'Failed',
|
||||
value: '3',
|
||||
caption: '需要人工介入',
|
||||
icon: Icons.error_outline_rounded,
|
||||
status: StatusInfo('Watch', StatusTone.warning),
|
||||
),
|
||||
MetricSummary(
|
||||
label: 'Scheduled',
|
||||
value: '18',
|
||||
caption: '已排程任务',
|
||||
icon: Icons.event_repeat_rounded,
|
||||
),
|
||||
];
|
||||
|
||||
static const queueTasks = [
|
||||
TaskSummary(
|
||||
name: 'Design Desktop IA Shell',
|
||||
owner: 'Coding Agent',
|
||||
status: StatusInfo('Queued', StatusTone.neutral),
|
||||
startedAt: '预计 16:50',
|
||||
duration: 'ETA 4m',
|
||||
surface: 'Assistant',
|
||||
),
|
||||
TaskSummary(
|
||||
name: 'Daily Report Draft',
|
||||
owner: 'Job Autopilot',
|
||||
status: StatusInfo('Queued', StatusTone.neutral),
|
||||
startedAt: '预计 17:00',
|
||||
duration: 'ETA 2m',
|
||||
surface: 'Scheduled',
|
||||
),
|
||||
];
|
||||
|
||||
static const runningTasks = [
|
||||
TaskSummary(
|
||||
name: 'XWorkmate Workspace Prototype',
|
||||
owner: 'Coding Agent',
|
||||
status: StatusInfo('Running', StatusTone.accent),
|
||||
startedAt: '刚刚',
|
||||
duration: '8m 12s',
|
||||
surface: 'Assistant',
|
||||
),
|
||||
TaskSummary(
|
||||
name: 'Market Monitor Rollup',
|
||||
owner: 'Research Agent',
|
||||
status: StatusInfo('Streaming', StatusTone.success),
|
||||
startedAt: '16:12',
|
||||
duration: '2m 43s',
|
||||
surface: 'Tasks',
|
||||
),
|
||||
];
|
||||
|
||||
static const historyTasks = [
|
||||
TaskSummary(
|
||||
name: 'Slack Thread Summarization',
|
||||
owner: 'Research Agent',
|
||||
status: StatusInfo('Completed', StatusTone.success),
|
||||
startedAt: '今天 15:10',
|
||||
duration: '3m 42s',
|
||||
surface: 'CoreSetup',
|
||||
),
|
||||
TaskSummary(
|
||||
name: 'Resume Scan Batch',
|
||||
owner: 'Job Autopilot',
|
||||
status: StatusInfo('Completed', StatusTone.success),
|
||||
startedAt: '今天 13:25',
|
||||
duration: '6m 03s',
|
||||
surface: 'Tasks',
|
||||
),
|
||||
];
|
||||
|
||||
static const failedTasks = [
|
||||
TaskSummary(
|
||||
name: 'Connector Credential Refresh',
|
||||
owner: 'Browser Agent',
|
||||
status: StatusInfo('Failed', StatusTone.danger),
|
||||
startedAt: '今天 14:03',
|
||||
duration: '58s',
|
||||
surface: 'Secrets',
|
||||
),
|
||||
];
|
||||
|
||||
static const scheduledTasks = [
|
||||
TaskSummary(
|
||||
name: 'Morning Standup Digest',
|
||||
owner: 'Job Autopilot',
|
||||
status: StatusInfo('Scheduled', StatusTone.accent),
|
||||
startedAt: '明天 08:40',
|
||||
duration: 'Daily',
|
||||
surface: 'Tasks',
|
||||
),
|
||||
TaskSummary(
|
||||
name: 'Inbox Triage',
|
||||
owner: 'Research Agent',
|
||||
status: StatusInfo('Scheduled', StatusTone.accent),
|
||||
startedAt: '18:00',
|
||||
duration: 'Every 3h',
|
||||
surface: 'Tasks',
|
||||
),
|
||||
];
|
||||
|
||||
static const workspaceModules = [
|
||||
ModuleSummary(
|
||||
name: 'Workspace Overview',
|
||||
description: '统一管理默认模型、默认 Agent、团队策略与入口偏好。',
|
||||
status: StatusInfo('Ready', StatusTone.success),
|
||||
meta: '3 workspaces · 12 members',
|
||||
icon: Icons.apartment_rounded,
|
||||
),
|
||||
];
|
||||
|
||||
static const gatewayModules = [
|
||||
ModuleSummary(
|
||||
name: 'LLM API',
|
||||
description:
|
||||
'Healthy · version $kAppVersion · 3 nodes · 12 active sessions',
|
||||
status: StatusInfo('Healthy', StatusTone.success),
|
||||
meta: '87 runs today',
|
||||
icon: Icons.wifi_tethering_rounded,
|
||||
),
|
||||
ModuleSummary(
|
||||
name: 'Edge Relay',
|
||||
description: 'Tokyo relay handling cross-region session routing.',
|
||||
status: StatusInfo('Healthy', StatusTone.success),
|
||||
meta: '3 active sessions',
|
||||
icon: Icons.hub_rounded,
|
||||
),
|
||||
];
|
||||
|
||||
static const nodeModules = [
|
||||
ModuleSummary(
|
||||
name: 'Mac mini studio-01',
|
||||
description: 'Desktop automation node with screenshot + shell access.',
|
||||
status: StatusInfo('Healthy', StatusTone.success),
|
||||
meta: 'CPU 42% · RAM 61%',
|
||||
icon: Icons.desktop_mac_rounded,
|
||||
),
|
||||
ModuleSummary(
|
||||
name: 'Cloud sandbox',
|
||||
description: 'Ephemeral workload node for heavy research tasks.',
|
||||
status: StatusInfo('Warning', StatusTone.warning),
|
||||
meta: 'Retry queue elevated',
|
||||
icon: Icons.cloud_queue_rounded,
|
||||
),
|
||||
];
|
||||
|
||||
static const nodes = [
|
||||
NodeSummary(
|
||||
name: 'Mac mini studio-01',
|
||||
type: 'local',
|
||||
region: 'Shanghai',
|
||||
heartbeat: '12s ago',
|
||||
load: '42%',
|
||||
status: StatusInfo('Healthy', StatusTone.success),
|
||||
),
|
||||
NodeSummary(
|
||||
name: 'Edge relay tokyo',
|
||||
type: 'edge',
|
||||
region: 'Tokyo',
|
||||
heartbeat: '28s ago',
|
||||
load: '31%',
|
||||
status: StatusInfo('Healthy', StatusTone.success),
|
||||
),
|
||||
NodeSummary(
|
||||
name: 'Sandbox remote-03',
|
||||
type: 'remote',
|
||||
region: 'Frankfurt',
|
||||
heartbeat: '2m ago',
|
||||
load: '76%',
|
||||
status: StatusInfo('Warning', StatusTone.warning),
|
||||
),
|
||||
];
|
||||
|
||||
static const agents = [
|
||||
AgentSummary(
|
||||
name: 'Browser Agent',
|
||||
description: '网页探索、采集、表单操作与流程验证。',
|
||||
status: StatusInfo('Idle', StatusTone.neutral),
|
||||
lastRun: '18 分钟前',
|
||||
capabilities: ['Browse', 'Capture', 'Validate'],
|
||||
),
|
||||
AgentSummary(
|
||||
name: 'Coding Agent',
|
||||
description: '读写代码、生成补丁、解释实现与验证构建。',
|
||||
status: StatusInfo('Running', StatusTone.accent),
|
||||
lastRun: '刚刚',
|
||||
capabilities: ['Code', 'Patch', 'Tests'],
|
||||
),
|
||||
AgentSummary(
|
||||
name: 'Research Agent',
|
||||
description: '检索资料、比对来源、收敛结论。',
|
||||
status: StatusInfo('Idle', StatusTone.neutral),
|
||||
lastRun: '43 分钟前',
|
||||
capabilities: ['Search', 'Compare', 'Synthesis'],
|
||||
),
|
||||
AgentSummary(
|
||||
name: 'Job Autopilot',
|
||||
description: '持续接单、编排子任务、回收输出物。',
|
||||
status: StatusInfo('Running', StatusTone.success),
|
||||
lastRun: '2 分钟前',
|
||||
capabilities: ['Delegation', 'Scheduling', 'Reports'],
|
||||
),
|
||||
AgentSummary(
|
||||
name: 'Custom Agent',
|
||||
description: '面向团队流程定制的草稿 Agent。',
|
||||
status: StatusInfo('Draft', StatusTone.warning),
|
||||
lastRun: '未运行',
|
||||
capabilities: ['Custom Prompt', 'Tools', 'Context'],
|
||||
),
|
||||
];
|
||||
|
||||
static const skills = [
|
||||
SkillSummary(
|
||||
name: 'db-migration-runbook',
|
||||
type: 'Runbook',
|
||||
source: 'Local skill',
|
||||
status: StatusInfo('Enabled', StatusTone.success),
|
||||
version: '1.4.0',
|
||||
modules: 'Tasks · Modules',
|
||||
),
|
||||
SkillSummary(
|
||||
name: 'playwright',
|
||||
type: 'Automation',
|
||||
source: 'Shared skill',
|
||||
status: StatusInfo('Enabled', StatusTone.success),
|
||||
version: '2.1.3',
|
||||
modules: 'Modules · Secrets',
|
||||
),
|
||||
SkillSummary(
|
||||
name: 'vercel-deploy',
|
||||
type: 'Deployment',
|
||||
source: 'Hub',
|
||||
status: StatusInfo('Preview', StatusTone.accent),
|
||||
version: '0.9.2',
|
||||
modules: 'Modules',
|
||||
),
|
||||
];
|
||||
|
||||
static const agentModules = [
|
||||
ModuleSummary(
|
||||
name: 'Job Autopilot',
|
||||
description: '持续接单、自动整理输出物、分发下一步行动。',
|
||||
status: StatusInfo('Running', StatusTone.success),
|
||||
meta: 'Queue depth 4',
|
||||
icon: Icons.auto_awesome_motion_rounded,
|
||||
),
|
||||
ModuleSummary(
|
||||
name: 'Coding Agent',
|
||||
description: '负责读写代码、补丁生成、编译检查与解释。',
|
||||
status: StatusInfo('Running', StatusTone.accent),
|
||||
meta: 'GPT-5 Code',
|
||||
icon: Icons.terminal_rounded,
|
||||
),
|
||||
];
|
||||
|
||||
static const skillModules = [
|
||||
ModuleSummary(
|
||||
name: 'db-migration-runbook',
|
||||
description: '面向数据库迁移和演练的工作指引 skill。',
|
||||
status: StatusInfo('Installed', StatusTone.success),
|
||||
meta: 'Last updated 2d ago',
|
||||
icon: Icons.menu_book_rounded,
|
||||
),
|
||||
ModuleSummary(
|
||||
name: 'playwright',
|
||||
description: '桌面与浏览器自动化能力封装。',
|
||||
status: StatusInfo('Installed', StatusTone.success),
|
||||
meta: 'Used in 18 runs',
|
||||
icon: Icons.web_asset_rounded,
|
||||
),
|
||||
];
|
||||
|
||||
static const clawHubModules = [
|
||||
ModuleSummary(
|
||||
name: 'Hub Registry',
|
||||
description: '统一查看共享模块、预设 Agent 与团队模板。',
|
||||
status: StatusInfo('Connected', StatusTone.accent),
|
||||
meta: '24 shared assets',
|
||||
icon: Icons.grid_view_rounded,
|
||||
),
|
||||
];
|
||||
|
||||
static const connectorModules = [
|
||||
ModuleSummary(
|
||||
name: 'Slack Connector',
|
||||
description: '线程总结、消息派发与任务回写。',
|
||||
status: StatusInfo('Warning', StatusTone.warning),
|
||||
meta: '480 events / day',
|
||||
icon: Icons.forum_rounded,
|
||||
),
|
||||
ModuleSummary(
|
||||
name: 'Email Relay',
|
||||
description: '日报投递、邮件摘要与草稿生成。',
|
||||
status: StatusInfo('Healthy', StatusTone.success),
|
||||
meta: '126 messages / day',
|
||||
icon: Icons.mail_rounded,
|
||||
),
|
||||
];
|
||||
|
||||
static const connectors = [
|
||||
ConnectorSummary(
|
||||
name: 'Vault',
|
||||
description: '管理远程 secret reference 与轮换策略。',
|
||||
status: StatusInfo('Connected', StatusTone.success),
|
||||
lastSync: '2 分钟前',
|
||||
permission: 'Read / Write',
|
||||
),
|
||||
ConnectorSummary(
|
||||
name: 'GitHub',
|
||||
description: '用于 issue、PR 和仓库自动化。',
|
||||
status: StatusInfo('Connected', StatusTone.success),
|
||||
lastSync: '5 分钟前',
|
||||
permission: 'Repo scoped',
|
||||
),
|
||||
ConnectorSummary(
|
||||
name: 'Google Drive',
|
||||
description: '读取文档、表格与共享目录。',
|
||||
status: StatusInfo('Pending', StatusTone.warning),
|
||||
lastSync: '未同步',
|
||||
permission: 'Awaiting OAuth',
|
||||
),
|
||||
ConnectorSummary(
|
||||
name: 'Slack',
|
||||
description: '消息接入、线程总结、任务回写。',
|
||||
status: StatusInfo('Warning', StatusTone.warning),
|
||||
lastSync: '12 分钟前',
|
||||
permission: 'Messages',
|
||||
),
|
||||
ConnectorSummary(
|
||||
name: 'MCP',
|
||||
description: '连接本地或远程 MCP 服务器。',
|
||||
status: StatusInfo('Connected', StatusTone.success),
|
||||
lastSync: '刚刚',
|
||||
permission: 'Tool access',
|
||||
),
|
||||
ConnectorSummary(
|
||||
name: 'Local FS',
|
||||
description: '本地文件系统读写与目录索引。',
|
||||
status: StatusInfo('Connected', StatusTone.success),
|
||||
lastSync: '实时',
|
||||
permission: 'Workspace only',
|
||||
),
|
||||
];
|
||||
|
||||
static const vaultSecrets = [
|
||||
SecretSummary(
|
||||
name: 'OPENAI_API_KEY',
|
||||
scope: 'Workspace',
|
||||
status: StatusInfo('Healthy', StatusTone.success),
|
||||
updatedAt: '2 小时前',
|
||||
provider: 'Vault',
|
||||
),
|
||||
SecretSummary(
|
||||
name: 'SLACK_BOT_TOKEN',
|
||||
scope: 'Connector',
|
||||
status: StatusInfo('Rotating', StatusTone.warning),
|
||||
updatedAt: '昨天',
|
||||
provider: 'Vault',
|
||||
),
|
||||
];
|
||||
|
||||
static const localSecrets = [
|
||||
SecretSummary(
|
||||
name: 'LOCAL_AGENT_CACHE',
|
||||
scope: 'Desktop',
|
||||
status: StatusInfo('Local', StatusTone.neutral),
|
||||
updatedAt: '今天',
|
||||
provider: 'Keychain',
|
||||
),
|
||||
];
|
||||
|
||||
static const providerSecrets = [
|
||||
SecretSummary(
|
||||
name: '1Password',
|
||||
scope: 'Provider',
|
||||
status: StatusInfo('Connected', StatusTone.success),
|
||||
updatedAt: '刚刚',
|
||||
provider: 'Provider',
|
||||
),
|
||||
SecretSummary(
|
||||
name: 'AWS Secrets Manager',
|
||||
scope: 'Provider',
|
||||
status: StatusInfo('Draft', StatusTone.warning),
|
||||
updatedAt: '未配置',
|
||||
provider: 'Provider',
|
||||
),
|
||||
];
|
||||
|
||||
static const secretReferences = [
|
||||
SecretReference(
|
||||
name: 'indeed_cookie',
|
||||
provider: 'Vault',
|
||||
module: 'Job Autopilot',
|
||||
status: StatusInfo('In use', StatusTone.success),
|
||||
maskedValue: '••••••••4ae7',
|
||||
),
|
||||
SecretReference(
|
||||
name: 'github_token',
|
||||
provider: 'Vault',
|
||||
module: 'Coding Agent',
|
||||
status: StatusInfo('In use', StatusTone.success),
|
||||
maskedValue: 'ghp_••••••8k2m',
|
||||
),
|
||||
SecretReference(
|
||||
name: 'openai_key',
|
||||
provider: 'Local',
|
||||
module: 'Gateway',
|
||||
status: StatusInfo('Warning', StatusTone.warning),
|
||||
maskedValue: 'sk-••••••••••',
|
||||
),
|
||||
];
|
||||
|
||||
static const providers = [
|
||||
ProviderSummary(
|
||||
name: 'HashiCorp Vault',
|
||||
description: '远程 secret provider,支持 namespace、TTL 和审计。',
|
||||
status: StatusInfo('Connected', StatusTone.success),
|
||||
capabilities: ['KV', 'TTL', 'Audit'],
|
||||
),
|
||||
ProviderSummary(
|
||||
name: 'Environment Variables',
|
||||
description: '适合本地开发与 CI 运行时注入。',
|
||||
status: StatusInfo('Available', StatusTone.neutral),
|
||||
capabilities: ['Runtime', 'Process', 'Masking'],
|
||||
),
|
||||
ProviderSummary(
|
||||
name: 'Local File',
|
||||
description: '为离线桌面环境提供本地加密存储。',
|
||||
status: StatusInfo('Enabled', StatusTone.success),
|
||||
capabilities: ['Encrypted', 'Local', 'Backup'],
|
||||
),
|
||||
ProviderSummary(
|
||||
name: 'External Secret Manager',
|
||||
description: '预留给企业第三方 secret manager 的接入位。',
|
||||
status: StatusInfo('Draft', StatusTone.warning),
|
||||
capabilities: ['Enterprise', 'Sync', 'Policy'],
|
||||
),
|
||||
];
|
||||
|
||||
static const auditSecrets = [
|
||||
AuditSummary(
|
||||
time: '16:12',
|
||||
action: 'Rotate token',
|
||||
provider: 'Vault',
|
||||
target: 'github_token',
|
||||
module: 'Coding Agent',
|
||||
status: StatusInfo('Success', StatusTone.success),
|
||||
),
|
||||
AuditSummary(
|
||||
time: '15:40',
|
||||
action: 'Read secret',
|
||||
provider: 'Vault',
|
||||
target: 'indeed_cookie',
|
||||
module: 'Job Autopilot',
|
||||
status: StatusInfo('Success', StatusTone.success),
|
||||
),
|
||||
AuditSummary(
|
||||
time: '14:52',
|
||||
action: 'Resolve reference',
|
||||
provider: 'Local',
|
||||
target: 'openai_key',
|
||||
module: 'Gateway',
|
||||
status: StatusInfo('Warning', StatusTone.warning),
|
||||
),
|
||||
];
|
||||
|
||||
static const generalSettings = [
|
||||
SettingSummary(
|
||||
title: 'Default launch surface',
|
||||
description: '始终保持 Assistant 为默认首页。',
|
||||
value: 'Assistant',
|
||||
),
|
||||
SettingSummary(
|
||||
title: 'Command palette shortcut',
|
||||
description: '桌面全局入口,Cmd/Ctrl + K。',
|
||||
value: 'Enabled',
|
||||
),
|
||||
];
|
||||
|
||||
static const workspaceSettings = [
|
||||
SettingSummary(
|
||||
title: 'Data path',
|
||||
description: '本地运行数据与缓存目录。',
|
||||
value: '/opt/data',
|
||||
),
|
||||
SettingSummary(
|
||||
title: 'Repo path',
|
||||
description: '默认代码工作区根目录。',
|
||||
value: '/opt/data/workspace',
|
||||
),
|
||||
SettingSummary(
|
||||
title: 'Default agent',
|
||||
description: '新建任务时默认挂载的 Agent。',
|
||||
value: 'Coding Agent',
|
||||
),
|
||||
];
|
||||
|
||||
static const gatewaySettings = [
|
||||
SettingSummary(
|
||||
title: 'Gateway default route',
|
||||
description: '控制面启动后默认挂载的主路由。',
|
||||
value: 'LLM API',
|
||||
),
|
||||
SettingSummary(
|
||||
title: 'Session retention',
|
||||
description: '会话日志保留策略。',
|
||||
value: '14 days',
|
||||
),
|
||||
];
|
||||
|
||||
static const appearanceSettings = [
|
||||
SettingSummary(
|
||||
title: 'Theme',
|
||||
description: '桌面浅色优先,同时支持暗色主题。',
|
||||
value: 'Auto / Manual',
|
||||
),
|
||||
SettingSummary(
|
||||
title: 'Dense tables',
|
||||
description: '关闭后保持更轻的桌面留白。',
|
||||
value: 'Off',
|
||||
),
|
||||
];
|
||||
|
||||
static const providerSettings = [
|
||||
SettingSummary(
|
||||
title: 'Module provider source',
|
||||
description: '决定 Skills / Agents / Connectors 的拉取来源。',
|
||||
value: 'ClawHub',
|
||||
),
|
||||
SettingSummary(
|
||||
title: 'Connector sync policy',
|
||||
description: '共享模块更新时的同步行为。',
|
||||
value: 'Manual review',
|
||||
),
|
||||
];
|
||||
|
||||
static const diagnosticSettings = [
|
||||
SettingSummary(
|
||||
title: 'Gateway health snapshot',
|
||||
description: '最近一次诊断汇总。',
|
||||
value: 'Healthy',
|
||||
),
|
||||
SettingSummary(
|
||||
title: 'Local data directory',
|
||||
description: '本地运行数据与缓存入口。',
|
||||
value: '/opt/data/',
|
||||
),
|
||||
];
|
||||
|
||||
static const experimentalSettings = [
|
||||
SettingSummary(
|
||||
title: 'Floating detail drawer',
|
||||
description: '在宽屏下保留右侧抽屉式详情。',
|
||||
value: 'Enabled',
|
||||
),
|
||||
SettingSummary(
|
||||
title: 'Desktop inline previews',
|
||||
description: '在 Tasks 和 Secrets 中显示更多 hover 预览。',
|
||||
value: 'Preview',
|
||||
),
|
||||
];
|
||||
|
||||
static const aboutSettings = [
|
||||
SettingSummary(
|
||||
title: kSystemAppName,
|
||||
description: 'Flutter Material 3 UI shell for macOS and Windows.',
|
||||
value: kAppVersionLabel,
|
||||
),
|
||||
SettingSummary(
|
||||
title: 'Build channel',
|
||||
description: 'Prototype only, mock data mode.',
|
||||
value: 'Desktop alpha',
|
||||
),
|
||||
];
|
||||
|
||||
static const workspaces = [
|
||||
WorkspaceProfile(
|
||||
name: 'XWorkmate Design Lab',
|
||||
role: 'Owner',
|
||||
members: '12 members',
|
||||
region: 'Asia Pacific',
|
||||
),
|
||||
WorkspaceProfile(
|
||||
name: 'Client Operations',
|
||||
role: 'Editor',
|
||||
members: '8 members',
|
||||
region: 'Global',
|
||||
),
|
||||
];
|
||||
|
||||
static const accountSessions = [
|
||||
TaskSummary(
|
||||
name: 'MacBook Pro · Desktop App',
|
||||
owner: 'This device',
|
||||
status: StatusInfo('Active', StatusTone.success),
|
||||
startedAt: '今天 16:00',
|
||||
duration: 'Current',
|
||||
surface: 'Desktop',
|
||||
),
|
||||
TaskSummary(
|
||||
name: 'Windows workstation',
|
||||
owner: 'Remote sign-in',
|
||||
status: StatusInfo('Idle', StatusTone.neutral),
|
||||
startedAt: '昨天',
|
||||
duration: '23h ago',
|
||||
surface: 'Desktop',
|
||||
),
|
||||
];
|
||||
|
||||
static DetailPanelData taskDetail(TaskSummary task) {
|
||||
return DetailPanelData(
|
||||
title: task.name,
|
||||
subtitle: 'Task Detail',
|
||||
icon: Icons.bolt_rounded,
|
||||
status: task.status,
|
||||
description: '任务来自 ${task.surface},当前由 ${task.owner} 持有。',
|
||||
meta: [task.surface, task.owner, task.duration],
|
||||
actions: const ['打开', '重试', '复制链接'],
|
||||
sections: [
|
||||
DetailSection(
|
||||
title: 'Execution',
|
||||
items: [
|
||||
DetailItem(label: '开始时间', value: task.startedAt),
|
||||
DetailItem(label: '耗时', value: task.duration),
|
||||
DetailItem(label: '状态', value: task.status.label),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static DetailPanelData moduleDetail(ModuleSummary module) {
|
||||
return DetailPanelData(
|
||||
title: module.name,
|
||||
subtitle: 'Module Detail',
|
||||
icon: module.icon,
|
||||
status: module.status,
|
||||
description: module.description,
|
||||
meta: [module.meta, module.status.label],
|
||||
actions: const ['打开', '配置', '查看状态'],
|
||||
sections: [
|
||||
DetailSection(
|
||||
title: 'Overview',
|
||||
items: [
|
||||
DetailItem(label: '状态', value: module.status.label),
|
||||
DetailItem(label: '摘要', value: module.meta),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static DetailPanelData secretDetail(SecretSummary secret) {
|
||||
return DetailPanelData(
|
||||
title: secret.name,
|
||||
subtitle: 'Secret Detail',
|
||||
icon: Icons.key_rounded,
|
||||
status: secret.status,
|
||||
description: '该密钥当前归属 ${secret.scope},由 ${secret.provider} 管理。',
|
||||
meta: [secret.scope, secret.provider, secret.updatedAt],
|
||||
actions: const ['查看审计', '轮换', '复制引用'],
|
||||
sections: [
|
||||
DetailSection(
|
||||
title: 'Metadata',
|
||||
items: [
|
||||
DetailItem(label: 'Scope', value: secret.scope),
|
||||
DetailItem(label: 'Provider', value: secret.provider),
|
||||
DetailItem(label: 'Updated', value: secret.updatedAt),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static DetailPanelData workspaceDetail(WorkspaceProfile workspace) {
|
||||
return DetailPanelData(
|
||||
title: workspace.name,
|
||||
subtitle: 'Workspace Detail',
|
||||
icon: Icons.apartment_rounded,
|
||||
status: const StatusInfo('Healthy', StatusTone.success),
|
||||
description: '工作区用于组织共享模块、成员权限和默认执行策略。',
|
||||
meta: [workspace.role, workspace.members, workspace.region],
|
||||
actions: const ['切换', '管理成员', '查看策略'],
|
||||
sections: [
|
||||
DetailSection(
|
||||
title: 'Profile',
|
||||
items: [
|
||||
DetailItem(label: 'Role', value: workspace.role),
|
||||
DetailItem(label: 'Members', value: workspace.members),
|
||||
DetailItem(label: 'Region', value: workspace.region),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,118 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
class OpencodeConfigBridge {
|
||||
OpencodeConfigBridge({String? opencodeHome})
|
||||
: opencodeHome =
|
||||
opencodeHome ?? '${Platform.environment['HOME'] ?? ''}/.opencode';
|
||||
|
||||
static const String _managedMcpBlockStart =
|
||||
'# BEGIN XWORKMATE MANAGED MCP BLOCK';
|
||||
static const String _managedMcpBlockEnd = '# END XWORKMATE MANAGED MCP BLOCK';
|
||||
|
||||
final String opencodeHome;
|
||||
|
||||
Future<void> configureManagedMcpServers({
|
||||
required List<OpencodeMcpServer> servers,
|
||||
}) async {
|
||||
final configDir = Directory(opencodeHome);
|
||||
if (!await configDir.exists()) {
|
||||
await configDir.create(recursive: true);
|
||||
}
|
||||
|
||||
final configFile = File('$opencodeHome/config.toml');
|
||||
final existingConfig = await configFile.exists()
|
||||
? await configFile.readAsString()
|
||||
: '';
|
||||
final preserved = _stripManagedMcpBlock(existingConfig).trimRight();
|
||||
final managedBlock = _buildManagedMcpBlock(servers);
|
||||
final merged = preserved.isEmpty
|
||||
? '$managedBlock\n'
|
||||
: '$preserved\n\n$managedBlock\n';
|
||||
await configFile.writeAsString(merged);
|
||||
}
|
||||
|
||||
Future<String> readConfig() async {
|
||||
final configFile = File('$opencodeHome/config.toml');
|
||||
if (!await configFile.exists()) {
|
||||
return '';
|
||||
}
|
||||
return configFile.readAsString();
|
||||
}
|
||||
|
||||
String _stripManagedMcpBlock(String content) {
|
||||
if (content.isEmpty) {
|
||||
return content;
|
||||
}
|
||||
|
||||
var remaining = content;
|
||||
while (true) {
|
||||
final start = remaining.indexOf(_managedMcpBlockStart);
|
||||
if (start < 0) {
|
||||
break;
|
||||
}
|
||||
final end = remaining.indexOf(_managedMcpBlockEnd, start);
|
||||
if (end < 0) {
|
||||
remaining = remaining.substring(0, start);
|
||||
break;
|
||||
}
|
||||
remaining =
|
||||
remaining.substring(0, start) +
|
||||
remaining.substring(end + _managedMcpBlockEnd.length);
|
||||
}
|
||||
return remaining;
|
||||
}
|
||||
|
||||
String _buildManagedMcpBlock(List<OpencodeMcpServer> servers) {
|
||||
final buffer = StringBuffer()
|
||||
..writeln(_managedMcpBlockStart)
|
||||
..writeln('# Generated by XWorkmate - Managed MCP Server Configuration')
|
||||
..writeln('# Last updated: ${DateTime.now().toIso8601String()}')
|
||||
..writeln();
|
||||
|
||||
for (final server in servers) {
|
||||
buffer.writeln('[mcp_servers.${server.name}]');
|
||||
if (server.url.trim().isNotEmpty) {
|
||||
buffer.writeln('url = "${server.url.trim()}"');
|
||||
} else {
|
||||
buffer.writeln('type = "stdio"');
|
||||
buffer.writeln('command = "${server.command}"');
|
||||
if (server.args.isNotEmpty) {
|
||||
buffer.writeln('args = ${_formatTomlArray(server.args)}');
|
||||
}
|
||||
}
|
||||
if (server.env.isNotEmpty) {
|
||||
final entries = server.env.entries
|
||||
.map((entry) => '${entry.key} = "${entry.value}"')
|
||||
.join(', ');
|
||||
buffer.writeln('env = { $entries }');
|
||||
}
|
||||
buffer.writeln();
|
||||
}
|
||||
|
||||
buffer.writeln(_managedMcpBlockEnd);
|
||||
return buffer.toString().trimRight();
|
||||
}
|
||||
|
||||
String _formatTomlArray(List<String> items) {
|
||||
if (items.isEmpty) {
|
||||
return '[]';
|
||||
}
|
||||
return '[${items.map((item) => '"$item"').join(', ')}]';
|
||||
}
|
||||
}
|
||||
|
||||
class OpencodeMcpServer {
|
||||
const OpencodeMcpServer({
|
||||
required this.name,
|
||||
this.command = '',
|
||||
this.url = '',
|
||||
this.args = const <String>[],
|
||||
this.env = const <String, String>{},
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String command;
|
||||
final String url;
|
||||
final List<String> args;
|
||||
final Map<String, String> env;
|
||||
}
|
||||
@ -1,164 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../theme/app_palette.dart';
|
||||
|
||||
enum DoorLogoSide { left, right, doubleDoor }
|
||||
|
||||
class AppBrandLogo extends StatelessWidget {
|
||||
const AppBrandLogo({
|
||||
super.key,
|
||||
this.size = 32,
|
||||
this.borderRadius = 10,
|
||||
this.showShadow = true,
|
||||
});
|
||||
|
||||
final double size;
|
||||
final double borderRadius;
|
||||
final bool showShadow;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final palette = context.palette;
|
||||
final iconColor = palette.textPrimary;
|
||||
|
||||
return Container(
|
||||
width: size,
|
||||
height: size,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: showShadow ? [palette.chromeShadowLift] : const [],
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(size * 0.16),
|
||||
child: CustomPaint(
|
||||
painter: _DoorLogoPainter(
|
||||
color: iconColor,
|
||||
side: DoorLogoSide.doubleDoor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DoorLogoIcon extends StatelessWidget {
|
||||
const DoorLogoIcon({
|
||||
super.key,
|
||||
required this.side,
|
||||
required this.color,
|
||||
this.size = 22,
|
||||
});
|
||||
|
||||
final DoorLogoSide side;
|
||||
final Color color;
|
||||
final double size;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: CustomPaint(
|
||||
painter: _DoorLogoPainter(color: color, side: side),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DoorLogoPainter extends CustomPainter {
|
||||
const _DoorLogoPainter({required this.color, required this.side});
|
||||
|
||||
final Color color;
|
||||
final DoorLogoSide side;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final stroke = size.width * 0.085;
|
||||
final outline = Paint()
|
||||
..color = color
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = stroke
|
||||
..strokeCap = StrokeCap.round
|
||||
..strokeJoin = StrokeJoin.round;
|
||||
|
||||
final panel = Paint()
|
||||
..color = color
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = stroke * 0.82
|
||||
..strokeCap = StrokeCap.round;
|
||||
|
||||
final shell = RRect.fromRectAndRadius(
|
||||
Rect.fromLTWH(
|
||||
size.width * 0.14,
|
||||
size.height * 0.16,
|
||||
size.width * 0.72,
|
||||
size.height * 0.68,
|
||||
),
|
||||
Radius.circular(size.width * 0.16),
|
||||
);
|
||||
canvas.drawRRect(shell, outline);
|
||||
|
||||
final centerX = size.width * 0.5;
|
||||
canvas.drawLine(
|
||||
Offset(centerX, size.height * 0.2),
|
||||
Offset(centerX, size.height * 0.8),
|
||||
outline,
|
||||
);
|
||||
|
||||
switch (side) {
|
||||
case DoorLogoSide.left:
|
||||
_paintLeftDoor(canvas, size, panel);
|
||||
case DoorLogoSide.right:
|
||||
_paintRightDoor(canvas, size, panel);
|
||||
case DoorLogoSide.doubleDoor:
|
||||
_paintLeftDoor(canvas, size, panel);
|
||||
_paintRightDoor(canvas, size, panel);
|
||||
_paintHandles(canvas, size);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _DoorLogoPainter oldDelegate) {
|
||||
return oldDelegate.color != color || oldDelegate.side != side;
|
||||
}
|
||||
|
||||
void _paintLeftDoor(Canvas canvas, Size size, Paint panel) {
|
||||
final insetTop = size.height * 0.25;
|
||||
final insetBottom = size.height * 0.75;
|
||||
final leftDoorInset = size.width * 0.3;
|
||||
canvas.drawLine(
|
||||
Offset(leftDoorInset, insetTop),
|
||||
Offset(leftDoorInset, insetBottom),
|
||||
panel,
|
||||
);
|
||||
_paintHandle(canvas, Offset(size.width * 0.435, size.height * 0.5), size);
|
||||
}
|
||||
|
||||
void _paintRightDoor(Canvas canvas, Size size, Paint panel) {
|
||||
final insetTop = size.height * 0.25;
|
||||
final insetBottom = size.height * 0.75;
|
||||
final rightDoorInset = size.width * 0.7;
|
||||
canvas.drawLine(
|
||||
Offset(rightDoorInset, insetTop),
|
||||
Offset(rightDoorInset, insetBottom),
|
||||
panel,
|
||||
);
|
||||
_paintHandle(canvas, Offset(size.width * 0.565, size.height * 0.5), size);
|
||||
}
|
||||
|
||||
void _paintHandles(Canvas canvas, Size size) {
|
||||
_paintHandle(canvas, Offset(size.width * 0.435, size.height * 0.5), size);
|
||||
_paintHandle(canvas, Offset(size.width * 0.565, size.height * 0.5), size);
|
||||
}
|
||||
|
||||
void _paintHandle(Canvas canvas, Offset center, Size size) {
|
||||
final handlePaint = Paint()
|
||||
..color = color
|
||||
..style = PaintingStyle.fill;
|
||||
final handleRadius = size.width * 0.035;
|
||||
canvas.drawCircle(center, handleRadius, handlePaint);
|
||||
}
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../models/app_models.dart';
|
||||
import '../theme/app_palette.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
import 'status_badge.dart';
|
||||
import 'surface_card.dart';
|
||||
|
||||
class MetricCard extends StatelessWidget {
|
||||
const MetricCard({super.key, required this.metric});
|
||||
|
||||
final MetricSummary metric;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final palette = context.palette;
|
||||
|
||||
return SurfaceCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: palette.surfaceSecondary,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
),
|
||||
child: Icon(metric.icon, color: palette.textPrimary, size: 20),
|
||||
),
|
||||
const Spacer(),
|
||||
if (metric.status != null)
|
||||
StatusBadge(status: metric.status!, compact: true),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
Text(metric.label, style: Theme.of(context).textTheme.bodyMedium),
|
||||
const SizedBox(height: AppSpacing.xxs),
|
||||
Text(metric.value, style: Theme.of(context).textTheme.headlineSmall),
|
||||
const SizedBox(height: AppSpacing.xxs),
|
||||
Text(metric.caption, style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
|
||||
class SectionHeader extends StatelessWidget {
|
||||
const SectionHeader({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
this.trailing,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final Widget? trailing;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
Text(subtitle, style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
),
|
||||
...[trailing].nonNulls,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,6 @@ import 'package:crypto/crypto.dart' as crypto;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:xworkmate/app/app_controller.dart';
|
||||
import 'package:xworkmate/app/app_controller_desktop_runtime_coordination_impl.dart';
|
||||
import 'package:xworkmate/app/app_controller_desktop_runtime_helpers.dart';
|
||||
import 'package:xworkmate/app/app_controller_desktop_thread_binding.dart';
|
||||
import 'package:xworkmate/runtime/assistant_artifacts.dart';
|
||||
import 'package:xworkmate/runtime/go_task_service_client.dart';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user