mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
fix(projects): separate registration from workspace setup
Adding or cloning a project now registers it and opens workspace setup instead of creating a workspace implicitly. Keep Add Project independently mounted from Search so closing one cannot control the other.
This commit is contained in:
@@ -157,7 +157,7 @@ describe("project command-center protocol", () => {
|
||||
).toBe(" paseo ");
|
||||
});
|
||||
|
||||
it("keeps both feature flags optional for older server_info payloads", () => {
|
||||
it("keeps project command feature flags optional for older server_info payloads", () => {
|
||||
const parsed = parseServerInfoStatusPayload({
|
||||
status: "server_info",
|
||||
serverId: "server-old",
|
||||
@@ -165,6 +165,7 @@ describe("project command-center protocol", () => {
|
||||
});
|
||||
|
||||
expect(parsed.features?.workspaceGithubRepositorySearch).toBeUndefined();
|
||||
expect(parsed.features?.projectGithubClone).toBeUndefined();
|
||||
expect(parsed.features?.projectCreateDirectory).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1822,12 +1822,12 @@ export const WorkspaceGithubSearchRepositoriesRequestSchema = z.object({
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const WorkspaceGithubCloneProtocolSchema = z.enum(["https", "ssh"]);
|
||||
export const ProjectGithubCloneProtocolSchema = z.enum(["https", "ssh"]);
|
||||
|
||||
export const WorkspaceGithubCloneRequestSchema = z.object({
|
||||
type: z.literal("workspace.github.clone.request"),
|
||||
export const ProjectGithubCloneRequestSchema = z.object({
|
||||
type: z.literal("project.github.clone.request"),
|
||||
repo: z.string().trim().min(MIN_REPOSITORY_PATH_LENGTH),
|
||||
cloneProtocol: WorkspaceGithubCloneProtocolSchema.optional(),
|
||||
cloneProtocol: ProjectGithubCloneProtocolSchema.optional(),
|
||||
targetDirectory: z.string().trim().min(1),
|
||||
requestId: z.string(),
|
||||
});
|
||||
@@ -2218,7 +2218,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
|
||||
ProjectAddRequestSchema,
|
||||
ProjectCreateDirectoryRequestSchema,
|
||||
WorkspaceGithubSearchRepositoriesRequestSchema,
|
||||
WorkspaceGithubCloneRequestSchema,
|
||||
ProjectGithubCloneRequestSchema,
|
||||
ArchiveWorkspaceRequestSchema,
|
||||
WorkspaceCreateRequestSchema,
|
||||
WorkspaceClearAttentionRequestSchema,
|
||||
@@ -2463,8 +2463,8 @@ export const ServerInfoStatusPayloadSchema = z
|
||||
providerSubagents: z.boolean().optional(),
|
||||
// COMPAT(workspacePinning): added in v0.1.107, remove gate after 2027-01-12.
|
||||
workspacePinning: z.boolean().optional(),
|
||||
// COMPAT(workspaceGithubClone): added in v0.1.108, remove gate after 2027-01-13.
|
||||
workspaceGithubClone: z.boolean().optional(),
|
||||
// COMPAT(projectGithubClone): added in v0.1.108, remove gate after 2027-01-15.
|
||||
projectGithubClone: z.boolean().optional(),
|
||||
// COMPAT(workspaceGithubRepositorySearch): added in v0.1.108, remove gate after 2027-01-15.
|
||||
workspaceGithubRepositorySearch: z.boolean().optional(),
|
||||
// COMPAT(projectCreateDirectory): added in v0.1.108, remove gate after 2027-01-15.
|
||||
@@ -3024,13 +3024,13 @@ export const WorkspaceGithubSearchRepositoriesResponseSchema = z.object({
|
||||
]),
|
||||
});
|
||||
|
||||
export const WorkspaceGithubCloneResponseSchema = z.object({
|
||||
type: z.literal("workspace.github.clone.response"),
|
||||
export const ProjectGithubCloneResponseSchema = z.object({
|
||||
type: z.literal("project.github.clone.response"),
|
||||
payload: z.object({
|
||||
requestId: z.string(),
|
||||
repo: z.string().trim().min(MIN_REPOSITORY_PATH_LENGTH),
|
||||
checkoutPath: z.string().nullable(),
|
||||
workspace: WorkspaceDescriptorPayloadSchema.nullable(),
|
||||
project: WorkspaceProjectDescriptorPayloadSchema.nullable(),
|
||||
error: z.string().nullable(),
|
||||
}),
|
||||
});
|
||||
@@ -4459,7 +4459,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
||||
ProjectCreateDirectoryResponseSchema,
|
||||
OpenProjectResponseMessageSchema,
|
||||
WorkspaceGithubSearchRepositoriesResponseSchema,
|
||||
WorkspaceGithubCloneResponseSchema,
|
||||
ProjectGithubCloneResponseSchema,
|
||||
StartWorkspaceScriptResponseMessageSchema,
|
||||
LegacyListAvailableEditorsResponseMessageSchema,
|
||||
LegacyOpenInEditorResponseMessageSchema,
|
||||
@@ -4624,7 +4624,7 @@ export type WorkspaceGithubSearchRepositoriesResponse = z.infer<
|
||||
typeof WorkspaceGithubSearchRepositoriesResponseSchema
|
||||
>;
|
||||
export type GithubRepository = z.infer<typeof GithubRepositorySchema>;
|
||||
export type WorkspaceGithubCloneResponse = z.infer<typeof WorkspaceGithubCloneResponseSchema>;
|
||||
export type ProjectGithubCloneResponse = z.infer<typeof ProjectGithubCloneResponseSchema>;
|
||||
export type StartWorkspaceScriptResponseMessage = z.infer<
|
||||
typeof StartWorkspaceScriptResponseMessageSchema
|
||||
>;
|
||||
@@ -4878,8 +4878,8 @@ export type ProjectCreateDirectoryErrorCode = z.infer<typeof ProjectCreateDirect
|
||||
export type WorkspaceGithubSearchRepositoriesRequest = z.infer<
|
||||
typeof WorkspaceGithubSearchRepositoriesRequestSchema
|
||||
>;
|
||||
export type WorkspaceGithubCloneRequest = z.infer<typeof WorkspaceGithubCloneRequestSchema>;
|
||||
export type WorkspaceGithubCloneProtocol = z.infer<typeof WorkspaceGithubCloneProtocolSchema>;
|
||||
export type ProjectGithubCloneRequest = z.infer<typeof ProjectGithubCloneRequestSchema>;
|
||||
export type ProjectGithubCloneProtocol = z.infer<typeof ProjectGithubCloneProtocolSchema>;
|
||||
export type ArchiveWorkspaceRequest = z.infer<typeof ArchiveWorkspaceRequestSchema>;
|
||||
export type WorkspaceClearAttentionRequest = z.infer<typeof WorkspaceClearAttentionRequestSchema>;
|
||||
export type FileExplorerRequest = z.infer<typeof FileExplorerRequestSchema>;
|
||||
|
||||
@@ -286,36 +286,45 @@ describe("workspace message schemas", () => {
|
||||
expect(parsed.type).toBe("open_project_request");
|
||||
});
|
||||
|
||||
test("parses workspace GitHub clone request and response repo paths", () => {
|
||||
test("parses a GitHub clone response that registers a project without a workspace", () => {
|
||||
const request = SessionInboundMessageSchema.parse({
|
||||
type: "workspace.github.clone.request",
|
||||
type: "project.github.clone.request",
|
||||
repo: "a/b",
|
||||
cloneProtocol: "https",
|
||||
targetDirectory: "~/workspace",
|
||||
requestId: "req-clone",
|
||||
});
|
||||
const response = SessionOutboundMessageSchema.parse({
|
||||
type: "workspace.github.clone.response",
|
||||
type: "project.github.clone.response",
|
||||
payload: {
|
||||
requestId: "req-clone",
|
||||
repo: "a/b",
|
||||
checkoutPath: "/tmp/b",
|
||||
workspace: null,
|
||||
project: {
|
||||
projectId: "project-b",
|
||||
projectDisplayName: "b",
|
||||
projectRootPath: "/tmp/b",
|
||||
projectKind: "git",
|
||||
},
|
||||
error: null,
|
||||
},
|
||||
});
|
||||
|
||||
expect(request.type).toBe("workspace.github.clone.request");
|
||||
if (request.type !== "workspace.github.clone.request") {
|
||||
throw new Error("expected workspace.github.clone.request");
|
||||
expect(request.type).toBe("project.github.clone.request");
|
||||
if (request.type !== "project.github.clone.request") {
|
||||
throw new Error("expected project.github.clone.request");
|
||||
}
|
||||
expect(request.cloneProtocol).toBe("https");
|
||||
expect(response.type).toBe("workspace.github.clone.response");
|
||||
expect(response.type).toBe("project.github.clone.response");
|
||||
if (response.type !== "project.github.clone.response") {
|
||||
throw new Error("expected project.github.clone.response");
|
||||
}
|
||||
expect(response.payload.project?.projectId).toBe("project-b");
|
||||
});
|
||||
|
||||
test("rejects invalid workspace GitHub clone protocols", () => {
|
||||
test("rejects invalid project GitHub clone protocols", () => {
|
||||
const request = SessionInboundMessageSchema.safeParse({
|
||||
type: "workspace.github.clone.request",
|
||||
type: "project.github.clone.request",
|
||||
repo: "a/b",
|
||||
cloneProtocol: "ftp",
|
||||
targetDirectory: "~/workspace",
|
||||
@@ -325,20 +334,20 @@ describe("workspace message schemas", () => {
|
||||
expect(request.success).toBe(false);
|
||||
});
|
||||
|
||||
test("rejects workspace GitHub clone repo paths shorter than owner slash repo", () => {
|
||||
test("rejects project GitHub clone repo paths shorter than owner slash repo", () => {
|
||||
const request = SessionInboundMessageSchema.safeParse({
|
||||
type: "workspace.github.clone.request",
|
||||
type: "project.github.clone.request",
|
||||
repo: "ab",
|
||||
targetDirectory: "~/workspace",
|
||||
requestId: "req-clone",
|
||||
});
|
||||
const response = SessionOutboundMessageSchema.safeParse({
|
||||
type: "workspace.github.clone.response",
|
||||
type: "project.github.clone.response",
|
||||
payload: {
|
||||
requestId: "req-clone",
|
||||
repo: "ab",
|
||||
checkoutPath: null,
|
||||
workspace: null,
|
||||
project: null,
|
||||
error: "failed",
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user