fix: migrate workspace/project IDs to string and widen schema enums
- Change workspace id/projectId from z.number() to z.union([z.string(), z.number()]).transform(String) for backward compat - Add "non_git" to projectKind, "local_checkout" to workspaceKind enums - Session converts numeric IDs to strings at the descriptor boundary - Update tests and daemon-client event types to match
This commit is contained in:
@@ -70,8 +70,8 @@ describe("openProjectDirectly", () => {
|
||||
requestId: "request-1",
|
||||
error: null,
|
||||
workspace: {
|
||||
id: 1,
|
||||
projectId: 1,
|
||||
id: "1",
|
||||
projectId: "1",
|
||||
projectDisplayName: "project",
|
||||
projectRootPath: WORKSPACE_ID,
|
||||
workspaceDirectory: WORKSPACE_ID,
|
||||
|
||||
@@ -127,7 +127,7 @@ export type DaemonEvent =
|
||||
}
|
||||
| {
|
||||
type: "workspace_update";
|
||||
workspaceId: number;
|
||||
workspaceId: string;
|
||||
payload: Extract<SessionOutboundMessage, { type: "workspace_update" }>["payload"];
|
||||
}
|
||||
| {
|
||||
|
||||
@@ -317,12 +317,12 @@ type WorkspaceUpdatesSubscriptionState = {
|
||||
subscriptionId: string;
|
||||
filter?: WorkspaceUpdatesFilter;
|
||||
isBootstrapping: boolean;
|
||||
pendingUpdatesByWorkspaceId: Map<number, WorkspaceUpdatePayload>;
|
||||
pendingUpdatesByWorkspaceId: Map<string, WorkspaceUpdatePayload>;
|
||||
};
|
||||
type FetchWorkspacesCursor = {
|
||||
sort: FetchWorkspacesRequestSort[];
|
||||
values: Record<string, string | number | null>;
|
||||
id: number;
|
||||
id: string;
|
||||
};
|
||||
|
||||
class SessionRequestError extends Error {
|
||||
@@ -2729,8 +2729,8 @@ export class Session {
|
||||
labels,
|
||||
);
|
||||
const resolvedWorkspace =
|
||||
typeof msg.workspaceId === "number"
|
||||
? await this.workspaceRegistry.get(msg.workspaceId)
|
||||
msg.workspaceId
|
||||
? await this.workspaceRegistry.get(Number(msg.workspaceId))
|
||||
: (await this.findWorkspaceByDirectory(sessionConfig.cwd)) ??
|
||||
(await this.findOrCreateWorkspaceForDirectory(sessionConfig.cwd));
|
||||
if (!resolvedWorkspace) {
|
||||
@@ -4093,7 +4093,7 @@ export class Session {
|
||||
workspaces: Iterable<WorkspaceDescriptorPayload>,
|
||||
): Promise<void> {
|
||||
for (const workspace of workspaces) {
|
||||
const persistedWorkspace = await this.workspaceRegistry.get(workspace.id);
|
||||
const persistedWorkspace = await this.workspaceRegistry.get(Number(workspace.id));
|
||||
if (!persistedWorkspace) {
|
||||
continue;
|
||||
}
|
||||
@@ -5181,17 +5181,19 @@ export class Session {
|
||||
if (cachedShortstat !== undefined) {
|
||||
diffStat = cachedShortstat;
|
||||
} else {
|
||||
warmCheckoutShortstatInBackground(workspace.directory);
|
||||
warmCheckoutShortstatInBackground(workspace.directory, undefined, () => {
|
||||
void this.emitWorkspaceUpdateForCwd(workspace.directory);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: workspace.id,
|
||||
projectId: workspace.projectId,
|
||||
id: String(workspace.id),
|
||||
projectId: String(workspace.projectId),
|
||||
projectDisplayName: resolvedProjectRecord?.displayName ?? String(workspace.projectId),
|
||||
projectRootPath: resolvedProjectRecord?.directory ?? workspace.directory,
|
||||
workspaceDirectory: workspace.directory,
|
||||
projectKind: resolvedProjectRecord?.kind ?? "directory",
|
||||
workspaceKind: workspace.kind,
|
||||
projectKind: (resolvedProjectRecord?.kind ?? "directory") === "git" ? "git" : "non_git",
|
||||
workspaceKind: workspace.kind === "checkout" ? "local_checkout" : workspace.kind,
|
||||
name: workspace.displayName,
|
||||
status: "done",
|
||||
activityAt: null,
|
||||
@@ -5313,7 +5315,7 @@ export class Session {
|
||||
}
|
||||
return spec.direction === "asc" ? base : -base;
|
||||
}
|
||||
return left.id - right.id;
|
||||
return Number(left.id) - Number(right.id);
|
||||
}
|
||||
|
||||
private encodeFetchWorkspacesCursor(
|
||||
@@ -5355,7 +5357,7 @@ export class Session {
|
||||
id?: unknown;
|
||||
};
|
||||
|
||||
if (!Array.isArray(payload.sort) || typeof payload.id !== "number") {
|
||||
if (!Array.isArray(payload.sort) || (typeof payload.id !== "number" && typeof payload.id !== "string")) {
|
||||
throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
|
||||
}
|
||||
if (!payload.values || typeof payload.values !== "object") {
|
||||
@@ -5403,7 +5405,7 @@ export class Session {
|
||||
return {
|
||||
sort: cursorSort,
|
||||
values: payload.values as Record<string, string | number | null>,
|
||||
id: payload.id,
|
||||
id: String(payload.id),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5422,7 +5424,7 @@ export class Session {
|
||||
}
|
||||
return spec.direction === "asc" ? base : -base;
|
||||
}
|
||||
return workspace.id - cursor.id;
|
||||
return Number(workspace.id) - Number(cursor.id);
|
||||
}
|
||||
|
||||
private matchesWorkspaceFilter(input: {
|
||||
@@ -5511,7 +5513,7 @@ export class Session {
|
||||
}
|
||||
|
||||
private flushBootstrappedWorkspaceUpdates(options?: {
|
||||
snapshotLatestActivityByWorkspaceId?: Map<number, number>;
|
||||
snapshotLatestActivityByWorkspaceId?: Map<string, number>;
|
||||
}): void {
|
||||
const subscription = this.workspaceUpdatesSubscription;
|
||||
if (!subscription || !subscription.isBootstrapping) {
|
||||
@@ -5687,7 +5689,7 @@ export class Session {
|
||||
const persistedWorkspace = await this.findWorkspaceByDirectory(normalizedCwd);
|
||||
const all = await this.listWorkspaceDescriptorsSnapshot();
|
||||
const descriptorsByWorkspaceId = new Map(all.map((entry) => [entry.id, entry] as const));
|
||||
const workspaceIdsToEmit = persistedWorkspace ? [persistedWorkspace.id] : [];
|
||||
const workspaceIdsToEmit = persistedWorkspace ? [String(persistedWorkspace.id)] : [];
|
||||
|
||||
for (const nextWorkspaceId of workspaceIdsToEmit) {
|
||||
const workspace = descriptorsByWorkspaceId.get(nextWorkspaceId);
|
||||
@@ -5738,7 +5740,7 @@ export class Session {
|
||||
|
||||
for (const workspaceCwd of uniqueWorkspaceCwds) {
|
||||
const persistedWorkspace = await this.findWorkspaceByDirectory(workspaceCwd);
|
||||
const workspace = persistedWorkspace ? descriptorsByWorkspaceId.get(persistedWorkspace.id) : null;
|
||||
const workspace = persistedWorkspace ? descriptorsByWorkspaceId.get(String(persistedWorkspace.id)) : null;
|
||||
const nextWorkspace =
|
||||
workspace && this.matchesWorkspaceFilter({ workspace, filter: subscription.filter })
|
||||
? workspace
|
||||
@@ -5749,7 +5751,7 @@ export class Session {
|
||||
if (persistedWorkspace) {
|
||||
this.bufferOrEmitWorkspaceUpdate(subscription, {
|
||||
kind: "remove",
|
||||
id: persistedWorkspace.id,
|
||||
id: String(persistedWorkspace.id),
|
||||
});
|
||||
}
|
||||
continue;
|
||||
@@ -5844,7 +5846,7 @@ export class Session {
|
||||
|
||||
const payload = await this.listFetchWorkspacesEntries(request);
|
||||
await this.primeWorkspaceGitWatchFingerprints(payload.entries);
|
||||
const snapshotLatestActivityByWorkspaceId = new Map<number, number>();
|
||||
const snapshotLatestActivityByWorkspaceId = new Map<string, number>();
|
||||
for (const entry of payload.entries) {
|
||||
const parsedLatestActivity = entry.activityAt
|
||||
? Date.parse(entry.activityAt)
|
||||
@@ -5973,7 +5975,8 @@ export class Session {
|
||||
request: Extract<SessionInboundMessage, { type: "archive_workspace_request" }>,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const existing = await this.workspaceRegistry.get(request.workspaceId);
|
||||
const numericWorkspaceId = Number(request.workspaceId);
|
||||
const existing = await this.workspaceRegistry.get(numericWorkspaceId);
|
||||
if (!existing) {
|
||||
throw new Error(`Workspace not found: ${request.workspaceId}`);
|
||||
}
|
||||
@@ -5981,7 +5984,7 @@ export class Session {
|
||||
throw new Error("Use worktree archive for Paseo worktrees");
|
||||
}
|
||||
const archivedAt = new Date().toISOString();
|
||||
await this.archiveWorkspaceRecord(request.workspaceId, archivedAt);
|
||||
await this.archiveWorkspaceRecord(numericWorkspaceId, archivedAt);
|
||||
await this.emitWorkspaceUpdateForCwd(existing.directory);
|
||||
this.emit({
|
||||
type: "archive_workspace_response",
|
||||
|
||||
@@ -658,7 +658,7 @@ export const FetchWorkspacesRequestMessageSchema = z.object({
|
||||
filter: z
|
||||
.object({
|
||||
query: z.string().optional(),
|
||||
projectId: z.number().int().optional(),
|
||||
projectId: z.union([z.string(), z.number()]).transform(String).optional(),
|
||||
idPrefix: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
@@ -757,7 +757,7 @@ export type GitSetupOptions = z.infer<typeof GitSetupOptionsSchema>;
|
||||
export const CreateAgentRequestMessageSchema = z.object({
|
||||
type: z.literal("create_agent_request"),
|
||||
config: AgentSessionConfigSchema,
|
||||
workspaceId: z.number().int().optional(),
|
||||
workspaceId: z.union([z.string(), z.number()]).transform(String).optional(),
|
||||
worktreeName: z.string().optional(),
|
||||
initialPrompt: z.string().optional(),
|
||||
clientMessageId: z.string().optional(),
|
||||
@@ -1102,7 +1102,7 @@ export const OpenProjectRequestSchema = z.object({
|
||||
|
||||
export const ArchiveWorkspaceRequestSchema = z.object({
|
||||
type: z.literal("archive_workspace_request"),
|
||||
workspaceId: z.number().int(),
|
||||
workspaceId: z.union([z.string(), z.number()]).transform(String),
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
@@ -1733,13 +1733,13 @@ export const WorkspaceServicePayloadSchema = z.object({
|
||||
});
|
||||
|
||||
export const WorkspaceDescriptorPayloadSchema = z.object({
|
||||
id: z.number().int(),
|
||||
projectId: z.number().int(),
|
||||
id: z.union([z.string(), z.number()]).transform(String),
|
||||
projectId: z.union([z.string(), z.number()]).transform(String),
|
||||
projectDisplayName: z.string(),
|
||||
projectRootPath: z.string(),
|
||||
workspaceDirectory: z.string(),
|
||||
projectKind: z.enum(["git", "directory"]),
|
||||
workspaceKind: z.enum(["checkout", "worktree"]),
|
||||
projectKind: z.enum(["git", "non_git", "directory"]),
|
||||
workspaceKind: z.enum(["local_checkout", "checkout", "worktree"]),
|
||||
name: z.string(),
|
||||
status: WorkspaceStateBucketSchema,
|
||||
activityAt: z.string().nullable(),
|
||||
@@ -1841,7 +1841,7 @@ export const WorkspaceUpdateMessageSchema = z.object({
|
||||
}),
|
||||
z.object({
|
||||
kind: z.literal("remove"),
|
||||
id: z.number().int(),
|
||||
id: z.union([z.string(), z.number()]).transform(String),
|
||||
}),
|
||||
]),
|
||||
});
|
||||
@@ -1892,7 +1892,7 @@ export const ArchiveWorkspaceResponseMessageSchema = z.object({
|
||||
type: z.literal("archive_workspace_response"),
|
||||
payload: z.object({
|
||||
requestId: z.string(),
|
||||
workspaceId: z.number().int(),
|
||||
workspaceId: z.union([z.string(), z.number()]).transform(String),
|
||||
archivedAt: z.string().nullable(),
|
||||
error: z.string().nullable(),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user