| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- /** Workspace command implementation and stable Remote failure mapping. */
- import type { Context } from '@deepseek-ai/cordis'
- import type { Workspace } from '@deepseek-ai/dsh-workspace'
- import {
- WorkspaceId,
- WorkspaceMoveInvalidError,
- WorkspaceOrderInvalidError,
- WorkspaceUnknownSessionError,
- } from '@deepseek-ai/dsh-workspace'
- import { RemoteError, remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol'
- import { workspaceView } from './feed.ts'
- import type {
- WorkspaceArchiveSessionRequest,
- WorkspaceArchiveValue,
- WorkspaceCreateRequest,
- WorkspaceCreateValue,
- WorkspaceDeleteRequest,
- WorkspaceDeleteValue,
- WorkspaceInsertBeforeRequest,
- WorkspaceInsertSessionBeforeRequest,
- WorkspaceOrderValue,
- WorkspaceRenameRequest,
- WorkspaceValue,
- } from './types.ts'
- /** Implements Workspace mutations against the authoritative registry. */
- export class WorkspaceCommands {
- private operationTail = Promise.resolve()
- /** @param ctx - Host context containing the Workspace registry. */
- constructor(private readonly ctx: Context) {}
- /**
- * Create or resolve one Workspace over an existing directory.
- * @param request - directory path to register.
- * @returns the Workspace and whether this call created it.
- */
- create(request: WorkspaceCreateRequest): Promise<WorkspaceCreateValue> {
- return this.enqueue(async () => {
- try {
- const existing = await this.ctx.workspaceRegistry.resolveByPath(request.path)
- if (existing !== undefined) {
- return { workspace: workspaceView(existing), created: false }
- }
- const workspace = await this.ctx.workspaceRegistry.create(request.path)
- return { workspace: workspaceView(workspace), created: true }
- } catch (error) {
- if (remoteErrorOf(error) !== undefined) throw error
- throw new RemoteError(
- 'workspace/invalid-path',
- `cannot create a Workspace at "${request.path}": ${errorMessage(error)}`,
- { path: request.path },
- { cause: error },
- )
- }
- })
- }
- /**
- * Rename one Workspace after serializing title ownership checks.
- * @param request - Workspace identity and proposed title.
- * @returns the updated Workspace projection.
- */
- rename(request: WorkspaceRenameRequest): Promise<WorkspaceValue> {
- const title = request.title.trim()
- if (title === '') {
- return Promise.reject(new RemoteError('gateway/bad-request', 'Workspace rename requires a non-blank title', {}))
- }
- return this.enqueue(async () => {
- const workspace = this.requireWorkspace(request.workspaceId)
- if (title !== workspace.title) {
- if (this.ctx.workspaceRegistry.list().some(candidate =>
- candidate.id !== workspace.id && candidate.title === title)) {
- throw new RemoteError(
- 'workspace/name-conflict',
- `Workspace name '${title}' is already in use`,
- { name: title },
- )
- }
- await workspace.setTitle(title)
- }
- return { workspace: workspaceView(workspace) }
- })
- }
- /**
- * Delete one Workspace registration without deleting its directory or Sessions.
- * @param request - Workspace identity to remove.
- * @returns deletion confirmation.
- */
- delete(request: WorkspaceDeleteRequest): Promise<WorkspaceDeleteValue> {
- return this.enqueue(async () => {
- if (!await this.ctx.workspaceRegistry.delete(WorkspaceId(request.workspaceId))) {
- throw workspaceNotFound(request.workspaceId)
- }
- return { deleted: true }
- })
- }
- /**
- * Move one Workspace within the durable registry order.
- * @param request - moved Workspace and optional anchor.
- * @returns the complete resulting Workspace order.
- */
- async insertBefore(request: WorkspaceInsertBeforeRequest): Promise<WorkspaceOrderValue> {
- try {
- const workspaceIds = await this.ctx.workspaceRegistry.insertBefore(
- WorkspaceId(request.workspaceId),
- request.beforeWorkspaceId === undefined
- ? undefined
- : WorkspaceId(request.beforeWorkspaceId),
- )
- return { workspaceIds: [...workspaceIds] }
- } catch (error) {
- if (!(error instanceof WorkspaceOrderInvalidError)) throw error
- throw workspaceNotFound(error.workspaceId)
- }
- }
- /**
- * Move one accounted Session within a Workspace's manual order.
- * @param request - Workspace, Session, and optional anchor identities.
- * @returns the updated Workspace projection.
- */
- async insertSessionBefore(request: WorkspaceInsertSessionBeforeRequest): Promise<WorkspaceValue> {
- const workspace = this.requireWorkspace(request.workspaceId)
- try {
- await workspace.insertSessionBefore(request.sessionId, request.beforeSessionId)
- } catch (error) {
- if (!(error instanceof WorkspaceMoveInvalidError)) throw error
- throw new RemoteError(
- 'workspace/move-invalid',
- error.message,
- {
- workspaceId: request.workspaceId,
- sessionId: request.sessionId,
- ...request.beforeSessionId === undefined
- ? {}
- : { beforeSessionId: request.beforeSessionId },
- },
- { cause: error },
- )
- }
- return { workspace: workspaceView(workspace) }
- }
- /**
- * Add one known Session to the registry-global archive set.
- * @param request - Session identity to archive.
- * @returns the complete resulting archive set.
- */
- async archiveSession(request: WorkspaceArchiveSessionRequest): Promise<WorkspaceArchiveValue> {
- try {
- await this.ctx.workspaceRegistry.archiveSession(request.sessionId)
- } catch (error) {
- if (!(error instanceof WorkspaceUnknownSessionError)) throw error
- throw new RemoteError('session/not-found', error.message, { sessionId: request.sessionId }, { cause: error })
- }
- return { archivedSessionIds: [...this.ctx.workspaceRegistry.archivedSessionIds] }
- }
- private requireWorkspace(workspaceId: WorkspaceId): Workspace {
- const workspace = this.ctx.workspaceRegistry.get(WorkspaceId(workspaceId))
- if (workspace === undefined) throw workspaceNotFound(workspaceId)
- return workspace
- }
- private enqueue<T>(operation: () => Promise<T>): Promise<T> {
- const result = this.operationTail.then(operation)
- this.operationTail = result.then(() => undefined, () => undefined)
- return result
- }
- }
- function workspaceNotFound(workspaceId: WorkspaceId): RemoteError<'workspace/not-found'> {
- return new RemoteError(
- 'workspace/not-found',
- `Workspace "${workspaceId}" not found`,
- { workspaceId },
- )
- }
- function errorMessage(error: unknown): string {
- return error instanceof Error ? error.message : String(error)
- }
|