feat: Implement GitLabProvider for issue management using glab CLI
- Add GitLabProvider class for handling issue operations, label management, and MR checks. - Implement methods for ensuring labels, creating issues, listing issues by label, and transitioning labels. - Introduce a provider factory to auto-detect GitLab or GitHub based on the repository URL. - Create project registration tool to validate repositories, create state labels, and log project entries. - Enhance queue status and session health tools to support new session management features. - Update task completion and task creation tools to support auto-chaining and improved session handling. - Refactor task pickup tool to streamline model selection and session management.
This commit is contained in:
@@ -7,10 +7,10 @@ import path from "node:path";
|
||||
|
||||
export type WorkerState = {
|
||||
active: boolean;
|
||||
sessionId: string | null;
|
||||
issueId: string | null;
|
||||
startTime: string | null;
|
||||
model: string | null;
|
||||
sessions: Record<string, string | null>;
|
||||
};
|
||||
|
||||
export type Project = {
|
||||
@@ -20,6 +20,7 @@ export type Project = {
|
||||
deployUrl: string;
|
||||
baseBranch: string;
|
||||
deployBranch: string;
|
||||
autoChain: boolean;
|
||||
dev: WorkerState;
|
||||
qa: WorkerState;
|
||||
};
|
||||
@@ -28,13 +29,84 @@ export type ProjectsData = {
|
||||
projects: Record<string, Project>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Migrate old WorkerState schema (sessionId field) to new sessions map.
|
||||
* Called transparently on read — old data is converted in memory,
|
||||
* persisted on next write.
|
||||
*/
|
||||
function migrateWorkerState(worker: Record<string, unknown>): WorkerState {
|
||||
// Already migrated — has sessions map
|
||||
if (worker.sessions && typeof worker.sessions === "object") {
|
||||
return worker as unknown as WorkerState;
|
||||
}
|
||||
|
||||
// Old schema: { sessionId, model, ... }
|
||||
const sessionId = worker.sessionId as string | null;
|
||||
const model = worker.model as string | null;
|
||||
const sessions: Record<string, string | null> = {};
|
||||
|
||||
if (sessionId && model) {
|
||||
sessions[model] = sessionId;
|
||||
}
|
||||
|
||||
return {
|
||||
active: worker.active as boolean,
|
||||
issueId: worker.issueId as string | null,
|
||||
startTime: worker.startTime as string | null,
|
||||
model,
|
||||
sessions,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a blank WorkerState with null sessions for given model aliases.
|
||||
*/
|
||||
export function emptyWorkerState(aliases: string[]): WorkerState {
|
||||
const sessions: Record<string, string | null> = {};
|
||||
for (const alias of aliases) {
|
||||
sessions[alias] = null;
|
||||
}
|
||||
return {
|
||||
active: false,
|
||||
issueId: null,
|
||||
startTime: null,
|
||||
model: null,
|
||||
sessions,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session key for a specific model alias from a worker's sessions map.
|
||||
*/
|
||||
export function getSessionForModel(
|
||||
worker: WorkerState,
|
||||
modelAlias: string,
|
||||
): string | null {
|
||||
return worker.sessions[modelAlias] ?? null;
|
||||
}
|
||||
|
||||
function projectsPath(workspaceDir: string): string {
|
||||
return path.join(workspaceDir, "memory", "projects.json");
|
||||
}
|
||||
|
||||
export async function readProjects(workspaceDir: string): Promise<ProjectsData> {
|
||||
const raw = await fs.readFile(projectsPath(workspaceDir), "utf-8");
|
||||
return JSON.parse(raw) as ProjectsData;
|
||||
const data = JSON.parse(raw) as ProjectsData;
|
||||
|
||||
// Migrate any old-schema or missing fields transparently
|
||||
for (const project of Object.values(data.projects)) {
|
||||
project.dev = project.dev
|
||||
? migrateWorkerState(project.dev as unknown as Record<string, unknown>)
|
||||
: emptyWorkerState([]);
|
||||
project.qa = project.qa
|
||||
? migrateWorkerState(project.qa as unknown as Record<string, unknown>)
|
||||
: emptyWorkerState([]);
|
||||
if (project.autoChain === undefined) {
|
||||
project.autoChain = false;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function writeProjects(
|
||||
@@ -79,6 +151,10 @@ export async function updateWorker(
|
||||
}
|
||||
|
||||
const worker = project[role];
|
||||
// Merge sessions maps if both exist
|
||||
if (updates.sessions && worker.sessions) {
|
||||
updates.sessions = { ...worker.sessions, ...updates.sessions };
|
||||
}
|
||||
project[role] = { ...worker, ...updates };
|
||||
|
||||
await writeProjects(workspaceDir, data);
|
||||
@@ -87,7 +163,7 @@ export async function updateWorker(
|
||||
|
||||
/**
|
||||
* Mark a worker as active with a new task.
|
||||
* Sets active=true, issueId, model. Preserves sessionId and startTime if reusing.
|
||||
* Sets active=true, issueId, model. Stores session key in sessions[model].
|
||||
*/
|
||||
export async function activateWorker(
|
||||
workspaceDir: string,
|
||||
@@ -96,7 +172,7 @@ export async function activateWorker(
|
||||
params: {
|
||||
issueId: string;
|
||||
model: string;
|
||||
sessionId?: string;
|
||||
sessionKey?: string;
|
||||
startTime?: string;
|
||||
},
|
||||
): Promise<ProjectsData> {
|
||||
@@ -105,9 +181,9 @@ export async function activateWorker(
|
||||
issueId: params.issueId,
|
||||
model: params.model,
|
||||
};
|
||||
// Only set sessionId and startTime if provided (new spawn)
|
||||
if (params.sessionId !== undefined) {
|
||||
updates.sessionId = params.sessionId;
|
||||
// Store session key in the sessions map for this model
|
||||
if (params.sessionKey !== undefined) {
|
||||
updates.sessions = { [params.model]: params.sessionKey };
|
||||
}
|
||||
if (params.startTime !== undefined) {
|
||||
updates.startTime = params.startTime;
|
||||
@@ -117,7 +193,7 @@ export async function activateWorker(
|
||||
|
||||
/**
|
||||
* Mark a worker as inactive after task completion.
|
||||
* Clears issueId and active, PRESERVES sessionId, model, startTime for reuse.
|
||||
* Clears issueId and active, PRESERVES sessions map, model, startTime for reuse.
|
||||
*/
|
||||
export async function deactivateWorker(
|
||||
workspaceDir: string,
|
||||
|
||||
Reference in New Issue
Block a user