From 2190910e6f3a88d2c73de2e89de73a39c7639264 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Mon, 30 Mar 2026 17:30:31 +0700 Subject: [PATCH] fix: resolve type errors from main merge - Remove orphaned PID lock code from bootstrap (moved to supervisor) - Fix worktree archive adapter to look up workspace by directory - Replace registerWorktreeWorkspaceRecord with inline SQLite implementation - Remove unused imports (stat, createPersistedWorkspaceRecord, PersistedProjectRecord) --- packages/server/src/server/bootstrap.ts | 16 ---- packages/server/src/server/session.ts | 84 +++++++++++++---- .../server/src/server/worktree-session.ts | 92 ++++++++----------- 3 files changed, 105 insertions(+), 87 deletions(-) diff --git a/packages/server/src/server/bootstrap.ts b/packages/server/src/server/bootstrap.ts index 43764d79c..f912fbf7b 100644 --- a/packages/server/src/server/bootstrap.ts +++ b/packages/server/src/server/bootstrap.ts @@ -206,19 +206,8 @@ export async function createPaseoDaemon( const bootstrapStart = performance.now(); const elapsed = () => `${(performance.now() - bootstrapStart).toFixed(0)}ms`; const daemonVersion = resolveDaemonVersion(import.meta.url); - const pidLockMode = config.pidLock?.mode ?? "self"; - const pidLockOwnerPid = config.pidLock?.ownerPid; - const ownsPidLock = pidLockMode === "self"; let database: PaseoDatabaseHandle | null = null; - // Acquire PID lock before expensive bootstrap work so duplicate starts fail immediately. - if (ownsPidLock) { - await acquirePidLock(config.paseoHome, config.listen, { - ownerPid: pidLockOwnerPid, - }); - logger.info({ elapsed: elapsed() }, "PID lock acquired"); - } - try { const serverId = getOrCreateServerId(config.paseoHome, { logger }); const daemonKeyPair = await loadOrCreateDaemonKeyPair(config.paseoHome, logger); @@ -796,11 +785,6 @@ export async function createPaseoDaemon( }; } catch (err) { await database?.close().catch(() => undefined); - if (ownsPidLock) { - await releasePidLock(config.paseoHome, { - ownerPid: pidLockOwnerPid, - }).catch(() => undefined); - } throw err; } } diff --git a/packages/server/src/server/session.ts b/packages/server/src/server/session.ts index a7a9915ef..7d2234ee0 100644 --- a/packages/server/src/server/session.ts +++ b/packages/server/src/server/session.ts @@ -1,6 +1,6 @@ import { v4 as uuidv4 } from "uuid"; import { watch, type FSWatcher } from "node:fs"; -import { readFile, stat } from "fs/promises"; +import { readFile } from "fs/promises"; import { exec } from "child_process"; import { promisify } from "util"; import { join, resolve, sep } from "path"; @@ -106,7 +106,6 @@ import type { ProjectRegistry, WorkspaceRegistry, } from "./workspace-registry.js"; -import { createPersistedWorkspaceRecord } from "./workspace-registry.js"; import { AgentLoadingService } from "./agent-loading-service.js"; import { buildVoiceAgentMcpServerConfig, @@ -166,7 +165,6 @@ import { handlePaseoWorktreeArchiveRequest as handleWorktreeArchiveRequest, handlePaseoWorktreeListRequest as handleWorktreeListRequest, killTerminalsUnderPath as killWorktreeTerminalsUnderPath, - registerPendingWorktreeWorkspace as registerPendingWorktreeWorkspaceSession, } from "./worktree-session.js"; const execAsync = promisify(exec); @@ -4078,7 +4076,12 @@ export class Session { paseoHome: this.paseoHome, agentManager: this.agentManager, agentStorage: this.agentStorage, - archiveWorkspaceRecord: (workspaceId) => this.archiveWorkspaceRecord(workspaceId), + archiveWorkspaceRecord: async (workspaceDirectory) => { + const workspace = await this.findWorkspaceByDirectory(workspaceDirectory); + if (workspace) { + await this.archiveWorkspaceRecord(workspace.id); + } + }, emit: (message) => this.emit(message), emitWorkspaceUpdatesForCwds: (cwds) => this.emitWorkspaceUpdatesForCwds(cwds), isPathWithinRoot: (rootPath, candidatePath) => @@ -5113,25 +5116,68 @@ export class Session { return (await this.workspaceRegistry.get(workspaceId))!; } - private async registerWorktreeWorkspaceRecord(options: { + private async registerPendingWorktreeWorkspace(options: { repoRoot: string; worktreePath: string; branchName: string; }): Promise { - return registerPendingWorktreeWorkspaceSession( - { - buildPersistedProjectRecord: (input) => this.buildPersistedProjectRecord(input), - buildPersistedWorkspaceRecord: (input) => this.buildPersistedWorkspaceRecord(input), - buildProjectPlacement: (cwd) => this.buildProjectPlacement(cwd), - projectRegistry: this.projectRegistry, - syncWorkspaceGitWatchTarget: (cwd, syncOptions) => - this.syncWorkspaceGitWatchTarget(cwd, syncOptions), - workspaceRegistry: this.workspaceRegistry, - archiveProjectRecordIfEmpty: (projectId, archivedAt) => - this.archiveProjectRecordIfEmpty(projectId, archivedAt), - }, - options, - ); + await this.findOrCreateWorkspaceForDirectory(options.repoRoot); + const workspaceDirectory = normalizePersistedWorkspaceId(options.worktreePath); + const basePlacement = await this.buildProjectPlacementForCwd(options.repoRoot); + if (!basePlacement) { + throw new Error(`Workspace not found for repo root ${options.repoRoot}`); + } + + const projectId = Number(basePlacement.projectKey); + if (!Number.isInteger(projectId)) { + throw new Error(`Invalid project id for repo root ${options.repoRoot}`); + } + + const now = new Date().toISOString(); + const existingWorkspace = await this.findWorkspaceByDirectory(workspaceDirectory); + if (!existingWorkspace) { + const workspaceId = await this.workspaceRegistry.insert({ + projectId, + directory: workspaceDirectory, + displayName: options.branchName, + kind: "worktree", + createdAt: now, + updatedAt: now, + archivedAt: null, + }); + const workspace = await this.workspaceRegistry.get(workspaceId); + if (!workspace) { + throw new Error(`Workspace not found after insert: ${workspaceId}`); + } + await this.syncWorkspaceGitWatchTarget(workspace.directory, { isGit: true }); + return workspace; + } + + await this.workspaceRegistry.upsert({ + id: existingWorkspace.id, + projectId, + directory: workspaceDirectory, + displayName: options.branchName, + kind: "worktree", + createdAt: existingWorkspace.createdAt, + updatedAt: now, + archivedAt: null, + }); + await this.syncWorkspaceGitWatchTarget(workspaceDirectory, { isGit: true }); + + if (!existingWorkspace.archivedAt && existingWorkspace.projectId !== projectId) { + const siblingWorkspaces = (await this.workspaceRegistry.list()).filter( + (workspace) => + workspace.projectId === existingWorkspace.projectId && + workspace.id !== existingWorkspace.id && + !workspace.archivedAt, + ); + if (siblingWorkspaces.length === 0) { + await this.projectRegistry.archive(existingWorkspace.projectId, now); + } + } + + return (await this.workspaceRegistry.get(existingWorkspace.id))!; } private async archiveWorkspaceRecord(workspaceId: number, archivedAt?: string): Promise { diff --git a/packages/server/src/server/worktree-session.ts b/packages/server/src/server/worktree-session.ts index 2076c53e9..9508a3e31 100644 --- a/packages/server/src/server/worktree-session.ts +++ b/packages/server/src/server/worktree-session.ts @@ -14,7 +14,6 @@ import { type WorkspaceDescriptorPayload, } from "./messages.js"; import type { - PersistedProjectRecord, PersistedWorkspaceRecord, ProjectRegistry, WorkspaceRegistry, @@ -77,26 +76,15 @@ type ArchivePaseoWorktreeDependencies = { }; type RegisterPendingWorktreeWorkspaceDependencies = { - buildPersistedProjectRecord: (input: { - workspaceId: string; - placement: ProjectPlacementPayload; - createdAt: string; - updatedAt: string; - }) => PersistedProjectRecord; - buildPersistedWorkspaceRecord: (input: { - workspaceId: string; - placement: ProjectPlacementPayload; - createdAt: string; - updatedAt: string; - }) => PersistedWorkspaceRecord; buildProjectPlacement: (cwd: string) => Promise; - projectRegistry: Pick; + findWorkspaceByDirectory: (directory: string) => Promise; + projectRegistry: Pick; syncWorkspaceGitWatchTarget: ( cwd: string, options: { isGit: boolean }, ) => Promise; - workspaceRegistry: Pick; - archiveProjectRecordIfEmpty: (projectId: string, archivedAt: string) => Promise; + workspaceRegistry: Pick; + archiveProjectRecordIfEmpty: (projectId: number, archivedAt: string) => Promise; }; type CreatePaseoWorktreeInBackgroundDependencies = { @@ -509,50 +497,50 @@ export async function registerPendingWorktreeWorkspace( branchName: string; }, ): Promise { - const workspaceId = normalizePersistedWorkspaceId(options.worktreePath); + const workspaceDirectory = normalizePersistedWorkspaceId(options.worktreePath); const basePlacement = await dependencies.buildProjectPlacement(options.repoRoot); - const placement: ProjectPlacementPayload = { - ...basePlacement, - checkout: { - cwd: workspaceId, - isGit: true, - currentBranch: options.branchName, - remoteUrl: basePlacement.checkout.remoteUrl, - isPaseoOwnedWorktree: true, - mainRepoRoot: options.repoRoot, - }, - }; + const projectId = Number(basePlacement.projectKey); + if (!Number.isInteger(projectId)) { + throw new Error(`Invalid project id for repo root ${options.repoRoot}`); + } + const now = new Date().toISOString(); - const existingWorkspace = await dependencies.workspaceRegistry.get(workspaceId); - const existingProject = await dependencies.projectRegistry.get(placement.projectKey); - const nextProjectRecord = dependencies.buildPersistedProjectRecord({ - workspaceId, - placement, - createdAt: existingProject?.createdAt ?? now, - updatedAt: now, - }); - const nextWorkspaceRecord = dependencies.buildPersistedWorkspaceRecord({ - workspaceId, - placement, - createdAt: existingWorkspace?.createdAt ?? now, - updatedAt: now, - }); + const existingWorkspace = await dependencies.findWorkspaceByDirectory(workspaceDirectory); + if (!existingWorkspace) { + const workspaceId = await dependencies.workspaceRegistry.insert({ + projectId, + directory: workspaceDirectory, + displayName: options.branchName, + kind: "worktree", + createdAt: now, + updatedAt: now, + archivedAt: null, + }); + const workspace = await dependencies.workspaceRegistry.get(workspaceId); + if (!workspace) { + throw new Error(`Workspace not found after insert: ${workspaceId}`); + } + await dependencies.syncWorkspaceGitWatchTarget(workspaceDirectory, { isGit: true }); + return workspace; + } - await dependencies.projectRegistry.upsert(nextProjectRecord); - await dependencies.workspaceRegistry.upsert(nextWorkspaceRecord); - await dependencies.syncWorkspaceGitWatchTarget(workspaceId, { - isGit: placement.checkout.isGit, + await dependencies.workspaceRegistry.upsert({ + id: existingWorkspace.id, + projectId, + directory: workspaceDirectory, + displayName: options.branchName, + kind: "worktree", + createdAt: existingWorkspace.createdAt, + updatedAt: now, + archivedAt: null, }); + await dependencies.syncWorkspaceGitWatchTarget(workspaceDirectory, { isGit: true }); - if ( - existingWorkspace && - !existingWorkspace.archivedAt && - existingWorkspace.projectId !== nextWorkspaceRecord.projectId - ) { + if (!existingWorkspace.archivedAt && existingWorkspace.projectId !== projectId) { await dependencies.archiveProjectRecordIfEmpty(existingWorkspace.projectId, now); } - return nextWorkspaceRecord; + return (await dependencies.workspaceRegistry.get(existingWorkspace.id))!; } export async function handleCreatePaseoWorktreeRequest(