* Archive a workspace. Archived workspaces are hidden from the main sidebar * but can be viewed on the project page. * * If init is still running, we abort it before archiving so we don't leave * orphaned post-create work running in the background. * * Returns a typed confirmation
(
workspaceId: string,
acknowledgedUntrackedPaths?: string[]
)
| 5391 | * untracked-file set must be re-reviewed before a lossy snapshot archive can proceed. |
| 5392 | */ |
| 5393 | async archive( |
| 5394 | workspaceId: string, |
| 5395 | acknowledgedUntrackedPaths?: string[] |
| 5396 | ): Promise<Result<ArchiveWorkspaceResult>> { |
| 5397 | this.archivingWorkspaces.add(workspaceId); |
| 5398 | |
| 5399 | try { |
| 5400 | const workspace = this.config.findWorkspace(workspaceId); |
| 5401 | if (!workspace) { |
| 5402 | return Err("Workspace not found"); |
| 5403 | } |
| 5404 | const initState = this.initStateManager.getInitState(workspaceId); |
| 5405 | if (initState?.status === "running") { |
| 5406 | // Archiving should not leave post-create setup running in the background. |
| 5407 | const initAbortController = this.initAbortControllers.get(workspaceId); |
| 5408 | if (initAbortController) { |
| 5409 | initAbortController.abort(); |
| 5410 | this.initAbortControllers.delete(workspaceId); |
| 5411 | } |
| 5412 | |
| 5413 | this.initStateManager.clearInMemoryState(workspaceId); |
| 5414 | |
| 5415 | // Clearing init state prevents init-end from firing (createInitLogger.logComplete() bails when |
| 5416 | // state is missing). If archiving fails before we persist archivedAt (e.g., beforeArchive hook |
| 5417 | // error), ensure the sidebar doesn't stay stuck on isInitializing/"Cancel creation". |
| 5418 | try { |
| 5419 | const allMetadata = await this.config.getAllWorkspaceMetadata(); |
| 5420 | const updatedMetadata = allMetadata.find((m) => m.id === workspaceId); |
| 5421 | if (updatedMetadata) { |
| 5422 | const enrichedMetadata = this.enrichFrontendMetadata(updatedMetadata); |
| 5423 | const session = this.sessions.get(workspaceId); |
| 5424 | if (session) { |
| 5425 | session.emitMetadata(enrichedMetadata); |
| 5426 | } else { |
| 5427 | this.emit("metadata", { workspaceId, metadata: enrichedMetadata }); |
| 5428 | } |
| 5429 | } |
| 5430 | } catch (error) { |
| 5431 | log.debug("Failed to emit metadata after init cancellation during archive", { |
| 5432 | workspaceId, |
| 5433 | error: getErrorMessage(error), |
| 5434 | }); |
| 5435 | } |
| 5436 | } |
| 5437 | |
| 5438 | const { projectPath, workspacePath } = workspace; |
| 5439 | const worktreeArchiveBehavior = this.getWorktreeArchiveBehavior(); |
| 5440 | const snapshotBehaviorEnabled = |
| 5441 | worktreeArchiveBehavior === "snapshot" && this.worktreeArchiveSnapshotService != null; |
| 5442 | |
| 5443 | let beforeArchiveMetadata: WorkspaceMetadata | undefined; |
| 5444 | if (this.workspaceLifecycleHooks || snapshotBehaviorEnabled) { |
| 5445 | const metadataResult = await this.aiService.getWorkspaceMetadata(workspaceId); |
| 5446 | if (!metadataResult.success) { |
| 5447 | return Err(metadataResult.error); |
| 5448 | } |
| 5449 | beforeArchiveMetadata = metadataResult.data; |
| 5450 | } |
no test coverage detected