@shaohui_jin/git-cockpit-mcp-server 0.1.18 → 0.1.20

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.
@@ -7,7 +7,7 @@ import {
7
7
  executeTool,
8
8
  toolSummaries,
9
9
  version
10
- } from "./chunk-HUTQV5AS.js";
10
+ } from "./chunk-XQ6CRLQA.js";
11
11
 
12
12
  // src/webServer.ts
13
13
  import * as path3 from "path";
@@ -27,10 +27,13 @@ import {
27
27
  methodForRepo,
28
28
  normalizeHostName,
29
29
  normalizeMrConfig as normalizeMrConfig2,
30
+ normalizeMrTemplate,
30
31
  normalizeRepoMethodKey,
32
+ parseMrTemplateMarkdown,
31
33
  pickRemoteName,
32
34
  probeAllMrCli,
33
35
  readCliAuthToken,
36
+ renderMrTemplate,
34
37
  RepoNotFoundError as RepoNotFoundError2,
35
38
  toHttpsRemoteUrl,
36
39
  upsertMrHost,
@@ -345,6 +348,7 @@ var REST_META = {
345
348
  "get /api/repos/{id}/branch-graph": { summary: "\u5206\u652F tip DAG\uFF08\u72B6\u6001\u9875 G6\uFF09", tags: ["\u53EA\u8BFB"] },
346
349
  "get /api/repos/{id}/file": { summary: "\u8BFB\u53D6\u67D0\u63D0\u4EA4\u4E2D\u7684\u6587\u4EF6", tags: ["\u53EA\u8BFB"] },
347
350
  "get /api/repos/{id}/stashes": { summary: "stash \u5217\u8868", tags: ["\u53EA\u8BFB"] },
351
+ "get /api/repos/{id}/worktrees": { summary: "worktree \u5217\u8868", tags: ["\u53EA\u8BFB"] },
348
352
  "get /api/repos/{id}/workspace-conflicts": { summary: "\u5DE5\u4F5C\u533A merge/rebase \u51B2\u7A81\uFF08index \u4E09\u9636\u6BB5\uFF09", tags: ["\u53EA\u8BFB"] },
349
353
  "get /api/repos/{id}/merge/preview": { summary: "merge-tree \u9884\u6F14\uFF08\u4E0D\u8FDB\u5BA1\u8BA1\uFF09", tags: ["\u53EA\u8BFB"] },
350
354
  "get /api/repos/{id}/merge/rehearse": { summary: "\u9884\u6F14 + \u51B2\u7A81\u6B63\u6587\uFF08\u4E0D\u8FDB\u5BA1\u8BA1\uFF09", tags: ["\u53EA\u8BFB"] },
@@ -362,6 +366,8 @@ var REST_META = {
362
366
  "get /api/logs": { summary: "\u64CD\u4F5C\u5BA1\u8BA1\u65E5\u5FD7", tags: ["\u7CFB\u7EDF"] },
363
367
  "get /api/settings": { summary: "\u914D\u7F6E\u4E0E\u6743\u9650", tags: ["\u8BBE\u7F6E"] },
364
368
  "put /api/settings": { summary: "\u66F4\u65B0\u6743\u9650\u6216 MR \u914D\u7F6E\uFF08Token \u660E\u6587\u4E0D\u56DE\u8BFB\uFF0C\u4FDD\u5B58\u524D\u6821\u9A8C\uFF09", tags: ["\u8BBE\u7F6E"] },
369
+ "post /api/settings/mr-template/parse": { summary: "\u5BFC\u5165 Markdown \u4E3A MR \u6B63\u6587\u6A21\u677F\u5B57\u6BB5\uFF08\u4E0D\u843D\u76D8\uFF09", tags: ["\u8BBE\u7F6E"] },
370
+ "post /api/settings/mr-template/preview": { summary: "\u6309\u6A21\u677F\u5B57\u6BB5\u6E32\u67D3 MR \u6B63\u6587\u9884\u89C8", tags: ["\u8BBE\u7F6E"] },
365
371
  "get /api/events": { summary: "SSE\uFF1A\u4ED3\u5E93\u53D8\u5316\u3001\u65E5\u5FD7\u3001\u540E\u53F0\u4EFB\u52A1\u8FDB\u5EA6", tags: ["\u7CFB\u7EDF"] }
366
372
  };
367
373
  function applyRestMeta(spec) {
@@ -751,6 +757,10 @@ async function createWebServer(runtime, options = {}) {
751
757
  "/api/repos/:id/stashes",
752
758
  async (req, reply) => withRepo(req, reply, async ({ service }) => service.listStashes())
753
759
  );
760
+ app.get(
761
+ "/api/repos/:id/worktrees",
762
+ async (req, reply) => withRepo(req, reply, async ({ service }) => ({ worktrees: await service.listWorktrees() }))
763
+ );
754
764
  app.get(
755
765
  "/api/repos/:id/workspace-conflicts",
756
766
  async (req, reply) => withRepo(req, reply, async ({ service }) => service.listWorkspaceConflicts())
@@ -876,6 +886,25 @@ async function createWebServer(runtime, options = {}) {
876
886
  })
877
887
  };
878
888
  });
