dsh-git-ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../src/host/index.ts", "../../src/host/git.ts", "../../src/host/parser.ts", "../../src/host/core.ts", "../../src/host/actions.ts", "../../src/host/queries.ts"],
4
- "sourcesContent": ["/**\n * dsh-git-ui host half: the `gitInfo` Remote service.\n *\n * Cordis shell only \u2014 every behavior lives in `core.ts`/`actions.ts`/\n * `queries.ts` behind injected structural faces, so tests never need a\n * cordis runtime. The class is a plugin in its own right (class form),\n * mounted by the bundle patch row with the package name; the gateway exposes\n * `gitInfo/snapshot`, `gitInfo/run` and `gitInfo/query` through SRC\n * discovery (`typertRemote` binding + `@Remote` marker).\n */\nimport { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { realpath, stat } from 'node:fs/promises'\nimport { createGitRunner, type SubprocessLike } from './git.ts'\nimport { normalizeConfig, snapshotForSession, type GitStatusConfig, type SnapshotDeps } from './core.ts'\nimport { runAction } from './actions.ts'\nimport { runQuery } from './queries.ts'\nimport type { GitActionResult, GitActionRequest, GitQueryRequest, GitQueryResponse, GitSnapshotRequest, GitSnapshotResult } from './types.ts'\n\nexport type { GitSnapshot, GitSnapshotResult, GitSnapshotFailure, GitSnapshotRequest, GitCommit, GitChange, GitAction, GitActionResult, GitActionRequest, GitQuery, GitQueryResult, GitQueryRequest, GitQueryResponse, GitBranch, GitFileStat, GitRef } from './types.ts'\nexport { normalizeConfig, DEFAULT_CONFIG } from './core.ts'\nexport { parseStatusOutput, parseLogOutput, parseBranchOutput, parseNameStatusOutput } from './parser.ts'\nexport { isSafePath, isValidBranchName, runAction } from './actions.ts'\nexport { runQuery } from './queries.ts'\n\n/** Structural face of a live session header. */\ninterface SessionLike {\n readonly header?: { readonly cwd?: string }\n}\n\n/** Structural face of the sessions service. */\ninterface SessionsLike {\n get(id: string): SessionLike | undefined\n}\n\n/** Structural face of the session-persistence service. */\ninterface SessionPersistenceLike {\n inspect(id: string): Promise<{ readonly meta: { readonly cwd?: string } }>\n}\n\n/** The `gitInfo` service: `snapshot` (read) and `run` (management) endpoints. */\nexport class GitStatusService extends TypertRemoteService {\n static inject = ['subprocess', 'sessions', 'sessionPersistence']\n\n private readonly config: GitStatusConfig\n\n constructor(ctx: Context, config: unknown) {\n super(ctx, 'gitInfo')\n this.config = normalizeConfig(config)\n }\n\n /** Adapter face shared by both endpoints (injected services + runner). */\n private deps(signal?: AbortSignal): { readonly deps: SnapshotDeps } | { readonly failure: { readonly code: 'git-unavailable'; readonly detail: string } } {\n const subprocess = this.ctx.get('subprocess') as SubprocessLike | undefined\n if (subprocess === undefined) {\n return { failure: { code: 'git-unavailable', detail: 'subprocess service unavailable' } }\n }\n const sessions = this.ctx.get('sessions') as SessionsLike | undefined\n const persistence = this.ctx.get('sessionPersistence') as SessionPersistenceLike | undefined\n const runner = createGitRunner(subprocess, this.config.timeoutMs, this.config.maxStatusBytes)\n return {\n deps: {\n run: runner,\n fs: { realpath, stat },\n sessions: {\n liveCwd: (id) => sessions?.get(id)?.header?.cwd,\n persistedMeta: async (id) => {\n if (persistence === undefined) return undefined\n try {\n const inspection = await persistence.inspect(id)\n return { cwd: inspection.meta.cwd }\n } catch {\n return undefined\n }\n },\n },\n signal,\n },\n }\n }\n\n @Remote('snapshot')\n async snapshot(request: GitSnapshotRequest, signal?: AbortSignal): Promise<GitSnapshotResult> {\n const adapted = this.deps(signal)\n if ('failure' in adapted) return { ok: false, error: adapted.failure }\n return snapshotForSession(adapted.deps, this.config, request.sessionId)\n }\n\n @Remote('run')\n async run(request: GitActionRequest, signal?: AbortSignal): Promise<GitActionResult> {\n const adapted = this.deps(signal)\n if ('failure' in adapted) {\n return { ok: false, error: { code: 'git-error', message: adapted.failure.detail } }\n }\n return runAction(adapted.deps, this.config, request)\n }\n\n @Remote('query')\n async query(request: GitQueryRequest, signal?: AbortSignal): Promise<GitQueryResponse> {\n const adapted = this.deps(signal)\n if ('failure' in adapted) {\n return { ok: false, error: { code: 'git-error', message: adapted.failure.detail } }\n }\n return runQuery(adapted.deps, this.config, request)\n }\n}\n\nexport default GitStatusService\n", "/**\n * Git command execution adapter over the host subprocess service.\n *\n * The widget only needs a tiny slice of the subprocess contract; declaring it\n * structurally here (instead of depending on the npm package, whose registry\n * chain is incomplete) keeps the plugin buildable standalone while remaining\n * wire-compatible with the host's `subprocess` service.\n */\nimport { readFile } from 'node:fs/promises'\n\n/** One collected stream disposition (matches the host SubprocessCollect). */\ninterface CollectDisposition {\n readonly collect: {\n readonly maxBytes: number\n /**\n * Spill disposition: when the stream overflows the in-memory tail, the\n * host appends the COMPLETE stream to a private spill file (up to this\n * cap) and `readFrom` reports its path. Without it, only the tail is\n * ever retained and the head (and its change counts) is lost.\n */\n readonly spill?: { readonly maxBytes: number }\n }\n}\n\n/** Structural slice of the host subprocess spawn spec. */\ninterface SpawnSpec {\n readonly argv: readonly string[]\n readonly cwd: string\n readonly stdio: {\n readonly stdout: CollectDisposition\n readonly stderr: CollectDisposition\n }\n readonly graceMs: number\n readonly signal?: AbortSignal\n}\n\n/** Structural slice of the host subprocess handle (collect-mode output). */\ninterface SpawnHandle {\n readonly done: Promise<{ readonly exitCode: number | null; readonly signal: NodeJS.Signals | null }>\n readonly collected: {\n readonly stdout?: {\n readFrom(fromByte: number): { readonly text: string; readonly lossy: boolean; readonly spillPath?: string }\n }\n readonly stderr?: {\n readFrom(fromByte: number): { readonly text: string; readonly lossy: boolean; readonly spillPath?: string }\n }\n }\n}\n\n/** Minimal subprocess-service face the adapter consumes. */\nexport interface SubprocessLike {\n spawn(spec: SpawnSpec): SpawnHandle\n}\n\n/** One git command outcome. */\nexport interface GitRunResult {\n /** Process exit code; null when terminated by a signal. */\n readonly exitCode: number | null\n readonly stdout: string\n readonly stderr: string\n /** True when the run was killed by our timeout (or the caller's signal). */\n readonly timedOut: boolean\n /**\n * True when the final stdout text is still incomplete: the collected\n * output overflowed its byte cap AND the spill file was unavailable (no\n * spill configured on the host, or the spill cap also overflowed).\n */\n readonly stdoutLossy: boolean\n}\n\n/** The run primitive the snapshot orchestration uses. */\nexport interface GitRunner {\n run(argv: readonly string[], opts: { readonly cwd: string; readonly signal?: AbortSignal }): Promise<GitRunResult>\n}\n\n/**\n * Adapt the host subprocess service into a `GitRunner` with a per-command\n * timeout. A timed-out run resolves (never rejects) with `timedOut: true`;\n * only spawn-level failures (e.g. git not installed) reject.\n *\n * Overflow handling: stdout/stderr collect with a spill cap of\n * `maxBytes * 16` (default 4 MiB memory tail \u2192 64 MiB spill file). When the\n * tail overflowed but the spill file holds the complete stream, the runner\n * reads the file and reports `stdoutLossy: false` \u2014 the change COUNTS stay\n * exact. `stdoutLossy: true` is reserved for the doubly-overflowed case\n * (spill also exceeded), where the head is genuinely lost.\n */\nexport function createGitRunner(subprocess: SubprocessLike, timeoutMs: number, maxBytes: number): GitRunner {\n const spillMaxBytes = maxBytes * 16\n return {\n async run(argv, opts) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const signal = opts.signal === undefined\n ? controller.signal\n : AbortSignal.any([controller.signal, opts.signal])\n const handle = subprocess.spawn({\n argv,\n cwd: opts.cwd,\n stdio: {\n stdout: { collect: { maxBytes, spill: { maxBytes: spillMaxBytes } } },\n stderr: { collect: { maxBytes, spill: { maxBytes: spillMaxBytes } } },\n },\n graceMs: 200,\n signal,\n })\n let outcome: Awaited<SpawnHandle['done']>\n try {\n // `done` rejects for spawn-level failures; an abort-triggered\n // rejection is the timeout path and resolves as timedOut.\n outcome = await handle.done\n } catch (error) {\n if (controller.signal.aborted || opts.signal?.aborted === true) {\n return { exitCode: null, stdout: '', stderr: '', timedOut: true, stdoutLossy: false }\n }\n throw error\n }\n const stdout = handle.collected.stdout?.readFrom(0)\n const stderr = handle.collected.stderr?.readFrom(0)\n const stdoutResolved = await resolveStdout(stdout)\n return {\n exitCode: outcome.exitCode,\n stdout: stdoutResolved.text,\n stderr: stderr?.text ?? '',\n timedOut: controller.signal.aborted || opts.signal?.aborted === true,\n stdoutLossy: stdoutResolved.lossy,\n }\n } finally {\n clearTimeout(timer)\n }\n },\n }\n}\n\n/**\n * Resolve the stdout text from a collect read: the in-memory tail, or \u2014 when\n * the read is lossy and the host spilled the complete stream to a file \u2014 the\n * spill file contents (so change COUNTS stay exact). A failed spill read\n * falls back to the tail and keeps `lossy: true` (head genuinely lost).\n */\nasync function resolveStdout(\n read: { readonly text: string; readonly lossy: boolean; readonly spillPath?: string } | undefined,\n): Promise<{ readonly text: string; readonly lossy: boolean }> {\n if (read === undefined) return { text: '', lossy: false }\n if (!read.lossy || read.spillPath === undefined) return { text: read.text, lossy: read.lossy }\n try {\n return { text: await readFile(read.spillPath, 'utf8'), lossy: false }\n } catch {\n return { text: read.text, lossy: true }\n }\n}\n", "/**\n * Pure parsers for the git porcelain/log output shapes used by the widget.\n * No side effects and no I/O \u2014 fully unit-testable against literal fixtures\n * (verified against real `git status --porcelain=v1 -z --branch` output).\n */\nimport type { GitChange, GitChangeStatus, GitCommit, GitRef, GraphCommit } from './types.ts'\n\n/** Parsed status counts plus the (possibly capped) change list. */\nexport interface ParsedStatus {\n readonly branch: string | null\n readonly unborn: boolean\n readonly staged: number\n readonly modified: number\n readonly untracked: number\n readonly ahead: number\n readonly behind: number\n readonly changes: readonly GitChange[]\n readonly truncated: boolean\n}\n\n/** The NUL byte separating porcelain v1 -z entries. */\nconst NUL = '\\u0000'\n/** The unit separator used by the log --format payload. */\nconst LOG_SEP = '\\u001f'\n\ninterface StatusHeader {\n readonly branch: string | null\n readonly unborn: boolean\n readonly ahead: number\n readonly behind: number\n}\n\n/**\n * Parse the `## ` header line of `git status --porcelain=v1 -z --branch`.\n * Recognized shapes (verified against git 2.x):\n * `## main`\n * `## main...origin/main`\n * `## main...origin/main [ahead 1]`\n * `## main...origin/main [behind 2]`\n * `## main...origin/main [ahead 1, behind 2]`\n * `## HEAD (no branch)` (detached)\n * `## HEAD (detached at <hash>)` (detached, older git)\n * `## No commits yet on main` (unborn)\n * `## Initial commit on main` (unborn, older git)\n */\nexport function parseStatusHeader(line: string): StatusHeader {\n const body = line.startsWith('## ') ? line.slice(3) : line\n if (body === '') return { branch: null, unborn: false, ahead: 0, behind: 0 }\n\n const unbornMatch = /^(?:No commits yet on|Initial commit on)\\s+(.+)$/.exec(body)\n if (unbornMatch !== null) {\n return { branch: unbornMatch[1] ?? null, unborn: true, ahead: 0, behind: 0 }\n }\n\n const detached = /^HEAD(?:\\s+\\([^)]*\\))?$/.exec(body)\n if (detached !== null) {\n return { branch: null, unborn: false, ahead: 0, behind: 0 }\n }\n\n const bracketMatch = /^(.*?)\\s*\\[([^\\]]+)\\]$/.exec(body)\n const core = bracketMatch?.[1] ?? body\n let ahead = 0\n let behind = 0\n if (bracketMatch?.[2] !== undefined) {\n for (const part of bracketMatch[2].split(',')) {\n const trimmed = part.trim()\n const aheadMatch = /^ahead (\\d+)$/.exec(trimmed)\n const behindMatch = /^behind (\\d+)$/.exec(trimmed)\n if (aheadMatch !== null) ahead = Number(aheadMatch[1])\n if (behindMatch !== null) behind = Number(behindMatch[1])\n }\n }\n // The core is `<branch>...<upstream>` \u2014 the branch never contains `...`.\n const branch = core.split('...', 1)[0] ?? core\n return { branch: branch === '' ? null : branch, unborn: false, ahead, behind }\n}\n\n/** \u5355\u5217\u72B6\u6001\u7801 \u2192 \u53D8\u66F4\u72B6\u6001\u6620\u5C04\uFF08\u771F\u5B9E\u51B2\u7A81\u7531 isConflicted \u5355\u72EC\u5224\u5B9A\uFF09\u3002 */\nfunction singleStatus(code: string): GitChangeStatus {\n switch (code) {\n case 'A': return 'added'\n case 'M': return 'modified'\n case 'D': return 'deleted'\n case 'R': return 'renamed'\n case 'T': return 'typechange'\n case 'C': return 'added'\n default: return 'modified'\n }\n}\n\n/**\n * \u771F\u5B9E\u5408\u5E76\u51B2\u7A81\uFF1A\u4EFB\u4E00\u4FA7\u4E3A U\uFF08UU/AU/UD/UA/DU\uFF09\uFF0C\u6216\u53CC\u65B9\u540C\u6DFB/\u540C\u5220\uFF08AA/DD\uFF09\u3002\n * \u6CE8\u610F MM/AM/MD \u7B49\u300C\u5DF2\u6682\u5B58 + \u5DE5\u4F5C\u533A\u518D\u6539\u300D\u662F\u5408\u6CD5\u6DF7\u5408\u6001\u800C\u975E\u51B2\u7A81\n * \uFF08\u65E7\u89C4\u5219\u300C\u53CC\u5217\u5747\u975E\u7A7A\u5373\u51B2\u7A81\u300D\u4F1A\u628A MM \u8BEF\u62A5\u4E3A\u51B2\u7A81\uFF09\u3002\n */\nfunction isConflicted(x: string, y: string): boolean {\n return x === 'U' || y === 'U' || (x === 'A' && y === 'A') || (x === 'D' && y === 'D')\n}\n\n/**\n * Parse the full `git status --porcelain=v1 -z --branch` output.\n * -z format: every entry (header and each `XY path`) is NUL-terminated; a\n * rename/copy entry emits `R <new>\\0<old>\\0` so the following item is the\n * source path and must be consumed without becoming a change itself.\n *\n * \u6DF7\u5408\u72B6\u6001\u62C6\u5206\uFF08IDEA \u5F0F\uFF09\uFF1AX\u3001Y \u5747\u975E\u7A7A\u7684\u5408\u6CD5\u5BF9\uFF08MM/AM/MD/RM\u2026\uFF09\u62C6\u4E3A\n * \u300C\u5DF2\u6682\u5B58\u4FA7 + \u672A\u6682\u5B58\u4FA7\u300D\u4E24\u6761 GitChange\u2014\u2014UI \u636E\u6B64\u628A\u540C\u4E00\u6587\u4EF6\u5206\u522B\u5217\u5165\n * \u300C\u5DF2\u6682\u5B58\u66F4\u6539\u300D\u4E0E\u300C\u66F4\u6539\u300D\u4E24\u7EC4\uFF0C\u4E24\u4FA7\u53EF\u72EC\u7ACB\u64CD\u4F5C\u3001\u5DEE\u5F02\u57FA\u7EBF\u552F\u4E00\u3002\n * \u771F\u5B9E\u51B2\u7A81\uFF08isConflicted\uFF09\u4FDD\u6301\u5355\u6761 conflicted \u6761\u76EE\u3002\n */\nexport function parseStatusOutput(output: string, maxChanges: number): ParsedStatus {\n const raw = output.split(NUL)\n // Trailing NUL produces a final empty segment; drop it.\n const segments = raw[raw.length - 1] === '' ? raw.slice(0, -1) : raw\n const header = parseStatusHeader(segments[0] ?? '')\n\n let staged = 0\n let modified = 0\n let untracked = 0\n const changes: GitChange[] = []\n let truncated = false\n\n /** \u6536\u5F55\u4E00\u6761\u53D8\u66F4\u6761\u76EE\uFF1B\u8D85\u51FA\u4E0A\u9650\u4EC5\u7F6E\u622A\u65AD\u6807\u8BB0\uFF08\u8BA1\u6570\u4E0D\u53D7\u5F71\u54CD\uFF09\u3002\n * isDirectory \u7531 git \u8F93\u51FA\u6743\u5A01\u6807\u8BB0\uFF08\u672A\u8DDF\u8E2A\u76EE\u5F55\u6761\u76EE\u4E3A `dir/` \u5C3E\u659C\u6760\uFF09\uFF0C\n * \u5C55\u793A\u5C42\u4F9D\u8D56\u6B64\u5B57\u6BB5\uFF0C\u4E0D\u518D\u81EA\u884C\u89E3\u6790\u8DEF\u5F84\u5B57\u7B26\u4E32\u3002 */\n const pushChange = (path: string, status: GitChangeStatus, isStaged: boolean): void => {\n if (changes.length < maxChanges) {\n changes.push({ path, status, staged: isStaged, isDirectory: path.endsWith('/') })\n } else {\n truncated = true\n }\n }\n\n for (let index = 1; index < segments.length; index += 1) {\n const entry = segments[index] ?? ''\n const x = entry[0] ?? ' '\n const y = entry[1] ?? ' '\n const path = entry.slice(3)\n if (x === ' ' && y === ' ') continue\n if (x === 'R' || x === 'C') {\n // -z: the source path is the next segment \u2014 consume it.\n index += 1\n }\n if (x === '?' && y === '?') {\n untracked += 1\n pushChange(path, 'untracked', false)\n continue\n }\n // \u8BA1\u6570\u4ECD\u6309 X/Y \u4E24\u5217\u5206\u522B\u7D2F\u8BA1\uFF1B\u62C6\u53CC\u6761\u76EE\u4E0D\u6539\u53D8\u603B\u6570\u3002\n if (x !== ' ' && x !== '?') staged += 1\n if (y !== ' ' && y !== '?') modified += 1\n\n if (isConflicted(x, y)) {\n // \u51B2\u7A81\u6587\u4EF6\u6309\u5355\u6761\u5C55\u793A\uFF08IDEA \u51B2\u7A81\u6761\u76EE\u5F62\u6001\uFF09\uFF0C\u5F52\u5165\u5DF2\u6682\u5B58\u7EC4\u3002\n pushChange(path, 'conflicted', true)\n } else if (x !== ' ' && y !== ' ') {\n // \u6DF7\u5408\u6001\uFF1A\u5DF2\u6682\u5B58\u4FA7\u72B6\u6001\u53D6 X\uFF0C\u672A\u6682\u5B58\u4FA7\u72B6\u6001\u53D6 Y\u3002\n pushChange(path, singleStatus(x), true)\n pushChange(path, singleStatus(y), false)\n } else {\n const isStaged = x !== ' '\n pushChange(path, singleStatus(isStaged ? x : y), isStaged)\n }\n }\n\n return {\n branch: header.branch,\n unborn: header.unborn,\n staged,\n modified,\n untracked,\n ahead: header.ahead,\n behind: header.behind,\n changes,\n truncated,\n }\n}\n\n/**\n * Parse `git log -n 5 --format=%H%x1f%h%x1f%s%x1f%an%x1f%aI` output.\n * One commit per line, fields separated by the unit separator; empty output\n * (unborn repository) yields `[]`.\n */\nexport function parseLogOutput(output: string): readonly GitCommit[] {\n const commits: GitCommit[] = []\n for (const line of output.split('\\n')) {\n if (line === '') continue\n const [hash, shortHash, subject, author, dateIso] = line.split(LOG_SEP)\n if (hash === undefined || hash === '') continue\n commits.push({\n hash,\n shortHash: shortHash ?? '',\n subject: subject ?? '',\n author: author ?? '',\n dateIso: dateIso ?? '',\n })\n }\n return commits\n}\n\n/**\n * \u89E3\u6790\u5E26\u56FE\u7684 log \u8F93\u51FA\uFF1A\n * `%H%x1f%h%x1f%s%x1f%an%x1f%aI%x1f%P%x1f%D`\n * \u5176\u4E2D `%P` \u4E3A\u7A7A\u683C\u5206\u9694\u7684\u7236\u63D0\u4EA4\u54C8\u5E0C\uFF08\u6839\u63D0\u4EA4\u4E3A\u7A7A\uFF09\uFF0C`%D` \u4E3A ref \u88C5\u9970\u3002\n * \u8FD4\u56DE\u9002\u5408\u5206\u652F\u56FE\u6E32\u67D3\u5668\u7684 `GraphCommit[]`\u3002\n */\nexport function parseGraphLogOutput(output: string, remotes: readonly string[] = []): readonly GraphCommit[] {\n const commits: GraphCommit[] = []\n for (const line of output.split('\\n')) {\n if (line === '') continue\n const [hash, shortHash, subject, author, dateIso, parentField, decoField] = line.split(LOG_SEP)\n if (hash === undefined || hash === '') continue\n const parents = (parentField ?? '')\n .split(' ')\n .filter((p) => p !== '')\n commits.push({\n hash,\n shortHash: shortHash ?? '',\n subject: subject ?? '',\n author: author ?? '',\n dateIso: dateIso ?? '',\n parents,\n refs: parseDecorations(decoField ?? '', remotes),\n })\n }\n return commits\n}\n\n/**\n * \u89E3\u6790 `%D` \u88C5\u9970\u4E32\uFF0C\u5F62\u5982 `HEAD -> main, origin/main, tag: v1.0`\uFF1B\u7A7A\u4E32\u65E0 refs\u3002\n * \u5206\u7C7B\u89C4\u5219\uFF1A`HEAD -> x` \u4E3A\u5F53\u524D\u5206\u652F\uFF1B`tag: t` \u4E3A\u6807\u7B7E\uFF1B\n * \u5E26\u8FDC\u7A0B\u524D\u7F00\uFF08`<remote>/\u2026`\uFF09\u4E3A\u8FDC\u7A0B\u5206\u652F\uFF1B\u5176\u4F59\u4E3A\u672C\u5730\u5206\u652F\u3002\n */\nexport function parseDecorations(decorations: string, remotes: readonly string[]): readonly GitRef[] {\n const trimmed = decorations.trim()\n if (trimmed === '') return []\n const refs: GitRef[] = []\n for (const token of trimmed.split(', ')) {\n if (token.startsWith('HEAD -> ')) {\n refs.push({ kind: 'branch', name: token.slice(8), head: true })\n } else if (token.startsWith('tag: ')) {\n refs.push({ kind: 'tag', name: token.slice(5), head: false })\n } else if (remotes.some((remote) => token === remote || token.startsWith(`${remote}/`))) {\n refs.push({ kind: 'remote', name: token, head: false })\n } else {\n refs.push({ kind: 'branch', name: token, head: false })\n }\n }\n return refs\n}\n\n/**\n * \u89E3\u6790 `git show -s --format=%H%x1f%h%x1f%s%x1f%an%x1f%aI%x1f%b` \u8F93\u51FA\uFF1A\n * \u524D\u4E94\u4E2A\u5B57\u6BB5\u4E3A\u673A\u5668\u53EF\u8BFB\u5143\u6570\u636E\uFF0C\u7B2C\u516D\u5B57\u6BB5\u8D77\u4E3A %b \u6B63\u6587\n * \uFF08%b \u5DF2\u6392\u9664 subject \u9996\u6BB5\u843D\uFF0C\u5929\u7136\u65E0\u91CD\u590D\u5C55\u793A\u95EE\u9898\uFF09\u3002\n */\nexport function parseShowMeta(output: string): { readonly commit: GitCommit; readonly body: string } | null {\n const trimmed = output.trimEnd()\n if (trimmed === '') return null\n const [hash, shortHash, subject, author, dateIso, ...bodyParts] = trimmed.split(LOG_SEP)\n if (hash === undefined || hash === '') return null\n return {\n commit: {\n hash,\n shortHash: shortHash ?? '',\n subject: subject ?? '',\n author: author ?? '',\n dateIso: dateIso ?? '',\n },\n body: bodyParts.join(LOG_SEP).trimEnd(),\n }\n}\n\n/** `--name-status` \u72B6\u6001\u7801 \u2192 \u53D8\u66F4\u72B6\u6001\u6620\u5C04\u3002 */\nfunction nameStatusCode(code: string): GitChangeStatus {\n switch (code) {\n case 'A': return 'added'\n case 'D': return 'deleted'\n case 'R': return 'renamed'\n case 'T': return 'typechange'\n case 'U': return 'conflicted'\n default: return 'modified'\n }\n}\n\n/**\n * \u89E3\u6790 `git show --format= --name-status -z` \u8F93\u51FA\uFF1ANUL \u5206\u9694\uFF0C\n * `X\\0path\\0`\uFF0Crename/copy \u4E3A `R100\\0old\\0new\\0`\uFF08\u53D6\u65B0\u8DEF\u5F84\uFF09\u3002\n * -z \u539F\u59CB\u8F93\u51FA\u4E0D\u5F15\u53F7\u5316\uFF0C\u975E ASCII \u8DEF\u5F84\u5929\u7136\u514D\u75AB\u4E71\u7801\uFF08\u65E7 --stat \u516B\u8FDB\u5236\u8F6C\u4E49\u95EE\u9898\u7684\u6839\u56E0\u6D88\u9664\uFF09\u3002\n */\nexport function parseNameStatusOutput(output: string): readonly { readonly path: string; readonly status: GitChangeStatus }[] {\n const raw = output.split(NUL)\n const segments = raw[raw.length - 1] === '' ? raw.slice(0, -1) : raw\n const rows: { path: string; status: GitChangeStatus }[] = []\n for (let i = 0; i < segments.length; i += 1) {\n const entry = segments[i] ?? ''\n if (entry === '') continue\n const code = entry[0] ?? ' '\n if (code === 'R' || code === 'C') {\n // rename/copy\uFF1Aold \u5728 i+1\u3001new \u5728 i+2\uFF0C\u5C55\u793A\u53D6\u65B0\u8DEF\u5F84\u3002\n rows.push({ path: segments[i + 2] ?? '', status: nameStatusCode(code) })\n i += 2\n } else {\n rows.push({ path: segments[i + 1] ?? '', status: nameStatusCode(code) })\n i += 1\n }\n }\n return rows\n}\n\n/**\n * Parse `git branch --show-current` output: the branch name, or null when\n * empty (detached HEAD).\n */\nexport function parseBranchOutput(output: string): string | null {\n const trimmed = output.trim()\n return trimmed === '' ? null : trimmed\n}\n\n", "/**\n * Framework-free snapshot orchestration: session cwd resolution + git command\n * sequence + frozen GitSnapshot assembly. Every dependency is injected\n * structurally, so the whole flow is testable without a cordis runtime; the\n * cordis shell (GitStatusService) only adapts host services into these faces.\n */\nimport { parseBranchOutput, parseLogOutput, parseStatusOutput } from './parser.ts'\nimport type { GitRunner } from './git.ts'\nimport type { GitSnapshot, GitSnapshotResult } from './types.ts'\n\n/** Resolved plugin config (already normalized; see normalizeConfig). */\nexport interface GitStatusConfig {\n readonly timeoutMs: number\n readonly maxStatusBytes: number\n readonly maxChanges: number\n readonly defaultRefreshIntervalMs: number\n}\n\n/** Session identity lookup: live first, persisted fallback. */\nexport interface SessionLookup {\n /** Live session cwd; undefined when the session is cold or absent in memory. */\n liveCwd(sessionId: string): string | undefined\n /**\n * Persisted session metadata; resolves to undefined when no persisted\n * session exists, and to `{ cwd }` (cwd possibly undefined) otherwise.\n */\n persistedMeta(sessionId: string): Promise<{ readonly cwd?: string } | undefined>\n}\n\n/** Filesystem primitives (node:fs/promises slices). */\nexport interface FsLike {\n realpath(path: string): Promise<string>\n stat(path: string): Promise<{ isDirectory(): boolean }>\n}\n\n/** Everything the snapshot flow needs beyond the session lookup. */\nexport interface SnapshotDeps {\n readonly run: GitRunner\n readonly fs: FsLike\n readonly sessions: SessionLookup\n /** Injectable clock for deterministic tests. */\n readonly now?: () => number\n /** Caller-side cancellation (Remote `signal` slot): aborts in-flight git runs. */\n readonly signal?: AbortSignal\n}\n\n/** Defaults applied by normalizeConfig when a value is absent or invalid. */\nexport const DEFAULT_CONFIG: GitStatusConfig = {\n timeoutMs: 5000,\n maxStatusBytes: 4 * 1024 * 1024,\n maxChanges: 100,\n defaultRefreshIntervalMs: 30_000,\n}\n\n/** Coerce a raw patch config value into a validated GitStatusConfig. */\nexport function normalizeConfig(raw: unknown): GitStatusConfig {\n const value = (raw ?? {}) as Record<string, unknown>\n const numberOr = (key: string, fallback: number): number => {\n const candidate = value[key]\n return typeof candidate === 'number' && Number.isFinite(candidate) && candidate >= 0\n ? candidate\n : fallback\n }\n return {\n timeoutMs: numberOr('timeoutMs', DEFAULT_CONFIG.timeoutMs) || DEFAULT_CONFIG.timeoutMs,\n maxStatusBytes: numberOr('maxStatusBytes', DEFAULT_CONFIG.maxStatusBytes) || DEFAULT_CONFIG.maxStatusBytes,\n maxChanges: Math.floor(numberOr('maxChanges', DEFAULT_CONFIG.maxChanges) || DEFAULT_CONFIG.maxChanges),\n defaultRefreshIntervalMs: numberOr('defaultRefreshIntervalMs', DEFAULT_CONFIG.defaultRefreshIntervalMs),\n }\n}\n\n/** Outcome of the cwd resolution step. */\ntype CwdResolution =\n | { readonly ok: true; readonly cwd: string }\n | { readonly ok: false; readonly error: Extract<GitSnapshotResult, { ok: false }>['error'] }\n\nasync function resolveCwd(sessions: SessionLookup, sessionId: string): Promise<CwdResolution> {\n const live = sessions.liveCwd(sessionId)\n if (live !== undefined) return { ok: true, cwd: live }\n const persisted = await sessions.persistedMeta(sessionId)\n if (persisted === undefined) return { ok: false, error: { code: 'session-not-found', sessionId } }\n if (persisted.cwd === undefined) return { ok: false, error: { code: 'cwd-unavailable', sessionId } }\n return { ok: true, cwd: persisted.cwd }\n}\n\n/** Classify a failed run outcome into a snapshot failure. */\nfunction runFailure(result: { readonly timedOut: boolean }, detail: string): Extract<GitSnapshotResult, { ok: false }>['error'] {\n return result.timedOut ? { code: 'timeout' } : { code: 'git-unavailable', detail }\n}\n\n/** Run one command, mapping a spawn-level failure to a snapshot failure. */\nexport async function runCommand(\n runner: GitRunner,\n argv: readonly string[],\n cwd: string,\n label: string,\n signal?: AbortSignal,\n): Promise<{ readonly run: Awaited<ReturnType<GitRunner['run']>> } | { readonly failure: Extract<GitSnapshotResult, { ok: false }>['error'] }> {\n try {\n return { run: await runner.run(argv, { cwd, ...(signal === undefined ? {} : { signal }) }) }\n } catch (error) {\n return { failure: { code: 'git-unavailable', detail: `${label}: ${error instanceof Error ? error.message : String(error)}` } }\n }\n}\n\n/**\n * Resolve a session's repository workspace: cwd (live or persisted), the\n * realpath'd directory, and the git work-tree root via `rev-parse\n * --show-toplevel`. Shared by the snapshot flow and the operation runner.\n */\nexport type WorkspaceResolution =\n | { readonly ok: true; readonly cwd: string; readonly root: string }\n | { readonly ok: false; readonly error: Extract<GitSnapshotResult, { ok: false }>['error'] }\n\nexport async function resolveWorkspace(\n deps: SnapshotDeps,\n sessionId: string,\n): Promise<WorkspaceResolution> {\n const resolved = await resolveCwd(deps.sessions, sessionId)\n if (!resolved.ok) return { ok: false, error: resolved.error }\n\n let realCwd: string\n try {\n realCwd = await deps.fs.realpath(resolved.cwd)\n const stat = await deps.fs.stat(realCwd)\n if (!stat.isDirectory()) {\n return { ok: false, error: { code: 'path-not-found', path: realCwd } }\n }\n } catch {\n return { ok: false, error: { code: 'path-not-found', path: resolved.cwd } }\n }\n\n const toplevel = await runCommand(deps.run, ['git', 'rev-parse', '--show-toplevel'], realCwd, 'rev-parse', deps.signal)\n if ('failure' in toplevel) return { ok: false, error: toplevel.failure }\n if (toplevel.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (toplevel.run.exitCode !== 0) {\n // exit 128 covers both \"not a git repository\" (plain directory) and\n // other git failures (dubious ownership, unreadable work tree, \u2026).\n // Only the former is a stable non-repo state; everything else surfaces\n // as git-unavailable with the actual reason instead of a misleading\n // \"no git repository\" pill.\n const stderr = toplevel.run.stderr\n if (!stderr.includes('not a git repository')) {\n return { ok: false, error: runFailure(toplevel.run, `git rev-parse failed: ${stderr.trim() || `exit ${String(toplevel.run.exitCode)}`}`) }\n }\n return { ok: false, error: { code: 'not-a-git-repo' } }\n }\n const root = toplevel.run.stdout.trim()\n if (root === '') return { ok: false, error: { code: 'not-a-git-repo' } }\n return { ok: true, cwd: realCwd, root }\n}\n\n/**\n * Build one frozen GitSnapshot for a session working directory.\n * Command sequence (all read-only; every command after the first runs with\n * the repository root as cwd):\n * 1. `git rev-parse --show-toplevel` \u2014 repo detection (exit 128 \u2192 not-a-git-repo)\n * 2. `git branch --show-current` \u2014 null when detached\n * 3. `git rev-parse --short HEAD` \u2014 null + unborn when the repo has no commits\n * 4. `git status --porcelain=v1 -z --branch --untracked-files=all`\n * 5. `git log -n 5 --format=%H%x1f%h%x1f%s%x1f%an%x1f%aI`\n *\n * --untracked-files=all\uFF1Agit \u9ED8\u8BA4 normal \u6A21\u5F0F\u4F1A\u628A\u6574\u76EE\u5F55\u672A\u8DDF\u8E2A\u6298\u53E0\u4E3A\u5355\u6761\n * `?? dir/`\uFF08\u5C3E\u659C\u6760\uFF09\u4E14\u4E0D\u679A\u4E3E\u5176\u5185\u90E8\u6587\u4EF6\u2014\u2014\u9690\u85CF\u76EE\u5F55\uFF08.agent/.tianqi \u7B49\uFF09\u7684\n * \u53D8\u66F4\u56E0\u6B64\u4ECE\u4E0D\u8FDB\u5165\u53D8\u66F4\u6E05\u5355\u3002`all` \u5F3A\u5236\u9010\u6587\u4EF6\u679A\u4E3E\uFF08\u4E0E IDEA / VSCode \u4E00\u81F4\uFF09\uFF0C\n * \u5185\u90E8\u6587\u4EF6\u5F97\u4EE5\u5C55\u793A\uFF1BmaxChanges \u622A\u65AD\u5217\u8868\u3001maxStatusBytes spill \u4FDD\u8BA1\u6570\u7CBE\u786E\uFF0C\n * \u8D85\u5927\u672A\u8DDF\u8E2A\u6811\uFF08\u5982\u672A gitignore \u7684\u6784\u5EFA\u4EA7\u7269\uFF09\u7ECF\u6B64\u8DEF\u5F84\u4F18\u96C5\u964D\u7EA7\u3002\n */\nexport async function snapshotForSession(\n deps: SnapshotDeps,\n config: GitStatusConfig,\n sessionId: string,\n): Promise<GitSnapshotResult> {\n const workspace = await resolveWorkspace(deps, sessionId)\n if (!workspace.ok) return { ok: false, error: workspace.error }\n const root = workspace.root\n\n const branchRun = await runCommand(deps.run, ['git', 'branch', '--show-current'], root, 'branch', deps.signal)\n if ('failure' in branchRun) return { ok: false, error: branchRun.failure }\n if (branchRun.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n const branch = branchRun.run.exitCode === 0 ? parseBranchOutput(branchRun.run.stdout) : null\n\n const headRun = await runCommand(deps.run, ['git', 'rev-parse', '--short', 'HEAD'], root, 'rev-parse HEAD', deps.signal)\n if ('failure' in headRun) return { ok: false, error: headRun.failure }\n if (headRun.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n // A failed HEAD read (non-timeout) only nulls the hash: the authoritative\n // unborn flag comes from the status header below (`## No commits yet on\n // main`), so a corrupt repo is never misreported as \"no commits\".\n const head = headRun.run.exitCode === 0 ? (headRun.run.stdout.trim() || null) : null\n\n // --untracked-files=all\uFF1A\u5F3A\u5236\u679A\u4E3E\u672A\u8DDF\u8E2A\u76EE\u5F55\u5185\u90E8\u6587\u4EF6\uFF08\u6839\u56E0\u4FEE\u590D\u2014\u2014\u89C1\u6A21\u5757\u6CE8\u91CA\uFF09\u3002\n const status = await runCommand(deps.run, ['git', 'status', '--porcelain=v1', '-z', '--branch', '--untracked-files=all'], root, 'status', deps.signal)\n if ('failure' in status) return { ok: false, error: status.failure }\n if (status.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (status.run.exitCode !== 0) {\n return { ok: false, error: runFailure(status.run, `git status exited ${String(status.run.exitCode)}`) }\n }\n const parsed = parseStatusOutput(status.run.stdout, config.maxChanges)\n\n const log = await runCommand(deps.run, ['git', 'log', '-n', '5', '--format=%H%x1f%h%x1f%s%x1f%an%x1f%aI'], root, 'log', deps.signal)\n if ('failure' in log) return { ok: false, error: log.failure }\n if (log.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n const recentCommits = log.run.exitCode === 0 ? parseLogOutput(log.run.stdout) : []\n\n const checkedAt = deps.now?.() ?? Date.now()\n const snapshot: GitSnapshot = {\n root,\n branch,\n head,\n unborn: parsed.unborn,\n dirty: parsed.staged + parsed.modified + parsed.untracked > 0,\n staged: parsed.staged,\n modified: parsed.modified,\n untracked: parsed.untracked,\n ahead: parsed.ahead,\n behind: parsed.behind,\n lastCommit: recentCommits[0] ?? null,\n recentCommits,\n changes: parsed.changes,\n truncated: parsed.truncated || ('run' in status && status.run.stdoutLossy),\n refreshIntervalMs: config.defaultRefreshIntervalMs,\n checkedAt,\n }\n return { ok: true, value: snapshot }\n}\n", "/**\n * Framework-free git management operation runner.\n *\n * Same layering as `core.ts`: every dependency is injected structurally, the\n * whole flow is testable against real temporary git repositories without a\n * cordis runtime, and `GitStatusService` only adapts host services into the\n * `SnapshotDeps` face.\n *\n * Security model: the browser only ever sends a `sessionId` plus\n * repository-relative paths (as listed in a snapshot's `changes`). Paths are\n * validated against the work-tree root (absolute paths and `..` escapes are\n * rejected) and every git invocation uses `--` so a path can never be\n * interpreted as an option. Commands run through the same subprocess adapter\n * as the read-only snapshot flow \u2014 no shell is involved.\n */\nimport { resolve, sep } from 'node:path'\nimport { resolveWorkspace, runCommand, snapshotForSession, type GitStatusConfig, type SnapshotDeps } from './core.ts'\nimport type { GitAction, GitActionResult, GitActionRequest, GitOperationErrorCode } from './types.ts'\n\n/** Build the command sequence for one action, validating every path against the root. */\nfunction buildArgv(action: GitAction, root: string): { readonly argv: readonly (readonly string[])[] } | { readonly error: string } {\n switch (action.kind) {\n case 'stage':\n return withPaths([['git', 'add', '--']], action.paths, root)\n case 'stage-all':\n return { argv: [['git', 'add', '-A']] }\n case 'unstage':\n return withPaths([['git', 'restore', '--staged', '--']], action.paths, root)\n case 'unstage-all':\n return { argv: [['git', 'restore', '--staged', '--', '.']] }\n case 'discard':\n return withPaths([['git', 'restore', '--']], action.paths, root)\n case 'discard-all':\n // Reset the index to HEAD first, then the work tree to the index \u2014 the\n // IDE-style \"roll back everything tracked\" semantics.\n return { argv: [['git', 'restore', '--staged', '--', '.'], ['git', 'restore', '--', '.']] }\n case 'commit': {\n // Message emptiness is validated by runAction (git-error), not here.\n const message = action.message.trim()\n if (action.paths === undefined || action.paths.length === 0) {\n return { argv: [['git', 'commit', '-m', message]] }\n }\n // \u4E24\u6B65\u5E8F\u5217\uFF08IDE \u5F0F\u300C\u63D0\u4EA4\u6240\u9009\u6587\u4EF6\u300D\u8BED\u4E49\uFF0C\u542B\u672A\u8DDF\u8E2A\u6587\u4EF6\uFF09\uFF1A\n // 1. `git add -- <paths>` \u5148\u628A\u6240\u9009\u8DEF\u5F84\u7EB3\u5165\u7D22\u5F15\u2014\u2014\u88F8\u7684\n // `git commit -- <\u672A\u8DDF\u8E2A\u8DEF\u5F84>` \u4F1A\u62A5 pathspec \u9519\u8BEF\uFF0C\u5148\u884C\u6682\u5B58\u4F7F\u5176\u53EF\u5339\u914D\uFF1B\n // 2. `git commit -m <msg> -- <paths>` \u6309\u8DEF\u5F84\u9650\u5B9A\u63D0\u4EA4\u8FD9\u4E9B\u8DEF\u5F84\u7684\u5DE5\u4F5C\u533A\u5185\u5BB9\uFF0C\n // \u5176\u4F59\u5DF2\u6682\u5B58\u6587\u4EF6\u4E0D\u53D7\u5F71\u54CD\u3002\u5BF9\u5DF2\u8DDF\u8E2A\u8DEF\u5F84\u4E0E\u5355\u547D\u4EE4\u5B8C\u5168\u7B49\u4EF7\uFF08\u5DF2\u5B9E\u6D4B\u9A8C\u8BC1\uFF09\u3002\n return withPaths([['git', 'add', '--'], ['git', 'commit', '-m', message, '--']], action.paths, root)\n }\n case 'branch-create': {\n // Name validity is validated by runAction (invalid-name), not here.\n const from = action.from === undefined || action.from === '' ? [] : [action.from]\n return { argv: [['git', 'branch', action.name, ...from]] }\n }\n case 'branch-checkout':\n return { argv: [['git', 'checkout', action.name]] }\n case 'branch-delete':\n return { argv: [['git', 'branch', action.force === true ? '-D' : '-d', action.name]] }\n case 'fetch':\n // fetch --all --prune\uFF1A\u62C9\u53D6\u6240\u6709\u8FDC\u7A0B\u5F15\u7528\u66F4\u65B0 + \u6E05\u7406\u5DF2\u5220\u9664\u7684\u8FDC\u7A0B\u8DDF\u8E2A\u5206\u652F\u3002\n return { argv: [['git', 'fetch', '--all', '--prune']] }\n }\n}\n\n/**\n * A branch name is valid when it matches git's ref-name grammar at the level\n * we care about: non-empty, ASCII ref chars only, no leading `-` (option\n * injection guard, though argv never shells out), no `..` (path traversal of\n * refs), no trailing `/`, and no double slashes.\n */\nexport function isValidBranchName(name: string): boolean {\n if (name === '' || name.startsWith('-') || name.includes('..') || name.endsWith('/') || name.includes('//')) return false\n return /^[A-Za-z0-9._/-]+$/.test(name)\n}\n\n/**\n * \u6821\u9A8C\u4ED3\u5E93\u76F8\u5BF9\u8DEF\u5F84\u540E\u8FFD\u52A0\u5230 `--` \u4E4B\u540E\uFF1B`prefixes` \u53EF\u7ED9\u51FA\u591A\u6761\u547D\u4EE4\u5E8F\u5217\uFF0C\n * \u6821\u9A8C\u540E\u7684\u8DEF\u5F84\u9010\u4E00\u9644\u52A0\u5230\u6BCF\u6761\u5E8F\u5217\uFF08commit \u6240\u9009\u8DEF\u5F84\u5373\u4E24\u6B65\u5E8F\u5217\uFF09\u3002\n */\nfunction withPaths(prefixes: readonly (readonly string[])[], paths: readonly string[], root: string): { readonly argv: readonly (readonly string[])[] } | { readonly error: string } {\n if (paths.length === 0) return { error: 'no paths given' }\n for (const path of paths) {\n if (!isSafePath(path, root)) return { error: `unsafe path: ${path}` }\n }\n return { argv: prefixes.map((prefix) => [...prefix, ...paths]) }\n}\n\n/**\n * A path is safe when it is repo-relative and stays inside the work tree:\n * reject absolute paths, drive letters / backslashes, and `..` escapes\n * (checked via path resolution against the realpath'd root).\n */\nexport function isSafePath(path: string, root: string): boolean {\n if (path === '') return false\n if (path.startsWith('/') || path.startsWith('\\\\') || /^[A-Za-z]:/.test(path)) return false\n const resolved = resolve(root, path)\n const prefix = root.endsWith(sep) ? root : `${root}${sep}`\n return resolved === root || resolved.startsWith(prefix)\n}\n\n/** Map a snapshot-flow failure (which may carry git-unavailable) onto the operation error shape. */\nexport function operationError(failure: Extract<Awaited<ReturnType<typeof resolveWorkspace>>, { ok: false }>['error']): GitActionResult & { ok: false } {\n if (failure.code === 'git-unavailable') {\n return { ok: false, error: { code: 'git-error', message: failure.detail } }\n }\n return { ok: false, error: failure }\n}\n\n/**\n * \u628A git \u547D\u4EE4\u5931\u8D25\u5F52\u7C7B\u4E3A\u53EF\u9884\u671F\u7684\u4E1A\u52A1\u9519\u8BEF\uFF08\u5176\u4F59\u4FDD\u6301 git-error\uFF09\u3002\n * \u5207\u5206\u652F\u88AB\u5DE5\u4F5C\u533A\u672A\u63D0\u4EA4\u53D8\u66F4\u963B\u6B62\u662F\u6700\u5E38\u89C1\u7684\u53EF\u9884\u671F\u5931\u8D25\uFF1Agit \u8F93\u51FA\n * \"would be overwritten by checkout\"\uFF08\u6216\u4E2D\u6587\u672C\u5730\u5316 \"\u5C06\u88AB checkout \u8986\u76D6\"\uFF09\uFF0C\n * \u5F52\u4E00\u5316\u4E3A local-changes-block\uFF0Cclient \u636E\u6B64\u7ED9\u53CB\u597D\u63D0\u793A + \u5904\u7406\u53D8\u66F4\u5F15\u5BFC\u3002\n */\nexport function classifyOperationError(kind: GitAction['kind'], message: string): GitOperationErrorCode {\n if (kind === 'branch-checkout' && /would be overwritten by checkout|\u5C06\u88AB checkout \u8986\u76D6|\u6709\u672A\u8DDF\u8E2A\u5DE5\u4F5C\u533A\u6587\u4EF6\u5C06\u4F1A\u88AB checkout \u8986\u76D6/i.test(message)) {\n return 'local-changes-block'\n }\n return 'git-error'\n}\n\n/**\n * Execute one management action against the session's repository and return\n * the refreshed snapshot on success (the caller re-renders from it, so the\n * UI never waits for the next poll).\n */\nexport async function runAction(\n deps: SnapshotDeps,\n config: GitStatusConfig,\n request: GitActionRequest,\n): Promise<GitActionResult> {\n const workspace = await resolveWorkspace(deps, request.sessionId)\n if (!workspace.ok) return operationError(workspace.error)\n const root = workspace.root\n\n if (request.action.kind === 'commit' && request.action.message.trim() === '') {\n return { ok: false, error: { code: 'git-error', message: 'commit message is empty' } }\n }\n\n const kind = request.action.kind\n if (kind === 'branch-create' || kind === 'branch-checkout' || kind === 'branch-delete') {\n const name = request.action.name\n if (!isValidBranchName(name)) {\n return { ok: false, error: { code: 'invalid-name', message: `invalid branch name: ${name}` } }\n }\n }\n\n const built = buildArgv(request.action, root)\n if ('error' in built) return { ok: false, error: { code: 'invalid-path', message: built.error } }\n\n // Run the command sequence; a failure stops the rest. \u5148\u884C\u547D\u4EE4\u53EF\u80FD\u5DF2\u751F\u6548\uFF1A\n // restore \u7C7B\u547D\u4EE4\u5E42\u7B49\u53EF\u91CD\u5165\uFF1B\u4E24\u6B65\u63D0\u4EA4\u82E5 add \u6210\u529F\u540E commit \u5931\u8D25\uFF0C\u6240\u9009\u8DEF\u5F84\n // \u7559\u5728\u6682\u5B58\u533A\uFF08IDE \u884C\u4E3A\u76F8\u540C\uFF0C\u4E0B\u6B21\u91CD\u8BD5\u5373\u53EF\u6210\u529F\uFF09\u3002\n let lastStdout = ''\n for (const argv of built.argv) {\n const outcome = await runCommand(deps.run, argv, root, `action ${request.action.kind}`, deps.signal)\n if ('failure' in outcome) return operationError(outcome.failure)\n if (outcome.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (outcome.run.exitCode !== 0) {\n // git writes user-facing failures to stderr OR stdout (e.g. a clean\n // repo's `git commit` reports \"nothing to commit\" on stdout).\n const message = outcome.run.stderr.trim() || outcome.run.stdout.trim()\n const code = classifyOperationError(request.action.kind, message)\n return {\n ok: false,\n error: {\n code,\n message: message !== '' ? message : `git ${request.action.kind} exited ${String(outcome.run.exitCode)}`,\n },\n }\n }\n lastStdout = outcome.run.stdout.trim()\n }\n\n const snapshot = await snapshotForSession(deps, config, request.sessionId)\n if (!snapshot.ok) return operationError(snapshot.error)\n return { ok: true, snapshot: snapshot.value, ...(lastStdout === '' ? {} : { output: lastStdout }) }\n}\n", "/**\n * Framework-free read-only query runner (history / diff / show / branches).\n *\n * Same layering as `core.ts`/`actions.ts`: structural injection, testable\n * against real temporary repositories without a cordis runtime. Every query\n * resolves the workspace once, then runs one or two read-only git commands\n * against the repository root.\n */\nimport { resolveWorkspace, runCommand, type GitStatusConfig, type SnapshotDeps } from './core.ts'\nimport { parseBranchOutput, parseGraphLogOutput, parseNameStatusOutput, parseShowMeta } from './parser.ts'\nimport { isSafePath, operationError } from './actions.ts'\nimport type { GitBranch, GitQueryRequest, GitQueryResponse } from './types.ts'\n\n/** Machine-readable log format for show queries (no parents). */\nconst LOG_FORMAT = '%H%x1f%h%x1f%s%x1f%an%x1f%aI'\n/** \u5E26\u56FE\u7684 log \u683C\u5F0F\uFF08%P = \u7236\u63D0\u4EA4\uFF0C%D = ref \u88C5\u9970\uFF09\u3002 */\nconst GRAPH_FORMAT = '%H%x1f%h%x1f%s%x1f%an%x1f%aI%x1f%P%x1f%D'\n\n/** History page size cap (and default). \u5343\u6761\u7EA7 + \u65E0\u9650\u6EDA\u52A8\u3002 */\nconst MAX_HISTORY_LIMIT = 1000\n\n/** A ref is acceptable when non-empty and free of whitespace. */\nfunction isValidRef(ref: string): boolean {\n return ref !== '' && !/\\s/.test(ref)\n}\n\n/**\n * \u6267\u884C\u4E00\u6761\u53EA\u8BFB\u67E5\u8BE2\u3002\u7ED3\u679C\u5747\u4E3A JSON \u7EAF\u6570\u636E\u4E14\u6709\u754C\n * \uFF08history \u5206\u9875\uFF1Bdiff \u6587\u672C\u53D7 runner \u7684 spill/\u622A\u65AD\u7EA6\u675F\uFF09\u3002\n * `config` \u5F53\u524D\u672A\u7528\uFF1A\u4FDD\u7559\u4EE5\u4E0E runAction \u5171\u4EAB runner \u7B7E\u540D\u5951\u7EA6\n * \uFF08deps, config, request\uFF09\uFF0C\u540E\u7EED\u67E5\u8BE2\u9650\u6D41\u7B49\u8C03\u4F18\u53EF\u76F4\u63A5\u542F\u7528\u3002\n */\nexport async function runQuery(\n deps: SnapshotDeps,\n config: GitStatusConfig,\n request: GitQueryRequest,\n): Promise<GitQueryResponse> {\n void config\n const workspace = await resolveWorkspace(deps, request.sessionId)\n if (!workspace.ok) return { ok: false, error: operationError(workspace.error).error }\n const root = workspace.root\n const query = request.query\n\n switch (query.kind) {\n case 'history':\n return historyQuery(deps, root, query)\n case 'diff':\n return diffQuery(deps, root, query.path, query.base)\n case 'show':\n return showQuery(deps, root, query.ref)\n case 'branches':\n return branchesQuery(deps, root)\n case 'tags':\n return tagsQuery(deps, root)\n case 'authors':\n return authorsQuery(deps, root)\n }\n}\n\nasync function historyQuery(\n deps: SnapshotDeps,\n root: string,\n query: Extract<GitQueryRequest['query'], { kind: 'history' }>,\n): Promise<GitQueryResponse> {\n const safeLimit = Math.min(Math.max(Math.floor(query.limit), 0), MAX_HISTORY_LIMIT)\n const safeSkip = Math.max(Math.floor(query.skip), 0)\n if (query.ref !== undefined && !isValidRef(query.ref)) {\n return { ok: false, error: { code: 'invalid-name', message: `invalid ref: ${query.ref}` } }\n }\n const search = query.search?.trim() ?? ''\n const hexLike = /^[0-9a-f]{7,40}$/i.test(search)\n // \u54C8\u5E0C\u7CBE\u51C6\u68C0\u7D22\uFF1A\u4EC5\u5B9A\u4F4D\u76EE\u6807\u63D0\u4EA4\u81EA\u8EAB\uFF08--no-walk \u4E0D\u904D\u5386\u7956\u5148\uFF09\u2192 \u5355\u6761\u76EE\uFF0C\n // \u4E0D\u518D\u5217\u51FA\u8BE5\u63D0\u4EA4\u7684\u5168\u90E8\u7956\u5148\uFF1B\u6587\u672C\u641C\u7D22\u8D70 --grep\uFF08-i -E\uFF0C\u8DE8\u5F15\u7528\u5339\u914D\uFF09\u3002\n const scope = hexLike ? [] : query.ref === undefined ? ['--all'] : [query.ref]\n const noWalk = hexLike ? ['--no-walk', search] : []\n const filters: string[] = []\n if (search !== '' && !hexLike) filters.push('--regexp-ignore-case', '--extended-regexp', `--grep=${search}`)\n const author = query.author?.trim() ?? ''\n if (author !== '') filters.push(`--author=${author}`)\n const since = query.since?.trim() ?? ''\n if (since !== '') filters.push(`--since=${since}`)\n\n const log = await runCommand(\n deps.run,\n // -n/--skip \u524D\u7F6E\uFF1Agit \u7684 `-n N` \u51FA\u73B0\u5728 `--no-walk` \u4E4B\u540E\u4F1A\u91CD\u7F6E no-walk\n // \uFF08hexLike \u4F1A\u9519\u8BEF\u5217\u51FA\u5168\u90E8\u7956\u5148\uFF09\uFF0C\u524D\u7F6E\u5219 `-n 1000 --no-walk x` \u6052\u8FD4\u56DE\u5355\u6761\u3002\n ['git', 'log', ...filters, `--skip=${String(safeSkip)}`, '-n', String(safeLimit), ...noWalk, ...scope, `--format=${GRAPH_FORMAT}`],\n root,\n 'log',\n deps.signal,\n )\n if ('failure' in log) return { ok: false, error: operationError(log.failure).error }\n if (log.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (log.run.exitCode !== 0) {\n // \u672A\u51FA\u751F\u4ED3\u5E93\u65E0\u63D0\u4EA4\uFF1Agit log \u4EE5 128 \u6B64\u4FE1\u606F\u9000\u51FA\u2014\u2014\u7A33\u5B9A\u7A7A\u5386\u53F2\uFF0C\u975E\u9519\u8BEF\u3002\n if (log.run.stderr.includes('does not have any commits')) {\n return { ok: true, value: { kind: 'history', commits: [], total: 0 } }\n }\n // \u54C8\u5E0C\u65E0\u89E3\u6790\uFF08\u672A\u547D\u4E2D\uFF09\u6216\u524D\u7F00\u4E0D\u552F\u4E00\uFF08ambiguous\uFF09\uFF1A\u7A33\u5B9A\u7A7A\u7ED3\u679C\uFF08\u8BA9\u7528\u6237\u8F93\u5165\u66F4\u957F\u524D\u7F00\uFF09\u3002\n if (hexLike && /unknown revision|bad revision|ambiguous/i.test(log.run.stderr)) {\n return { ok: true, value: { kind: 'history', commits: [], total: 0 } }\n }\n return gitError('log', log.run.stderr, log.run.stdout)\n }\n\n // \u8FC7\u6EE4\u8303\u56F4\u5185\u7684\u63D0\u4EA4\u603B\u6570\uFF08best-effort\uFF09\u3002\n let total = 0\n const count = await runCommand(deps.run, ['git', 'rev-list', '--count', ...noWalk, ...scope, ...filters], root, 'rev-list', deps.signal)\n if ('run' in count && count.run.exitCode === 0) {\n const parsed = Number(count.run.stdout.trim())\n if (Number.isFinite(parsed) && parsed >= 0) total = parsed\n }\n\n // \u8FDC\u7A0B\u540D\u7528\u4E8E %D \u88C5\u9970\u7684\u8FDC\u7A0B\u5206\u652F\u5206\u7C7B\uFF1B\u5931\u8D25\u65F6\u964D\u7EA7\u4E3A\u7A7A\u5217\u8868\uFF08\u5176\u4F59\u6309\u672C\u5730\u5206\u652F\u5904\u7406\uFF09\u3002\n let remotes: readonly string[] = []\n const remoteRun = await runCommand(deps.run, ['git', 'remote'], root, 'remote', deps.signal)\n if ('run' in remoteRun && remoteRun.run.exitCode === 0) {\n remotes = remoteRun.run.stdout.split('\\n').map((s) => s.trim()).filter((s) => s !== '')\n }\n\n return { ok: true, value: { kind: 'history', commits: parseGraphLogOutput(log.run.stdout, remotes), total } }\n}\n\n/**\n * \u5355\u6587\u4EF6\u5DEE\u5F02\uFF08\u53D8\u66F4\u754C\u9762\u5BF9\u7167\u67E5\u770B\u7528\uFF09\u3002\n * staged = --cached\uFF1Bworktree = \u5DE5\u4F5C\u533A\u5BF9\u7D22\u5F15\uFF1B\n * \u672A\u7248\u672C\u7BA1\u7406\u6587\u4EF6 worktree \u5DEE\u5F02\u4E3A\u7A7A \u2192 \u56DE\u9000 --no-index \u4E0E /dev/null \u5BF9\u6BD4\uFF08\u9000\u51FA\u7801 1 \u89C6\u4E3A\u6709\u5DEE\u5F02\u7684\u6210\u529F\uFF09\u3002\n */\nasync function diffQuery(\n deps: SnapshotDeps,\n root: string,\n path: string,\n base: 'worktree' | 'staged',\n): Promise<GitQueryResponse> {\n if (!isSafePath(path, root)) return { ok: false, error: { code: 'invalid-path', message: `unsafe path: ${path}` } }\n // \u4F7F\u7528 -U999999 \u663E\u793A\u5B8C\u6574\u6587\u6863\u4E0A\u4E0B\u6587\uFF08\u800C\u975E\u4EC5\u53D8\u66F4 hunk\uFF09\uFF0C\u652F\u6301\u6587\u6863\u6D4F\u89C8\u4F53\u9A8C\u3002\n const argv = base === 'staged'\n ? ['git', 'diff', '--cached', '-U999999', '--', path]\n : ['git', 'diff', '-U999999', '--', path]\n const run = await runCommand(deps.run, argv, root, 'diff', deps.signal)\n if ('failure' in run) return { ok: false, error: operationError(run.failure).error }\n if (run.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (run.run.exitCode !== 0) return gitError('diff', run.run.stderr, run.run.stdout)\n if (run.run.stdout !== '' || base === 'staged') {\n return { ok: true, value: { kind: 'diff', path, text: run.run.stdout } }\n }\n // \u7A7A\u5DEE\u5F02\uFF1A\u53EF\u80FD\u662F\u672A\u7248\u672C\u7BA1\u7406\u6587\u4EF6\u2014\u2014\u4E0E /dev/null \u5BF9\u6BD4\u751F\u6210\u5168\u589E\u5DEE\u5F02\u3002\n const ni = await runCommand(deps.run, ['git', 'diff', '--no-index', '-U999999', '--', '/dev/null', path], root, 'diff --no-index', deps.signal)\n if ('failure' in ni) return { ok: false, error: operationError(ni.failure).error }\n if (ni.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (ni.run.exitCode !== 0 && ni.run.exitCode !== 1) return gitError('diff', ni.run.stderr, ni.run.stdout)\n return { ok: true, value: { kind: 'diff', path, text: ni.run.stdout } }\n}\n\nasync function showQuery(deps: SnapshotDeps, root: string, ref: string): Promise<GitQueryResponse> {\n if (!isValidRef(ref)) return { ok: false, error: { code: 'invalid-name', message: `invalid ref: ${ref}` } }\n // -s \u4EC5\u8F93\u51FA\u683C\u5F0F\u5757\uFF1A%b \u4E3A\u6392\u9664\u9996\u6BB5\u843D\u540E\u7684\u6B63\u6587\uFF0C\u72EC\u7ACB\u8C03\u7528\u907F\u514D\u89E3\u6790\u6B67\u4E49\u3002\n const meta = await runCommand(\n deps.run,\n ['git', 'show', '-s', `--format=${LOG_FORMAT}%x1f%b`, ref],\n root,\n 'show',\n deps.signal,\n )\n if ('failure' in meta) return { ok: false, error: operationError(meta.failure).error }\n if (meta.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (meta.run.exitCode !== 0) return gitError('show', meta.run.stderr, meta.run.stdout)\n\n const stat = await runCommand(\n deps.run,\n ['git', '-c', 'core.quotePath=false', 'show', '--format=', '--name-status', '-z', ref],\n root,\n 'show --name-status',\n deps.signal,\n )\n if ('failure' in stat) return { ok: false, error: operationError(stat.failure).error }\n if (stat.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (stat.run.exitCode !== 0) return gitError('show', stat.run.stderr, stat.run.stdout)\n\n const parsed = parseShowMeta(meta.run.stdout)\n return {\n ok: true,\n value: {\n kind: 'show',\n ref,\n commit: parsed?.commit ?? null,\n body: parsed?.body ?? '',\n stats: parseNameStatusOutput(stat.run.stdout),\n },\n }\n}\n\n/** \u4F5C\u8005\u5217\u8868\uFF08\u5DE5\u5177\u680F\u7528\u6237\u9009\u62E9\u7528\uFF09\uFF0C\u53BB\u91CD\u6392\u5E8F\u622A\u65AD 100\u3002 */\nasync function authorsQuery(deps: SnapshotDeps, root: string): Promise<GitQueryResponse> {\n const run = await runCommand(deps.run, ['git', 'log', '--all', '-n', '1000', '--format=%an'], root, 'log authors', deps.signal)\n if ('failure' in run) return { ok: false, error: operationError(run.failure).error }\n if (run.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (run.run.exitCode !== 0) return { ok: true, value: { kind: 'authors', authors: [] } }\n const authors = [...new Set(run.run.stdout.split('\\n').map((s) => s.trim()).filter((s) => s !== ''))].sort().slice(0, 100)\n return { ok: true, value: { kind: 'authors', authors } }\n}\n\n/** \u6807\u7B7E\u5217\u8868\uFF08\u5DE6\u680F\u8FC7\u6EE4\u6811\u7528\uFF09\uFF0C\u590D\u7528 tab \u5206\u9694\u89E3\u6790\u3002 */\nasync function tagsQuery(deps: SnapshotDeps, root: string): Promise<GitQueryResponse> {\n const FORMAT = '--format=%(refname:short)%09%(objectname:short)'\n const run = await runCommand(deps.run, ['git', 'tag', FORMAT], root, 'tag', deps.signal)\n if ('failure' in run) return { ok: false, error: operationError(run.failure).error }\n if (run.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (run.run.exitCode !== 0) return gitError('tag', run.run.stderr, run.run.stdout)\n return { ok: true, value: { kind: 'tags', tags: parseBranchList(run.run.stdout) } }\n}\n\nasync function branchesQuery(deps: SnapshotDeps, root: string): Promise<GitQueryResponse> {\n // \u672C\u5730\u5206\u652F\u683C\u5F0F\uFF1Aname\\thash\\tupstream\\ttrack\uFF08track \u5982 [ahead 2, behind 1]\uFF09\u3002\n // \u8FDC\u7A0B\u5206\u652F\u65E0\u4E0A\u6E38 \u2192 upstream/track \u4E3A\u7A7A\u3002\n const LOCAL_FORMAT = '--format=%(refname:short)%09%(objectname:short)%09%(upstream:short)%09%(upstream:track)'\n const REMOTE_FORMAT = '--format=%(refname:short)%09%(objectname:short)'\n const local = await runCommand(deps.run, ['git', 'branch', LOCAL_FORMAT], root, 'branch', deps.signal)\n if ('failure' in local) return { ok: false, error: operationError(local.failure).error }\n if (local.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (local.run.exitCode !== 0) return gitError('branch', local.run.stderr, local.run.stdout)\n\n const remote = await runCommand(deps.run, ['git', 'branch', '-r', REMOTE_FORMAT], root, 'branch -r', deps.signal)\n if ('failure' in remote) return { ok: false, error: operationError(remote.failure).error }\n if (remote.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (remote.run.exitCode !== 0) return gitError('branch -r', remote.run.stderr, remote.run.stdout)\n\n const current = await runCommand(deps.run, ['git', 'branch', '--show-current'], root, 'branch', deps.signal)\n const currentName = 'run' in current && current.run.exitCode === 0 ? parseBranchOutput(current.run.stdout) : null\n\n // \u9ED8\u8BA4\u5206\u652F\uFF1Aorigin/HEAD \u7B26\u53F7\u5F15\u7528\uFF08\u5982 origin/main\uFF09\uFF1B\u5931\u8D25\u964D\u7EA7 null\u3002\n let defaultBranch: string | null = null\n const def = await runCommand(deps.run, ['git', 'symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'], root, 'symbolic-ref', deps.signal)\n if ('run' in def && def.run.exitCode === 0) {\n const value = def.run.stdout.trim()\n const slash = value.indexOf('/')\n defaultBranch = value === '' ? null : slash === -1 ? value : value.slice(slash + 1)\n }\n\n return {\n ok: true,\n value: {\n kind: 'branches',\n current: currentName,\n defaultBranch,\n local: parseBranchList(local.run.stdout),\n remote: parseBranchList(remote.run.stdout).filter((branch) => !branch.name.endsWith('/HEAD')),\n },\n }\n}\n\n/**\n * \u89E3\u6790 `%(refname:short)%09%(objectname:short)[%09%(upstream:short)%09%(upstream:track)]` \u884C\u3002\n * \u672C\u5730\u5206\u652F\u542B 4 \u5B57\u6BB5\uFF08upstream + track\uFF09\uFF0C\u8FDC\u7A0B\u5206\u652F\u4EC5 2 \u5B57\u6BB5\uFF08\u65E0\u4E0A\u6E38\uFF09\u3002\n * track \u683C\u5F0F\uFF1A`[ahead N]`\u3001`[behind N]`\u3001`[ahead N, behind N]` \u6216\u7A7A\uFF08\u65E0\u4E0A\u6E38/\u5DF2\u540C\u6B65\uFF09\u3002\n */\nfunction parseBranchList(output: string): readonly GitBranch[] {\n const branches: GitBranch[] = []\n for (const line of output.split('\\n')) {\n if (line === '') continue\n const parts = line.split('\\t')\n const name = parts[0]\n const hash = parts[1]\n if (name === undefined || name === '') continue\n const track = parts[3] ?? ''\n const aheadMatch = /ahead (\\d+)/.exec(track)\n const behindMatch = /behind (\\d+)/.exec(track)\n const ahead = aheadMatch ? Number(aheadMatch[1]) : 0\n const behind = behindMatch ? Number(behindMatch[1]) : 0\n branches.push({\n name,\n shortHash: hash === undefined || hash === '' ? null : hash,\n ...(ahead > 0 ? { ahead } : {}),\n ...(behind > 0 ? { behind } : {}),\n })\n }\n return branches\n}\n\nfunction gitError(label: string, stderr: string, stdout: string): GitQueryResponse {\n const message = stderr.trim() || stdout.trim()\n return {\n ok: false,\n error: {\n code: 'git-error',\n message: message !== '' ? message : `git ${label} failed`,\n },\n }\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../../src/host/index.ts", "../../src/host/git.ts", "../../src/host/parser.ts", "../../src/host/core.ts", "../../src/host/actions.ts", "../../src/host/queries.ts", "../../src/contracts/host-endpoints.ts"],
4
+ "sourcesContent": ["/**\n * dsh-git-ui host \u9002\u914D\u5C42\uFF1ACordis/typert \u58F3\u3002\n *\n * \u672C\u6587\u4EF6\u662F host \u7AEF**\u552F\u4E00** import `@deepseek-ai/*` \u7684\u5730\u65B9\u3002\u804C\u8D23\uFF1A\n * 1. \u5C06 Cordis \u670D\u52A1\uFF08subprocess / sessions / sessionPersistence\uFF09\u9002\u914D\u4E3A\n * \u7ED3\u6784\u5316 `SnapshotDeps` \u63A5\u53E3\uFF1B\n * 2. \u8C03\u7528 `createHostEndpoints(deps, config)` \u83B7\u5F97\u7EAF\u4E1A\u52A1\u7AEF\u70B9\uFF1B\n * 3. \u4EE5 `@Remote` \u88C5\u9970\u5668\u5C06\u7AEF\u70B9\u66B4\u9732\u7ED9 typert Gateway\u3002\n *\n * \u4E1A\u52A1\u903B\u8F91\u5168\u90E8\u5728 `contracts/host-endpoints.ts` \u2192 `host/core.ts` /\n * `host/actions.ts` / `host/queries.ts`\uFF0C\u4E0E\u6846\u67B6\u65E0\u5173\u3002\n */\nimport { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { realpath, stat } from 'node:fs/promises'\nimport { createGitRunner, type SubprocessLike } from './git.ts'\nimport { normalizeConfig, type GitStatusConfig, type SnapshotDeps } from './core.ts'\nimport { createHostEndpoints, type HostEndpoints } from '../contracts/host-endpoints.ts'\nimport type { GitActionRequest, GitActionResult, GitQueryRequest, GitQueryResponse, GitSnapshotRequest, GitSnapshotResult } from './types.ts'\n\nexport type { GitSnapshot, GitSnapshotResult, GitSnapshotFailure, GitSnapshotRequest, GitCommit, GitChange, GitAction, GitActionResult, GitActionRequest, GitQuery, GitQueryResult, GitQueryRequest, GitQueryResponse, GitBranch, GitFileStat, GitRef } from './types.ts'\nexport { normalizeConfig, DEFAULT_CONFIG } from './core.ts'\nexport { parseStatusOutput, parseLogOutput, parseBranchOutput, parseNameStatusOutput } from './parser.ts'\nexport { isSafePath, isValidBranchName, runAction } from './actions.ts'\nexport { runQuery } from './queries.ts'\nexport { createHostEndpoints, type HostEndpoints } from '../contracts/host-endpoints.ts'\n\n/** Cordis sessions \u670D\u52A1\u7684\u7ED3\u6784\u5316\u5207\u7247\u3002 */\ninterface SessionsLike {\n get(id: string): { readonly header?: { readonly cwd?: string } } | undefined\n}\n\n/** Cordis sessionPersistence \u670D\u52A1\u7684\u7ED3\u6784\u5316\u5207\u7247\u3002 */\ninterface SessionPersistenceLike {\n inspect(id: string): Promise<{ readonly meta: { readonly cwd?: string } }>\n}\n\n/**\n * gitInfo Remote \u670D\u52A1\uFF1ACordis \u58F3\u3002\n *\n * \u6784\u9020\u65F6\u4ECE Cordis Context \u53D6\u51FA\u5BBF\u4E3B\u670D\u52A1\uFF0C\u9002\u914D\u4E3A `SnapshotDeps`\uFF0C\u518D\u8C03\u7528\n * `createHostEndpoints` \u83B7\u5F97\u7EAF\u4E1A\u52A1\u7AEF\u70B9\u3002\u4E09\u4E2A `@Remote` \u65B9\u6CD5\u4EC5\u505A\u59D4\u6258\u3002\n */\nexport class GitStatusService extends TypertRemoteService {\n static inject = ['subprocess', 'sessions', 'sessionPersistence']\n\n private readonly endpoints: HostEndpoints\n\n constructor(ctx: Context, config: unknown) {\n super(ctx, 'gitInfo')\n const normalizedConfig = normalizeConfig(config)\n const deps = this.buildDeps(ctx, normalizedConfig)\n this.endpoints = createHostEndpoints(deps, normalizedConfig)\n }\n\n /** \u5C06 Cordis \u670D\u52A1\u9002\u914D\u4E3A\u7ED3\u6784\u5316 SnapshotDeps\u3002 */\n private buildDeps(ctx: Context, config: GitStatusConfig): SnapshotDeps {\n const subprocess = ctx.get('subprocess') as SubprocessLike | undefined\n if (subprocess === undefined) {\n // \u8FD4\u56DE\u4E00\u4E2A\u6C38\u8FDC\u5931\u8D25\u7684 deps\u2014\u2014\u7AEF\u70B9\u8C03\u7528\u4F1A\u8D70\u5230 git-unavailable \u964D\u7EA7\u8DEF\u5F84\u3002\n return {\n run: { run: async () => { throw new Error('subprocess service unavailable') } },\n fs: { realpath, stat },\n sessions: { liveCwd: () => undefined, persistedMeta: async () => undefined },\n }\n }\n const sessions = ctx.get('sessions') as SessionsLike | undefined\n const persistence = ctx.get('sessionPersistence') as SessionPersistenceLike | undefined\n return {\n run: createGitRunner(subprocess, config.timeoutMs, config.maxStatusBytes),\n fs: { realpath, stat },\n sessions: {\n liveCwd: (id) => sessions?.get(id)?.header?.cwd,\n persistedMeta: async (id) => {\n if (persistence === undefined) return undefined\n try {\n const inspection = await persistence.inspect(id)\n return { cwd: inspection.meta.cwd }\n } catch {\n return undefined\n }\n },\n },\n }\n }\n\n @Remote('snapshot')\n async snapshot(request: GitSnapshotRequest, signal?: AbortSignal): Promise<GitSnapshotResult> {\n return this.endpoints.snapshot(request, signal)\n }\n\n @Remote('run')\n async run(request: GitActionRequest, signal?: AbortSignal): Promise<GitActionResult> {\n return this.endpoints.run(request, signal)\n }\n\n @Remote('query')\n async query(request: GitQueryRequest, signal?: AbortSignal): Promise<GitQueryResponse> {\n return this.endpoints.query(request, signal)\n }\n}\n\nexport default GitStatusService\n", "/**\n * Git command execution adapter over the host subprocess service.\n *\n * The widget only needs a tiny slice of the subprocess contract; declaring it\n * structurally here (instead of depending on the npm package, whose registry\n * chain is incomplete) keeps the plugin buildable standalone while remaining\n * wire-compatible with the host's `subprocess` service.\n */\nimport { readFile } from 'node:fs/promises'\n\n/** One collected stream disposition (matches the host SubprocessCollect). */\ninterface CollectDisposition {\n readonly collect: {\n readonly maxBytes: number\n /**\n * Spill disposition: when the stream overflows the in-memory tail, the\n * host appends the COMPLETE stream to a private spill file (up to this\n * cap) and `readFrom` reports its path. Without it, only the tail is\n * ever retained and the head (and its change counts) is lost.\n */\n readonly spill?: { readonly maxBytes: number }\n }\n}\n\n/** Structural slice of the host subprocess spawn spec. */\ninterface SpawnSpec {\n readonly argv: readonly string[]\n readonly cwd: string\n readonly stdio: {\n readonly stdout: CollectDisposition\n readonly stderr: CollectDisposition\n }\n readonly graceMs: number\n readonly signal?: AbortSignal\n}\n\n/** Structural slice of the host subprocess handle (collect-mode output). */\ninterface SpawnHandle {\n readonly done: Promise<{ readonly exitCode: number | null; readonly signal: NodeJS.Signals | null }>\n readonly collected: {\n readonly stdout?: {\n readFrom(fromByte: number): { readonly text: string; readonly lossy: boolean; readonly spillPath?: string }\n }\n readonly stderr?: {\n readFrom(fromByte: number): { readonly text: string; readonly lossy: boolean; readonly spillPath?: string }\n }\n }\n}\n\n/** Minimal subprocess-service face the adapter consumes. */\nexport interface SubprocessLike {\n spawn(spec: SpawnSpec): SpawnHandle\n}\n\n/** One git command outcome. */\nexport interface GitRunResult {\n /** Process exit code; null when terminated by a signal. */\n readonly exitCode: number | null\n readonly stdout: string\n readonly stderr: string\n /** True when the run was killed by our timeout (or the caller's signal). */\n readonly timedOut: boolean\n /**\n * True when the final stdout text is still incomplete: the collected\n * output overflowed its byte cap AND the spill file was unavailable (no\n * spill configured on the host, or the spill cap also overflowed).\n */\n readonly stdoutLossy: boolean\n}\n\n/** The run primitive the snapshot orchestration uses. */\nexport interface GitRunner {\n run(argv: readonly string[], opts: { readonly cwd: string; readonly signal?: AbortSignal }): Promise<GitRunResult>\n}\n\n/**\n * Adapt the host subprocess service into a `GitRunner` with a per-command\n * timeout. A timed-out run resolves (never rejects) with `timedOut: true`;\n * only spawn-level failures (e.g. git not installed) reject.\n *\n * Overflow handling: stdout/stderr collect with a spill cap of\n * `maxBytes * 16` (default 4 MiB memory tail \u2192 64 MiB spill file). When the\n * tail overflowed but the spill file holds the complete stream, the runner\n * reads the file and reports `stdoutLossy: false` \u2014 the change COUNTS stay\n * exact. `stdoutLossy: true` is reserved for the doubly-overflowed case\n * (spill also exceeded), where the head is genuinely lost.\n */\nexport function createGitRunner(subprocess: SubprocessLike, timeoutMs: number, maxBytes: number): GitRunner {\n const spillMaxBytes = maxBytes * 16\n return {\n async run(argv, opts) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n const signal = opts.signal === undefined\n ? controller.signal\n : AbortSignal.any([controller.signal, opts.signal])\n const handle = subprocess.spawn({\n argv,\n cwd: opts.cwd,\n stdio: {\n stdout: { collect: { maxBytes, spill: { maxBytes: spillMaxBytes } } },\n stderr: { collect: { maxBytes, spill: { maxBytes: spillMaxBytes } } },\n },\n graceMs: 200,\n signal,\n })\n let outcome: Awaited<SpawnHandle['done']>\n try {\n // `done` rejects for spawn-level failures; an abort-triggered\n // rejection is the timeout path and resolves as timedOut.\n outcome = await handle.done\n } catch (error) {\n if (controller.signal.aborted || opts.signal?.aborted === true) {\n return { exitCode: null, stdout: '', stderr: '', timedOut: true, stdoutLossy: false }\n }\n throw error\n }\n const stdout = handle.collected.stdout?.readFrom(0)\n const stderr = handle.collected.stderr?.readFrom(0)\n const stdoutResolved = await resolveStdout(stdout)\n return {\n exitCode: outcome.exitCode,\n stdout: stdoutResolved.text,\n stderr: stderr?.text ?? '',\n timedOut: controller.signal.aborted || opts.signal?.aborted === true,\n stdoutLossy: stdoutResolved.lossy,\n }\n } finally {\n clearTimeout(timer)\n }\n },\n }\n}\n\n/**\n * Resolve the stdout text from a collect read: the in-memory tail, or \u2014 when\n * the read is lossy and the host spilled the complete stream to a file \u2014 the\n * spill file contents (so change COUNTS stay exact). A failed spill read\n * falls back to the tail and keeps `lossy: true` (head genuinely lost).\n */\nasync function resolveStdout(\n read: { readonly text: string; readonly lossy: boolean; readonly spillPath?: string } | undefined,\n): Promise<{ readonly text: string; readonly lossy: boolean }> {\n if (read === undefined) return { text: '', lossy: false }\n if (!read.lossy || read.spillPath === undefined) return { text: read.text, lossy: read.lossy }\n try {\n return { text: await readFile(read.spillPath, 'utf8'), lossy: false }\n } catch {\n return { text: read.text, lossy: true }\n }\n}\n", "/**\n * Pure parsers for the git porcelain/log output shapes used by the widget.\n * No side effects and no I/O \u2014 fully unit-testable against literal fixtures\n * (verified against real `git status --porcelain=v1 -z --branch` output).\n */\nimport type { GitChange, GitChangeStatus, GitCommit, GitRef, GraphCommit } from './types.ts'\n\n/** Parsed status counts plus the (possibly capped) change list. */\nexport interface ParsedStatus {\n readonly branch: string | null\n readonly unborn: boolean\n readonly staged: number\n readonly modified: number\n readonly untracked: number\n readonly ahead: number\n readonly behind: number\n readonly changes: readonly GitChange[]\n readonly truncated: boolean\n}\n\n/** The NUL byte separating porcelain v1 -z entries. */\nconst NUL = '\\u0000'\n/** The unit separator used by the log --format payload. */\nconst LOG_SEP = '\\u001f'\n\ninterface StatusHeader {\n readonly branch: string | null\n readonly unborn: boolean\n readonly ahead: number\n readonly behind: number\n}\n\n/**\n * Parse the `## ` header line of `git status --porcelain=v1 -z --branch`.\n * Recognized shapes (verified against git 2.x):\n * `## main`\n * `## main...origin/main`\n * `## main...origin/main [ahead 1]`\n * `## main...origin/main [behind 2]`\n * `## main...origin/main [ahead 1, behind 2]`\n * `## HEAD (no branch)` (detached)\n * `## HEAD (detached at <hash>)` (detached, older git)\n * `## No commits yet on main` (unborn)\n * `## Initial commit on main` (unborn, older git)\n */\nexport function parseStatusHeader(line: string): StatusHeader {\n const body = line.startsWith('## ') ? line.slice(3) : line\n if (body === '') return { branch: null, unborn: false, ahead: 0, behind: 0 }\n\n const unbornMatch = /^(?:No commits yet on|Initial commit on)\\s+(.+)$/.exec(body)\n if (unbornMatch !== null) {\n return { branch: unbornMatch[1] ?? null, unborn: true, ahead: 0, behind: 0 }\n }\n\n const detached = /^HEAD(?:\\s+\\([^)]*\\))?$/.exec(body)\n if (detached !== null) {\n return { branch: null, unborn: false, ahead: 0, behind: 0 }\n }\n\n const bracketMatch = /^(.*?)\\s*\\[([^\\]]+)\\]$/.exec(body)\n const core = bracketMatch?.[1] ?? body\n let ahead = 0\n let behind = 0\n if (bracketMatch?.[2] !== undefined) {\n for (const part of bracketMatch[2].split(',')) {\n const trimmed = part.trim()\n const aheadMatch = /^ahead (\\d+)$/.exec(trimmed)\n const behindMatch = /^behind (\\d+)$/.exec(trimmed)\n if (aheadMatch !== null) ahead = Number(aheadMatch[1])\n if (behindMatch !== null) behind = Number(behindMatch[1])\n }\n }\n // The core is `<branch>...<upstream>` \u2014 the branch never contains `...`.\n const branch = core.split('...', 1)[0] ?? core\n return { branch: branch === '' ? null : branch, unborn: false, ahead, behind }\n}\n\n/** \u5355\u5217\u72B6\u6001\u7801 \u2192 \u53D8\u66F4\u72B6\u6001\u6620\u5C04\uFF08\u771F\u5B9E\u51B2\u7A81\u7531 isConflicted \u5355\u72EC\u5224\u5B9A\uFF09\u3002 */\nfunction singleStatus(code: string): GitChangeStatus {\n switch (code) {\n case 'A': return 'added'\n case 'M': return 'modified'\n case 'D': return 'deleted'\n case 'R': return 'renamed'\n case 'T': return 'typechange'\n case 'C': return 'added'\n default: return 'modified'\n }\n}\n\n/**\n * \u771F\u5B9E\u5408\u5E76\u51B2\u7A81\uFF1A\u4EFB\u4E00\u4FA7\u4E3A U\uFF08UU/AU/UD/UA/DU\uFF09\uFF0C\u6216\u53CC\u65B9\u540C\u6DFB/\u540C\u5220\uFF08AA/DD\uFF09\u3002\n * \u6CE8\u610F MM/AM/MD \u7B49\u300C\u5DF2\u6682\u5B58 + \u5DE5\u4F5C\u533A\u518D\u6539\u300D\u662F\u5408\u6CD5\u6DF7\u5408\u6001\u800C\u975E\u51B2\u7A81\n * \uFF08\u65E7\u89C4\u5219\u300C\u53CC\u5217\u5747\u975E\u7A7A\u5373\u51B2\u7A81\u300D\u4F1A\u628A MM \u8BEF\u62A5\u4E3A\u51B2\u7A81\uFF09\u3002\n */\nfunction isConflicted(x: string, y: string): boolean {\n return x === 'U' || y === 'U' || (x === 'A' && y === 'A') || (x === 'D' && y === 'D')\n}\n\n/**\n * Parse the full `git status --porcelain=v1 -z --branch` output.\n * -z format: every entry (header and each `XY path`) is NUL-terminated; a\n * rename/copy entry emits `R <new>\\0<old>\\0` so the following item is the\n * source path and must be consumed without becoming a change itself.\n *\n * \u6DF7\u5408\u72B6\u6001\u62C6\u5206\uFF08IDEA \u5F0F\uFF09\uFF1AX\u3001Y \u5747\u975E\u7A7A\u7684\u5408\u6CD5\u5BF9\uFF08MM/AM/MD/RM\u2026\uFF09\u62C6\u4E3A\n * \u300C\u5DF2\u6682\u5B58\u4FA7 + \u672A\u6682\u5B58\u4FA7\u300D\u4E24\u6761 GitChange\u2014\u2014UI \u636E\u6B64\u628A\u540C\u4E00\u6587\u4EF6\u5206\u522B\u5217\u5165\n * \u300C\u5DF2\u6682\u5B58\u66F4\u6539\u300D\u4E0E\u300C\u66F4\u6539\u300D\u4E24\u7EC4\uFF0C\u4E24\u4FA7\u53EF\u72EC\u7ACB\u64CD\u4F5C\u3001\u5DEE\u5F02\u57FA\u7EBF\u552F\u4E00\u3002\n * \u771F\u5B9E\u51B2\u7A81\uFF08isConflicted\uFF09\u4FDD\u6301\u5355\u6761 conflicted \u6761\u76EE\u3002\n */\nexport function parseStatusOutput(output: string, maxChanges: number): ParsedStatus {\n const raw = output.split(NUL)\n // Trailing NUL produces a final empty segment; drop it.\n const segments = raw[raw.length - 1] === '' ? raw.slice(0, -1) : raw\n const header = parseStatusHeader(segments[0] ?? '')\n\n let staged = 0\n let modified = 0\n let untracked = 0\n const changes: GitChange[] = []\n let truncated = false\n\n /** \u6536\u5F55\u4E00\u6761\u53D8\u66F4\u6761\u76EE\uFF1B\u8D85\u51FA\u4E0A\u9650\u4EC5\u7F6E\u622A\u65AD\u6807\u8BB0\uFF08\u8BA1\u6570\u4E0D\u53D7\u5F71\u54CD\uFF09\u3002\n * isDirectory \u7531 git \u8F93\u51FA\u6743\u5A01\u6807\u8BB0\uFF08\u672A\u8DDF\u8E2A\u76EE\u5F55\u6761\u76EE\u4E3A `dir/` \u5C3E\u659C\u6760\uFF09\uFF0C\n * \u5C55\u793A\u5C42\u4F9D\u8D56\u6B64\u5B57\u6BB5\uFF0C\u4E0D\u518D\u81EA\u884C\u89E3\u6790\u8DEF\u5F84\u5B57\u7B26\u4E32\u3002 */\n const pushChange = (path: string, status: GitChangeStatus, isStaged: boolean): void => {\n if (changes.length < maxChanges) {\n changes.push({ path, status, staged: isStaged, isDirectory: path.endsWith('/') })\n } else {\n truncated = true\n }\n }\n\n for (let index = 1; index < segments.length; index += 1) {\n const entry = segments[index] ?? ''\n const x = entry[0] ?? ' '\n const y = entry[1] ?? ' '\n const path = entry.slice(3)\n if (x === ' ' && y === ' ') continue\n if (x === 'R' || x === 'C') {\n // -z: the source path is the next segment \u2014 consume it.\n index += 1\n }\n if (x === '?' && y === '?') {\n untracked += 1\n pushChange(path, 'untracked', false)\n continue\n }\n // \u8BA1\u6570\u4ECD\u6309 X/Y \u4E24\u5217\u5206\u522B\u7D2F\u8BA1\uFF1B\u62C6\u53CC\u6761\u76EE\u4E0D\u6539\u53D8\u603B\u6570\u3002\n if (x !== ' ' && x !== '?') staged += 1\n if (y !== ' ' && y !== '?') modified += 1\n\n if (isConflicted(x, y)) {\n // \u51B2\u7A81\u6587\u4EF6\u6309\u5355\u6761\u5C55\u793A\uFF08IDEA \u51B2\u7A81\u6761\u76EE\u5F62\u6001\uFF09\uFF0C\u5F52\u5165\u5DF2\u6682\u5B58\u7EC4\u3002\n pushChange(path, 'conflicted', true)\n } else if (x !== ' ' && y !== ' ') {\n // \u6DF7\u5408\u6001\uFF1A\u5DF2\u6682\u5B58\u4FA7\u72B6\u6001\u53D6 X\uFF0C\u672A\u6682\u5B58\u4FA7\u72B6\u6001\u53D6 Y\u3002\n pushChange(path, singleStatus(x), true)\n pushChange(path, singleStatus(y), false)\n } else {\n const isStaged = x !== ' '\n pushChange(path, singleStatus(isStaged ? x : y), isStaged)\n }\n }\n\n return {\n branch: header.branch,\n unborn: header.unborn,\n staged,\n modified,\n untracked,\n ahead: header.ahead,\n behind: header.behind,\n changes,\n truncated,\n }\n}\n\n/**\n * Parse `git log -n 5 --format=%H%x1f%h%x1f%s%x1f%an%x1f%aI` output.\n * One commit per line, fields separated by the unit separator; empty output\n * (unborn repository) yields `[]`.\n */\nexport function parseLogOutput(output: string): readonly GitCommit[] {\n const commits: GitCommit[] = []\n for (const line of output.split('\\n')) {\n if (line === '') continue\n const [hash, shortHash, subject, author, dateIso] = line.split(LOG_SEP)\n if (hash === undefined || hash === '') continue\n commits.push({\n hash,\n shortHash: shortHash ?? '',\n subject: subject ?? '',\n author: author ?? '',\n dateIso: dateIso ?? '',\n })\n }\n return commits\n}\n\n/**\n * \u89E3\u6790\u5E26\u56FE\u7684 log \u8F93\u51FA\uFF1A\n * `%H%x1f%h%x1f%s%x1f%an%x1f%aI%x1f%P%x1f%D`\n * \u5176\u4E2D `%P` \u4E3A\u7A7A\u683C\u5206\u9694\u7684\u7236\u63D0\u4EA4\u54C8\u5E0C\uFF08\u6839\u63D0\u4EA4\u4E3A\u7A7A\uFF09\uFF0C`%D` \u4E3A ref \u88C5\u9970\u3002\n * \u8FD4\u56DE\u9002\u5408\u5206\u652F\u56FE\u6E32\u67D3\u5668\u7684 `GraphCommit[]`\u3002\n */\nexport function parseGraphLogOutput(output: string, remotes: readonly string[] = []): readonly GraphCommit[] {\n const commits: GraphCommit[] = []\n for (const line of output.split('\\n')) {\n if (line === '') continue\n const [hash, shortHash, subject, author, dateIso, parentField, decoField] = line.split(LOG_SEP)\n if (hash === undefined || hash === '') continue\n const parents = (parentField ?? '')\n .split(' ')\n .filter((p) => p !== '')\n commits.push({\n hash,\n shortHash: shortHash ?? '',\n subject: subject ?? '',\n author: author ?? '',\n dateIso: dateIso ?? '',\n parents,\n refs: parseDecorations(decoField ?? '', remotes),\n })\n }\n return commits\n}\n\n/**\n * \u89E3\u6790 `%D` \u88C5\u9970\u4E32\uFF0C\u5F62\u5982 `HEAD -> main, origin/main, tag: v1.0`\uFF1B\u7A7A\u4E32\u65E0 refs\u3002\n * \u5206\u7C7B\u89C4\u5219\uFF1A`HEAD -> x` \u4E3A\u5F53\u524D\u5206\u652F\uFF1B`tag: t` \u4E3A\u6807\u7B7E\uFF1B\n * \u5E26\u8FDC\u7A0B\u524D\u7F00\uFF08`<remote>/\u2026`\uFF09\u4E3A\u8FDC\u7A0B\u5206\u652F\uFF1B\u5176\u4F59\u4E3A\u672C\u5730\u5206\u652F\u3002\n */\nexport function parseDecorations(decorations: string, remotes: readonly string[]): readonly GitRef[] {\n const trimmed = decorations.trim()\n if (trimmed === '') return []\n const refs: GitRef[] = []\n for (const token of trimmed.split(', ')) {\n if (token.startsWith('HEAD -> ')) {\n refs.push({ kind: 'branch', name: token.slice(8), head: true })\n } else if (token.startsWith('tag: ')) {\n refs.push({ kind: 'tag', name: token.slice(5), head: false })\n } else if (remotes.some((remote) => token === remote || token.startsWith(`${remote}/`))) {\n refs.push({ kind: 'remote', name: token, head: false })\n } else {\n refs.push({ kind: 'branch', name: token, head: false })\n }\n }\n return refs\n}\n\n/**\n * \u89E3\u6790 `git show -s --format=%H%x1f%h%x1f%s%x1f%an%x1f%aI%x1f%b` \u8F93\u51FA\uFF1A\n * \u524D\u4E94\u4E2A\u5B57\u6BB5\u4E3A\u673A\u5668\u53EF\u8BFB\u5143\u6570\u636E\uFF0C\u7B2C\u516D\u5B57\u6BB5\u8D77\u4E3A %b \u6B63\u6587\n * \uFF08%b \u5DF2\u6392\u9664 subject \u9996\u6BB5\u843D\uFF0C\u5929\u7136\u65E0\u91CD\u590D\u5C55\u793A\u95EE\u9898\uFF09\u3002\n */\nexport function parseShowMeta(output: string): { readonly commit: GitCommit; readonly body: string } | null {\n const trimmed = output.trimEnd()\n if (trimmed === '') return null\n const [hash, shortHash, subject, author, dateIso, ...bodyParts] = trimmed.split(LOG_SEP)\n if (hash === undefined || hash === '') return null\n return {\n commit: {\n hash,\n shortHash: shortHash ?? '',\n subject: subject ?? '',\n author: author ?? '',\n dateIso: dateIso ?? '',\n },\n body: bodyParts.join(LOG_SEP).trimEnd(),\n }\n}\n\n/** `--name-status` \u72B6\u6001\u7801 \u2192 \u53D8\u66F4\u72B6\u6001\u6620\u5C04\u3002 */\nfunction nameStatusCode(code: string): GitChangeStatus {\n switch (code) {\n case 'A': return 'added'\n case 'D': return 'deleted'\n case 'R': return 'renamed'\n case 'T': return 'typechange'\n case 'U': return 'conflicted'\n default: return 'modified'\n }\n}\n\n/**\n * \u89E3\u6790 `git show --format= --name-status -z` \u8F93\u51FA\uFF1ANUL \u5206\u9694\uFF0C\n * `X\\0path\\0`\uFF0Crename/copy \u4E3A `R100\\0old\\0new\\0`\uFF08\u53D6\u65B0\u8DEF\u5F84\uFF09\u3002\n * -z \u539F\u59CB\u8F93\u51FA\u4E0D\u5F15\u53F7\u5316\uFF0C\u975E ASCII \u8DEF\u5F84\u5929\u7136\u514D\u75AB\u4E71\u7801\uFF08\u65E7 --stat \u516B\u8FDB\u5236\u8F6C\u4E49\u95EE\u9898\u7684\u6839\u56E0\u6D88\u9664\uFF09\u3002\n */\nexport function parseNameStatusOutput(output: string): readonly { readonly path: string; readonly status: GitChangeStatus }[] {\n const raw = output.split(NUL)\n const segments = raw[raw.length - 1] === '' ? raw.slice(0, -1) : raw\n const rows: { path: string; status: GitChangeStatus }[] = []\n for (let i = 0; i < segments.length; i += 1) {\n const entry = segments[i] ?? ''\n if (entry === '') continue\n const code = entry[0] ?? ' '\n if (code === 'R' || code === 'C') {\n // rename/copy\uFF1Aold \u5728 i+1\u3001new \u5728 i+2\uFF0C\u5C55\u793A\u53D6\u65B0\u8DEF\u5F84\u3002\n rows.push({ path: segments[i + 2] ?? '', status: nameStatusCode(code) })\n i += 2\n } else {\n rows.push({ path: segments[i + 1] ?? '', status: nameStatusCode(code) })\n i += 1\n }\n }\n return rows\n}\n\n/**\n * Parse `git branch --show-current` output: the branch name, or null when\n * empty (detached HEAD).\n */\nexport function parseBranchOutput(output: string): string | null {\n const trimmed = output.trim()\n return trimmed === '' ? null : trimmed\n}\n\n", "/**\n * Framework-free snapshot orchestration: session cwd resolution + git command\n * sequence + frozen GitSnapshot assembly. Every dependency is injected\n * structurally, so the whole flow is testable without a cordis runtime; the\n * cordis shell (GitStatusService) only adapts host services into these faces.\n */\nimport { parseBranchOutput, parseLogOutput, parseStatusOutput } from './parser.ts'\nimport type { GitRunner } from './git.ts'\nimport type { GitSnapshot, GitSnapshotResult } from './types.ts'\n\n/** Resolved plugin config (already normalized; see normalizeConfig). */\nexport interface GitStatusConfig {\n readonly timeoutMs: number\n readonly maxStatusBytes: number\n readonly maxChanges: number\n readonly defaultRefreshIntervalMs: number\n}\n\n/** Session identity lookup: live first, persisted fallback. */\nexport interface SessionLookup {\n /** Live session cwd; undefined when the session is cold or absent in memory. */\n liveCwd(sessionId: string): string | undefined\n /**\n * Persisted session metadata; resolves to undefined when no persisted\n * session exists, and to `{ cwd }` (cwd possibly undefined) otherwise.\n */\n persistedMeta(sessionId: string): Promise<{ readonly cwd?: string } | undefined>\n}\n\n/** Filesystem primitives (node:fs/promises slices). */\nexport interface FsLike {\n realpath(path: string): Promise<string>\n stat(path: string): Promise<{ isDirectory(): boolean }>\n}\n\n/** Everything the snapshot flow needs beyond the session lookup. */\nexport interface SnapshotDeps {\n readonly run: GitRunner\n readonly fs: FsLike\n readonly sessions: SessionLookup\n /** Injectable clock for deterministic tests. */\n readonly now?: () => number\n /** Caller-side cancellation (Remote `signal` slot): aborts in-flight git runs. */\n readonly signal?: AbortSignal\n}\n\n/** Defaults applied by normalizeConfig when a value is absent or invalid. */\nexport const DEFAULT_CONFIG: GitStatusConfig = {\n timeoutMs: 5000,\n maxStatusBytes: 4 * 1024 * 1024,\n maxChanges: 100,\n defaultRefreshIntervalMs: 30_000,\n}\n\n/** Coerce a raw patch config value into a validated GitStatusConfig. */\nexport function normalizeConfig(raw: unknown): GitStatusConfig {\n const value = (raw ?? {}) as Record<string, unknown>\n const numberOr = (key: string, fallback: number): number => {\n const candidate = value[key]\n return typeof candidate === 'number' && Number.isFinite(candidate) && candidate >= 0\n ? candidate\n : fallback\n }\n return {\n timeoutMs: numberOr('timeoutMs', DEFAULT_CONFIG.timeoutMs) || DEFAULT_CONFIG.timeoutMs,\n maxStatusBytes: numberOr('maxStatusBytes', DEFAULT_CONFIG.maxStatusBytes) || DEFAULT_CONFIG.maxStatusBytes,\n maxChanges: Math.floor(numberOr('maxChanges', DEFAULT_CONFIG.maxChanges) || DEFAULT_CONFIG.maxChanges),\n defaultRefreshIntervalMs: numberOr('defaultRefreshIntervalMs', DEFAULT_CONFIG.defaultRefreshIntervalMs),\n }\n}\n\n/** Outcome of the cwd resolution step. */\ntype CwdResolution =\n | { readonly ok: true; readonly cwd: string }\n | { readonly ok: false; readonly error: Extract<GitSnapshotResult, { ok: false }>['error'] }\n\nasync function resolveCwd(sessions: SessionLookup, sessionId: string): Promise<CwdResolution> {\n const live = sessions.liveCwd(sessionId)\n if (live !== undefined) return { ok: true, cwd: live }\n const persisted = await sessions.persistedMeta(sessionId)\n if (persisted === undefined) return { ok: false, error: { code: 'session-not-found', sessionId } }\n if (persisted.cwd === undefined) return { ok: false, error: { code: 'cwd-unavailable', sessionId } }\n return { ok: true, cwd: persisted.cwd }\n}\n\n/** Classify a failed run outcome into a snapshot failure. */\nfunction runFailure(result: { readonly timedOut: boolean }, detail: string): Extract<GitSnapshotResult, { ok: false }>['error'] {\n return result.timedOut ? { code: 'timeout' } : { code: 'git-unavailable', detail }\n}\n\n/** Run one command, mapping a spawn-level failure to a snapshot failure. */\nexport async function runCommand(\n runner: GitRunner,\n argv: readonly string[],\n cwd: string,\n label: string,\n signal?: AbortSignal,\n): Promise<{ readonly run: Awaited<ReturnType<GitRunner['run']>> } | { readonly failure: Extract<GitSnapshotResult, { ok: false }>['error'] }> {\n try {\n return { run: await runner.run(argv, { cwd, ...(signal === undefined ? {} : { signal }) }) }\n } catch (error) {\n return { failure: { code: 'git-unavailable', detail: `${label}: ${error instanceof Error ? error.message : String(error)}` } }\n }\n}\n\n/**\n * Resolve a session's repository workspace: cwd (live or persisted), the\n * realpath'd directory, and the git work-tree root via `rev-parse\n * --show-toplevel`. Shared by the snapshot flow and the operation runner.\n */\nexport type WorkspaceResolution =\n | { readonly ok: true; readonly cwd: string; readonly root: string }\n | { readonly ok: false; readonly error: Extract<GitSnapshotResult, { ok: false }>['error'] }\n\nexport async function resolveWorkspace(\n deps: SnapshotDeps,\n sessionId: string,\n): Promise<WorkspaceResolution> {\n const resolved = await resolveCwd(deps.sessions, sessionId)\n if (!resolved.ok) return { ok: false, error: resolved.error }\n\n let realCwd: string\n try {\n realCwd = await deps.fs.realpath(resolved.cwd)\n const stat = await deps.fs.stat(realCwd)\n if (!stat.isDirectory()) {\n return { ok: false, error: { code: 'path-not-found', path: realCwd } }\n }\n } catch {\n return { ok: false, error: { code: 'path-not-found', path: resolved.cwd } }\n }\n\n const toplevel = await runCommand(deps.run, ['git', 'rev-parse', '--show-toplevel'], realCwd, 'rev-parse', deps.signal)\n if ('failure' in toplevel) return { ok: false, error: toplevel.failure }\n if (toplevel.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (toplevel.run.exitCode !== 0) {\n // exit 128 covers both \"not a git repository\" (plain directory) and\n // other git failures (dubious ownership, unreadable work tree, \u2026).\n // Only the former is a stable non-repo state; everything else surfaces\n // as git-unavailable with the actual reason instead of a misleading\n // \"no git repository\" pill.\n const stderr = toplevel.run.stderr\n if (!stderr.includes('not a git repository')) {\n return { ok: false, error: runFailure(toplevel.run, `git rev-parse failed: ${stderr.trim() || `exit ${String(toplevel.run.exitCode)}`}`) }\n }\n return { ok: false, error: { code: 'not-a-git-repo' } }\n }\n const root = toplevel.run.stdout.trim()\n if (root === '') return { ok: false, error: { code: 'not-a-git-repo' } }\n return { ok: true, cwd: realCwd, root }\n}\n\n/**\n * Build one frozen GitSnapshot for a session working directory.\n * Command sequence (all read-only; every command after the first runs with\n * the repository root as cwd):\n * 1. `git rev-parse --show-toplevel` \u2014 repo detection (exit 128 \u2192 not-a-git-repo)\n * 2. `git branch --show-current` \u2014 null when detached\n * 3. `git rev-parse --short HEAD` \u2014 null + unborn when the repo has no commits\n * 4. `git status --porcelain=v1 -z --branch --untracked-files=all`\n * 5. `git log -n 5 --format=%H%x1f%h%x1f%s%x1f%an%x1f%aI`\n *\n * --untracked-files=all\uFF1Agit \u9ED8\u8BA4 normal \u6A21\u5F0F\u4F1A\u628A\u6574\u76EE\u5F55\u672A\u8DDF\u8E2A\u6298\u53E0\u4E3A\u5355\u6761\n * `?? dir/`\uFF08\u5C3E\u659C\u6760\uFF09\u4E14\u4E0D\u679A\u4E3E\u5176\u5185\u90E8\u6587\u4EF6\u2014\u2014\u9690\u85CF\u76EE\u5F55\uFF08.agent/.tianqi \u7B49\uFF09\u7684\n * \u53D8\u66F4\u56E0\u6B64\u4ECE\u4E0D\u8FDB\u5165\u53D8\u66F4\u6E05\u5355\u3002`all` \u5F3A\u5236\u9010\u6587\u4EF6\u679A\u4E3E\uFF08\u4E0E IDEA / VSCode \u4E00\u81F4\uFF09\uFF0C\n * \u5185\u90E8\u6587\u4EF6\u5F97\u4EE5\u5C55\u793A\uFF1BmaxChanges \u622A\u65AD\u5217\u8868\u3001maxStatusBytes spill \u4FDD\u8BA1\u6570\u7CBE\u786E\uFF0C\n * \u8D85\u5927\u672A\u8DDF\u8E2A\u6811\uFF08\u5982\u672A gitignore \u7684\u6784\u5EFA\u4EA7\u7269\uFF09\u7ECF\u6B64\u8DEF\u5F84\u4F18\u96C5\u964D\u7EA7\u3002\n */\nexport async function snapshotForSession(\n deps: SnapshotDeps,\n config: GitStatusConfig,\n sessionId: string,\n): Promise<GitSnapshotResult> {\n const workspace = await resolveWorkspace(deps, sessionId)\n if (!workspace.ok) return { ok: false, error: workspace.error }\n const root = workspace.root\n\n const branchRun = await runCommand(deps.run, ['git', 'branch', '--show-current'], root, 'branch', deps.signal)\n if ('failure' in branchRun) return { ok: false, error: branchRun.failure }\n if (branchRun.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n const branch = branchRun.run.exitCode === 0 ? parseBranchOutput(branchRun.run.stdout) : null\n\n const headRun = await runCommand(deps.run, ['git', 'rev-parse', '--short', 'HEAD'], root, 'rev-parse HEAD', deps.signal)\n if ('failure' in headRun) return { ok: false, error: headRun.failure }\n if (headRun.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n // A failed HEAD read (non-timeout) only nulls the hash: the authoritative\n // unborn flag comes from the status header below (`## No commits yet on\n // main`), so a corrupt repo is never misreported as \"no commits\".\n const head = headRun.run.exitCode === 0 ? (headRun.run.stdout.trim() || null) : null\n\n // --untracked-files=all\uFF1A\u5F3A\u5236\u679A\u4E3E\u672A\u8DDF\u8E2A\u76EE\u5F55\u5185\u90E8\u6587\u4EF6\uFF08\u6839\u56E0\u4FEE\u590D\u2014\u2014\u89C1\u6A21\u5757\u6CE8\u91CA\uFF09\u3002\n const status = await runCommand(deps.run, ['git', 'status', '--porcelain=v1', '-z', '--branch', '--untracked-files=all'], root, 'status', deps.signal)\n if ('failure' in status) return { ok: false, error: status.failure }\n if (status.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (status.run.exitCode !== 0) {\n return { ok: false, error: runFailure(status.run, `git status exited ${String(status.run.exitCode)}`) }\n }\n const parsed = parseStatusOutput(status.run.stdout, config.maxChanges)\n\n const log = await runCommand(deps.run, ['git', 'log', '-n', '5', '--format=%H%x1f%h%x1f%s%x1f%an%x1f%aI'], root, 'log', deps.signal)\n if ('failure' in log) return { ok: false, error: log.failure }\n if (log.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n const recentCommits = log.run.exitCode === 0 ? parseLogOutput(log.run.stdout) : []\n\n const checkedAt = deps.now?.() ?? Date.now()\n const snapshot: GitSnapshot = {\n root,\n branch,\n head,\n unborn: parsed.unborn,\n dirty: parsed.staged + parsed.modified + parsed.untracked > 0,\n staged: parsed.staged,\n modified: parsed.modified,\n untracked: parsed.untracked,\n ahead: parsed.ahead,\n behind: parsed.behind,\n lastCommit: recentCommits[0] ?? null,\n recentCommits,\n changes: parsed.changes,\n truncated: parsed.truncated || ('run' in status && status.run.stdoutLossy),\n refreshIntervalMs: config.defaultRefreshIntervalMs,\n checkedAt,\n }\n return { ok: true, value: snapshot }\n}\n", "/**\n * Framework-free git management operation runner.\n *\n * Same layering as `core.ts`: every dependency is injected structurally, the\n * whole flow is testable against real temporary git repositories without a\n * cordis runtime, and `GitStatusService` only adapts host services into the\n * `SnapshotDeps` face.\n *\n * Security model: the browser only ever sends a `sessionId` plus\n * repository-relative paths (as listed in a snapshot's `changes`). Paths are\n * validated against the work-tree root (absolute paths and `..` escapes are\n * rejected) and every git invocation uses `--` so a path can never be\n * interpreted as an option. Commands run through the same subprocess adapter\n * as the read-only snapshot flow \u2014 no shell is involved.\n */\nimport { resolve, sep } from 'node:path'\nimport { resolveWorkspace, runCommand, snapshotForSession, type GitStatusConfig, type SnapshotDeps } from './core.ts'\nimport type { GitAction, GitActionResult, GitActionRequest, GitOperationErrorCode } from './types.ts'\n\n/** Build the command sequence for one action, validating every path against the root. */\nfunction buildArgv(action: GitAction, root: string): { readonly argv: readonly (readonly string[])[] } | { readonly error: string } {\n switch (action.kind) {\n case 'stage':\n return withPaths([['git', 'add', '--']], action.paths, root)\n case 'stage-all':\n return { argv: [['git', 'add', '-A']] }\n case 'unstage':\n return withPaths([['git', 'restore', '--staged', '--']], action.paths, root)\n case 'unstage-all':\n return { argv: [['git', 'restore', '--staged', '--', '.']] }\n case 'discard':\n return withPaths([['git', 'restore', '--']], action.paths, root)\n case 'discard-all':\n // Reset the index to HEAD first, then the work tree to the index \u2014 the\n // IDE-style \"roll back everything tracked\" semantics.\n return { argv: [['git', 'restore', '--staged', '--', '.'], ['git', 'restore', '--', '.']] }\n case 'commit': {\n // Message emptiness is validated by runAction (git-error), not here.\n const message = action.message.trim()\n if (action.paths === undefined || action.paths.length === 0) {\n return { argv: [['git', 'commit', '-m', message]] }\n }\n // \u4E24\u6B65\u5E8F\u5217\uFF08IDE \u5F0F\u300C\u63D0\u4EA4\u6240\u9009\u6587\u4EF6\u300D\u8BED\u4E49\uFF0C\u542B\u672A\u8DDF\u8E2A\u6587\u4EF6\uFF09\uFF1A\n // 1. `git add -- <paths>` \u5148\u628A\u6240\u9009\u8DEF\u5F84\u7EB3\u5165\u7D22\u5F15\u2014\u2014\u88F8\u7684\n // `git commit -- <\u672A\u8DDF\u8E2A\u8DEF\u5F84>` \u4F1A\u62A5 pathspec \u9519\u8BEF\uFF0C\u5148\u884C\u6682\u5B58\u4F7F\u5176\u53EF\u5339\u914D\uFF1B\n // 2. `git commit -m <msg> -- <paths>` \u6309\u8DEF\u5F84\u9650\u5B9A\u63D0\u4EA4\u8FD9\u4E9B\u8DEF\u5F84\u7684\u5DE5\u4F5C\u533A\u5185\u5BB9\uFF0C\n // \u5176\u4F59\u5DF2\u6682\u5B58\u6587\u4EF6\u4E0D\u53D7\u5F71\u54CD\u3002\u5BF9\u5DF2\u8DDF\u8E2A\u8DEF\u5F84\u4E0E\u5355\u547D\u4EE4\u5B8C\u5168\u7B49\u4EF7\uFF08\u5DF2\u5B9E\u6D4B\u9A8C\u8BC1\uFF09\u3002\n return withPaths([['git', 'add', '--'], ['git', 'commit', '-m', message, '--']], action.paths, root)\n }\n case 'branch-create': {\n // Name validity is validated by runAction (invalid-name), not here.\n const from = action.from === undefined || action.from === '' ? [] : [action.from]\n return { argv: [['git', 'branch', action.name, ...from]] }\n }\n case 'branch-checkout':\n return { argv: [['git', 'checkout', action.name]] }\n case 'branch-delete':\n return { argv: [['git', 'branch', action.force === true ? '-D' : '-d', action.name]] }\n case 'fetch':\n // fetch --all --prune\uFF1A\u62C9\u53D6\u6240\u6709\u8FDC\u7A0B\u5F15\u7528\u66F4\u65B0 + \u6E05\u7406\u5DF2\u5220\u9664\u7684\u8FDC\u7A0B\u8DDF\u8E2A\u5206\u652F\u3002\n return { argv: [['git', 'fetch', '--all', '--prune']] }\n }\n}\n\n/**\n * A branch name is valid when it matches git's ref-name grammar at the level\n * we care about: non-empty, ASCII ref chars only, no leading `-` (option\n * injection guard, though argv never shells out), no `..` (path traversal of\n * refs), no trailing `/`, and no double slashes.\n */\nexport function isValidBranchName(name: string): boolean {\n if (name === '' || name.startsWith('-') || name.includes('..') || name.endsWith('/') || name.includes('//')) return false\n return /^[A-Za-z0-9._/-]+$/.test(name)\n}\n\n/**\n * \u6821\u9A8C\u4ED3\u5E93\u76F8\u5BF9\u8DEF\u5F84\u540E\u8FFD\u52A0\u5230 `--` \u4E4B\u540E\uFF1B`prefixes` \u53EF\u7ED9\u51FA\u591A\u6761\u547D\u4EE4\u5E8F\u5217\uFF0C\n * \u6821\u9A8C\u540E\u7684\u8DEF\u5F84\u9010\u4E00\u9644\u52A0\u5230\u6BCF\u6761\u5E8F\u5217\uFF08commit \u6240\u9009\u8DEF\u5F84\u5373\u4E24\u6B65\u5E8F\u5217\uFF09\u3002\n */\nfunction withPaths(prefixes: readonly (readonly string[])[], paths: readonly string[], root: string): { readonly argv: readonly (readonly string[])[] } | { readonly error: string } {\n if (paths.length === 0) return { error: 'no paths given' }\n for (const path of paths) {\n if (!isSafePath(path, root)) return { error: `unsafe path: ${path}` }\n }\n return { argv: prefixes.map((prefix) => [...prefix, ...paths]) }\n}\n\n/**\n * A path is safe when it is repo-relative and stays inside the work tree:\n * reject absolute paths, drive letters / backslashes, and `..` escapes\n * (checked via path resolution against the realpath'd root).\n */\nexport function isSafePath(path: string, root: string): boolean {\n if (path === '') return false\n if (path.startsWith('/') || path.startsWith('\\\\') || /^[A-Za-z]:/.test(path)) return false\n const resolved = resolve(root, path)\n const prefix = root.endsWith(sep) ? root : `${root}${sep}`\n return resolved === root || resolved.startsWith(prefix)\n}\n\n/** Map a snapshot-flow failure (which may carry git-unavailable) onto the operation error shape. */\nexport function operationError(failure: Extract<Awaited<ReturnType<typeof resolveWorkspace>>, { ok: false }>['error']): GitActionResult & { ok: false } {\n if (failure.code === 'git-unavailable') {\n return { ok: false, error: { code: 'git-error', message: failure.detail } }\n }\n return { ok: false, error: failure }\n}\n\n/**\n * \u628A git \u547D\u4EE4\u5931\u8D25\u5F52\u7C7B\u4E3A\u53EF\u9884\u671F\u7684\u4E1A\u52A1\u9519\u8BEF\uFF08\u5176\u4F59\u4FDD\u6301 git-error\uFF09\u3002\n * \u5207\u5206\u652F\u88AB\u5DE5\u4F5C\u533A\u672A\u63D0\u4EA4\u53D8\u66F4\u963B\u6B62\u662F\u6700\u5E38\u89C1\u7684\u53EF\u9884\u671F\u5931\u8D25\uFF1Agit \u8F93\u51FA\n * \"would be overwritten by checkout\"\uFF08\u6216\u4E2D\u6587\u672C\u5730\u5316 \"\u5C06\u88AB checkout \u8986\u76D6\"\uFF09\uFF0C\n * \u5F52\u4E00\u5316\u4E3A local-changes-block\uFF0Cclient \u636E\u6B64\u7ED9\u53CB\u597D\u63D0\u793A + \u5904\u7406\u53D8\u66F4\u5F15\u5BFC\u3002\n */\nexport function classifyOperationError(kind: GitAction['kind'], message: string): GitOperationErrorCode {\n if (kind === 'branch-checkout' && /would be overwritten by checkout|\u5C06\u88AB checkout \u8986\u76D6|\u6709\u672A\u8DDF\u8E2A\u5DE5\u4F5C\u533A\u6587\u4EF6\u5C06\u4F1A\u88AB checkout \u8986\u76D6/i.test(message)) {\n return 'local-changes-block'\n }\n return 'git-error'\n}\n\n/**\n * Execute one management action against the session's repository and return\n * the refreshed snapshot on success (the caller re-renders from it, so the\n * UI never waits for the next poll).\n */\nexport async function runAction(\n deps: SnapshotDeps,\n config: GitStatusConfig,\n request: GitActionRequest,\n): Promise<GitActionResult> {\n const workspace = await resolveWorkspace(deps, request.sessionId)\n if (!workspace.ok) return operationError(workspace.error)\n const root = workspace.root\n\n if (request.action.kind === 'commit' && request.action.message.trim() === '') {\n return { ok: false, error: { code: 'git-error', message: 'commit message is empty' } }\n }\n\n const kind = request.action.kind\n if (kind === 'branch-create' || kind === 'branch-checkout' || kind === 'branch-delete') {\n const name = request.action.name\n if (!isValidBranchName(name)) {\n return { ok: false, error: { code: 'invalid-name', message: `invalid branch name: ${name}` } }\n }\n }\n\n const built = buildArgv(request.action, root)\n if ('error' in built) return { ok: false, error: { code: 'invalid-path', message: built.error } }\n\n // Run the command sequence; a failure stops the rest. \u5148\u884C\u547D\u4EE4\u53EF\u80FD\u5DF2\u751F\u6548\uFF1A\n // restore \u7C7B\u547D\u4EE4\u5E42\u7B49\u53EF\u91CD\u5165\uFF1B\u4E24\u6B65\u63D0\u4EA4\u82E5 add \u6210\u529F\u540E commit \u5931\u8D25\uFF0C\u6240\u9009\u8DEF\u5F84\n // \u7559\u5728\u6682\u5B58\u533A\uFF08IDE \u884C\u4E3A\u76F8\u540C\uFF0C\u4E0B\u6B21\u91CD\u8BD5\u5373\u53EF\u6210\u529F\uFF09\u3002\n let lastStdout = ''\n for (const argv of built.argv) {\n const outcome = await runCommand(deps.run, argv, root, `action ${request.action.kind}`, deps.signal)\n if ('failure' in outcome) return operationError(outcome.failure)\n if (outcome.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (outcome.run.exitCode !== 0) {\n // git writes user-facing failures to stderr OR stdout (e.g. a clean\n // repo's `git commit` reports \"nothing to commit\" on stdout).\n const message = outcome.run.stderr.trim() || outcome.run.stdout.trim()\n const code = classifyOperationError(request.action.kind, message)\n return {\n ok: false,\n error: {\n code,\n message: message !== '' ? message : `git ${request.action.kind} exited ${String(outcome.run.exitCode)}`,\n },\n }\n }\n lastStdout = outcome.run.stdout.trim()\n }\n\n const snapshot = await snapshotForSession(deps, config, request.sessionId)\n if (!snapshot.ok) return operationError(snapshot.error)\n return { ok: true, snapshot: snapshot.value, ...(lastStdout === '' ? {} : { output: lastStdout }) }\n}\n", "/**\n * Framework-free read-only query runner (history / diff / show / branches).\n *\n * Same layering as `core.ts`/`actions.ts`: structural injection, testable\n * against real temporary repositories without a cordis runtime. Every query\n * resolves the workspace once, then runs one or two read-only git commands\n * against the repository root.\n */\nimport { resolveWorkspace, runCommand, type GitStatusConfig, type SnapshotDeps } from './core.ts'\nimport { parseBranchOutput, parseGraphLogOutput, parseNameStatusOutput, parseShowMeta } from './parser.ts'\nimport { isSafePath, operationError } from './actions.ts'\nimport type { GitBranch, GitQueryRequest, GitQueryResponse } from './types.ts'\n\n/** Machine-readable log format for show queries (no parents). */\nconst LOG_FORMAT = '%H%x1f%h%x1f%s%x1f%an%x1f%aI'\n/** \u5E26\u56FE\u7684 log \u683C\u5F0F\uFF08%P = \u7236\u63D0\u4EA4\uFF0C%D = ref \u88C5\u9970\uFF09\u3002 */\nconst GRAPH_FORMAT = '%H%x1f%h%x1f%s%x1f%an%x1f%aI%x1f%P%x1f%D'\n\n/** History page size cap (and default). \u5343\u6761\u7EA7 + \u65E0\u9650\u6EDA\u52A8\u3002 */\nconst MAX_HISTORY_LIMIT = 1000\n\n/** A ref is acceptable when non-empty and free of whitespace. */\nfunction isValidRef(ref: string): boolean {\n return ref !== '' && !/\\s/.test(ref)\n}\n\n/**\n * \u6267\u884C\u4E00\u6761\u53EA\u8BFB\u67E5\u8BE2\u3002\u7ED3\u679C\u5747\u4E3A JSON \u7EAF\u6570\u636E\u4E14\u6709\u754C\n * \uFF08history \u5206\u9875\uFF1Bdiff \u6587\u672C\u53D7 runner \u7684 spill/\u622A\u65AD\u7EA6\u675F\uFF09\u3002\n * `config` \u5F53\u524D\u672A\u7528\uFF1A\u4FDD\u7559\u4EE5\u4E0E runAction \u5171\u4EAB runner \u7B7E\u540D\u5951\u7EA6\n * \uFF08deps, config, request\uFF09\uFF0C\u540E\u7EED\u67E5\u8BE2\u9650\u6D41\u7B49\u8C03\u4F18\u53EF\u76F4\u63A5\u542F\u7528\u3002\n */\nexport async function runQuery(\n deps: SnapshotDeps,\n config: GitStatusConfig,\n request: GitQueryRequest,\n): Promise<GitQueryResponse> {\n void config\n const workspace = await resolveWorkspace(deps, request.sessionId)\n if (!workspace.ok) return { ok: false, error: operationError(workspace.error).error }\n const root = workspace.root\n const query = request.query\n\n switch (query.kind) {\n case 'history':\n return historyQuery(deps, root, query)\n case 'diff':\n return diffQuery(deps, root, query.path, query.base)\n case 'show':\n return showQuery(deps, root, query.ref)\n case 'branches':\n return branchesQuery(deps, root)\n case 'tags':\n return tagsQuery(deps, root)\n case 'authors':\n return authorsQuery(deps, root)\n }\n}\n\nasync function historyQuery(\n deps: SnapshotDeps,\n root: string,\n query: Extract<GitQueryRequest['query'], { kind: 'history' }>,\n): Promise<GitQueryResponse> {\n const safeLimit = Math.min(Math.max(Math.floor(query.limit), 0), MAX_HISTORY_LIMIT)\n const safeSkip = Math.max(Math.floor(query.skip), 0)\n if (query.ref !== undefined && !isValidRef(query.ref)) {\n return { ok: false, error: { code: 'invalid-name', message: `invalid ref: ${query.ref}` } }\n }\n const search = query.search?.trim() ?? ''\n const hexLike = /^[0-9a-f]{7,40}$/i.test(search)\n // \u54C8\u5E0C\u7CBE\u51C6\u68C0\u7D22\uFF1A\u4EC5\u5B9A\u4F4D\u76EE\u6807\u63D0\u4EA4\u81EA\u8EAB\uFF08--no-walk \u4E0D\u904D\u5386\u7956\u5148\uFF09\u2192 \u5355\u6761\u76EE\uFF0C\n // \u4E0D\u518D\u5217\u51FA\u8BE5\u63D0\u4EA4\u7684\u5168\u90E8\u7956\u5148\uFF1B\u6587\u672C\u641C\u7D22\u8D70 --grep\uFF08-i -E\uFF0C\u8DE8\u5F15\u7528\u5339\u914D\uFF09\u3002\n const scope = hexLike ? [] : query.ref === undefined ? ['--all'] : [query.ref]\n const noWalk = hexLike ? ['--no-walk', search] : []\n const filters: string[] = []\n if (search !== '' && !hexLike) filters.push('--regexp-ignore-case', '--extended-regexp', `--grep=${search}`)\n const author = query.author?.trim() ?? ''\n if (author !== '') filters.push(`--author=${author}`)\n const since = query.since?.trim() ?? ''\n if (since !== '') filters.push(`--since=${since}`)\n\n const log = await runCommand(\n deps.run,\n // -n/--skip \u524D\u7F6E\uFF1Agit \u7684 `-n N` \u51FA\u73B0\u5728 `--no-walk` \u4E4B\u540E\u4F1A\u91CD\u7F6E no-walk\n // \uFF08hexLike \u4F1A\u9519\u8BEF\u5217\u51FA\u5168\u90E8\u7956\u5148\uFF09\uFF0C\u524D\u7F6E\u5219 `-n 1000 --no-walk x` \u6052\u8FD4\u56DE\u5355\u6761\u3002\n ['git', 'log', ...filters, `--skip=${String(safeSkip)}`, '-n', String(safeLimit), ...noWalk, ...scope, `--format=${GRAPH_FORMAT}`],\n root,\n 'log',\n deps.signal,\n )\n if ('failure' in log) return { ok: false, error: operationError(log.failure).error }\n if (log.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (log.run.exitCode !== 0) {\n // \u672A\u51FA\u751F\u4ED3\u5E93\u65E0\u63D0\u4EA4\uFF1Agit log \u4EE5 128 \u6B64\u4FE1\u606F\u9000\u51FA\u2014\u2014\u7A33\u5B9A\u7A7A\u5386\u53F2\uFF0C\u975E\u9519\u8BEF\u3002\n if (log.run.stderr.includes('does not have any commits')) {\n return { ok: true, value: { kind: 'history', commits: [], total: 0 } }\n }\n // \u54C8\u5E0C\u65E0\u89E3\u6790\uFF08\u672A\u547D\u4E2D\uFF09\u6216\u524D\u7F00\u4E0D\u552F\u4E00\uFF08ambiguous\uFF09\uFF1A\u7A33\u5B9A\u7A7A\u7ED3\u679C\uFF08\u8BA9\u7528\u6237\u8F93\u5165\u66F4\u957F\u524D\u7F00\uFF09\u3002\n if (hexLike && /unknown revision|bad revision|ambiguous/i.test(log.run.stderr)) {\n return { ok: true, value: { kind: 'history', commits: [], total: 0 } }\n }\n return gitError('log', log.run.stderr, log.run.stdout)\n }\n\n // \u8FC7\u6EE4\u8303\u56F4\u5185\u7684\u63D0\u4EA4\u603B\u6570\uFF08best-effort\uFF09\u3002\n let total = 0\n const count = await runCommand(deps.run, ['git', 'rev-list', '--count', ...noWalk, ...scope, ...filters], root, 'rev-list', deps.signal)\n if ('run' in count && count.run.exitCode === 0) {\n const parsed = Number(count.run.stdout.trim())\n if (Number.isFinite(parsed) && parsed >= 0) total = parsed\n }\n\n // \u8FDC\u7A0B\u540D\u7528\u4E8E %D \u88C5\u9970\u7684\u8FDC\u7A0B\u5206\u652F\u5206\u7C7B\uFF1B\u5931\u8D25\u65F6\u964D\u7EA7\u4E3A\u7A7A\u5217\u8868\uFF08\u5176\u4F59\u6309\u672C\u5730\u5206\u652F\u5904\u7406\uFF09\u3002\n let remotes: readonly string[] = []\n const remoteRun = await runCommand(deps.run, ['git', 'remote'], root, 'remote', deps.signal)\n if ('run' in remoteRun && remoteRun.run.exitCode === 0) {\n remotes = remoteRun.run.stdout.split('\\n').map((s) => s.trim()).filter((s) => s !== '')\n }\n\n return { ok: true, value: { kind: 'history', commits: parseGraphLogOutput(log.run.stdout, remotes), total } }\n}\n\n/**\n * \u5355\u6587\u4EF6\u5DEE\u5F02\uFF08\u53D8\u66F4\u754C\u9762\u5BF9\u7167\u67E5\u770B\u7528\uFF09\u3002\n * staged = --cached\uFF1Bworktree = \u5DE5\u4F5C\u533A\u5BF9\u7D22\u5F15\uFF1B\n * \u672A\u7248\u672C\u7BA1\u7406\u6587\u4EF6 worktree \u5DEE\u5F02\u4E3A\u7A7A \u2192 \u56DE\u9000 --no-index \u4E0E /dev/null \u5BF9\u6BD4\uFF08\u9000\u51FA\u7801 1 \u89C6\u4E3A\u6709\u5DEE\u5F02\u7684\u6210\u529F\uFF09\u3002\n */\nasync function diffQuery(\n deps: SnapshotDeps,\n root: string,\n path: string,\n base: 'worktree' | 'staged',\n): Promise<GitQueryResponse> {\n if (!isSafePath(path, root)) return { ok: false, error: { code: 'invalid-path', message: `unsafe path: ${path}` } }\n // \u4F7F\u7528 -U999999 \u663E\u793A\u5B8C\u6574\u6587\u6863\u4E0A\u4E0B\u6587\uFF08\u800C\u975E\u4EC5\u53D8\u66F4 hunk\uFF09\uFF0C\u652F\u6301\u6587\u6863\u6D4F\u89C8\u4F53\u9A8C\u3002\n const argv = base === 'staged'\n ? ['git', 'diff', '--cached', '-U999999', '--', path]\n : ['git', 'diff', '-U999999', '--', path]\n const run = await runCommand(deps.run, argv, root, 'diff', deps.signal)\n if ('failure' in run) return { ok: false, error: operationError(run.failure).error }\n if (run.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (run.run.exitCode !== 0) return gitError('diff', run.run.stderr, run.run.stdout)\n if (run.run.stdout !== '' || base === 'staged') {\n return { ok: true, value: { kind: 'diff', path, text: run.run.stdout } }\n }\n // \u7A7A\u5DEE\u5F02\uFF1A\u53EF\u80FD\u662F\u672A\u7248\u672C\u7BA1\u7406\u6587\u4EF6\u2014\u2014\u4E0E /dev/null \u5BF9\u6BD4\u751F\u6210\u5168\u589E\u5DEE\u5F02\u3002\n const ni = await runCommand(deps.run, ['git', 'diff', '--no-index', '-U999999', '--', '/dev/null', path], root, 'diff --no-index', deps.signal)\n if ('failure' in ni) return { ok: false, error: operationError(ni.failure).error }\n if (ni.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (ni.run.exitCode !== 0 && ni.run.exitCode !== 1) return gitError('diff', ni.run.stderr, ni.run.stdout)\n return { ok: true, value: { kind: 'diff', path, text: ni.run.stdout } }\n}\n\nasync function showQuery(deps: SnapshotDeps, root: string, ref: string): Promise<GitQueryResponse> {\n if (!isValidRef(ref)) return { ok: false, error: { code: 'invalid-name', message: `invalid ref: ${ref}` } }\n // -s \u4EC5\u8F93\u51FA\u683C\u5F0F\u5757\uFF1A%b \u4E3A\u6392\u9664\u9996\u6BB5\u843D\u540E\u7684\u6B63\u6587\uFF0C\u72EC\u7ACB\u8C03\u7528\u907F\u514D\u89E3\u6790\u6B67\u4E49\u3002\n const meta = await runCommand(\n deps.run,\n ['git', 'show', '-s', `--format=${LOG_FORMAT}%x1f%b`, ref],\n root,\n 'show',\n deps.signal,\n )\n if ('failure' in meta) return { ok: false, error: operationError(meta.failure).error }\n if (meta.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (meta.run.exitCode !== 0) return gitError('show', meta.run.stderr, meta.run.stdout)\n\n const stat = await runCommand(\n deps.run,\n ['git', '-c', 'core.quotePath=false', 'show', '--format=', '--name-status', '-z', ref],\n root,\n 'show --name-status',\n deps.signal,\n )\n if ('failure' in stat) return { ok: false, error: operationError(stat.failure).error }\n if (stat.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (stat.run.exitCode !== 0) return gitError('show', stat.run.stderr, stat.run.stdout)\n\n const parsed = parseShowMeta(meta.run.stdout)\n return {\n ok: true,\n value: {\n kind: 'show',\n ref,\n commit: parsed?.commit ?? null,\n body: parsed?.body ?? '',\n stats: parseNameStatusOutput(stat.run.stdout),\n },\n }\n}\n\n/** \u4F5C\u8005\u5217\u8868\uFF08\u5DE5\u5177\u680F\u7528\u6237\u9009\u62E9\u7528\uFF09\uFF0C\u53BB\u91CD\u6392\u5E8F\u622A\u65AD 100\u3002 */\nasync function authorsQuery(deps: SnapshotDeps, root: string): Promise<GitQueryResponse> {\n const run = await runCommand(deps.run, ['git', 'log', '--all', '-n', '1000', '--format=%an'], root, 'log authors', deps.signal)\n if ('failure' in run) return { ok: false, error: operationError(run.failure).error }\n if (run.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (run.run.exitCode !== 0) return { ok: true, value: { kind: 'authors', authors: [] } }\n const authors = [...new Set(run.run.stdout.split('\\n').map((s) => s.trim()).filter((s) => s !== ''))].sort().slice(0, 100)\n return { ok: true, value: { kind: 'authors', authors } }\n}\n\n/** \u6807\u7B7E\u5217\u8868\uFF08\u5DE6\u680F\u8FC7\u6EE4\u6811\u7528\uFF09\uFF0C\u590D\u7528 tab \u5206\u9694\u89E3\u6790\u3002 */\nasync function tagsQuery(deps: SnapshotDeps, root: string): Promise<GitQueryResponse> {\n const FORMAT = '--format=%(refname:short)%09%(objectname:short)'\n const run = await runCommand(deps.run, ['git', 'tag', FORMAT], root, 'tag', deps.signal)\n if ('failure' in run) return { ok: false, error: operationError(run.failure).error }\n if (run.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (run.run.exitCode !== 0) return gitError('tag', run.run.stderr, run.run.stdout)\n return { ok: true, value: { kind: 'tags', tags: parseBranchList(run.run.stdout) } }\n}\n\nasync function branchesQuery(deps: SnapshotDeps, root: string): Promise<GitQueryResponse> {\n // \u672C\u5730\u5206\u652F\u683C\u5F0F\uFF1Aname\\thash\\tupstream\\ttrack\uFF08track \u5982 [ahead 2, behind 1]\uFF09\u3002\n // \u8FDC\u7A0B\u5206\u652F\u65E0\u4E0A\u6E38 \u2192 upstream/track \u4E3A\u7A7A\u3002\n const LOCAL_FORMAT = '--format=%(refname:short)%09%(objectname:short)%09%(upstream:short)%09%(upstream:track)'\n const REMOTE_FORMAT = '--format=%(refname:short)%09%(objectname:short)'\n const local = await runCommand(deps.run, ['git', 'branch', LOCAL_FORMAT], root, 'branch', deps.signal)\n if ('failure' in local) return { ok: false, error: operationError(local.failure).error }\n if (local.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (local.run.exitCode !== 0) return gitError('branch', local.run.stderr, local.run.stdout)\n\n const remote = await runCommand(deps.run, ['git', 'branch', '-r', REMOTE_FORMAT], root, 'branch -r', deps.signal)\n if ('failure' in remote) return { ok: false, error: operationError(remote.failure).error }\n if (remote.run.timedOut) return { ok: false, error: { code: 'timeout' } }\n if (remote.run.exitCode !== 0) return gitError('branch -r', remote.run.stderr, remote.run.stdout)\n\n const current = await runCommand(deps.run, ['git', 'branch', '--show-current'], root, 'branch', deps.signal)\n const currentName = 'run' in current && current.run.exitCode === 0 ? parseBranchOutput(current.run.stdout) : null\n\n // \u9ED8\u8BA4\u5206\u652F\uFF1Aorigin/HEAD \u7B26\u53F7\u5F15\u7528\uFF08\u5982 origin/main\uFF09\uFF1B\u5931\u8D25\u964D\u7EA7 null\u3002\n let defaultBranch: string | null = null\n const def = await runCommand(deps.run, ['git', 'symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'], root, 'symbolic-ref', deps.signal)\n if ('run' in def && def.run.exitCode === 0) {\n const value = def.run.stdout.trim()\n const slash = value.indexOf('/')\n defaultBranch = value === '' ? null : slash === -1 ? value : value.slice(slash + 1)\n }\n\n return {\n ok: true,\n value: {\n kind: 'branches',\n current: currentName,\n defaultBranch,\n local: parseBranchList(local.run.stdout),\n remote: parseBranchList(remote.run.stdout).filter((branch) => !branch.name.endsWith('/HEAD')),\n },\n }\n}\n\n/**\n * \u89E3\u6790 `%(refname:short)%09%(objectname:short)[%09%(upstream:short)%09%(upstream:track)]` \u884C\u3002\n * \u672C\u5730\u5206\u652F\u542B 4 \u5B57\u6BB5\uFF08upstream + track\uFF09\uFF0C\u8FDC\u7A0B\u5206\u652F\u4EC5 2 \u5B57\u6BB5\uFF08\u65E0\u4E0A\u6E38\uFF09\u3002\n * track \u683C\u5F0F\uFF1A`[ahead N]`\u3001`[behind N]`\u3001`[ahead N, behind N]` \u6216\u7A7A\uFF08\u65E0\u4E0A\u6E38/\u5DF2\u540C\u6B65\uFF09\u3002\n */\nfunction parseBranchList(output: string): readonly GitBranch[] {\n const branches: GitBranch[] = []\n for (const line of output.split('\\n')) {\n if (line === '') continue\n const parts = line.split('\\t')\n const name = parts[0]\n const hash = parts[1]\n if (name === undefined || name === '') continue\n const track = parts[3] ?? ''\n const aheadMatch = /ahead (\\d+)/.exec(track)\n const behindMatch = /behind (\\d+)/.exec(track)\n const ahead = aheadMatch ? Number(aheadMatch[1]) : 0\n const behind = behindMatch ? Number(behindMatch[1]) : 0\n branches.push({\n name,\n shortHash: hash === undefined || hash === '' ? null : hash,\n ...(ahead > 0 ? { ahead } : {}),\n ...(behind > 0 ? { behind } : {}),\n })\n }\n return branches\n}\n\nfunction gitError(label: string, stderr: string, stdout: string): GitQueryResponse {\n const message = stderr.trim() || stdout.trim()\n return {\n ok: false,\n error: {\n code: 'git-error',\n message: message !== '' ? message : `git ${label} failed`,\n },\n }\n}\n", "/**\n * Host \u7AEF\u70B9\u5951\u7EA6\uFF1A\u7EAF\u4E1A\u52A1\u903B\u8F91\uFF0C\u96F6\u6846\u67B6\u4F9D\u8D56\u3002\n *\n * \u5C06 `snapshotForSession` / `runAction` / `runQuery` \u805A\u5408\u4E3A\u7EDF\u4E00\u7684\n * `HostEndpoints` \u63A5\u53E3\u2014\u2014\u5BBF\u4E3B\u9002\u914D\u5C42\uFF08Cordis/typert \u6216\u5176\u4ED6\u6846\u67B6\uFF09\u53EA\u9700\u8C03\u7528\n * `createHostEndpoints(deps, config)` \u5373\u53EF\u83B7\u5F97\u5168\u90E8\u7AEF\u70B9\u65B9\u6CD5\uFF0C\u518D\u4EE5\u4EFB\u4F55\n * RPC \u673A\u5236\u66B4\u9732\u3002\u4E1A\u52A1\u5C42\u5BF9\u6B64\u4E00\u65E0\u6240\u77E5\u3002\n */\nimport { snapshotForSession, type GitStatusConfig, type SnapshotDeps } from '../host/core.ts'\nimport { runAction } from '../host/actions.ts'\nimport { runQuery } from '../host/queries.ts'\nimport type { GitActionRequest, GitActionResult, GitQueryRequest, GitQueryResponse, GitSnapshotRequest, GitSnapshotResult } from '../host/types.ts'\n\n/**\n * \u4E1A\u52A1\u7AEF\u70B9\u96C6\u5408\uFF1A\u4E09\u4E2A RPC \u65B9\u6CD5\u7684\u7EAF\u51FD\u6570\u5B9E\u73B0\u3002\n * \u6BCF\u4E2A\u65B9\u6CD5\u63A5\u6536 wire \u8BF7\u6C42\u3001\u53EF\u9009\u53D6\u6D88\u4FE1\u53F7\uFF0C\u8FD4\u56DE wire \u54CD\u5E94\u3002\n */\nexport interface HostEndpoints {\n snapshot(request: GitSnapshotRequest, signal?: AbortSignal): Promise<GitSnapshotResult>\n run(request: GitActionRequest, signal?: AbortSignal): Promise<GitActionResult>\n query(request: GitQueryRequest, signal?: AbortSignal): Promise<GitQueryResponse>\n}\n\n/**\n * \u6784\u9020\u4E1A\u52A1\u7AEF\u70B9\u5B9E\u4F8B\u3002\n *\n * `deps` \u643A\u5E26\u5168\u90E8\u5BBF\u4E3B\u80FD\u529B\uFF08\u5B50\u8FDB\u7A0B\u3001\u4F1A\u8BDD\u67E5\u627E\u3001\u6587\u4EF6\u7CFB\u7EDF\uFF09\uFF1B`config` \u643A\u5E26\n * \u8FD0\u884C\u53C2\u6570\u3002\u4E24\u8005\u5747\u4E3A\u7ED3\u6784\u5316\u63A5\u53E3\uFF0C\u4E0E\u4EFB\u4F55\u6846\u67B6\u65E0\u5173\u3002\u8FD4\u56DE\u7684\u7AEF\u70B9\u5BF9\u8C61\u53EF\u76F4\u63A5\n * \u7ED1\u5B9A\u5230 RPC \u88C5\u9970\u5668\u3001HTTP \u8DEF\u7531\u3001\u6216\u6D4B\u8BD5\u6869\u3002\n */\nexport function createHostEndpoints(deps: SnapshotDeps, config: GitStatusConfig): HostEndpoints {\n return {\n snapshot(request, signal) {\n // \u5408\u5E76\u8C03\u7528\u65B9\u4FE1\u53F7\u4E0E deps \u81EA\u5E26\u4FE1\u53F7\uFF08deps.signal \u6765\u81EA Cordis Remote \u7684\n // \u53D6\u6D88\u69FD\uFF1B\u6B64\u5904 signal \u6765\u81EA\u9002\u914D\u5C42\u4F20\u5165\u2014\u2014\u4E24\u8005\u53D6\u5E76\u96C6\uFF0C\u4EFB\u4E00\u89E6\u53D1\u5373\u4E2D\u6B62\uFF09\u3002\n const merged = mergeSignals(deps.signal, signal)\n const effectiveDeps = merged === deps.signal ? deps : { ...deps, signal: merged }\n return snapshotForSession(effectiveDeps, config, request.sessionId)\n },\n run(request, signal) {\n const merged = mergeSignals(deps.signal, signal)\n const effectiveDeps = merged === deps.signal ? deps : { ...deps, signal: merged }\n return runAction(effectiveDeps, config, request)\n },\n query(request, signal) {\n const merged = mergeSignals(deps.signal, signal)\n const effectiveDeps = merged === deps.signal ? deps : { ...deps, signal: merged }\n return runQuery(effectiveDeps, config, request)\n },\n }\n}\n\n/** \u5408\u5E76\u4E24\u4E2A\u53EF\u9009\u4FE1\u53F7\uFF1A\u4EFB\u4E00 undefined \u53D6\u53E6\u4E00\u4E2A\uFF0C\u5747\u5B58\u5728\u5219 AbortSignal.any\u3002 */\nfunction mergeSignals(a: AbortSignal | undefined, b: AbortSignal | undefined): AbortSignal | undefined {\n if (a === undefined) return b\n if (b === undefined) return a\n return AbortSignal.any([a, b])\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["stat", "stat"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-git-ui",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "DeepSeek Harness (dsh) plugin: visualize Git status in the Web UI — current branch, HEAD, staged/modified/untracked counts, ahead/behind, recent commits and changed files.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,137 @@
1
+ /**
2
+ * dsh Client 平台适配:将 Cordis Context 翻译为 `ClientPlatform` 接口。
3
+ *
4
+ * 本文件是 client 端**唯一**知道 Cordis / typert 插件生命周期的地方。
5
+ * dsh 升级导致插件 API 变更时,只需修改此文件。
6
+ *
7
+ * 关键适配点:
8
+ * - `mountRemoteAndGetService`:处理 typert Remote 挂载 + Cordis child fiber
9
+ * - `registerSlotEntry`:翻译 slot 注册两步模式(inject + register)
10
+ * - `onEvent` / `effect`:直接映射 Cordis 同名方法
11
+ */
12
+ import type { TypertRemoteContribution } from '@deepseek-ai/dsh-typert-protocol'
13
+ import type { ReactNode } from 'react'
14
+ import type { ClientPlatform, LocaleDicts, RemoteContribution, SlotEntryDescriptor, GitInjected, GitRemoteLike } from '../../contracts/client-platform.ts'
15
+
16
+ /**
17
+ * Cordis Context 的结构化切片。
18
+ *
19
+ * 只声明本适配器实际使用的属性和方法。dsh 升级时若 API 变更,
20
+ * 只需更新此接口和下方的适配实现。
21
+ */
22
+ export interface DshClientContext {
23
+ /** 订阅应用事件(自动在 fiber 卸载时清理)。 */
24
+ on(event: string, listener: (...args: never[]) => void): (() => void) | void
25
+ /** 注册副作用(自动在 fiber 卸载时清理)。 */
26
+ effect(callback: () => void | (() => void | Promise<void>), label?: string): void
27
+ /** 启动一个嵌套插件 fiber(子上下文)。 */
28
+ plugin(definition: {
29
+ readonly name: string
30
+ readonly inject: readonly string[]
31
+ apply: (ctx: DshClientContext) => void | Promise<void>
32
+ }): Promise<unknown>
33
+ /** Remote 服务:挂载贡献 + 访问已挂载的命名空间。 */
34
+ remote: {
35
+ $mount(contribution: TypertRemoteContribution): Promise<() => Promise<void>>
36
+ gitInfo: GitRemoteLike
37
+ }
38
+ /** Slot 注册表。 */
39
+ slots: {
40
+ inject(slotName: string, provider: () => (() => void) | void): void
41
+ register(
42
+ options: {
43
+ readonly name: string
44
+ readonly id: string
45
+ readonly order?: number
46
+ readonly locale?: string
47
+ readonly inject: (sessionId: string) => GitInjected
48
+ },
49
+ component: unknown,
50
+ ): () => void
51
+ }
52
+ /** 国际化服务。 */
53
+ locale: {
54
+ register(namespace: string, dictionaries: LocaleDicts): void
55
+ }
56
+ }
57
+
58
+ /**
59
+ * 将我们的 `RemoteContribution` 转换为 dsh 的 `TypertRemoteContribution`。
60
+ *
61
+ * 当前两者结构相同(字段一一对应),直接透传。若未来 typert 协议变更,
62
+ * 在此处做字段映射。
63
+ */
64
+ function toTypertContribution(ours: RemoteContribution): TypertRemoteContribution {
65
+ return ours as TypertRemoteContribution
66
+ }
67
+
68
+ /**
69
+ * 将 Cordis Context 适配为 `ClientPlatform`。
70
+ *
71
+ * `mountRemoteAndGetService` 必须通过 child fiber 访问命名空间服务:
72
+ * Cordis 的访问控制要求读取 `remote.gitInfo` 前必须在 inject 中声明它,
73
+ * 而主 fiber 不能声明(服务由我们自己的 apply 挂载,声明会死锁——
74
+ * cordis 会等待 apply 执行后才存在的服务)。child fiber 在 mount 之后
75
+ * 激活,声明 `remote.gitInfo` 时服务已存在——无等待、无访问违规。
76
+ */
77
+ export function adaptDshClientContext(ctx: DshClientContext): ClientPlatform {
78
+ return {
79
+ registerLocale(namespace, dicts) {
80
+ ctx.locale.register(namespace, dicts)
81
+ },
82
+
83
+ async mountRemoteAndGetService(contribution, namespace) {
84
+ // 挂载 Remote 贡献(主 fiber 内执行,仅调用 $mount 方法本身)
85
+ await ctx.remote.$mount(toTypertContribution(contribution))
86
+ if (namespace !== 'gitInfo') {
87
+ throw new Error(`adaptDshClientContext: 未知命名空间 "${namespace}"`)
88
+ }
89
+ // child fiber:inject 声明 remote.gitInfo,apply 内读取合法
90
+ let service: GitRemoteLike | undefined
91
+ await ctx.plugin({
92
+ name: 'dsh-git-ui:git',
93
+ inject: ['remote.gitInfo'],
94
+ apply: (sub) => {
95
+ service = sub.remote.gitInfo
96
+ },
97
+ })
98
+ if (service === undefined) {
99
+ throw new Error('adaptDshClientContext: gitInfo 服务未就绪')
100
+ }
101
+ return service
102
+ },
103
+
104
+ registerSlotEntry(options, component) {
105
+ // Cordis slot 注册是两步模式:inject + register
106
+ // inject 注册一个 provider 工厂,register 在 provider 内调用
107
+ let disposeRegister: (() => void) | undefined
108
+ ctx.slots.inject(options.name, () => {
109
+ disposeRegister = ctx.slots.register(
110
+ {
111
+ name: options.name,
112
+ id: options.id,
113
+ order: options.order,
114
+ locale: options.locale,
115
+ inject: options.inject,
116
+ },
117
+ component,
118
+ )
119
+ return () => {
120
+ disposeRegister?.()
121
+ }
122
+ })
123
+ // 返回释放函数:调用时注销 slot 条目
124
+ return () => {
125
+ disposeRegister?.()
126
+ }
127
+ },
128
+
129
+ onEvent(event, listener) {
130
+ return ctx.on(event, listener as (...args: never[]) => void)
131
+ },
132
+
133
+ effect(callback, label) {
134
+ ctx.effect(callback, label)
135
+ },
136
+ }
137
+ }