This commit is contained in:
ZacharyZhang-NY committed 2026-04-04 12:03:50 -04:00
1 parent 66158e222e
commit 67c757f481
314 files changed
+37945 -22962

No files matched your search

+177
View File
@@ -0,0 +1,177 @@
import type { DatabaseSync } from "node:sqlite";
import type { RunStatus, WorkspaceRecord } from "@shared/types";
import type { ReadAll, Transaction } from "./helpers";
import { ensureId, normalizeOptionalId, nowIso, toBoolean } from "./helpers";
export function buildWorkspaceMethods(
getDb: () => DatabaseSync,
readAll: ReadAll,
transaction: Transaction,
) {
return {
saveWorkspace(input: {
id?: string | null;
companyId: string;
name: string;
localPath: string;
repoUrl: string;
repoRef: string;
projectId?: string | null;
isPrimary: boolean;
}) {
const id = ensureId(input.id ?? null);
const ts = nowIso();
if (input.isPrimary) {
getDb().prepare("update workspaces set is_primary = 0 where company_id = ?").run(input.companyId);
}
const existing = getDb().prepare("select id from workspaces where id = ? and company_id = ?").get(id, input.companyId) as { id: string } | undefined;
if (existing) {
getDb()
.prepare(
"update workspaces set name = ?, local_path = ?, repo_url = ?, repo_ref = ?, project_id = ?, is_primary = ?, updated_at = ? where id = ? and company_id = ?",
)
.run(input.name, input.localPath, input.repoUrl, input.repoRef, normalizeOptionalId(input.projectId), input.isPrimary ? 1 : 0, ts, id, input.companyId);
} else {
getDb()
.prepare(
"insert into workspaces (id, company_id, name, local_path, repo_url, repo_ref, project_id, state, is_primary, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, 'ready', ?, ?, ?)",
)
.run(id, input.companyId, input.name, input.localPath, input.repoUrl, input.repoRef, normalizeOptionalId(input.projectId), input.isPrimary ? 1 : 0, ts, ts);
}
return id;
},
getWorkspace(id: string | null | undefined): WorkspaceRecord | null {
if (!id) return null;
const row = getDb()
.prepare(
"select id, company_id as companyId, name, local_path as localPath, repo_url as repoUrl, repo_ref as repoRef, project_id as projectId, state, is_primary as isPrimary, created_at as createdAt, updated_at as updatedAt from workspaces where id = ?",
)
.get(id) as WorkspaceRecord | undefined;
return row ? { ...row, isPrimary: toBoolean(row.isPrimary) } : null;
},
getProjectPrimaryWorkspace(projectId: string | null | undefined, companyId: string): WorkspaceRecord | null {
if (!projectId) return null;
const row = getDb()
.prepare(
"select id, company_id as companyId, name, local_path as localPath, repo_url as repoUrl, repo_ref as repoRef, project_id as projectId, state, is_primary as isPrimary, created_at as createdAt, updated_at as updatedAt from workspaces where company_id = ? and project_id = ? order by is_primary desc, created_at asc limit 1",
)
.get(companyId, projectId) as WorkspaceRecord | undefined;
return row ? { ...row, isPrimary: toBoolean(row.isPrimary) } : null;
},
listWorkspaceLocalPaths(companyId: string): string[] {
const rows = getDb()
.prepare("select local_path as localPath from workspaces where company_id = ? and local_path is not null")
.all(companyId) as Array<{ localPath: string }>;
return rows.map((r) => r.localPath).filter(Boolean);
},
getAnyCompanyWorkspace(companyId: string): WorkspaceRecord | null {
const row = getDb()
.prepare(
"select id, company_id as companyId, name, local_path as localPath, repo_url as repoUrl, repo_ref as repoRef, project_id as projectId, state, is_primary as isPrimary, created_at as createdAt, updated_at as updatedAt from workspaces where company_id = ? order by is_primary desc, created_at asc limit 1",
)
.get(companyId) as WorkspaceRecord | undefined;
return row ? { ...row, isPrimary: toBoolean(row.isPrimary) } : null;
},
deleteWorkspace(id: string, companyId: string) {
transaction(() => {
getDb().prepare("update agents set workspace_id = null where workspace_id = ? and company_id = ?").run(id, companyId);
getDb().prepare("update tasks set workspace_id = null where workspace_id = ? and company_id = ?").run(id, companyId);
getDb().prepare("delete from workspaces where id = ? and company_id = ?").run(id, companyId);
});
},
acquireWorkspaceLock(workspaceId: string, runId: string): boolean {
const ts = nowIso();
const result = getDb()
.prepare(
`update workspaces
set lock_holder_run_id = ?, lock_acquired_at = ?, state = 'busy', updated_at = ?
where id = ?
and (lock_holder_run_id is null or lock_holder_run_id = ?)`,
)
.run(runId, ts, ts, workspaceId, runId);
return result.changes > 0;
},
claimWorkspaceLock(workspaceId: string, runId: string): boolean {
const holder = getDb()
.prepare("select lock_holder_run_id as lockHolderRunId from workspaces where id = ?")
.get(workspaceId) as { lockHolderRunId: string | null } | undefined;
if (!holder) return false;
if (!holder.lockHolderRunId || holder.lockHolderRunId === runId) {
const ts = nowIso();
const result = getDb()
.prepare(
`update workspaces
set lock_holder_run_id = ?, lock_acquired_at = ?, state = 'busy', updated_at = ?
where id = ?
and (lock_holder_run_id is null or lock_holder_run_id = ?)`,
)
.run(runId, ts, ts, workspaceId, runId);
return result.changes > 0;
}
const blockingRun = getDb()
.prepare("select status from runs where id = ?")
.get(holder.lockHolderRunId) as { status: RunStatus } | undefined;
if (blockingRun && (blockingRun.status === "queued" || blockingRun.status === "running")) {
return false;
}
const ts = nowIso();
const result = getDb()
.prepare(
`update workspaces
set lock_holder_run_id = ?, lock_acquired_at = ?, state = 'busy', updated_at = ?
where id = ? and lock_holder_run_id = ?`,
)
.run(runId, ts, ts, workspaceId, holder.lockHolderRunId);
return result.changes > 0;
},
releaseWorkspaceLock(workspaceId: string, runId: string) {
getDb()
.prepare(
`update workspaces
set lock_holder_run_id = null, lock_acquired_at = null, state = 'ready', updated_at = ?
where id = ? and lock_holder_run_id = ?`,
)
.run(nowIso(), workspaceId, runId);
},
releaseAllWorkspaceLocks() {
getDb()
.prepare(
"update workspaces set lock_holder_run_id = null, lock_acquired_at = null, state = 'ready', updated_at = ? where lock_holder_run_id is not null",
)
.run(nowIso());
},
releaseStaleWorkspaceLocks(): number {
const result = getDb()
.prepare(
`update workspaces
set lock_holder_run_id = null, lock_acquired_at = null, state = 'ready', updated_at = ?
where lock_holder_run_id is not null
and not exists (
select 1
from runs
where runs.id = workspaces.lock_holder_run_id
and runs.status in ('queued', 'running')
)`,
)
.run(nowIso());
return Number(result.changes);
},
isWorkspaceLocked(workspaceId: string): boolean {
const row = getDb()
.prepare("select lock_holder_run_id from workspaces where id = ?")
.get(workspaceId) as { lock_holder_run_id: string | null } | undefined;
return Boolean(row?.lock_holder_run_id);
},
};
}