openclaw / src /projects /project-git-url.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
253e783 verified
Raw History Blame Contribute Delete
1.81 kB
const GITHUB_PATH_SEGMENT = /^[A-Za-z0-9_.-]+$/u;
type ParsedProjectGitUrl = {
url: string;
name: string;
};
function githubPathParts(pathname: string): { owner: string; repo: string } | null {
const segments = pathname.split("/").filter(Boolean);
const owner = segments[0];
const repo = segments[1]?.replace(/\.git$/iu, "");
if (
segments.length !== 2 ||
!owner ||
!repo ||
!GITHUB_PATH_SEGMENT.test(owner) ||
!GITHUB_PATH_SEGMENT.test(repo) ||
owner === "." ||
owner === ".." ||
repo === "." ||
repo === ".."
) {
return null;
}
return { owner, repo };
}
/** Canonicalizes the GitHub clone forms accepted by projects.add. */
export function parseProjectGitUrl(raw: string): ParsedProjectGitUrl | null {
const trimmed = raw.trim();
if (!trimmed || trimmed.startsWith("-") || trimmed.includes("\0") || /[\r\n\t ]/u.test(trimmed)) {
return null;
}
const scp = /^git@github\.com:(.+)$/iu.exec(trimmed);
let parts: { owner: string; repo: string } | null;
if (scp) {
parts = githubPathParts(scp[1] ?? "");
} else {
try {
const url = new URL(trimmed);
const isHttps = url.protocol === "https:";
const isDefaultSsh =
url.protocol === "ssh:" && url.username === "git" && (!url.port || url.port === "22");
if (
(!isHttps && !isDefaultSsh) ||
url.hostname.toLowerCase() !== "github.com" ||
url.password ||
(isHttps && url.username) ||
url.search ||
url.hash
) {
return null;
}
parts = githubPathParts(url.pathname);
} catch {
return null;
}
}
if (!parts) {
return null;
}
return {
url: `https://github.com/${parts.owner.toLowerCase()}/${parts.repo.toLowerCase()}.git`,
name: parts.repo,
};
}