Implement Docker Web project management on top of workspace_id while keeping host mode workspace behavior intact.
Backend changes:
- Move Docker user files to users/<user>/projects/<project_id>/{project,data,logs} and shared user state to users/<user>/personal.
- Add automatic legacy migration from old project/data/logs/agentskills layouts and from the temporary project/shared layout.
- Generate project IDs server-side from the project name, reserve internal IDs, and hide project paths from the UI.
- Add project rename/delete APIs; deleting a Docker project removes its project directory and releases terminal/container state.
- Add host workspace rename/delete APIs; deleting a host workspace only removes catalog entries and keeps files on disk.
- Scope Docker containers, terminals, uploads, conversations, storage, and running task metadata by workspace_id.
- Store private skills under users/<user>/personal/agentskills and sync them into each project workspace.
- Inject the current project/workspace name through prompts/workspace_system.txt.
Frontend changes:
- Replace Docker file-tree area with project management UX and reuse host multi-workspace running-task behavior.
- Add a management dialog for creating, renaming, and deleting projects/workspaces.
- Show project labels instead of internal IDs in toasts and project lists.
- Clear stale conversation history during project/workspace switching and show loading until the new list is ready.
- Keep running-task loader/check behavior consistent across host workspaces and Docker projects.
Validation:
- python3 -m py_compile modules/user_manager.py modules/host_workspace_manager.py modules/skills_manager.py core/main_terminal_parts/tools_execution.py server/auth.py server/context.py server/status.py server/conversation.py server/tasks.py utils/context_manager.py
- python3 -m unittest test.test_server_refactor_smoke
- npm run build --silent 2>&1 | tail -n 20
254 lines
8.1 KiB
TypeScript
254 lines
8.1 KiB
TypeScript
// @ts-nocheck
|
||
import { ICONS, TOOL_CATEGORY_ICON_MAP } from '../utils/icons';
|
||
|
||
export function dataState() {
|
||
return {
|
||
// 路由相关
|
||
initialRouteResolved: false,
|
||
dropToolEvents: false,
|
||
|
||
// 轮询模式标志(禁用 WebSocket 事件处理)
|
||
usePollingMode: true,
|
||
// 后台子智能体等待状态
|
||
waitingForSubAgent: false,
|
||
// 后台 run_command 等待状态(用于文案区分)
|
||
waitingForBackgroundCommand: false,
|
||
// 是否在对话区展示 system 消息
|
||
hideSystemMessages: true,
|
||
|
||
// 工具状态跟踪
|
||
preparingTools: new Map(),
|
||
activeTools: new Map(),
|
||
toolActionIndex: new Map(),
|
||
toolStacks: new Map(),
|
||
// 当前任务是否仍在进行中(用于保持输入区的"停止"状态)
|
||
taskInProgress: false,
|
||
// 等待后台通知期间,用于发现并接管新建任务的轮询定时器
|
||
waitingTaskProbeTimer: null,
|
||
// 宿主机多工作区任务列表后台刷新定时器(用于当前未查看运行对话时同步完成态)
|
||
runningWorkspaceTasksRefreshTimer: null,
|
||
// 运行期消息堆积(提前发送 / 引导对话)
|
||
runtimeQueuedMessages: [],
|
||
runtimeGuidanceFallbackQueue: [],
|
||
runtimeQueueSuppressedMessageIds: new Set(),
|
||
runtimeGuidanceSuppressedTextCounts: {},
|
||
runtimeQueueLimit: 5,
|
||
runtimeQueueAutoSendInProgress: false,
|
||
runtimeQueueSyncLockKey: '',
|
||
runtimeQueueSyncLockUntil: 0,
|
||
// 输入区动态保留高度(用于同步扩大消息区可滚动范围)
|
||
composerReservedHeight: 80,
|
||
// 记录上一次成功加载历史的对话ID,防止初始化阶段重复加载导致动画播放两次
|
||
lastHistoryLoadedConversationId: null,
|
||
|
||
// ==========================================
|
||
// 对话管理相关状态
|
||
// ==========================================
|
||
|
||
// 搜索功能
|
||
// ==========================================
|
||
searchRequestSeq: 0,
|
||
searchActiveQuery: '',
|
||
searchResultIdSet: new Set(),
|
||
searchPreviewCache: {},
|
||
|
||
// Token统计相关状态(修复版)
|
||
// ==========================================
|
||
|
||
// 对话压缩状态
|
||
compressing: false,
|
||
compressionInProgress: false,
|
||
compressionMode: '',
|
||
compressionStage: '',
|
||
compressionError: '',
|
||
compressionToastId: null,
|
||
skipConversationLoadedEvent: false,
|
||
skipConversationHistoryReload: false,
|
||
_scrollListenerReady: false,
|
||
historyLoading: false,
|
||
historyLoadingFor: null,
|
||
historyLoadSeq: 0,
|
||
blankHeroActive: false,
|
||
blankHeroExiting: false,
|
||
blankWelcomeText: '',
|
||
lastBlankConversationId: null,
|
||
// 对话标题打字效果
|
||
titleTypingText: '',
|
||
titleTypingTarget: '',
|
||
titleTypingTimer: null,
|
||
titleReady: false,
|
||
suppressTitleTyping: false,
|
||
headerMenuOpen: false,
|
||
blankWelcomePool: [
|
||
'有什么可以帮忙的?',
|
||
'想了解些热点吗?',
|
||
'要我帮你完成作业吗?',
|
||
'整点代码?',
|
||
'随便聊点什么?',
|
||
'想让我帮你整理一下思路吗?',
|
||
'要不要我帮你写个小工具?',
|
||
'发我一句话,我来接着做。',
|
||
'我来给你画幅画?',
|
||
'想看小猫吗?',
|
||
'有什么难题丢过来吧。',
|
||
'想不想看个有意思的?',
|
||
'来,说说你的需求~'
|
||
],
|
||
mobileViewportQuery: null,
|
||
modeMenuOpen: false,
|
||
modelMenuOpen: false,
|
||
permissionMenuOpen: false,
|
||
currentPermissionMode: 'unrestricted',
|
||
pendingPermissionMode: '',
|
||
executionModeEnabled: false,
|
||
currentExecutionMode: 'sandbox',
|
||
pendingExecutionMode: '',
|
||
executionModeDirectUntil: null,
|
||
executionModeAutoFallbackToastId: null,
|
||
executionModeTtlSeconds: 600,
|
||
executionModeExpirySyncTimer: null,
|
||
pathAuthorizationDialogOpen: false,
|
||
pathAuthorizationMode: 'writable',
|
||
pathAuthorizationWritableDraft: '',
|
||
pathAuthorizationReadableDraft: '',
|
||
pathAuthorizationDraft: '',
|
||
pathAuthorizationSaving: false,
|
||
versioningHostMode: false,
|
||
dockerProjectMode: false,
|
||
hostWorkspaces: [],
|
||
currentHostWorkspaceId: '',
|
||
defaultHostWorkspaceId: '',
|
||
hostWorkspaceSwitching: false,
|
||
hostWorkspaceCreateDialogOpen: false,
|
||
hostWorkspaceCreatePath: '',
|
||
hostWorkspaceCreateLabel: '',
|
||
hostWorkspaceCreateSubmitting: false,
|
||
hostWorkspaceCreateError: '',
|
||
hostWorkspaceManageDialogOpen: false,
|
||
hostWorkspaceManageSubmitting: false,
|
||
versioningEnabled: false,
|
||
newConversationVersioningEnabled: false,
|
||
versioningTrackingMode: 'workspace_and_conversation',
|
||
newConversationVersioningTrackingMode: 'workspace_and_conversation',
|
||
versioningMode: 'overwrite',
|
||
versioningMismatch: false,
|
||
versioningWorkspaceMatched: true,
|
||
versioningDialogOpen: false,
|
||
versioningLoading: false,
|
||
versioningCheckpoints: [],
|
||
versioningSelectedSeq: null,
|
||
versioningSelectedDetail: null,
|
||
versioningDetailLoading: false,
|
||
versioningRestoring: false,
|
||
versioningRestoreMode: 'overwrite',
|
||
permissionModeOptions: [
|
||
{
|
||
value: 'readonly',
|
||
label: '只读',
|
||
description: '仅允许读取/搜索类工具,禁止修改工作区'
|
||
},
|
||
{
|
||
value: 'approval',
|
||
label: '批准',
|
||
description: '对工作区文件进行修改的工具需用户批准后才会执行'
|
||
},
|
||
{
|
||
value: 'auto_approval',
|
||
label: '自动审核',
|
||
description: '工作区内写入直通,高风险操作由后台审核智能体自动审批'
|
||
},
|
||
{
|
||
value: 'unrestricted',
|
||
label: '无限制',
|
||
description: '保持当前默认行为,不额外拦截'
|
||
}
|
||
],
|
||
executionModeOptions: [
|
||
{
|
||
value: 'sandbox',
|
||
label: '沙箱',
|
||
description: '所有指令会在系统沙箱中执行'
|
||
},
|
||
{
|
||
value: 'direct',
|
||
label: '完全访问权限',
|
||
description: '所有指令会在宿主机直接执行'
|
||
}
|
||
],
|
||
pendingToolApprovals: [],
|
||
decidingApprovalIds: [],
|
||
pendingUserQuestions: [],
|
||
userQuestionDialogVisible: false,
|
||
userQuestionMinimized: false,
|
||
userQuestionActiveIndex: 0,
|
||
answeringUserQuestionIds: [],
|
||
userQuestionOriginalTitle: '',
|
||
userQuestionTitleBlinkTimer: null,
|
||
userQuestionTitleBlinkRed: true,
|
||
autoApprovalFeedLines: [],
|
||
autoApprovalFinalMessage: '',
|
||
approvalAutoCloseTimer: null,
|
||
imageEntries: [],
|
||
imageLoading: false,
|
||
videoEntries: [],
|
||
videoLoading: false,
|
||
conversationHasImages: false,
|
||
conversationHasVideos: false,
|
||
conversationListRequestSeq: 0,
|
||
conversationListRefreshToken: 0,
|
||
connectionHeartbeatTimer: null,
|
||
connectionHeartbeatActive: false,
|
||
connectionHeartbeatFailCount: 0,
|
||
connectionHeartbeatSeq: 0,
|
||
connectionHeartbeatLastLatencyMs: 0,
|
||
connectionHeartbeatLastError: '',
|
||
connectionHeartbeatLastStatusCode: null,
|
||
connectionHeartbeatLastChangeAt: 0,
|
||
connectionHeartbeatInFlight: false,
|
||
connectionHeartbeatFailThreshold: 3,
|
||
connectionHeartbeatRequestTimeoutMs: 5000,
|
||
connectionHeartbeatIntervalMs: 8000,
|
||
connectionHeartbeatDisconnectedIntervalMs: 1000,
|
||
composerDraftSaveTimer: null,
|
||
composerDraftDirty: false,
|
||
composerDraftLastSyncedContent: '',
|
||
composerDraftFetchSeq: 0,
|
||
|
||
// 工具控制菜单
|
||
icons: ICONS,
|
||
toolCategoryIcons: TOOL_CATEGORY_ICON_MAP,
|
||
|
||
// 对话回顾
|
||
reviewDialogOpen: false,
|
||
reviewSelectedConversationId: null,
|
||
reviewSubmitting: false,
|
||
reviewPreviewLines: [],
|
||
reviewPreviewLoading: false,
|
||
reviewPreviewError: null,
|
||
reviewPreviewLimit: 20,
|
||
reviewSendToModel: true,
|
||
reviewGeneratedPath: null,
|
||
|
||
// 新手教程首次引导弹窗
|
||
tutorialPromptVisible: false,
|
||
tutorialPromptLoading: false,
|
||
tutorialPromptUsername: '',
|
||
|
||
// 拖拽上传状态
|
||
dragOverActive: false,
|
||
|
||
// 拖拽事件绑定函数(用于正确移除监听)
|
||
_boundDragEnter: null,
|
||
_boundDragOver: null,
|
||
_boundDragLeave: null,
|
||
_boundDrop: null,
|
||
_manualScrollSuppressUntil: 0,
|
||
_escapedByUserScroll: false,
|
||
_autoRelockCooldownUntil: 0,
|
||
|
||
// stick-to-bottom 状态(用于“回到底部”按钮显隐)
|
||
stickIsAtBottom: true,
|
||
stickIsNearBottom: true
|
||
};
|
||
}
|