889
+ app.post("/api/settings/mr-template/parse", async (req, reply) => {
890
+ const markdown = typeof req.body?.markdown === "string" ? req.body.markdown : "";
891
+ if (!markdown.trim()) return reply.code(400).send({ error: "\u8BF7\u63D0\u4F9B Markdown \u6587\u672C" });
892
+ try {
893
+ const template = parseMrTemplateMarkdown(markdown, req.body?.filename ?? "");
894
+ return { template, preview: renderMrTemplate(template) };
895
+ } catch (err) {
896
+ const message = err instanceof Error ? err.message : String(err);
897
+ return reply.code(400).send({ error: message });
898
+ }
899
+ });
900
+ app.post(
901
+ "/api/settings/mr-template/preview",
902
+ async (req, reply) => {
903
+ const template = normalizeMrTemplate(req.body?.template);
904
+ if (!template) return reply.code(400).send({ error: "\u6CA1\u6709\u53EF\u9884\u89C8\u7684\u6A21\u677F\u5B57\u6BB5" });
905
+ return { preview: renderMrTemplate(template, req.body?.values ?? {}) };
906
+ }
907
+ );
879
908
  app.get("/api/settings", async (req) => {
880
909
  const repoId = req.query.repoId ? Number(req.query.repoId) : void 0;
881
910
  return {
@@ -955,7 +984,15 @@ async function createWebServer(runtime, options = {}) {
955
984
  const del = normalizeHostName(patch.mr.deleteHost);
956
985
  next.hosts = next.hosts.filter((h) => h.host !== del);
957
986
  }
987
+ if (patch.mr.template !== void 0) {
988
+ next.template = normalizeMrTemplate(patch.mr.template);
989
+ }
958
990
  runtime.configStore.update({ mr: next });
991
+ if (patch.mr.template !== void 0) {
992
+ const cfg = runtime.configStore.get();
993
+ cfg.mr.template = next.template;
994
+ runtime.configStore.save();
995
+ }
959
996
  }
960
997
  runtime.config = runtime.configStore.get();
961
998
  runtime.permissions = new PermissionManager2(runtime.config);
@@ -1173,6 +1210,7 @@ async function mrClientView(runtime, repoId, opts) {
1173
1210
  remotes,
1174
1211
  current,
1175
1212
  hosts: showHosts,
1213
+ template: mr.template,
1176
1214
  cli: await withCliTokenStatus(await probeAllMrCli(repoPath), {
1177
1215
  validate: Boolean(opts?.validateToken),
1178
1216
  platform: current?.platform ?? "unknown",
@@ -1191,4 +1229,4 @@ export {
1191
1229
  disposeRuntime,
1192
1230
  createWebServer
1193
1231
  };
1194
- //# sourceMappingURL=chunk-363R2SNK.js.map
1232
+ //# sourceMappingURL=chunk-ZIZILLXV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/webServer.ts","../src/runtime.ts","../src/config.ts","../src/repoManager.ts","../src/openapi.ts"],"sourcesContent":["/**\n * Fastify Web 服务:\n * - REST API 供 Web 前端调用(仓库管理、状态/历史/差异/branch 等只读查询、通用写操作入口);\n * - SSE(GET /api/events)推送仓库变化事件;\n * - 挂载 MCP Streamable HTTP 传输(/mcp),供 AI 客户端(Cursor/Claude Desktop)连接;\n * - 托管 Web 前端构建产物(packages/web/dist,若存在)。\n */\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport Fastify from 'fastify';\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport cors from '@fastify/cors';\nimport fastifyStatic from '@fastify/static';\nimport {\n GitOperationError,\n GitService,\n BackupManager,\n PermissionManager,\n detectMrPlatform,\n enrichPrepareMr,\n findMrHost,\n hostnameOf,\n methodForRepo,\n normalizeHostName,\n normalizeMrConfig,\n normalizeMrTemplate,\n normalizeRepoMethodKey,\n parseMrTemplateMarkdown,\n pickRemoteName,\n probeAllMrCli,\n readCliAuthToken,\n renderMrTemplate,\n RepoNotFoundError,\n toHttpsRemoteUrl,\n upsertMrHost,\n maskToken,\n validateMrToken\n} from '@shaohui_jin/git-cockpit-core';\nimport type { MrTemplate } from '@shaohui_jin/git-cockpit-core';\nimport { collectRepoOverviews } from './overview.ts';\nimport type { MrCliStatus, MrTokenStatus, OpenedRepo } from '@shaohui_jin/git-cockpit-core';\nimport { disposeRuntime } from './runtime.ts';\nimport type { Runtime } from './runtime.ts';\nimport { McpHttpHandler } from './mcpServer.ts';\nimport { executeTool } from './tools/handlers.ts';\nimport { TOOL_DEF_MAP, toolSummaries } from './tools/index.ts';\nimport { registerApiDocs } from './openapi.ts';\nimport { version } from '../package.json';\n\nexport interface WebServerHandle {\n app: FastifyInstance;\n /** 关闭 HTTP 服务与相关资源 */\n close: () => Promise<void>;\n}\n\n/** 静态资源目录:优先 Web 构建产物;不存在则仅提供 API。\n * 解析基于脚本自身位置(而非 process.cwd()),保证任意启动目录都能找到前端。 */\nfunction resolveWebDist(): string | null {\n // 源码布局下脚本位于 packages/mcp-server/dist,../.. 回到 packages,再进 web/dist\n const here = path.resolve(path.dirname(fileURLToPath(import.meta.url)));\n const candidates = [\n path.resolve(here, '..', '..', 'web', 'dist'), // 源码布局:packages/mcp-server/dist → packages/web/dist\n path.resolve(here, 'web'), // 发布布局:产物由 copy-web.mjs 打入 dist/web(含 index.html)\n path.resolve(here, 'web', 'dist'), // 兼容旧式 dist/web/dist 布局\n path.resolve(here, '..', 'web', 'dist'), // 兼容 npm 全局安装目录的旧式布局\n path.resolve(process.cwd(), 'dist', 'web'),\n path.resolve(process.cwd(), '..', 'web', 'dist'),\n path.resolve(process.cwd(), 'node_modules', '@shaohui_jin', 'git-cockpit-web', 'dist')\n ];\n for (const c of candidates) {\n if (fs.existsSync(path.join(c, 'index.html'))) return c;\n }\n return null;\n}\n\nfunction queryFlag(v: string | undefined): boolean | undefined {\n if (v === undefined || v === '') return undefined;\n return v === '1' || v === 'true';\n}\n\nfunction queryList(q: Record<string, string | string[] | undefined>, key: string): string[] {\n const v = q[key];\n if (v == null || v === '') return [];\n const parts = Array.isArray(v) ? v : [v];\n return parts\n .flatMap((s) => String(s).split(','))\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\nexport async function createWebServer(\n runtime: Runtime,\n options: { host?: string; port?: number; staticDir?: string | null; noListen?: boolean } = {}\n): Promise<WebServerHandle> {\n const app = Fastify({ logger: false, bodyLimit: 2 * 1024 * 1024 });\n\n await app.register(cors, {\n origin: true,\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],\n allowedHeaders: ['Content-Type', 'Mcp-Session-Id', 'Authorization']\n });\n\n await registerApiDocs(app);\n\n const staticDir = options.staticDir !== undefined ? options.staticDir : resolveWebDist();\n if (staticDir && fs.existsSync(staticDir)) {\n await app.register(fastifyStatic, { root: staticDir, prefix: '/' });\n }\n\n const mcpHttp = new McpHttpHandler(runtime);\n const jobs = runtime.jobs;\n\n // Health\n app.get('/api/health', async () => ({\n ok: true,\n service: 'git-cockpit',\n version,\n uptimeMs: process.uptime() * 1000\n }));\n\n app.get('/api/jobs', async () => ({\n jobs: jobs.list().map((j) => jobs.summary(j))\n }));\n\n app.get<{ Params: { id: string } }>('/api/jobs/:id', async (req, reply) => {\n const job = jobs.get(req.params.id);\n if (!job) return reply.code(404).send({ error: '任务不存在' });\n return { job: jobs.detail(job) };\n });\n\n app.post<{ Params: { id: string } }>('/api/jobs/:id/cancel', async (req, reply) => {\n try {\n const job = jobs.cancel(req.params.id);\n return { job: jobs.summary(job) };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const code = msg === '任务不存在' ? 404 : 400;\n return reply.code(code).send({ error: msg } as never);\n }\n });\n\n app.post<{ Body: { url?: string; destDir?: string } }>('/api/jobs/clone', async (req, reply) => {\n const url = req.body?.url?.trim();\n const destDir = req.body?.destDir?.trim();\n if (!url || !destDir) {\n return reply.code(400).send({ error: '需要 url 与 destDir' } as never);\n }\n try {\n const job = jobs.startClone({\n url,\n destDir,\n allowedRepos: runtime.configStore.get().git.allowedRepos,\n onSuccess: async (dest) => {\n const handle = await runtime.repoManager.open(dest);\n return handle.record.id;\n }\n });\n return { job: jobs.summary(job) };\n } catch (err) {\n return reply.code(400).send({ error: toError(err) } as never);\n }\n });\n\n app.post<{\n Body: { kind?: string; payload?: Record<string, unknown>; repoPath?: string; repoId?: number };\n }>('/api/jobs', async (req, reply) => {\n const kind = req.body?.kind?.trim();\n const payload = req.body?.payload ?? {};\n try {\n if (kind === 'clone') {\n const job = jobs.startClone({\n url: String(payload.url ?? ''),\n destDir: String(payload.destDir ?? ''),\n allowedRepos: runtime.configStore.get().git.allowedRepos,\n onSuccess: async (dest) => {\n const handle = await runtime.repoManager.open(dest);\n return handle.record.id;\n }\n });\n return { job: jobs.summary(job) };\n }\n if (kind === 'survey' || kind === 'fetch') {\n let repoPath = typeof req.body?.repoPath === 'string' ? req.body.repoPath.trim() : '';\n if (!repoPath && req.body?.repoId != null) {\n const handle = await runtime.repoManager.getById(Number(req.body.repoId));\n repoPath = handle?.service.repoPath ?? '';\n }\n if (!repoPath) {\n const current = await runtime.repoManager.getCurrent();\n repoPath = current?.service.repoPath ?? '';\n }\n if (!repoPath) return reply.code(400).send({ error: 'survey/fetch 需要仓库' } as never);\n const handle = await runtime.repoManager.open(repoPath);\n const job =\n kind === 'survey'\n ? jobs.startSurvey({\n repoPath: handle.service.repoPath,\n intos: (payload.intos as string[]) ?? [],\n froms: (payload.froms as string[]) ?? [],\n fetch: payload.fetch as boolean | undefined,\n remote: payload.remote as string | undefined,\n git: handle.service\n })\n : jobs.startFetch({\n repoPath: handle.service.repoPath,\n remote: payload.remote as string | undefined,\n git: handle.service\n });\n return { job: jobs.summary(job) };\n }\n return reply.code(400).send({ error: '不支持的 kind,使用 clone / survey / fetch' } as never);\n } catch (err) {\n return reply.code(400).send({ error: toError(err) } as never);\n }\n });\n\n // ---------------------------------------------------------------------------\n // 仓库管理\n // ---------------------------------------------------------------------------\n app.get('/api/repos/overview', async () => ({\n repos: await collectRepoOverviews(runtime)\n }));\n\n app.get('/api/repos', async () => ({\n repos: runtime.repoManager.list()\n }));\n\n app.put<{ Body: { ids?: number[] } }>('/api/repos/order', async (req, reply) => {\n const ids = req.body?.ids;\n if (!Array.isArray(ids) || ids.some((id) => !Number.isInteger(id))) {\n return reply.code(400).send({ error: '缺少 ids' } as never);\n }\n try {\n return { repos: runtime.repoManager.reorder(ids) };\n } catch (err) {\n return reply.code(400).send({ error: toError(err) } as never);\n }\n });\n\n app.post<{ Body: { path?: string } }>('/api/repos/open', async (req, reply) => {\n const target = req.body?.path?.trim();\n if (!target) {\n return reply.code(400).send({ error: '缺少 path 参数' } as never);\n }\n try {\n const handle = await runtime.repoManager.open(target);\n return { repo: handle.record };\n } catch (err) {\n return reply.code(400).send({ error: toError(err) } as never);\n }\n });\n\n app.delete<{ Params: { id: string } }>('/api/repos/:id', async (req, reply) => {\n const id = Number(req.params.id);\n if (!Number.isInteger(id)) {\n return reply.code(400).send({ error: '非法仓库 id' } as never);\n }\n runtime.repoManager.remove(id);\n return { ok: true };\n });\n\n /** 激活/进入仓库:刷新 last_opened_at 并记日志,不改拖拽顺序 */\n app.post<{ Params: { id: string } }>('/api/repos/:id/activate', async (req, reply) => {\n const id = Number(req.params.id);\n if (!Number.isInteger(id)) {\n return reply.code(400).send({ error: '非法仓库 id' } as never);\n }\n const t0 = Date.now();\n const repo = runtime.repoManager.activate(id);\n if (!repo) {\n return reply.code(404).send({ error: '仓库不存在或已失效' } as never);\n }\n runtime.auditLogger.log({\n timestamp: new Date().toISOString(),\n source: 'web',\n tool: 'repo_activate',\n repoPath: repo.path,\n params: { id: repo.id, path: repo.path },\n result: 'success',\n error: null,\n durationMs: Date.now() - t0,\n dryRun: false\n });\n runtime.eventBus.emit('log', { tool: 'repo_activate', result: 'success', at: new Date().toISOString() });\n return { repo };\n });\n\n // ---------------------------------------------------------------------------\n // 只读查询(按仓库 id)\n // ---------------------------------------------------------------------------\n async function withRepo<T>(\n req: { params: { id: string } },\n reply: FastifyReply,\n fn: (handle: { service: GitService; record: OpenedRepo }) => Promise<T>\n ): Promise<T | void> {\n const id = Number(req.params.id);\n const handle = await runtime.repoManager.getById(id);\n if (!handle) {\n await reply.code(404).send({ error: '仓库不存在或已失效' });\n return undefined;\n }\n try {\n return await fn(handle);\n } catch (err) {\n await reply.code(400).send({ error: toError(err) });\n return undefined;\n }\n }\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/status', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.getStatus())\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>('/api/repos/:id/log', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => {\n const q = req.query;\n return service.getLog({\n maxCount: q.maxCount ? Math.min(Number(q.maxCount) || 100, 10000) : undefined,\n from: q.from,\n to: q.to,\n author: q.author,\n path: q.path,\n all: q.all === 'true'\n });\n })\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>('/api/repos/:id/diff', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => {\n const q = req.query;\n return service.getDiff({\n from: q.from,\n to: q.to,\n path: q.path,\n staged: q.staged === 'true',\n maxPatchBytes: q.maxPatchBytes ? Number(q.maxPatchBytes) : undefined\n });\n })\n );\n\n app.get<{ Params: { id: string; commit: string }; Querystring: Record<string, string | undefined> }>(\n '/api/repos/:id/show/:commit',\n async (req, reply) =>\n withRepo(req, reply, async ({ service }) =>\n service.getShow(req.params.commit, {\n path: req.query.path,\n maxPatchBytes: req.query.maxPatchBytes ? Number(req.query.maxPatchBytes) : undefined\n })\n )\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/branches', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.listBranches())\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/tags', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.listTags())\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/remotes', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.listRemotes())\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>('/api/repos/:id/graph', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.getGraph(req.query.maxCount ? Number(req.query.maxCount) : 500))\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>('/api/repos/:id/branch-graph', async (req, reply) =>\n withRepo(req, reply, async ({ service }) =>\n service.getBranchGraph({\n maxNodes: req.query.maxNodes ? Number(req.query.maxNodes) : 200,\n into: req.query.into,\n from: req.query.from\n })\n )\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>('/api/repos/:id/reflog', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.getReflog(req.query.maxCount ? Number(req.query.maxCount) : 50))\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/backups', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => new BackupManager(service).listBackups())\n );\n\n app.get<{ Params: { id: string }; Querystring: { commit?: string; path?: string } }>('/api/repos/:id/file', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => {\n if (!req.query.commit || !req.query.path) {\n return { error: '需要 commit 与 path 参数' };\n }\n return service.getFileContent(req.query.commit, req.query.path);\n })\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/stashes', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.listStashes())\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/worktrees', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => ({ worktrees: await service.listWorktrees() }))\n );\n\n app.get<{ Params: { id: string } }>('/api/repos/:id/workspace-conflicts', async (req, reply) =>\n withRepo(req, reply, async ({ service }) => service.listWorkspaceConflicts())\n );\n\n // ---------------------------------------------------------------------------\n // 只读预演(不走 executeTool,避免大 patch 进审计)。MCP 仍用工具。\n // ---------------------------------------------------------------------------\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>(\n '/api/repos/:id/merge/preview',\n async (req, reply) =>\n withRepo(req, reply, async ({ service }) =>\n service.previewMerge({\n into: req.query.into ?? '',\n from: req.query.from ?? '',\n fetch: queryFlag(req.query.fetch),\n remote: req.query.remote,\n path: req.query.path\n })\n )\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>(\n '/api/repos/:id/merge/rehearse',\n async (req, reply) =>\n withRepo(req, reply, async ({ service }) =>\n service.rehearseMerge({\n into: req.query.into ?? '',\n from: req.query.from ?? '',\n fetch: queryFlag(req.query.fetch),\n remote: req.query.remote,\n path: req.query.path,\n maxFiles: req.query.maxFiles ? Number(req.query.maxFiles) : undefined\n })\n )\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>(\n '/api/repos/:id/merge/blame',\n async (req, reply) =>\n withRepo(req, reply, async ({ service }) => {\n if (!req.query.into || !req.query.from || !req.query.path) {\n throw new GitOperationError('需要 into、from 与 path', 'INVALID_REF');\n }\n return service.blameConflictFile({\n into: req.query.into,\n from: req.query.from,\n path: req.query.path,\n fetch: queryFlag(req.query.fetch),\n remote: req.query.remote\n });\n })\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | string[] | undefined> }>(\n '/api/repos/:id/merge/survey',\n async (req, reply) =>\n withRepo(req, reply, async ({ service }) =>\n service.surveyMerges({\n intos: queryList(req.query, 'intos'),\n froms: queryList(req.query, 'froms'),\n fetch: queryFlag(typeof req.query.fetch === 'string' ? req.query.fetch : undefined),\n remote: typeof req.query.remote === 'string' ? req.query.remote : undefined\n })\n )\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | string[] | undefined> }>(\n '/api/repos/:id/merge/order',\n async (req, reply) =>\n withRepo(req, reply, async ({ service }) => {\n const branches = queryList(req.query, 'branches');\n return service.suggestMergeOrder({\n into: typeof req.query.into === 'string' ? req.query.into : '',\n branches,\n fetch: queryFlag(typeof req.query.fetch === 'string' ? req.query.fetch : undefined),\n remote: typeof req.query.remote === 'string' ? req.query.remote : undefined\n });\n })\n );\n\n app.get<{ Params: { id: string }; Querystring: Record<string, string | undefined> }>(\n '/api/repos/:id/mr/prepare',\n async (req, reply) =>\n withRepo(req, reply, async ({ service }) => {\n if (!req.query.into || !req.query.from) {\n throw new GitOperationError('需要 into 与 from', 'INVALID_REF');\n }\n const prep = await service.prepareMr({\n into: req.query.into,\n from: req.query.from,\n remote: req.query.remote?.trim() || undefined,\n sourceBranch: req.query.sourceBranch\n });\n return enrichPrepareMr({ prep, mr: runtime.config.mr, cwd: service.repoPath });\n })\n );\n\n // ---------------------------------------------------------------------------\n // 通用写操作入口(复用 MCP 同款安全链路)\n // ---------------------------------------------------------------------------\n app.post<{ Params: { id: string; tool: string }; Body: { params?: Record<string, unknown> } }>(\n '/api/repos/:id/tools/:tool',\n { schema: { hide: true } },\n async (req, reply) => {\n const def = TOOL_DEF_MAP.get(req.params.tool);\n if (!def) return reply.code(404).send({ error: `未知工具: ${req.params.tool}` });\n const params = req.body?.params ?? {};\n const exec = await executeTool(def, params, {\n runtime,\n source: 'web',\n repoId: Number(req.params.id)\n });\n if (!exec.success) {\n const status = exec.error?.requiredApproval ? 403 : 400;\n return reply\n .code(status)\n .send({ error: exec.error?.message, requiredApproval: exec.error?.requiredApproval, code: exec.error?.code });\n }\n return { ...exec };\n }\n );\n\n // ---------------------------------------------------------------------------\n // 工具注册表信息(供前端渲染可操作按钮)\n // ---------------------------------------------------------------------------\n app.get('/api/tools', async () => ({\n tools: toolSummaries().map((t) => ({\n ...t,\n riskLevel: runtime.permissions.getRiskLevel(t.name),\n enabled: runtime.permissions.isEnabled(t.name)\n }))\n }));\n\n // ---------------------------------------------------------------------------\n // 操作日志\n // ---------------------------------------------------------------------------\n app.get<{ Querystring: Record<string, string | undefined> }>('/api/logs', async (req) => {\n const q = req.query;\n return {\n logs: runtime.auditLogger.list({\n limit: q.limit ? Number(q.limit) : undefined,\n offset: q.offset ? Number(q.offset) : undefined,\n tool: q.tool,\n source: (q.source as 'mcp' | 'web' | 'cli' | undefined) ?? undefined\n })\n };\n });\n\n // ---------------------------------------------------------------------------\n // 配置 / 权限\n // ---------------------------------------------------------------------------\n app.post<{ Body: { markdown?: string; filename?: string } }>('/api/settings/mr-template/parse', async (req, reply) => {\n const markdown = typeof req.body?.markdown === 'string' ? req.body.markdown : '';\n if (!markdown.trim()) return reply.code(400).send({ error: '请提供 Markdown 文本' });\n try {\n const template = parseMrTemplateMarkdown(markdown, req.body?.filename ?? '');\n return { template, preview: renderMrTemplate(template) };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return reply.code(400).send({ error: message });\n }\n });\n\n app.post<{ Body: { template?: unknown; values?: Record<string, unknown> } }>(\n '/api/settings/mr-template/preview',\n async (req, reply) => {\n const template = normalizeMrTemplate(req.body?.template);\n if (!template) return reply.code(400).send({ error: '没有可预览的模板字段' });\n return { preview: renderMrTemplate(template, req.body?.values ?? {}) };\n }\n );\n\n app.get<{ Querystring: { repoId?: string; validateToken?: string } }>('/api/settings', async (req) => {\n const repoId = req.query.repoId ? Number(req.query.repoId) : undefined;\n return {\n config: runtime.configStore.snapshot(),\n permissions: {\n disabledTools: runtime.config.permissions.disabledTools,\n requireApprovalFor: runtime.config.permissions.requireApprovalFor,\n dryRunDefault: runtime.config.permissions.dryRunDefault\n },\n git: {\n allowedRepos: runtime.config.git.allowedRepos\n },\n mr: await mrClientView(runtime, Number.isInteger(repoId) ? repoId : undefined, {\n validateToken: req.query.validateToken === '1' || req.query.validateToken === 'true'\n }),\n tools: toolSummaries().map((t) => ({\n name: t.name,\n description: t.description,\n riskLevel: runtime.permissions.getRiskLevel(t.name),\n enabled: runtime.permissions.isEnabled(t.name)\n }))\n };\n });\n\n app.put<{\n Querystring: { repoId?: string };\n Body: {\n permissions?: { disabledTools?: string[]; requireApprovalFor?: string[]; dryRunDefault?: boolean };\n git?: { allowedRepos?: string[] };\n mr?: {\n method?: 'cli' | 'token' | 'browser' | 'auto';\n defaultRemote?: string;\n upsertHost?: {\n host: string;\n platform?: 'github' | 'gitlab';\n token?: string;\n apiBaseUrl?: string;\n clearToken?: boolean;\n };\n deleteHost?: string;\n template?: MrTemplate | null;\n };\n };\n }>('/api/settings', async (req, reply) => {\n const patch = req.body ?? {};\n if (patch.permissions === undefined && patch.mr === undefined && patch.git === undefined) {\n return reply.code(400).send({ error: '仅支持更新 permissions、git 或 mr 段' });\n }\n let pendingTokenStatus: MrTokenStatus | undefined;\n const repoIdForMr =\n req.query.repoId && Number.isInteger(Number(req.query.repoId)) ? Number(req.query.repoId) : undefined;\n if (patch.permissions !== undefined) {\n runtime.configStore.update({ permissions: patch.permissions });\n }\n if (patch.git !== undefined) {\n const allowedRepos = Array.isArray(patch.git.allowedRepos)\n ? patch.git.allowedRepos.map((p) => p.trim()).filter(Boolean)\n : runtime.configStore.get().git.allowedRepos;\n runtime.configStore.update({ git: { allowedRepos } });\n }\n if (patch.mr !== undefined) {\n const next = normalizeMrConfig(runtime.config.mr);\n if (\n patch.mr.method === 'cli' ||\n patch.mr.method === 'token' ||\n patch.mr.method === 'browser'\n ) {\n next.method = patch.mr.method;\n const handle =\n repoIdForMr != null\n ? await runtime.repoManager.getById(repoIdForMr)\n : await runtime.repoManager.getCurrent();\n if (handle) {\n next.repoMethods = {\n ...next.repoMethods,\n [normalizeRepoMethodKey(handle.service.repoPath)]: patch.mr.method\n };\n }\n } else if (patch.mr.method === 'auto') {\n next.method = 'browser';\n }\n if (typeof patch.mr.defaultRemote === 'string') {\n next.defaultRemote = patch.mr.defaultRemote.trim() || 'origin';\n }\n if (patch.mr.upsertHost?.host) {\n const token = patch.mr.upsertHost.token?.trim();\n const plat = patch.mr.upsertHost.platform;\n if (token) {\n if (plat !== 'github' && plat !== 'gitlab') {\n return reply.code(400).send({ error: '请先选择 GitHub 或 GitLab' });\n }\n const ctx = await repoRemoteContext(runtime, repoIdForMr);\n const status = await validateMrToken({\n platform: plat,\n token,\n remoteUrl: remoteUrlForValidation(patch.mr.upsertHost.host, ctx),\n apiBaseUrl: patch.mr.upsertHost.apiBaseUrl\n });\n if (!status.ok) {\n return reply.code(400).send({ error: status.titleStatus, tokenStatus: status });\n }\n pendingTokenStatus = status;\n }\n next.hosts = upsertMrHost(next.hosts, patch.mr.upsertHost);\n }\n if (typeof patch.mr.deleteHost === 'string' && patch.mr.deleteHost.trim()) {\n const del = normalizeHostName(patch.mr.deleteHost);\n next.hosts = next.hosts.filter((h) => h.host !== del);\n }\n if (patch.mr.template !== undefined) {\n next.template = normalizeMrTemplate(patch.mr.template);\n }\n runtime.configStore.update({ mr: next });\n if (patch.mr.template !== undefined) {\n const cfg = runtime.configStore.get();\n cfg.mr.template = next.template;\n runtime.configStore.save();\n }\n }\n runtime.config = runtime.configStore.get();\n runtime.permissions = new PermissionManager(runtime.config);\n return {\n ok: true,\n config: runtime.configStore.snapshot(),\n mr: await mrClientView(runtime, repoIdForMr, {\n tokenStatus: pendingTokenStatus,\n tokenStatusHost: patch.mr?.upsertHost?.host\n })\n };\n });\n\n // ---------------------------------------------------------------------------\n // SSE:仓库变化推送\n // ---------------------------------------------------------------------------\n app.get('/api/events', async (req, reply) => {\n reply.hijack();\n const raw = reply.raw;\n raw.writeHead(200, {\n 'Content-Type': 'text/event-stream; charset=utf-8',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no'\n });\n raw.write(': connected\\n\\n');\n\n const listener = (payload: unknown) => {\n raw.write(`event: repo-changed\\ndata: ${JSON.stringify(payload)}\\n\\n`);\n };\n const logListener = () => {\n raw.write('event: log\\ndata: {}\\n\\n');\n };\n const jobListener = (payload: unknown) => {\n raw.write(`event: job-progress\\ndata: ${JSON.stringify(payload)}\\n\\n`);\n };\n runtime.eventBus.on('repo-changed', listener);\n runtime.eventBus.on('log', logListener);\n runtime.eventBus.on('job-progress', jobListener);\n\n const keepAlive = setInterval(() => raw.write(': ping\\n\\n'), 15_000);\n\n req.raw.on('close', () => {\n clearInterval(keepAlive);\n runtime.eventBus.off('repo-changed', listener);\n runtime.eventBus.off('log', logListener);\n runtime.eventBus.off('job-progress', jobListener);\n });\n });\n\n // ---------------------------------------------------------------------------\n // MCP Streamable HTTP\n // ---------------------------------------------------------------------------\n app.get('/mcp', { schema: { hide: true } }, async (req, reply) => {\n reply.hijack();\n await mcpHttp.handle(req.raw, reply.raw, undefined);\n });\n app.post('/mcp', { schema: { hide: true } }, async (req, reply) => {\n // fastify 预读并解析了请求体(req.body);SDK transport 需要 parsedBody 透传\n // (handleRequest(req, res, parsedBody)),否则它再读 req.raw 时流已空,initialize 无响应。\n reply.hijack();\n await mcpHttp.handle(req.raw, reply.raw, req.body);\n });\n\n // SPA fallback(静态托管存在时)\n if (staticDir) {\n app.setNotFoundHandler(async (req, reply) => {\n if (req.url.startsWith('/api') || req.url.startsWith('/mcp') || req.url.startsWith('/docs')) {\n return reply.code(404).send({ error: 'Not Found' });\n }\n const index = path.join(staticDir, 'index.html');\n if (fs.existsSync(index)) {\n reply.type('text/html').send(fs.readFileSync(index, 'utf8'));\n return;\n }\n return reply.code(404).send({ error: 'Not Found' });\n });\n }\n\n const port = options.port ?? runtime.config.server.port;\n const host = options.host ?? runtime.config.server.host;\n if (!options.noListen) {\n await app.listen({ port, host });\n }\n\n return {\n app,\n close: async () => {\n mcpHttp.dispose();\n await app.close();\n disposeRuntime(runtime);\n }\n };\n}\n\nfunction toError(err: unknown): { code: string; message: string; requiredApproval?: boolean } {\n if (err instanceof GitOperationError) return { code: err.code, message: err.message };\n if (err instanceof RepoNotFoundError) return { code: 'REPO_NOT_FOUND', message: err.message };\n if (err instanceof Error) return { code: 'INTERNAL_ERROR', message: err.message.slice(0, 500) };\n return { code: 'INTERNAL_ERROR', message: String(err) };\n}\n\nasync function withCliTokenStatus(\n cli: { gh: MrCliStatus; glab: MrCliStatus },\n opts: {\n validate: boolean;\n platform: 'github' | 'gitlab' | 'unknown';\n remoteUrl: string;\n apiBaseUrl: string;\n host: string | null;\n cwd?: string;\n }\n): Promise<{ gh: MrCliStatus; glab: MrCliStatus }> {\n if (!opts.validate) return cli;\n const jobs: Promise<void>[] = [];\n if (cli.gh.loggedIn && opts.platform !== 'gitlab') {\n jobs.push(\n (async () => {\n const token = await readCliAuthToken('gh', { cwd: opts.cwd, hostname: opts.host ?? undefined });\n if (!token) return;\n cli.gh.tokenStatus = await validateMrToken({\n platform: 'github',\n token,\n remoteUrl: opts.remoteUrl,\n apiBaseUrl: opts.apiBaseUrl,\n skipFormat: true\n });\n })()\n );\n }\n if (cli.glab.loggedIn && opts.platform !== 'github') {\n jobs.push(\n (async () => {\n const token = await readCliAuthToken('glab', { cwd: opts.cwd, hostname: opts.host ?? undefined });\n if (!token) return;\n cli.glab.tokenStatus = await validateMrToken({\n platform: 'gitlab',\n token,\n remoteUrl: opts.remoteUrl,\n apiBaseUrl: opts.apiBaseUrl,\n skipFormat: true\n });\n })()\n );\n }\n await Promise.all(jobs);\n return cli;\n}\n\nfunction remoteUrlForValidation(\n host: string,\n ctx: { remoteUrl: string; host: string | null } | null\n): string {\n const name = normalizeHostName(host);\n if (ctx?.remoteUrl && ctx.host === name) return ctx.remoteUrl;\n return `https://${name}/verify/token.git`;\n}\n\nasync function repoRemoteContext(\n runtime: Runtime,\n repoId?: number\n): Promise<{ remoteUrl: string; host: string | null } | null> {\n try {\n const handle =\n repoId != null && Number.isInteger(repoId)\n ? await runtime.repoManager.getById(repoId)\n : await runtime.repoManager.getCurrent();\n if (!handle) return null;\n const remotes = await handle.service.listRemotes();\n const mr = normalizeMrConfig(runtime.config.mr);\n const names = remotes.map((r) => r.name);\n const preferred = names.includes(mr.defaultRemote) ? mr.defaultRemote : undefined;\n const remote = pickRemoteName('', names, preferred);\n const hit = remotes.find((r) => r.name === remote);\n const remoteUrl = (hit?.pushUrl || hit?.fetchUrl || '').trim();\n return { remoteUrl, host: remoteUrl ? hostnameOf(remoteUrl) : null };\n } catch {\n return null;\n }\n}\n\nasync function mrClientView(\n runtime: Runtime,\n repoId?: number,\n opts?: { validateToken?: boolean; tokenStatus?: MrTokenStatus; tokenStatusHost?: string }\n) {\n const mr = normalizeMrConfig(runtime.config.mr);\n let remotes: Array<{ name: string; fetchUrl: string | null; pushUrl: string | null }> = [];\n let repoPath: string | undefined;\n let current: {\n host: string | null;\n origin: string | null;\n platform: 'github' | 'gitlab' | 'unknown';\n remote: string;\n remoteUrl: string;\n tokenSet: boolean;\n tokenPreview: string;\n tokenStatus: MrTokenStatus | null;\n apiBaseUrl: string;\n } | null = null;\n try {\n const handle =\n repoId != null && Number.isInteger(repoId)\n ? await runtime.repoManager.getById(repoId)\n : await runtime.repoManager.getCurrent();\n if (handle) {\n repoPath = handle.service.repoPath;\n remotes = await handle.service.listRemotes();\n const names = remotes.map((r) => r.name);\n const preferred = names.includes(mr.defaultRemote) ? mr.defaultRemote : undefined;\n const remote = pickRemoteName('', names, preferred);\n const hit = remotes.find((r) => r.name === remote);\n const remoteUrl = (hit?.pushUrl || hit?.fetchUrl || '').trim();\n const host = remoteUrl ? hostnameOf(remoteUrl) : null;\n const https = remoteUrl ? toHttpsRemoteUrl(remoteUrl) : null;\n let origin: string | null = null;\n if (https) {\n try {\n origin = new URL(https).origin;\n } catch {\n origin = null;\n }\n }\n const detected = remoteUrl ? detectMrPlatform(remoteUrl) : 'unknown';\n const profile = remoteUrl ? findMrHost(mr, remoteUrl) : undefined;\n const platform = profile?.platform ?? detected;\n const token = profile?.token?.trim() ?? '';\n let tokenStatus: MrTokenStatus | null = null;\n if (\n opts?.tokenStatus &&\n host &&\n opts.tokenStatusHost &&\n normalizeHostName(opts.tokenStatusHost) === host\n ) {\n tokenStatus = opts.tokenStatus;\n }\n if (!tokenStatus && opts?.validateToken && token && (platform === 'github' || platform === 'gitlab')) {\n tokenStatus = await validateMrToken({\n platform,\n token,\n remoteUrl,\n apiBaseUrl: profile?.apiBaseUrl\n });\n }\n current = {\n host,\n origin,\n platform,\n remote,\n remoteUrl,\n tokenSet: Boolean(token),\n tokenPreview: token ? maskToken(token) : '',\n tokenStatus,\n apiBaseUrl: profile?.apiBaseUrl ?? ''\n };\n }\n } catch {\n current = null;\n }\n const hostsPublic = mr.hosts.map((h) => ({\n host: h.host,\n platform: h.platform,\n tokenSet: Boolean(h.token.trim()),\n tokenPreview: h.token.trim() ? maskToken(h.token) : '',\n apiBaseUrl: h.apiBaseUrl\n }));\n const currentHost = current?.host ?? null;\n const showHosts =\n Boolean(currentHost) && current?.platform !== 'unknown'\n ? hostsPublic.filter((h) => h.host === currentHost)\n : [];\n return {\n method: methodForRepo(mr, repoPath),\n defaultRemote: mr.defaultRemote,\n remotes,\n current,\n hosts: showHosts,\n template: mr.template,\n cli: await withCliTokenStatus(await probeAllMrCli(repoPath), {\n validate: Boolean(opts?.validateToken),\n platform: current?.platform ?? 'unknown',\n remoteUrl: current?.remoteUrl ?? '',\n apiBaseUrl: current?.apiBaseUrl ?? '',\n host: current?.host ?? null,\n cwd: repoPath\n })\n };\n}\n","/**\n * Runtime:mcp-server 共享运行时上下文。\n * - 唯一数据库实例、审计日志、权限管理器、仓库存储与其管理模式;\n * - eventBus 承载 Git 变更事件(changed / log),被 Web SSE 与订阅者消费。\n */\nimport { EventEmitter } from 'node:events';\nimport type { DatabaseSync } from 'node:sqlite';\nimport {\n AuditLogger,\n JobEngine,\n JobStore,\n PermissionManager,\n RepoStore,\n openDatabase,\n trustSystemCa\n} from '@shaohui_jin/git-cockpit-core';\nimport type { GitCockpitConfig } from '@shaohui_jin/git-cockpit-core';\nimport { ConfigStore } from './config.ts';\nimport { RepoManager } from './repoManager.ts';\n\nexport interface RuntimeOptions {\n dataDir?: string;\n configOverrides?: ConstructorParameters<typeof ConfigStore>[1];\n db?: DatabaseSync;\n}\n\nexport interface Runtime {\n configStore: ConfigStore;\n config: GitCockpitConfig;\n db: DatabaseSync;\n auditLogger: AuditLogger;\n permissions: PermissionManager;\n repoStore: RepoStore;\n repoManager: RepoManager;\n jobs: JobEngine;\n /** 全局事件总线:'repo-changed' | 'log' */\n eventBus: EventEmitter;\n}\n\n/** 创建运行时(唯一后端共享);须在进程生命周期内调用一次 */\nexport function createRuntime(options: RuntimeOptions = {}): Runtime {\n trustSystemCa();\n const configStore = new ConfigStore(options.dataDir ?? '~/.git-cockpit', options.configOverrides);\n const config = configStore.get();\n const db = options.db ?? openDatabase(config.storage.dataDir);\n const eventBus = new EventEmitter();\n eventBus.setMaxListeners(200);\n\n const auditLogger = new AuditLogger(db, config.logging);\n const permissions = new PermissionManager(config);\n const repoStore = new RepoStore(db);\n const repoManager = new RepoManager({ repoStore, eventBus, getConfig: () => configStore.get() });\n const jobs = new JobEngine(eventBus, new JobStore(db), {\n resolveGit: async (repoPath) => {\n try {\n return repoManager.getByPath(repoPath).service;\n } catch {\n const handle = await repoManager.open(repoPath);\n return handle.service;\n }\n }\n });\n\n return {\n configStore,\n config,\n db,\n auditLogger,\n permissions,\n repoStore,\n repoManager,\n jobs,\n eventBus\n };\n}\n\n/** 关闭运行时占用的资源(数据库等) */\nexport function disposeRuntime(runtime: Runtime): void {\n try {\n runtime.eventBus.removeAllListeners();\n runtime.repoManager.dispose();\n } catch {\n /* ignore */\n }\n try {\n runtime.db.close();\n } catch {\n /* ignore */\n }\n}","/**\n * 配置管理:默认值 <- 配置文件(config.json)合并(浅/深合并)。\n * 运行时通过 ConfigStore 读写,权限配置界面直接修改其内部状态并持久化。\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { DEFAULT_CONFIG, expandHome, normalizeMrConfig } from '@shaohui_jin/git-cockpit-core';\nimport type { GitCockpitConfig, MrConfigRaw } from '@shaohui_jin/git-cockpit-core';\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\n/** 递归合并:target 上已存在的值优先保留,仅用 source 填充缺失字段与新增对象 */\nfunction deepMerge<T>(target: T, source: unknown): T {\n if (source === null || source === undefined) return target;\n if (Array.isArray(source)) {\n return (source as unknown[]) as T;\n }\n if (typeof source === 'object' && typeof target === 'object' && target !== null) {\n const out = { ...(target as Record<string, unknown>) } as Record<string, unknown>;\n for (const [k, v] of Object.entries(source as Record<string, unknown>)) {\n if (v !== undefined) out[k] = deepMerge(out[k], v);\n }\n return out as T;\n }\n return source as T;\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/**\n * ConfigStore:\n * - 配置存储在 <dataDir>/config.json;\n * - 数据目录默认 ~/.git-cockpit(支持 ~ 展开)。\n */\nexport class ConfigStore {\n private config: GitCockpitConfig;\n\n constructor(\n private readonly dataDir: string,\n overrides?: DeepPartial<GitCockpitConfig>\n ) {\n const dir = expandHome(dataDir);\n const file = path.join(dir, 'config.json');\n // 数据目录同时写入 storage.dataDir,保证数据库与配置存放在同一目录\n this.config = {\n ...this.load(file, overrides),\n storage: { dataDir: dir }\n };\n }\n\n private load(file: string, overrides?: DeepPartial<GitCockpitConfig>): GitCockpitConfig {\n let fileConfig: unknown = {};\n try {\n if (fs.existsSync(file)) {\n fileConfig = JSON.parse(fs.readFileSync(file, 'utf8'));\n }\n } catch (err) {\n // 配置文件损坏时回退默认(不阻断启动)\n console.warn(`[git-cockpit] 配置文件 ${file} 无法解析,已使用默认配置:`, (err as Error).message);\n }\n let merged = deepMerge(structuredClone(DEFAULT_CONFIG), fileConfig);\n if (overrides) merged = deepMerge(merged, overrides);\n merged.mr = normalizeMrConfig(merged.mr as MrConfigRaw);\n return merged;\n }\n\n get(): GitCockpitConfig {\n return this.config;\n }\n\n /** 合并并持久化;返回新配置 */\n update(patch: DeepPartial<GitCockpitConfig>): GitCockpitConfig {\n this.config = deepMerge(this.config, patch);\n this.config = { ...this.config, mr: normalizeMrConfig(this.config.mr as MrConfigRaw) };\n this.save();\n return this.config;\n }\n\n save(): void {\n const dir = expandHome(this.config.storage.dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const file = path.join(dir, 'config.json');\n fs.writeFileSync(file, JSON.stringify(this.config, null, 2), 'utf8');\n }\n\n /** 供日志/调试:仅返回脱敏后的配置快照(隐藏敏感键名) */\n snapshot(): Record<string, unknown> {\n const copy = structuredClone(this.config);\n const redact = (v: unknown): unknown => {\n if (isPlainObject(v)) {\n const out: Record<string, unknown> = {};\n for (const [k, val] of Object.entries(v)) {\n const lower = k.toLowerCase();\n if (/password|token|secret|authorization/i.test(lower)) {\n out[k] = typeof val === 'string' && val.length > 0 ? '[REDACTED]' : val;\n } else {\n out[k] = redact(val);\n }\n }\n return out;\n }\n if (Array.isArray(v)) return v.map(redact);\n return v;\n };\n return redact(copy) as Record<string, unknown>;\n }\n}","/**\n * RepoManager:GitService 实例池 + 已打开仓库存储。\n * - 每个仓库根路径对应一个 GitService 实例(内含串行队列),Web 与 MCP 共用,\n * 保证\"唯一后端\"与锁全局有效;\n * - 打开仓库时校验合法性(GitService.open),并在 SQLite 中记录最近列表;\n * - 监听 Git 变更事件并转发到全局 eventBus,驱动前端实时刷新。\n */\nimport { EventEmitter } from 'node:events';\nimport * as path from 'node:path';\nimport {\n assertRepoAllowed,\n GitService,\n RepoNotFoundError,\n RepoStore\n} from '@shaohui_jin/git-cockpit-core';\nimport type { GitCockpitConfig, OpenedRepo } from '@shaohui_jin/git-cockpit-core';\n\nexport interface RepoHandle {\n service: GitService;\n record: OpenedRepo;\n}\n\nexport interface RepoManagerOptions {\n repoStore: RepoStore;\n eventBus: EventEmitter;\n getConfig: () => GitCockpitConfig;\n}\n\nexport class RepoManager {\n /** repo 根路径(规范化绝对路径) -> repoId */\n private readonly pathToId = new Map<string, number>();\n private readonly idToService = new Map<number, GitService>();\n private readonly eventBus: EventEmitter;\n\n constructor(private readonly options: RepoManagerOptions) {\n this.eventBus = options.eventBus;\n // 为历史记录建立 路径->id 索引(服务实例懒创建)\n for (const repo of options.repoStore.list()) {\n this.pathToId.set(normalizePath(repo.path), repo.id);\n }\n }\n\n list(): (OpenedRepo & { available: boolean })[] {\n return this.options.repoStore.list().map((r) => ({\n ...r,\n available: r.path === '' || true\n }));\n }\n\n /** 取最近打开的仓库(懒创建服务)。与工作台拖拽顺序无关。 */\n async getCurrent(): Promise<RepoHandle | null> {\n const record = this.options.repoStore.listByLastOpened()[0];\n return record ? this.getByRecord(record) : null;\n }\n\n reorder(ids: number[]): OpenedRepo[] {\n return this.options.repoStore.reorder(ids);\n }\n\n /** 按 id 取仓库(懒创建服务) */\n async getById(id: number): Promise<RepoHandle | null> {\n const record = this.options.repoStore.getById(id);\n if (!record) return null;\n try {\n return await this.getByRecord(record);\n } catch (err) {\n if (err instanceof Error && /NOT_A_GIT_REPO|REPO_NOT_FOUND/.test(err.message)) {\n return null;\n }\n throw err;\n }\n }\n\n /** 打开仓库:校验并记录;重复打开返回既有实例 */\n async open(repoPath: string): Promise<RepoHandle> {\n const service = await GitService.open(repoPath);\n assertRepoAllowed(service.repoPath, this.options.getConfig().git.allowedRepos);\n const root = service.repoPath;\n const key = normalizePath(root);\n const existingId = this.pathToId.get(key);\n if (existingId !== undefined && existingId !== 0) {\n const record = this.options.repoStore.getById(existingId);\n if (record) {\n this.options.repoStore.open(root); // 刷新最近打开时间\n this.idToService.set(record.id, service);\n this.attach(service);\n return { service, record: this.options.repoStore.getById(record.id) ?? record };\n }\n }\n const record = this.options.repoStore.open(root);\n this.pathToId.set(key, record.id);\n this.idToService.set(record.id, service);\n this.attach(service);\n return { service, record };\n }\n\n /** 激活仓库:只刷新 last_opened_at,不改工作台拖拽顺序;仓库不存在返回 null */\n activate(id: number): OpenedRepo | null {\n const record = this.options.repoStore.getById(id);\n if (!record) return null;\n this.options.repoStore.open(record.path); // 同路径 open 仅更新 last_opened_at\n return this.options.repoStore.getById(id) ?? record;\n }\n\n /** 按路径取服务,未打开则报错(避免隐式副作用) */\n getByPath(repoPath: string): RepoHandle {\n const key = normalizePath(repoPath);\n const id = this.pathToId.get(key);\n if (id !== undefined && id !== 0) {\n const record = this.options.repoStore.getById(id);\n if (record) {\n const service = this.idToService.get(id);\n if (service) {\n assertRepoAllowed(service.repoPath, this.options.getConfig().git.allowedRepos);\n return { service, record };\n }\n }\n }\n throw new RepoNotFoundError(`仓库未打开: ${repoPath}。请先打开仓库(POST /api/repos/open)。`);\n }\n\n /** 关闭:仅从内存服务池移除;保留历史记录 */\n close(id: number): void {\n const record = this.options.repoStore.getById(id);\n if (record) this.pathToId.delete(normalizePath(record.path));\n const svc = this.idToService.get(id);\n if (svc) svc.removeAllListeners('changed');\n this.idToService.delete(id);\n }\n\n /** 从历史记录中删除并关闭 */\n remove(id: number): void {\n this.close(id);\n this.options.repoStore.remove(id);\n }\n\n dispose(): void {\n for (const id of [...this.idToService.keys()]) this.close(id);\n }\n\n /** 按记录取句柄;服务缺失时(合法)创建并缓存 */\n private async getByRecord(record: OpenedRepo): Promise<RepoHandle> {\n let service = this.idToService.get(record.id);\n if (!service) {\n service = await GitService.open(record.path);\n assertRepoAllowed(service.repoPath, this.options.getConfig().git.allowedRepos);\n this.pathToId.set(normalizePath(service.repoPath), record.id);\n this.idToService.set(record.id, service);\n this.attach(service);\n } else {\n assertRepoAllowed(service.repoPath, this.options.getConfig().git.allowedRepos);\n }\n return { service, record };\n }\n\n private attach(service: GitService): void {\n service.on('changed', (payload: { repoPath: string; command: string[] }) => {\n this.eventBus.emit('repo-changed', {\n repoPath: payload.repoPath,\n command: payload.command,\n at: new Date().toISOString()\n });\n });\n }\n}\n\nfunction normalizePath(p: string): string {\n let normalized = path.resolve(p);\n if (process.platform === 'win32') normalized = normalized.toLowerCase();\n return normalized;\n}","/**\n * OpenAPI / Swagger UI:挂在同一 Fastify 进程的 /docs。\n * REST 摘要集中在此;MCP 工具从 TOOL_DEFS + zod 生成,避免手写 32 份。\n */\nimport type { FastifyInstance } from 'fastify';\nimport swagger from '@fastify/swagger';\nimport swaggerUi from '@fastify/swagger-ui';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport { version } from '../package.json';\nimport { TOOL_DEFS } from './tools/index.ts';\n\nconst REST_META: Record<string, { summary: string; tags: string[] }> = {\n 'get /api/health': { summary: '探活', tags: ['系统'] },\n 'get /api/repos': { summary: '已打开仓库列表', tags: ['仓库'] },\n 'get /api/repos/overview': { summary: '已打开仓库脉搏(工作台)', tags: ['仓库'] },\n 'post /api/jobs': { summary: '提交后台任务(clone / survey / fetch)', tags: ['仓库'] },\n 'post /api/repos/open': { summary: '打开本地仓库', tags: ['仓库'] },\n 'delete /api/repos/{id}': { summary: '关闭并移除仓库', tags: ['仓库'] },\n 'put /api/repos/order': { summary: '保存工作台卡片拖拽顺序', tags: ['仓库'] },\n 'post /api/repos/{id}/activate': { summary: '激活仓库(刷新最近打开时间,不改卡片顺序)', tags: ['仓库'] },\n 'get /api/repos/{id}/status': { summary: '工作区状态', tags: ['只读'] },\n 'get /api/repos/{id}/log': { summary: '提交历史', tags: ['只读'] },\n 'get /api/repos/{id}/diff': { summary: '差异', tags: ['只读'] },\n 'get /api/repos/{id}/show/{commit}': { summary: '提交详情', tags: ['只读'] },\n 'get /api/repos/{id}/branches': { summary: '分支列表', tags: ['只读'] },\n 'get /api/repos/{id}/tags': { summary: '标签列表', tags: ['只读'] },\n 'get /api/repos/{id}/remotes': { summary: '远程列表', tags: ['只读'] },\n 'get /api/repos/{id}/graph': { summary: '提交列表图数据(git log)', tags: ['只读'] },\n 'get /api/repos/{id}/branch-graph': { summary: '分支 tip DAG(状态页 G6)', tags: ['只读'] },\n 'get /api/repos/{id}/file': { summary: '读取某提交中的文件', tags: ['只读'] },\n 'get /api/repos/{id}/stashes': { summary: 'stash 列表', tags: ['只读'] },\n 'get /api/repos/{id}/worktrees': { summary: 'worktree 列表', tags: ['只读'] },\n 'get /api/repos/{id}/workspace-conflicts': { summary: '工作区 merge/rebase 冲突(index 三阶段)', tags: ['只读'] },\n 'get /api/repos/{id}/merge/preview': { summary: 'merge-tree 预演(不进审计)', tags: ['只读'] },\n 'get /api/repos/{id}/merge/rehearse': { summary: '预演 + 冲突正文(不进审计)', tags: ['只读'] },\n 'get /api/repos/{id}/merge/blame': { summary: '冲突文件两侧 blame', tags: ['只读'] },\n 'get /api/repos/{id}/merge/survey': { summary: '合并矩阵批量预演', tags: ['只读'] },\n 'get /api/repos/{id}/merge/order': { summary: '建议合入顺序', tags: ['只读'] },\n 'get /api/repos/{id}/mr/prepare': { summary: '开 PR/MR 前只读解析', tags: ['只读'] },\n 'get /api/repos/{id}/backups': { summary: '高危操作备份分支 / stash', tags: ['只读'] },\n 'get /api/repos/{id}/reflog': { summary: 'reflog', tags: ['只读'] },\n 'get /api/jobs': { summary: '后台任务列表(克隆等)', tags: ['仓库'] },\n 'get /api/jobs/{id}': { summary: '后台任务详情与日志', tags: ['仓库'] },\n 'post /api/jobs/{id}/cancel': { summary: '取消进行中的后台任务', tags: ['仓库'] },\n 'post /api/jobs/clone': { summary: '后台克隆远程仓库', tags: ['仓库'] },\n 'get /api/tools': { summary: '工具注册表(含风险与是否启用)', tags: ['系统'] },\n 'get /api/logs': { summary: '操作审计日志', tags: ['系统'] },\n 'get /api/settings': { summary: '配置与权限', tags: ['设置'] },\n 'put /api/settings': { summary: '更新权限或 MR 配置(Token 明文不回读,保存前校验)', tags: ['设置'] },\n 'post /api/settings/mr-template/parse': { summary: '导入 Markdown 为 MR 正文模板字段(不落盘)', tags: ['设置'] },\n 'post /api/settings/mr-template/preview': { summary: '按模板字段渲染 MR 正文预览', tags: ['设置'] },\n 'get /api/events': { summary: 'SSE:仓库变化、日志、后台任务进度', tags: ['系统'] }\n};\n\ntype OpenApiDoc = {\n paths?: Record<string, Record<string, Record<string, unknown> | undefined> | undefined>;\n};\n\nfunction applyRestMeta(spec: OpenApiDoc): void {\n for (const [path, item] of Object.entries(spec.paths ?? {})) {\n if (!item) continue;\n for (const method of Object.keys(item)) {\n const op = item[method];\n if (!op || typeof op !== 'object') continue;\n const meta = REST_META[`${method} ${path}`];\n if (meta) {\n if (!op.summary) op.summary = meta.summary;\n if (!op.tags) op.tags = meta.tags;\n }\n }\n }\n}\n\nfunction injectToolPaths(spec: OpenApiDoc): void {\n if (!spec.paths) spec.paths = {};\n delete spec.paths['/api/repos/{id}/tools/{tool}'];\n\n const riskTag = (risk: string): string =>\n risk === 'dangerous' ? '高风险工具' : risk === 'write' ? '写操作工具' : '只读工具';\n\n for (const def of TOOL_DEFS) {\n const jsonSchema = zodToJsonSchema(def.schema, {\n target: 'openApi3',\n $refStrategy: 'none'\n }) as Record<string, unknown>;\n delete jsonSchema.$schema;\n\n spec.paths[`/api/repos/{id}/tools/${def.name}`] = {\n post: {\n tags: [riskTag(def.risk)],\n summary: def.name,\n description: def.description,\n operationId: def.name,\n parameters: [\n {\n name: 'id',\n in: 'path',\n required: true,\n schema: { type: 'integer' },\n description: '仓库 id(GET /api/repos)'\n }\n ],\n requestBody: {\n required: false,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n properties: { params: jsonSchema }\n }\n }\n }\n },\n responses: {\n '200': { description: '成功(result 或 dry-run preview)' },\n '400': { description: '失败或参数错误' },\n '403': { description: '工具禁用或需要审批' },\n '404': { description: '未知工具或仓库不存在' }\n }\n }\n };\n }\n}\n\nexport async function registerApiDocs(app: FastifyInstance): Promise<void> {\n await app.register(swagger, {\n openapi: {\n info: {\n title: 'Git Cockpit API',\n description:\n 'Web REST。写操作与 MCP 工具均为 POST /api/repos/{id}/tools/{name},请求体 { params }。MCP 传输 /mcp 不在本文档。',\n version\n }\n },\n exposeHeadRoutes: false,\n transformObject: (doc) => {\n if (!('openapiObject' in doc)) return 'swaggerObject' in doc ? doc.swaggerObject : doc;\n const spec = doc.openapiObject as OpenApiDoc;\n applyRestMeta(spec);\n injectToolPaths(spec);\n return spec;\n }\n });\n\n await app.register(swaggerUi, {\n routePrefix: '/docs',\n uiConfig: { docExpansion: 'list', deepLinking: true }\n });\n}\n"],"mappings":"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","names":["path","fs","PermissionManager","normalizeMrConfig","RepoNotFoundError","EventEmitter","RepoStore","path","record","EventEmitter","RepoStore","path","handle","normalizeMrConfig","PermissionManager","RepoNotFoundError"]}
package/dist/cli-entry.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "./chunk-XARI6A27.js";
5
- import "./chunk-363R2SNK.js";
6
- import "./chunk-HUTQV5AS.js";
4
+ } from "./chunk-SRORGAUX.js";
5
+ import "./chunk-ZIZILLXV.js";
6
+ import "./chunk-XQ6CRLQA.js";
7
7
 
8
8
  // src/cli-entry.ts
9
9
  main(process.argv).then((code) => {
package/dist/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "./chunk-XARI6A27.js";
4
+ } from "./chunk-SRORGAUX.js";
5
5
  import {
6
6
  createRuntime,
7
7
  disposeRuntime
8
- } from "./chunk-363R2SNK.js";
9
- import "./chunk-HUTQV5AS.js";
8
+ } from "./chunk-ZIZILLXV.js";
9
+ import "./chunk-XQ6CRLQA.js";
10
10
  export {
11
11
  createRuntime,
12
12
  disposeRuntime,
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export { C as ConfigStore, D as DeepPartial, a as RepoHandle, b as RepoManager,
3
3
  export { MCP_SERVER_INFO, McpHttpHandler, createMcpServer, startMcpStdio } from './mcpServer.js';
4
4
  export { WebServerHandle, createWebServer } from './webServer.js';
5
5
  import { ExecutionResult, Capability } from '@shaohui_jin/git-cockpit-core';
6
+ export { TOOL_DEFS, TOOL_DEF_MAP, toolSummaries } from '@shaohui_jin/git-cockpit-core';
6
7
  export { main as runCli } from './cli.js';
7
8
  import 'node:events';
8
9
  import 'node:sqlite';
@@ -27,20 +28,6 @@ interface ToolExecutionContext {
27
28
  */
28
29
  declare function executeTool(def: ToolDef, rawArgs: Record<string, unknown>, ctx: ToolExecutionContext): Promise<ToolExecutionResult>;
29
30
 
30
- /**
31
- * 工具注册表:全部 MCP 工具的静态定义(名称/描述/风险等级/schema/handler)。
32
- * handler 直接调用 core GitService 的方法;统一安全链路由 core executeCapability 承担。
33
- * 加载本模块时写入 Capability registry,权限表从 registry 推导。
34
- */
35
-
36
- declare const TOOL_DEFS: ToolDef[];
37
- declare const TOOL_DEF_MAP: ReadonlyMap<string, ToolDef>;
38
- /** 供 mcpServer 注册工具列表(名称、描述、schema) */
39
- declare function toolSummaries(): {
40
- name: string;
41
- description: string;
42
- }[];
43
-
44
31
  /**
45
32
  * MCP Prompt:。正文抄仓库根 README「Agent 该怎么用」,不许另写流程
46
33
  */
@@ -53,18 +40,18 @@ declare const MCP_PROMPTS: readonly [{
53
40
  }, {
54
41
  readonly name: "merge_preview_apply";
55
42
  readonly title: "合不合得进去,以及写进仓库";
56
- readonly description: "只做预演,人选边,独立目录落盘。工作区已经卡住时不要走这条。";
43
+ readonly description: "只做预演。有冲突给人网页,不要选边。工作区已经卡住时不要走这条。";
57
44
  readonly text: string;
58
45
  }, {
59
46
  readonly name: "workspace_continue";
60
47
  readonly title: "工作区已经卡在 merge 或 rebase 里";
61
- readonly description: "看状态,然后继续或放弃。不要用预演落盘去收尾。";
48
+ readonly description: "看状态。人先解决,再继续或放弃。不要传 files,不要用预演落盘去收尾。";
62
49
  readonly text: string;
63
50
  }, {
64
51
  readonly name: "open_mr";
65
52
  readonly title: "开 Pull Request / Merge Request";
66
- readonly description: "先准备标题和目标分支,再创建。Token 在网页设置里,不要塞进工具参数。";
53
+ readonly description: "先预演;冲突则停。干净才落盘开单。Token 在网页设置里,不要塞进工具参数。";
67
54
  readonly text: string;
68
55
  }];
69
56
 
70
- export { MCP_PROMPTS, Runtime, TOOL_DEFS, TOOL_DEF_MAP, type ToolDef, type ToolExecutionContext, type ToolExecutionResult, executeTool, formatResultForMcp, toolSummaries };
57
+ export { MCP_PROMPTS, Runtime, type ToolDef, type ToolExecutionContext, type ToolExecutionResult, executeTool, formatResultForMcp };
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "./chunk-XARI6A27.js";
4
+ } from "./chunk-SRORGAUX.js";
5
5
  import {
6
6
  ConfigStore,
7
7
  RepoManager,
8
8
  createRuntime,
9
9
  createWebServer,
10
10
  disposeRuntime
11
- } from "./chunk-363R2SNK.js";
11
+ } from "./chunk-ZIZILLXV.js";
12
12
  import {
13
13
  MCP_PROMPTS,
14
14
  MCP_SERVER_INFO,
@@ -20,7 +20,7 @@ import {
20
20
  formatResultForMcp,
21
21
  startMcpStdio,
22
22
  toolSummaries
23
- } from "./chunk-HUTQV5AS.js";
23
+ } from "./chunk-XQ6CRLQA.js";
24
24
  export {
25
25
  ConfigStore,
26
26
  MCP_PROMPTS,
package/dist/mcpServer.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  McpHttpHandler,
5
5
  createMcpServer,
6
6
  startMcpStdio
7
- } from "./chunk-HUTQV5AS.js";
7
+ } from "./chunk-XQ6CRLQA.js";
8
8
  export {
9
9
  MCP_SERVER_INFO,
10
10
  McpHttpHandler,