Spaces:
Sleeping
Sleeping
| import { runCommand, runJsonCommand } from './command.js'; | |
| import { recordAuditEvent } from './audit.js'; | |
| export async function getProfile() { | |
| const profile = await runJsonCommand<{ | |
| login: string; | |
| id: number; | |
| name: string | null; | |
| company: string | null; | |
| blog: string | null; | |
| location: string | null; | |
| bio: string | null; | |
| twitter_username: string | null; | |
| hireable: boolean | null; | |
| public_repos: number; | |
| followers: number; | |
| following: number; | |
| html_url: string; | |
| }>('gh', ['api', 'user']); | |
| return { ok: true, profile }; | |
| } | |
| export async function updateProfile(input: { | |
| name?: string; | |
| company?: string; | |
| blog?: string; | |
| location?: string; | |
| bio?: string; | |
| twitter_username?: string; | |
| hireable?: boolean; | |
| }) { | |
| const args = ['api', 'user', '--method', 'PATCH']; | |
| if (input.name !== undefined) args.push('-f', `name=${input.name}`); | |
| if (input.company !== undefined) args.push('-f', `company=${input.company}`); | |
| if (input.blog !== undefined) args.push('-f', `blog=${input.blog}`); | |
| if (input.location !== undefined) args.push('-f', `location=${input.location}`); | |
| if (input.bio !== undefined) args.push('-f', `bio=${input.bio}`); | |
| if (input.twitter_username !== undefined) args.push('-f', `twitter_username=${input.twitter_username}`); | |
| if (input.hireable !== undefined) args.push('-F', `hireable=${input.hireable}`); | |
| const result = await runJsonCommand<object>('gh', args); | |
| await recordAuditEvent({ kind: 'profile.update', result }); | |
| return { ok: true, profile: result }; | |
| } | |
| export async function listPinnedRepos() { | |
| const query = ` | |
| query { | |
| user(login: "Delqhi") { | |
| pinnedItems(first: 6, types: REPOSITORY) { | |
| nodes { | |
| ... on Repository { | |
| id | |
| name | |
| description | |
| url | |
| isFork | |
| stargazerCount | |
| primaryLanguage { name } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| `.replace(/\s+/g, ' '); | |
| const response = await runJsonCommand<{ data: { user: { pinnedItems: { nodes: Array<{ id: string; name: string; description: string | null; url: string; isFork: boolean; stargazerCount: number; primaryLanguage: { name: string } | null }> } } } }>('gh', ['api', 'graphql', '-f', `query=${query}`]); | |
| const nodes = response.data?.user?.pinnedItems?.nodes ?? []; | |
| return { ok: true, pinned: nodes }; | |
| } | |
| export async function setPinnedRepos(repoIds?: string[]) { | |
| const current = await listPinnedRepos(); | |
| const existingIds = new Set(current.pinned.map((r) => r.id)); | |
| for (const existingId of existingIds) { | |
| try { | |
| await runCommand('gh', ['api', 'graphql', '-f', `query=mutation { unpinRepository(input: {repositoryId: "${existingId}"}) { repository { name } } }`], undefined); | |
| } catch {} | |
| } | |
| const desiredIds = repoIds ?? []; | |
| for (const repoId of desiredIds) { | |
| await runCommand('gh', ['api', 'graphql', '-f', `query=mutation { pinRepository(input: {repositoryId: "${repoId}"}) { repository { name } } }`], undefined); | |
| } | |
| await recordAuditEvent({ kind: 'profile.pinned_repos.set', result: { repoIds: desiredIds } }); | |
| return { ok: true, pinned: desiredIds }; | |
| } | |
| export async function updateRepoMetadata(input: { repo: string; description?: string; homepage?: string; topics?: string[]; visibility?: 'public' | 'private' }) { | |
| const args = ['api', `repos/${input.repo}`, '--method', 'PATCH']; | |
| if (input.description !== undefined) args.push('-f', `description=${input.description}`); | |
| if (input.homepage !== undefined) args.push('-f', `homepage=${input.homepage}`); | |
| if (input.visibility !== undefined) args.push('-f', `visibility=${input.visibility}`); | |
| await runCommand('gh', args, undefined); | |
| if (input.topics && input.topics.length > 0) { | |
| const topicArgs = ['api', `repos/${input.repo}/topics`, '--method', 'PUT', '-H', 'Accept: application/vnd.github+json']; | |
| for (const topic of input.topics) { | |
| topicArgs.push('-F', `names[]=${topic}`); | |
| } | |
| await runCommand('gh', topicArgs, undefined); | |
| } | |
| await recordAuditEvent({ kind: 'repo.metadata.update', result: input }); | |
| return { ok: true, repo: input.repo }; | |
| } | |
| export async function triggerQuickdraw(repo?: string) { | |
| const target = repo ?? 'Delqhi/Delqhi'; | |
| const issue = await runJsonCommand<{ number: number; html_url: string }>('gh', ['issue', 'create', '--repo', target, '--title', 'Quickdraw profile check', '--body', 'Triggering Quickdraw achievement via issue lifecycle.']); | |
| await runCommand('gh', ['issue', 'close', String(issue.number), '--repo', target, '--comment', 'Closed for Quickdraw verification.'], undefined); | |
| await recordAuditEvent({ kind: 'achievement.quickdraw.trigger', result: { repo: target, issueNumber: issue.number } }); | |
| return { ok: true, repo: target, issueNumber: issue.number, issueUrl: issue.html_url }; | |
| } | |
| export async function quickdrawStatus() { | |
| const issues = await runJsonCommand<Array<{ number: number; state: string; closed_at: string | null }>>('gh', ['issue', 'list', '--repo', 'Delqhi/Delqhi', '--state', 'all', '--limit', '10', '--json', 'number,state,closed_at']); | |
| const closed = issues.filter((i) => i.state === 'closed' && i.closed_at).length; | |
| return { ok: true, totalClosed: closed, likelyQuickdraw: closed >= 1 }; | |
| } | |