Files
bot-hermes 0fc857959e feat(web,ui): per-provider git forge API base URL and detection URLs
Configure a default API base URL per git provider (github/gitlab/gitea)
in settings.json gitProviders, with GitHub Enterprise support (Octokit
baseUrl + device-flow web origin derived from the API base), and replace
the client-side custom-domains list with server-persisted detection URL
chips (SSH/HTTPS forms normalized to hosts). The configured API base host
auto-counts as a detection host. Settings round-trip through the existing
/api/config/settings sanitizer; the UI store hydrates from server settings
with a one-time localStorage migration.
2026-08-17 09:55:11 +00:00

71 lines
2.2 KiB
JavaScript

import { getRemoteUrl } from '../../git/index.js';
import { getProviderApiBaseUrl, githubWebOriginFromApiBase } from '../../git-providers/config.js';
const getGitHubWebOrigin = () => githubWebOriginFromApiBase(getProviderApiBaseUrl('github'));
const webHostFromOrigin = (webOrigin) => {
try {
return new URL(webOrigin).hostname || 'github.com';
} catch {
return 'github.com';
}
};
export const parseGitHubRemoteUrl = (raw, { host = 'github.com', webOrigin = 'https://github.com' } = {}) => {
if (typeof raw !== 'string') {
return null;
}
const value = raw.trim();
if (!value) {
return null;
}
// git@github.com:OWNER/REPO.git
const scpPrefix = `git@${host}:`;
if (value.startsWith(scpPrefix)) {
const rest = value.slice(scpPrefix.length);
const cleaned = rest.endsWith('.git') ? rest.slice(0, -4) : rest;
const [owner, repo] = cleaned.split('/');
if (!owner || !repo) return null;
return { owner, repo, url: `${webOrigin}/${owner}/${repo}` };
}
// ssh://git@github.com/OWNER/REPO.git
const sshPrefix = `ssh://git@${host}/`;
if (value.startsWith(sshPrefix)) {
const rest = value.slice(sshPrefix.length);
const cleaned = rest.endsWith('.git') ? rest.slice(0, -4) : rest;
const [owner, repo] = cleaned.split('/');
if (!owner || !repo) return null;
return { owner, repo, url: `${webOrigin}/${owner}/${repo}` };
}
// https://github.com/OWNER/REPO(.git)
try {
const url = new URL(value);
if (url.hostname !== host) {
return null;
}
const path = url.pathname.replace(/^\/+/, '').replace(/\/+$/, '');
const cleaned = path.endsWith('.git') ? path.slice(0, -4) : path;
const [owner, repo] = cleaned.split('/');
if (!owner || !repo) return null;
return { owner, repo, url: `${webOrigin}/${owner}/${repo}` };
} catch {
return null;
}
};
export async function resolveGitHubRepoFromDirectory(directory, remoteName = 'origin') {
const remoteUrl = await getRemoteUrl(directory, remoteName).catch(() => null);
if (!remoteUrl) {
return { repo: null, remoteUrl: null };
}
const webOrigin = getGitHubWebOrigin();
const host = webHostFromOrigin(webOrigin);
return {
repo: parseGitHubRemoteUrl(remoteUrl, { host, webOrigin }),
remoteUrl,
};
}