feat(git): add token authentication and credential discovery

Add support for token-based git authentication in identity profiles.
Enable discovery and import of credentials from ~/.git-credentials file.
Introduce remote URL-based filtering for token identities in git view.
This commit is contained in:
btriapitsyn
2026-01-14 02:47:26 +02:00
parent d56eef8aa6
commit fa83c1e645
16 changed files with 704 additions and 44 deletions
+12
View File
@@ -168,16 +168,25 @@ export interface GitPullResult {
deletions: number;
}
export type GitIdentityAuthType = 'ssh' | 'token';
export interface GitIdentityProfile {
id: string;
name: string;
userName: string;
userEmail: string;
authType?: GitIdentityAuthType;
sshKey?: string | null;
host?: string | null;
color?: string | null;
icon?: string | null;
}
export interface DiscoveredGitCredential {
host: string;
username: string;
}
export interface GitIdentitySummary {
userName: string | null;
userEmail: string | null;
@@ -290,6 +299,9 @@ export interface GitAPI {
createGitIdentity(profile: GitIdentityProfile): Promise<GitIdentityProfile>;
updateGitIdentity(id: string, updates: GitIdentityProfile): Promise<GitIdentityProfile>;
deleteGitIdentity(id: string): Promise<void>;
discoverGitCredentials?(): Promise<DiscoveredGitCredential[]>;
getGlobalGitIdentity?(): Promise<GitIdentitySummary | null>;
getRemoteUrl?(directory: string, remote?: string): Promise<string | null>;
}
export interface FileListEntry {
+20
View File
@@ -13,6 +13,7 @@ export type {
GitPushResult,
GitPullResult,
GitIdentityProfile,
GitIdentityAuthType,
GitIdentitySummary,
GitLogEntry,
GitLogResponse,
@@ -21,6 +22,7 @@ export type {
GitRemoveWorktreePayload,
GitDeleteBranchPayload,
GitDeleteRemoteBranchPayload,
DiscoveredGitCredential,
} from './api/types';
declare global {
@@ -245,3 +247,21 @@ export async function setGitIdentity(
if (runtime) return runtime.setGitIdentity(directory, profileId);
return gitHttp.setGitIdentity(directory, profileId);
}
export async function discoverGitCredentials(): Promise<import('./api/types').DiscoveredGitCredential[]> {
const runtime = getRuntimeGit();
if (runtime?.discoverGitCredentials) return runtime.discoverGitCredentials();
return gitHttp.discoverGitCredentials();
}
export async function getGlobalGitIdentity(): Promise<import('./api/types').GitIdentitySummary | null> {
const runtime = getRuntimeGit();
if (runtime?.getGlobalGitIdentity) return runtime.getGlobalGitIdentity();
return gitHttp.getGlobalGitIdentity();
}
export async function getRemoteUrl(directory: string, remote?: string): Promise<string | null> {
const runtime = getRuntimeGit();
if (runtime?.getRemoteUrl) return runtime.getRemoteUrl(directory, remote);
return gitHttp.getRemoteUrl(directory, remote);
}
+37
View File
@@ -22,6 +22,7 @@ import type {
GitCommitFilesResponse,
GitIdentityProfile,
GitIdentitySummary,
DiscoveredGitCredential,
} from './api/types';
declare global {
@@ -515,6 +516,22 @@ export async function getCurrentGitIdentity(directory: string): Promise<GitIdent
};
}
export async function getGlobalGitIdentity(): Promise<GitIdentitySummary | null> {
const response = await fetch(buildUrl(`${API_BASE}/global-identity`, undefined));
if (!response.ok) {
throw new Error(`Failed to get global git identity: ${response.statusText}`);
}
const data = await response.json();
if (!data || (!data.userName && !data.userEmail)) {
return null;
}
return {
userName: data.userName ?? null,
userEmail: data.userEmail ?? null,
sshCommand: data.sshCommand ?? null,
};
}
export async function setGitIdentity(
directory: string,
profileId: string
@@ -530,3 +547,23 @@ export async function setGitIdentity(
}
return response.json();
}
export async function discoverGitCredentials(): Promise<DiscoveredGitCredential[]> {
const response = await fetch(buildUrl(`${API_BASE}/discover-credentials`, undefined));
if (!response.ok) {
throw new Error(`Failed to discover git credentials: ${response.statusText}`);
}
return response.json();
}
export async function getRemoteUrl(directory: string, remote?: string): Promise<string | null> {
if (!directory) {
return null;
}
const response = await fetch(buildUrl(`${API_BASE}/remote-url`, directory, { remote }));
if (!response.ok) {
return null;
}
const data = await response.json();
return data.url ?? null;
}