Add the experimental forge capability seam: - dsh-experimental-forge: provider-neutral repository and change request references, a provider registry, and shared status polling. Watchers of one change request share one loop; each forge has one batched fetch in flight, and the cadence follows check, mergeability, and lifecycle state. - dsh-experimental-forge-github: GitHub provider that runs the GitHub CLI on the host with a scrubbed environment and reads every due pull request of a host in one GraphQL query. - dsh-experimental-forge-session: a host-only projection derives Session bindings from gh pr create and git push shell results and from plugin:forge-session/binding records; push results trigger head-branch discovery of open pull requests, and Session watchers poll bound change requests through ctx.forge.
49 lines
2.0 KiB
TypeScript
49 lines
2.0 KiB
TypeScript
/** Registers the GitHub provider of `ctx.forge`, which reads pull requests through the GitHub CLI on the host. */
|
|
|
|
import type { Context } from '@deepseek-ai/cordis'
|
|
import z from '@deepseek-ai/schemastery'
|
|
import type {} from '@deepseek-ai/dsh-experimental-forge'
|
|
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
|
|
import { ghRunner } from './gh.ts'
|
|
import { GitHubProvider } from './provider.ts'
|
|
|
|
export const name = 'forge-github'
|
|
export const inject = ['forge']
|
|
|
|
/** GitHub CLI executable, served hosts, and invocation limits. */
|
|
export interface Config {
|
|
/** GitHub CLI executable name or absolute host path. */
|
|
ghCommand?: string
|
|
/** Host names this provider serves; list GitHub Enterprise hosts explicitly. */
|
|
hosts?: string[]
|
|
/** Deadline in milliseconds for one `gh` invocation. */
|
|
timeoutMs?: number
|
|
/** Maximum captured bytes per `gh` output stream. */
|
|
maxOutputBytes?: number
|
|
}
|
|
|
|
export const Config: z<Config> = z.object({
|
|
ghCommand: z.string().default('gh'),
|
|
hosts: z.array(z.string()).default(['github.com']),
|
|
timeoutMs: z.number().default(30_000),
|
|
maxOutputBytes: z.number().default(8_388_608),
|
|
})
|
|
|
|
const HOST = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*(:\d+)?$/i
|
|
|
|
export function apply(ctx: Context, config: Config): void {
|
|
const resolved = config as Required<Config>
|
|
if (resolved.ghCommand.trim().length === 0) throw new Error('forge-github: ghCommand must not be empty')
|
|
if (resolved.hosts.length === 0) throw new Error('forge-github: hosts must list at least one host')
|
|
for (const host of resolved.hosts) {
|
|
if (!HOST.test(host)) throw new Error(`forge-github: invalid host ${JSON.stringify(host)}`)
|
|
}
|
|
for (const key of ['timeoutMs', 'maxOutputBytes'] as const) {
|
|
const value = resolved[key]
|
|
if (!Number.isSafeInteger(value) || value <= 0 || value > MAX_TIMER_DELAY_MS) {
|
|
throw new Error(`forge-github: ${key} must be a positive integer no greater than ${MAX_TIMER_DELAY_MS}`)
|
|
}
|
|
}
|
|
ctx.forge.register(new GitHubProvider(ghRunner(resolved), resolved.hosts))
|
|
}
|