feat: clone GitHub repo into a workspace (#1331)
* feat: clone GitHub repo into a workspace Add an end-to-end "clone a GitHub repo and register it as a Paseo workspace" flow: a new workspace.github.clone RPC, daemon handler, client method, CLI `paseo clone` command, and a GitHub-repo mode in the project picker modal. Gated behind the workspaceGithubClone server capability flag. - protocol: workspace.github.clone request/response schemas + feature flag - server: handleWorkspaceGithubCloneRequest, normalizeCloneRepository - client: DaemonClient.cloneGithubWorkspace - cli: `paseo clone <repo> --dir <path> [--protocol https|ssh]` - app: GitHub-repo mode, clone-protocol picker, error surfacing Review fixes: - CLI clone now checks the workspaceGithubClone capability and fails fast with a clear "update the host" error instead of hanging for the full request timeout against an older daemon. - Replace the duplicated client-side URL-detection regex (app + CLI) with a shared isCompleteGitRemote() in @getpaseo/protocol/git-remote, backed by parseGitRemoteLocation so clients classify remotes identically to the daemon (fixes confusing errors for git://, ftp://, file:// inputs). - Add git-remote.test.ts covering the shared classifier. * Fix GitHub clone failure handling --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
This commit is contained in:
co-authored by
Mohamed Boudra
parent
bac302626f
commit
218097b7cc
@@ -1794,6 +1794,18 @@ export const ProjectAddRequestSchema = z.object({
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
// Smallest shorthand repo path is "a/b": owner, slash, repository.
|
||||
const MIN_REPOSITORY_PATH_LENGTH = 3;
|
||||
export const WorkspaceGithubCloneProtocolSchema = z.enum(["https", "ssh"]);
|
||||
|
||||
export const WorkspaceGithubCloneRequestSchema = z.object({
|
||||
type: z.literal("workspace.github.clone.request"),
|
||||
repo: z.string().trim().min(MIN_REPOSITORY_PATH_LENGTH),
|
||||
cloneProtocol: WorkspaceGithubCloneProtocolSchema.optional(),
|
||||
targetDirectory: z.string().trim().min(1),
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const ArchiveWorkspaceRequestSchema = z.object({
|
||||
type: z.literal("archive_workspace_request"),
|
||||
workspaceId: z.string(),
|
||||
@@ -2178,6 +2190,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
|
||||
LegacyOpenInEditorRequestSchema,
|
||||
OpenProjectRequestSchema,
|
||||
ProjectAddRequestSchema,
|
||||
WorkspaceGithubCloneRequestSchema,
|
||||
ArchiveWorkspaceRequestSchema,
|
||||
WorkspaceCreateRequestSchema,
|
||||
WorkspaceClearAttentionRequestSchema,
|
||||
@@ -2420,6 +2433,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(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
@@ -2918,6 +2933,17 @@ export const ProjectAddResponseSchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
export const WorkspaceGithubCloneResponseSchema = z.object({
|
||||
type: z.literal("workspace.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(),
|
||||
error: z.string().nullable(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const StartWorkspaceScriptResponseMessageSchema = z.object({
|
||||
type: z.literal("start_workspace_script_response"),
|
||||
payload: z.object({
|
||||
@@ -4338,6 +4364,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
||||
FetchWorkspacesResponseMessageSchema,
|
||||
ProjectAddResponseSchema,
|
||||
OpenProjectResponseMessageSchema,
|
||||
WorkspaceGithubCloneResponseSchema,
|
||||
StartWorkspaceScriptResponseMessageSchema,
|
||||
LegacyListAvailableEditorsResponseMessageSchema,
|
||||
LegacyOpenInEditorResponseMessageSchema,
|
||||
@@ -4497,6 +4524,7 @@ export type FetchWorkspacesResponseMessage = z.infer<typeof FetchWorkspacesRespo
|
||||
export type ProjectAddResponse = z.infer<typeof ProjectAddResponseSchema>;
|
||||
export type ScriptStatusUpdateMessage = z.infer<typeof ScriptStatusUpdateMessageSchema>;
|
||||
export type OpenProjectResponseMessage = z.infer<typeof OpenProjectResponseMessageSchema>;
|
||||
export type WorkspaceGithubCloneResponse = z.infer<typeof WorkspaceGithubCloneResponseSchema>;
|
||||
export type StartWorkspaceScriptResponseMessage = z.infer<
|
||||
typeof StartWorkspaceScriptResponseMessageSchema
|
||||
>;
|
||||
@@ -4745,6 +4773,8 @@ 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 WorkspaceGithubCloneRequest = z.infer<typeof WorkspaceGithubCloneRequestSchema>;
|
||||
export type WorkspaceGithubCloneProtocol = z.infer<typeof WorkspaceGithubCloneProtocolSchema>;
|
||||
export type ArchiveWorkspaceRequest = z.infer<typeof ArchiveWorkspaceRequestSchema>;
|
||||
export type WorkspaceClearAttentionRequest = z.infer<typeof WorkspaceClearAttentionRequestSchema>;
|
||||
export type FileExplorerRequest = z.infer<typeof FileExplorerRequestSchema>;
|
||||
|
||||
Reference in New Issue
Block a user