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
@@ -286,6 +286,67 @@ describe("workspace message schemas", () => {
|
||||
expect(parsed.type).toBe("open_project_request");
|
||||
});
|
||||
|
||||
test("parses workspace GitHub clone request and response repo paths", () => {
|
||||
const request = SessionInboundMessageSchema.parse({
|
||||
type: "workspace.github.clone.request",
|
||||
repo: "a/b",
|
||||
cloneProtocol: "https",
|
||||
targetDirectory: "~/workspace",
|
||||
requestId: "req-clone",
|
||||
});
|
||||
const response = SessionOutboundMessageSchema.parse({
|
||||
type: "workspace.github.clone.response",
|
||||
payload: {
|
||||
requestId: "req-clone",
|
||||
repo: "a/b",
|
||||
checkoutPath: "/tmp/b",
|
||||
workspace: null,
|
||||
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.cloneProtocol).toBe("https");
|
||||
expect(response.type).toBe("workspace.github.clone.response");
|
||||
});
|
||||
|
||||
test("rejects invalid workspace GitHub clone protocols", () => {
|
||||
const request = SessionInboundMessageSchema.safeParse({
|
||||
type: "workspace.github.clone.request",
|
||||
repo: "a/b",
|
||||
cloneProtocol: "ftp",
|
||||
targetDirectory: "~/workspace",
|
||||
requestId: "req-clone",
|
||||
});
|
||||
|
||||
expect(request.success).toBe(false);
|
||||
});
|
||||
|
||||
test("rejects workspace GitHub clone repo paths shorter than owner slash repo", () => {
|
||||
const request = SessionInboundMessageSchema.safeParse({
|
||||
type: "workspace.github.clone.request",
|
||||
repo: "ab",
|
||||
targetDirectory: "~/workspace",
|
||||
requestId: "req-clone",
|
||||
});
|
||||
const response = SessionOutboundMessageSchema.safeParse({
|
||||
type: "workspace.github.clone.response",
|
||||
payload: {
|
||||
requestId: "req-clone",
|
||||
repo: "ab",
|
||||
checkoutPath: null,
|
||||
workspace: null,
|
||||
error: "failed",
|
||||
},
|
||||
});
|
||||
|
||||
expect(request.success).toBe(false);
|
||||
expect(response.success).toBe(false);
|
||||
});
|
||||
|
||||
test("parses legacy editor RPC messages for compatibility", () => {
|
||||
const listRequest = SessionInboundMessageSchema.parse({
|
||||
type: "list_available_editors_request",
|
||||
|
||||
Reference in New Issue
Block a user