mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
* feat(github): bind projects to a specific gh account Adds per-project `Repo.ghAccount` so repo-scoped gh calls (create-worktree issue/PR search, work items, hosted-review reads and mutations) run as the bound account via ephemeral child-env token injection instead of the globally active gh login. Multi-account resolution is capability-gated (gh >= 2.40) and fails closed when the bound account or host is unavailable; Project View stays ambient by design. Repository settings gains a section for selecting or clearing a keyring-backed account (shadcn `Select`), with mixed-version "not enforced" handling for older remote runtimes. Attached `-Rhost/owner/repo` forms are covered by the host-drift guard and its tests; es/ja/ko/zh catalogs carry the section's strings. `getLocalProjectGhExecOptions` centralizes the binding lookup so every gh execution path picks it up, including the Electron `hostedReview:*` handlers that previously stayed on the ambient login. `gh auth token` (a keyring read) is exempt from the rate-limit breaker gate so a tripped bucket cannot turn a bound-token resolve into a false "unavailable". The `ghAccount` update field and the two binding RPC methods live in the shared RPC params contract; the generated catalog is regenerated. Fixes #13612 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012B3QEP5iP4WGGEpPLtkHqA * fix(settings): make GitHub account refresh secondary * fix(github): satisfy strict casting quality checks * test(rpc): use runtime fixture for repo binding * fix(github): preserve project account for PR worktree lookups * test(rpc): avoid incomplete runtime settings fixture * fix(i18n): add GitHub account refresh label * fix(i18n): refresh runtime required catalog * fix(windows): preserve mobile patch bytes --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Neil <neil@stably.ai>
133 lines
4.3 KiB
TypeScript
133 lines
4.3 KiB
TypeScript
import type { Store } from './persistence'
|
|
import type { Repo } from '../shared/repo-types'
|
|
import { isFolderRepo } from '../shared/repo-kind'
|
|
import {
|
|
resolveLocalProjectRuntimeForRepo,
|
|
type ProjectRuntimeResolutionStore
|
|
} from './local-project-runtime-resolution'
|
|
import type { ProjectExecutionRuntimeResolution } from '../shared/project-execution-runtime'
|
|
|
|
export {
|
|
resolveLocalProjectRuntimeForRepo,
|
|
resolveLocalProjectRuntimesForRepos
|
|
} from './local-project-runtime-resolution'
|
|
|
|
export type LocalProjectGitExecOptions = {
|
|
cwd: string
|
|
wslDistro?: string
|
|
}
|
|
|
|
export type LocalProjectWorktreeGitOptions = {
|
|
wslDistro?: string
|
|
}
|
|
|
|
export type LocalProjectGhExecOptions = LocalProjectWorktreeGitOptions & {
|
|
ghAccount?: Repo['ghAccount']
|
|
}
|
|
|
|
export function getLocalProjectGitExecOptions(
|
|
store: Store,
|
|
repo: Repo
|
|
): LocalProjectGitExecOptions {
|
|
// Why: local git must run in the same resolved project runtime as agents,
|
|
// terminals, and preflight; repair states must not silently fall back to host git.
|
|
return getLocalProjectGitExecOptionsForRuntime(
|
|
repo,
|
|
resolveLocalProjectRuntimeForRepo(store, repo)
|
|
)
|
|
}
|
|
|
|
function getLocalProjectGitExecOptionsForRuntime(
|
|
repo: Repo,
|
|
projectRuntime: ProjectExecutionRuntimeResolution | undefined
|
|
): LocalProjectGitExecOptions {
|
|
if (!projectRuntime) {
|
|
return { cwd: repo.path }
|
|
}
|
|
if (projectRuntime.status === 'repair-required') {
|
|
throw new Error(
|
|
`Project runtime requires repair before git execution: ${projectRuntime.repair.reason}`
|
|
)
|
|
}
|
|
if (projectRuntime.runtime.kind === 'wsl') {
|
|
return { cwd: repo.path, wslDistro: projectRuntime.runtime.distro }
|
|
}
|
|
return { cwd: repo.path }
|
|
}
|
|
|
|
export function getLocalProjectWorktreeGitOptions(
|
|
store: Store,
|
|
repo: Repo
|
|
): LocalProjectWorktreeGitOptions {
|
|
const { wslDistro } = getLocalProjectGitExecOptions(store, repo)
|
|
return wslDistro ? { wslDistro } : {}
|
|
}
|
|
|
|
/**
|
|
* Execution options for repo-scoped gh calls: the project's WSL routing plus its account binding.
|
|
*
|
|
* Why: every gh call site must resolve options through here — one that reaches for
|
|
* `getLocalProjectWorktreeGitOptions` instead silently runs as the ambient login.
|
|
*/
|
|
export function getLocalProjectGhExecOptions(store: Store, repo: Repo): LocalProjectGhExecOptions {
|
|
return {
|
|
...getLocalProjectWorktreeGitOptions(store, repo),
|
|
...(repo.ghAccount ? { ghAccount: repo.ghAccount } : {})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Git routing for the speculative worktree-create warm-up.
|
|
*
|
|
* Deliberately non-throwing where `getLocalProjectWorktreeGitOptions` throws: an
|
|
* optimistic prefetch must not report a repair-required runtime as a failure, so
|
|
* an unresolved runtime falls back to the host Git the warm-up used before
|
|
* routing existed.
|
|
*/
|
|
export function getWorktreeCreatePrefetchGitOptions(
|
|
store: Store,
|
|
repo: Repo
|
|
): LocalProjectWorktreeGitOptions {
|
|
if (isFolderRepo(repo)) {
|
|
return {}
|
|
}
|
|
const projectRuntime = resolveLocalProjectRuntimeForRepo(store, repo)
|
|
if (!projectRuntime || projectRuntime.status !== 'resolved') {
|
|
return {}
|
|
}
|
|
return getLocalProjectWorktreeGitOptionsForRuntime(repo, projectRuntime)
|
|
}
|
|
|
|
export function getLocalProjectWorktreeGitOptionsForRuntime(
|
|
repo: Repo,
|
|
projectRuntime: ProjectExecutionRuntimeResolution | undefined
|
|
): LocalProjectWorktreeGitOptions {
|
|
// Why: callers that already batch-resolved project runtimes must not rescan
|
|
// every project once per repo on a polling path.
|
|
const { wslDistro } = getLocalProjectGitExecOptionsForRuntime(repo, projectRuntime)
|
|
return wslDistro ? { wslDistro } : {}
|
|
}
|
|
|
|
/**
|
|
* Distro whose filesystem this repo's worktrees belong on, or undefined.
|
|
*
|
|
* Deliberately non-throwing where `getLocalProjectGitExecOptions` throws: a
|
|
* runtime that needs repair must not block creating a worktree, it just falls
|
|
* back to the Windows-side placement that has always been used.
|
|
*/
|
|
export function getWorktreeMirrorDistro(
|
|
store: ProjectRuntimeResolutionStore,
|
|
repo: Repo
|
|
): string | undefined {
|
|
return getWorktreeMirrorDistroForRuntime(resolveLocalProjectRuntimeForRepo(store, repo))
|
|
}
|
|
|
|
export function getWorktreeMirrorDistroForRuntime(
|
|
projectRuntime: ProjectExecutionRuntimeResolution | undefined
|
|
): string | undefined {
|
|
if (!projectRuntime || projectRuntime.status !== 'resolved') {
|
|
return undefined
|
|
}
|
|
return projectRuntime.runtime.kind === 'wsl' ? projectRuntime.runtime.distro : undefined
|
|
}
|