mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
* feat(native-chat): hover cards for GitHub PR and issue links Links to the chat's own repo (origin or upstream) open a card with the state, title, author, update time, and +/- or comment count. Bare #123 references in agent replies now link too. Cards use a light one-call lookup; older hosts ignore the flag and answer with the full lookup. * test(github): expect the preview options argument in runtime owner/repo lookups * fix(native-chat): route GitHub link cards by the chat's own host and retry failed repo lookups The repo lookup took the first worktree with the chat's id, so a twin on another host could answer, and the preview cache keyed by repo id alone. A failed slug lookup also left cards off until the chat remounted.
181 lines
5.5 KiB
TypeScript
181 lines
5.5 KiB
TypeScript
import type { ListWorkItemsResult } from '../../shared/github/work-item-types'
|
|
import type { Repo } from '../../shared/repo-types'
|
|
import type { LocalProjectGhExecOptions } from '../project-runtime-git-options'
|
|
import {
|
|
countWorkItems,
|
|
getWorkItem,
|
|
getWorkItemByOwnerRepo,
|
|
listAssignableUsers,
|
|
listIssues,
|
|
listLabels,
|
|
listWorkItems,
|
|
type MainWorkItem
|
|
} from '../github/client'
|
|
import {
|
|
listGhAccountBindingInventory,
|
|
validateGhAccountBinding
|
|
} from '../github/gh-account-binding-inventory'
|
|
import { getRateLimit } from '../github/rate-limit'
|
|
import { resolveGhAccountRequiredHost } from '../github/account-binding-required-host'
|
|
import { getWorkItemDetails } from '../github/work-item-details'
|
|
import { getRepoRoutingConnectionId } from '../../shared/execution-host'
|
|
|
|
type LocalGitArgs = [] | [LocalProjectGhExecOptions]
|
|
|
|
type RuntimeGitHubRepositoryQueryCommandsDeps = {
|
|
resolveRepo: (selector: string) => Promise<Repo>
|
|
getLocalGitArgs: (repo: Repo) => LocalGitArgs
|
|
}
|
|
|
|
export class RuntimeGitHubRepositoryQueryCommands {
|
|
constructor(private readonly deps: RuntimeGitHubRepositoryQueryCommandsDeps) {}
|
|
|
|
async listRepoWorkItems(
|
|
repoSelector: string,
|
|
limit?: number,
|
|
query?: string,
|
|
page?: number,
|
|
noCache?: boolean
|
|
): Promise<ListWorkItemsResult<MainWorkItem>> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
return listWorkItems(
|
|
repo.path,
|
|
limit,
|
|
query,
|
|
page,
|
|
repo.issueSourcePreference,
|
|
getRepoRoutingConnectionId(repo),
|
|
noCache,
|
|
...this.deps.getLocalGitArgs(repo)
|
|
)
|
|
}
|
|
|
|
async listRepoIssues(
|
|
repoSelector: string,
|
|
limit?: number
|
|
): Promise<Awaited<ReturnType<typeof listIssues>>['items']> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
const result = await listIssues(
|
|
repo.path,
|
|
limit,
|
|
repo.issueSourcePreference,
|
|
getRepoRoutingConnectionId(repo),
|
|
...this.deps.getLocalGitArgs(repo)
|
|
)
|
|
return result.items
|
|
}
|
|
|
|
async getRepoWorkItem(
|
|
repoSelector: string,
|
|
number: number,
|
|
type?: 'issue' | 'pr'
|
|
): Promise<Awaited<ReturnType<typeof getWorkItem>>> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
return getWorkItem(
|
|
repo.path,
|
|
number,
|
|
type,
|
|
getRepoRoutingConnectionId(repo),
|
|
this.deps.getLocalGitArgs(repo)[0] ?? {},
|
|
repo.issueSourcePreference
|
|
)
|
|
}
|
|
|
|
async getRepoWorkItemByOwnerRepo(
|
|
repoSelector: string,
|
|
ownerRepo: { owner: string; repo: string; host?: string },
|
|
number: number,
|
|
type: 'issue' | 'pr',
|
|
options: { preview?: boolean } = {}
|
|
): Promise<Awaited<ReturnType<typeof getWorkItemByOwnerRepo>>> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
return getWorkItemByOwnerRepo(
|
|
repo.path,
|
|
ownerRepo,
|
|
number,
|
|
type,
|
|
getRepoRoutingConnectionId(repo),
|
|
this.deps.getLocalGitArgs(repo)[0],
|
|
options
|
|
)
|
|
}
|
|
|
|
async getRepoWorkItemDetails(
|
|
repoSelector: string,
|
|
number: number,
|
|
type?: 'issue' | 'pr'
|
|
): Promise<Awaited<ReturnType<typeof getWorkItemDetails>>> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
return getWorkItemDetails(
|
|
repo.path,
|
|
number,
|
|
type,
|
|
getRepoRoutingConnectionId(repo),
|
|
this.deps.getLocalGitArgs(repo)[0] ?? {},
|
|
repo.issueSourcePreference
|
|
)
|
|
}
|
|
|
|
async countRepoWorkItems(repoSelector: string, query?: string): Promise<number> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
return countWorkItems(
|
|
repo.path,
|
|
query,
|
|
repo.issueSourcePreference,
|
|
getRepoRoutingConnectionId(repo),
|
|
...this.deps.getLocalGitArgs(repo)
|
|
)
|
|
}
|
|
|
|
async listRepoLabels(repoSelector: string): Promise<Awaited<ReturnType<typeof listLabels>>> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
return listLabels(
|
|
repo.path,
|
|
repo.issueSourcePreference,
|
|
getRepoRoutingConnectionId(repo),
|
|
...this.deps.getLocalGitArgs(repo)
|
|
)
|
|
}
|
|
|
|
async listRepoAssignableUsers(
|
|
repoSelector: string
|
|
): Promise<Awaited<ReturnType<typeof listAssignableUsers>>> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
return listAssignableUsers(
|
|
repo.path,
|
|
repo.issueSourcePreference,
|
|
getRepoRoutingConnectionId(repo),
|
|
...this.deps.getLocalGitArgs(repo)
|
|
)
|
|
}
|
|
|
|
getGitHubRateLimit(options?: {
|
|
force?: boolean
|
|
}): Promise<Awaited<ReturnType<typeof getRateLimit>>> {
|
|
return getRateLimit(options)
|
|
}
|
|
|
|
async listGitHubBindableAccounts(
|
|
repoSelector: string,
|
|
options?: { refreshCapability?: boolean }
|
|
): Promise<Awaited<ReturnType<typeof listGhAccountBindingInventory>>> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
const localGitOptions = this.deps.getLocalGitArgs(repo)[0] ?? {}
|
|
const requiredHost = await resolveGhAccountRequiredHost(repo, localGitOptions)
|
|
return listGhAccountBindingInventory(localGitOptions, {
|
|
refreshCapability: options?.refreshCapability === true,
|
|
...(requiredHost ? { requiredHost } : {})
|
|
})
|
|
}
|
|
|
|
async validateGitHubAccountBinding(
|
|
repoSelector: string,
|
|
binding: { host: string; user: string }
|
|
): Promise<Awaited<ReturnType<typeof validateGhAccountBinding>>> {
|
|
const repo = await this.deps.resolveRepo(repoSelector)
|
|
const localGitOptions = this.deps.getLocalGitArgs(repo)[0] ?? {}
|
|
const requiredHost = await resolveGhAccountRequiredHost(repo, localGitOptions)
|
|
return validateGhAccountBinding(binding, localGitOptions, requiredHost ? { requiredHost } : {})
|
|
}
|
|
}
|