mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Add projects without creating workspaces
Keep the legacy open-project request creating a workspace for old clients; new clients now add projects and create workspaces through separate flows.
This commit is contained in:
@@ -354,4 +354,18 @@ describe("checkout PR schemas", () => {
|
||||
projectRemove: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("accepts the project add server_info feature flag", () => {
|
||||
expect(
|
||||
ServerInfoStatusPayloadSchema.parse({
|
||||
status: "server_info",
|
||||
serverId: "srv_test",
|
||||
features: {
|
||||
projectAdd: true,
|
||||
},
|
||||
}).features,
|
||||
).toEqual({
|
||||
projectAdd: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1714,6 +1714,12 @@ export const OpenProjectRequestSchema = z.object({
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const ProjectAddRequestSchema = z.object({
|
||||
type: z.literal("project.add.request"),
|
||||
cwd: z.string(),
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const ArchiveWorkspaceRequestSchema = z.object({
|
||||
type: z.literal("archive_workspace_request"),
|
||||
workspaceId: z.string(),
|
||||
@@ -2080,6 +2086,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
|
||||
LegacyListAvailableEditorsRequestSchema,
|
||||
LegacyOpenInEditorRequestSchema,
|
||||
OpenProjectRequestSchema,
|
||||
ProjectAddRequestSchema,
|
||||
ArchiveWorkspaceRequestSchema,
|
||||
WorkspaceCreateRequestSchema,
|
||||
WorkspaceClearAttentionRequestSchema,
|
||||
@@ -2304,6 +2311,8 @@ export const ServerInfoStatusPayloadSchema = z
|
||||
workspaceMultiplicity: z.boolean().optional(),
|
||||
// COMPAT(projectRemove): added in v0.1.97, drop the gate when floor >= v0.1.97.
|
||||
projectRemove: z.boolean().optional(),
|
||||
// COMPAT(projectAdd): added in v0.1.97, drop the gate when floor >= v0.1.97.
|
||||
projectAdd: z.boolean().optional(),
|
||||
// COMPAT(worktreeRestore): added in v0.1.97, drop the gate when floor >= v0.1.97
|
||||
worktreeRestore: z.boolean().optional(),
|
||||
// COMPAT(providerUsageList): added in v0.1.98, drop the gate when daemon floor >= v0.1.98.
|
||||
@@ -2699,8 +2708,9 @@ export const FetchRecentProviderSessionsResponseMessageSchema = z.object({
|
||||
});
|
||||
|
||||
// COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
|
||||
// A project parent that has zero active workspaces. The sidebar renders it as an
|
||||
// empty project row so projects persist after their last workspace is archived.
|
||||
// A project parent that has zero active workspaces. The sidebar renders the
|
||||
// project row with a new-workspace child so projects persist after their last
|
||||
// workspace is archived.
|
||||
export const WorkspaceProjectDescriptorPayloadSchema = z.object({
|
||||
projectId: z.string(),
|
||||
projectDisplayName: z.string(),
|
||||
@@ -2739,10 +2749,10 @@ export const WorkspaceUpdateMessageSchema = z.object({
|
||||
id: z.string(),
|
||||
// COMPAT(workspaceProjects): added in v0.1.97, drop the optional gate when floor >= v0.1.97.
|
||||
// When archiving this workspace leaves its project with no active
|
||||
// workspaces, the daemon includes the now-empty project parent so the
|
||||
// sidebar keeps rendering it without waiting for a full re-hydration. Old
|
||||
// daemons omit it; old clients ignore it and surface the empty project on
|
||||
// their next workspace fetch instead.
|
||||
// workspaces, the daemon includes the project parent so the sidebar keeps
|
||||
// rendering it without waiting for a full re-hydration. Old daemons omit
|
||||
// it; old clients ignore it and surface the project on their next
|
||||
// workspace fetch instead.
|
||||
emptyProject: WorkspaceProjectDescriptorPayloadSchema.optional(),
|
||||
// Project removal is represented on the existing workspace update channel
|
||||
// so old clients can still parse the message and ignore the extra field.
|
||||
@@ -2795,6 +2805,16 @@ export const OpenProjectResponseMessageSchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
export const ProjectAddResponseSchema = z.object({
|
||||
type: z.literal("project.add.response"),
|
||||
payload: z.object({
|
||||
requestId: z.string(),
|
||||
project: WorkspaceProjectDescriptorPayloadSchema.nullable(),
|
||||
error: z.string().nullable(),
|
||||
errorCode: z.enum(["directory_not_found"]).nullish().catch(null),
|
||||
}),
|
||||
});
|
||||
|
||||
export const StartWorkspaceScriptResponseMessageSchema = z.object({
|
||||
type: z.literal("start_workspace_script_response"),
|
||||
payload: z.object({
|
||||
@@ -4081,6 +4101,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
||||
FetchAgentHistoryResponseMessageSchema,
|
||||
FetchRecentProviderSessionsResponseMessageSchema,
|
||||
FetchWorkspacesResponseMessageSchema,
|
||||
ProjectAddResponseSchema,
|
||||
OpenProjectResponseMessageSchema,
|
||||
StartWorkspaceScriptResponseMessageSchema,
|
||||
LegacyListAvailableEditorsResponseMessageSchema,
|
||||
@@ -4230,6 +4251,7 @@ export type FetchRecentProviderSessionsResponseMessage = z.infer<
|
||||
typeof FetchRecentProviderSessionsResponseMessageSchema
|
||||
>;
|
||||
export type FetchWorkspacesResponseMessage = z.infer<typeof FetchWorkspacesResponseMessageSchema>;
|
||||
export type ProjectAddResponse = z.infer<typeof ProjectAddResponseSchema>;
|
||||
export type ScriptStatusUpdateMessage = z.infer<typeof ScriptStatusUpdateMessageSchema>;
|
||||
export type OpenProjectResponseMessage = z.infer<typeof OpenProjectResponseMessageSchema>;
|
||||
export type StartWorkspaceScriptResponseMessage = z.infer<
|
||||
@@ -4473,6 +4495,7 @@ export type LegacyListAvailableEditorsRequest = z.infer<
|
||||
>;
|
||||
export type LegacyOpenInEditorRequest = z.infer<typeof LegacyOpenInEditorRequestSchema>;
|
||||
export type OpenProjectRequest = z.infer<typeof OpenProjectRequestSchema>;
|
||||
export type ProjectAddRequest = z.infer<typeof ProjectAddRequestSchema>;
|
||||
export type ArchiveWorkspaceRequest = z.infer<typeof ArchiveWorkspaceRequestSchema>;
|
||||
export type WorkspaceClearAttentionRequest = z.infer<typeof WorkspaceClearAttentionRequestSchema>;
|
||||
export type FileExplorerRequest = z.infer<typeof FileExplorerRequestSchema>;
|
||||
|
||||
@@ -27,6 +27,50 @@ describe("workspace message schemas", () => {
|
||||
expect(parsed.type).toBe("fetch_workspaces_request");
|
||||
});
|
||||
|
||||
test("parses project.add request and response", () => {
|
||||
expect(
|
||||
SessionInboundMessageSchema.parse({
|
||||
type: "project.add.request",
|
||||
requestId: "req-add-project",
|
||||
cwd: "/repo",
|
||||
}),
|
||||
).toEqual({
|
||||
type: "project.add.request",
|
||||
requestId: "req-add-project",
|
||||
cwd: "/repo",
|
||||
});
|
||||
|
||||
expect(
|
||||
SessionOutboundMessageSchema.parse({
|
||||
type: "project.add.response",
|
||||
payload: {
|
||||
requestId: "req-add-project",
|
||||
project: {
|
||||
projectId: "/repo",
|
||||
projectDisplayName: "repo",
|
||||
projectCustomName: null,
|
||||
projectRootPath: "/repo",
|
||||
projectKind: "git",
|
||||
},
|
||||
error: null,
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
type: "project.add.response",
|
||||
payload: {
|
||||
requestId: "req-add-project",
|
||||
project: {
|
||||
projectId: "/repo",
|
||||
projectDisplayName: "repo",
|
||||
projectCustomName: null,
|
||||
projectRootPath: "/repo",
|
||||
projectKind: "git",
|
||||
},
|
||||
error: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("parses active-scoped fetch_agents_request as an optional extension", () => {
|
||||
const legacy = SessionInboundMessageSchema.parse({
|
||||
type: "fetch_agents_request",
|
||||
|
||||
Reference in New Issue
Block a user