@shaohui_jin/git-cockpit-mcp-server 0.1.7 → 0.1.9

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,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  McpHttpHandler,
4
+ TOOL_DEFS,
4
5
  TOOL_DEF_MAP,
5
6
  executeTool,
6
7
  toolSummaries
7
- } from "./chunk-WCLC5KOG.js";
8
+ } from "./chunk-OKXMVKBU.js";
8
9
 
9
10
  // src/webServer.ts
10
11
  import * as path3 from "path";
@@ -271,6 +272,121 @@ function disposeRuntime(runtime) {
271
272
  }
272
273
  }
273
274
 
275
+ // src/openapi.ts
276
+ import swagger from "@fastify/swagger";
277
+ import swaggerUi from "@fastify/swagger-ui";
278
+ import { zodToJsonSchema } from "zod-to-json-schema";
279
+
280
+ // package.json
281
+ var version = "0.1.9";
282
+
283
+ // src/openapi.ts
284
+ var REST_META = {
285
+ "get /api/health": { summary: "\u63A2\u6D3B", tags: ["\u7CFB\u7EDF"] },
286
+ "get /api/repos": { summary: "\u5DF2\u6253\u5F00\u4ED3\u5E93\u5217\u8868", tags: ["\u4ED3\u5E93"] },
287
+ "post /api/repos/open": { summary: "\u6253\u5F00\u672C\u5730\u4ED3\u5E93", tags: ["\u4ED3\u5E93"] },
288
+ "delete /api/repos/{id}": { summary: "\u5173\u95ED\u5E76\u79FB\u9664\u4ED3\u5E93", tags: ["\u4ED3\u5E93"] },
289
+ "post /api/repos/{id}/activate": { summary: "\u6FC0\u6D3B\u4ED3\u5E93\uFF08\u7F6E\u9876\uFF09", tags: ["\u4ED3\u5E93"] },
290
+ "get /api/repos/{id}/status": { summary: "\u5DE5\u4F5C\u533A\u72B6\u6001", tags: ["\u53EA\u8BFB"] },
291
+ "get /api/repos/{id}/log": { summary: "\u63D0\u4EA4\u5386\u53F2", tags: ["\u53EA\u8BFB"] },
292
+ "get /api/repos/{id}/diff": { summary: "\u5DEE\u5F02", tags: ["\u53EA\u8BFB"] },
293
+ "get /api/repos/{id}/show/{commit}": { summary: "\u63D0\u4EA4\u8BE6\u60C5", tags: ["\u53EA\u8BFB"] },
294
+ "get /api/repos/{id}/branches": { summary: "\u5206\u652F\u5217\u8868", tags: ["\u53EA\u8BFB"] },
295
+ "get /api/repos/{id}/tags": { summary: "\u6807\u7B7E\u5217\u8868", tags: ["\u53EA\u8BFB"] },
296
+ "get /api/repos/{id}/remotes": { summary: "\u8FDC\u7A0B\u5217\u8868", tags: ["\u53EA\u8BFB"] },
297
+ "get /api/repos/{id}/graph": { summary: "\u63D0\u4EA4\u56FE\u6570\u636E", tags: ["\u53EA\u8BFB"] },
298
+ "get /api/repos/{id}/file": { summary: "\u8BFB\u53D6\u67D0\u63D0\u4EA4\u4E2D\u7684\u6587\u4EF6", tags: ["\u53EA\u8BFB"] },
299
+ "get /api/repos/{id}/stashes": { summary: "stash \u5217\u8868", tags: ["\u53EA\u8BFB"] },
300
+ "get /api/tools": { summary: "\u5DE5\u5177\u6CE8\u518C\u8868\uFF08\u542B\u98CE\u9669\u4E0E\u662F\u5426\u542F\u7528\uFF09", tags: ["\u7CFB\u7EDF"] },
301
+ "get /api/logs": { summary: "\u64CD\u4F5C\u5BA1\u8BA1\u65E5\u5FD7", tags: ["\u7CFB\u7EDF"] },
302
+ "get /api/settings": { summary: "\u914D\u7F6E\u4E0E\u6743\u9650", tags: ["\u8BBE\u7F6E"] },
303
+ "put /api/settings": { summary: "\u66F4\u65B0\u6743\u9650\u6216 MR \u914D\u7F6E\uFF08Token \u53EA\u5199\u4E0D\u8BFB\uFF09", tags: ["\u8BBE\u7F6E"] },
304
+ "get /api/events": { summary: "SSE\uFF1A\u4ED3\u5E93\u53D8\u5316\u4E0E\u65E5\u5FD7", tags: ["\u7CFB\u7EDF"] }
305
+ };
306
+ function applyRestMeta(spec) {
307
+ for (const [path4, item] of Object.entries(spec.paths ?? {})) {
308
+ if (!item) continue;
309
+ for (const method of Object.keys(item)) {
310
+ const op = item[method];
311
+ if (!op || typeof op !== "object") continue;
312
+ const meta = REST_META[`${method} ${path4}`];
313
+ if (meta) {
314
+ if (!op.summary) op.summary = meta.summary;
315
+ if (!op.tags) op.tags = meta.tags;
316
+ }
317
+ }
318
+ }
319
+ }
320
+ function injectToolPaths(spec) {
321
+ if (!spec.paths) spec.paths = {};
322
+ delete spec.paths["/api/repos/{id}/tools/{tool}"];
323
+ const riskTag = (risk) => risk === "dangerous" ? "\u9AD8\u98CE\u9669\u5DE5\u5177" : risk === "write" ? "\u5199\u64CD\u4F5C\u5DE5\u5177" : "\u53EA\u8BFB\u5DE5\u5177";
324
+ for (const def of TOOL_DEFS) {
325
+ const jsonSchema = zodToJsonSchema(def.schema, {
326
+ target: "openApi3",
327
+ $refStrategy: "none"
328
+ });
329
+ delete jsonSchema.$schema;
330
+ spec.paths[`/api/repos/{id}/tools/${def.name}`] = {
331
+ post: {
332
+ tags: [riskTag(def.risk)],
333
+ summary: def.name,
334
+ description: def.description,
335
+ operationId: def.name,
336
+ parameters: [
337
+ {
338
+ name: "id",
339
+ in: "path",
340
+ required: true,
341
+ schema: { type: "integer" },
342
+ description: "\u4ED3\u5E93 id\uFF08GET /api/repos\uFF09"
343
+ }
344
+ ],
345
+ requestBody: {
346
+ required: false,
347
+ content: {
348
+ "application/json": {
349
+ schema: {
350
+ type: "object",
351
+ properties: { params: jsonSchema }
352
+ }
353
+ }
354
+ }
355
+ },
356
+ responses: {
357
+ "200": { description: "\u6210\u529F\uFF08result \u6216 dry-run preview\uFF09" },
358
+ "400": { description: "\u5931\u8D25\u6216\u53C2\u6570\u9519\u8BEF" },
359
+ "403": { description: "\u5DE5\u5177\u7981\u7528\u6216\u9700\u8981\u5BA1\u6279" },
360
+ "404": { description: "\u672A\u77E5\u5DE5\u5177\u6216\u4ED3\u5E93\u4E0D\u5B58\u5728" }
361
+ }
362
+ }
363
+ };
364
+ }
365
+ }
366
+ async function registerApiDocs(app) {
367
+ await app.register(swagger, {
368
+ openapi: {
369
+ info: {
370
+ title: "Git Cockpit API",
371
+ description: "Web REST\u3002\u5199\u64CD\u4F5C\u4E0E MCP \u5DE5\u5177\u5747\u4E3A POST /api/repos/{id}/tools/{name}\uFF0C\u8BF7\u6C42\u4F53 { params }\u3002MCP \u4F20\u8F93 /mcp \u4E0D\u5728\u672C\u6587\u6863\u3002",
372
+ version
373
+ }
374
+ },
375
+ exposeHeadRoutes: false,
376
+ transformObject: (doc) => {
377
+ if (!("openapiObject" in doc)) return "swaggerObject" in doc ? doc.swaggerObject : doc;
378
+ const spec = doc.openapiObject;
379
+ applyRestMeta(spec);
380
+ injectToolPaths(spec);
381
+ return spec;
382
+ }
383
+ });
384
+ await app.register(swaggerUi, {
385
+ routePrefix: "/docs",
386
+ uiConfig: { docExpansion: "list", deepLinking: true }
387
+ });
388
+ }
389
+
274
390
  // src/webServer.ts
275
391
  function resolveWebDist() {
276
392
  const here = path3.resolve(path3.dirname(fileURLToPath(import.meta.url)));
@@ -299,6 +415,7 @@ async function createWebServer(runtime, options = {}) {
299
415
  methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
300
416
  allowedHeaders: ["Content-Type", "Mcp-Session-Id", "Authorization"]
301
417
  });
418
+ await registerApiDocs(app);
302
419
  const staticDir = options.staticDir !== void 0 ? options.staticDir : resolveWebDist();
303
420
  if (staticDir && fs2.existsSync(staticDir)) {
304
421
  await app.register(fastifyStatic, { root: staticDir, prefix: "/" });
@@ -444,6 +561,7 @@ async function createWebServer(runtime, options = {}) {
444
561
  );
445
562
  app.post(
446
563
  "/api/repos/:id/tools/:tool",
564
+ { schema: { hide: true } },
447
565
  async (req, reply) => {
448
566
  const def = TOOL_DEF_MAP.get(req.params.tool);
449
567
  if (!def) return reply.code(404).send({ error: `\u672A\u77E5\u5DE5\u5177: ${req.params.tool}` });
@@ -485,6 +603,9 @@ async function createWebServer(runtime, options = {}) {
485
603
  requireApprovalFor: runtime.config.permissions.requireApprovalFor,
486
604
  dryRunDefault: runtime.config.permissions.dryRunDefault
487
605
  },
606
+ mr: {
607
+ githubTokenSet: Boolean(runtime.config.mr?.githubToken?.trim())
608
+ },
488
609
  tools: toolSummaries().map((t) => ({
489
610
  name: t.name,
490
611
  description: t.description,
@@ -494,13 +615,22 @@ async function createWebServer(runtime, options = {}) {
494
615
  }));
495
616
  app.put("/api/settings", async (req, reply) => {
496
617
  const patch = req.body ?? {};
497
- if (patch.permissions === void 0) {
498
- return reply.code(400).send({ error: "\u4EC5\u652F\u6301\u66F4\u65B0 permissions \u6BB5" });
618
+ if (patch.permissions === void 0 && patch.mr === void 0) {
619
+ return reply.code(400).send({ error: "\u4EC5\u652F\u6301\u66F4\u65B0 permissions \u6216 mr \u6BB5" });
620
+ }
621
+ if (patch.permissions !== void 0) {
622
+ runtime.configStore.update({ permissions: patch.permissions });
623
+ }
624
+ if (patch.mr !== void 0 && typeof patch.mr.githubToken === "string") {
625
+ runtime.configStore.update({ mr: { githubToken: patch.mr.githubToken } });
499
626
  }
500
- runtime.configStore.update({ permissions: patch.permissions });
501
627
  runtime.config = runtime.configStore.get();
502
628
  runtime.permissions = new PermissionManager2(runtime.config);
503
- return { ok: true, config: runtime.configStore.snapshot() };
629
+ return {
630
+ ok: true,
631
+ config: runtime.configStore.snapshot(),
632
+ mr: { githubTokenSet: Boolean(runtime.config.mr?.githubToken?.trim()) }
633
+ };
504
634
  });
505
635
  app.get("/api/events", async (req, reply) => {
506
636
  reply.hijack();
@@ -530,17 +660,17 @@ data: ${JSON.stringify(payload)}
530
660
  runtime.eventBus.off("log", logListener);
531
661
  });
532
662
  });
533
- app.get("/mcp", async (req, reply) => {
663
+ app.get("/mcp", { schema: { hide: true } }, async (req, reply) => {
534
664
  reply.hijack();
535
665
  await mcpHttp.handle(req.raw, reply.raw, void 0);
536
666
  });
537
- app.post("/mcp", async (req, reply) => {
667
+ app.post("/mcp", { schema: { hide: true } }, async (req, reply) => {
538
668
  reply.hijack();
539
669
  await mcpHttp.handle(req.raw, reply.raw, req.body);
540
670
  });
541
671
  if (staticDir) {
542
672
  app.setNotFoundHandler(async (req, reply) => {
543
- if (req.url.startsWith("/api") || req.url.startsWith("/mcp")) {
673
+ if (req.url.startsWith("/api") || req.url.startsWith("/mcp") || req.url.startsWith("/docs")) {
544
674
  return reply.code(404).send({ error: "Not Found" });
545
675
  }
546
676
  const index = path3.join(staticDir, "index.html");
@@ -577,6 +707,7 @@ export {
577
707
  RepoManager,
578
708
  createRuntime,
579
709
  disposeRuntime,
710
+ version,
580
711
  createWebServer
581
712
  };
582
- //# sourceMappingURL=chunk-5CZ3BO3H.js.map
713
+ //# sourceMappingURL=chunk-2U5EIJJV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/webServer.ts","../src/runtime.ts","../src/config.ts","../src/repoManager.ts","../src/openapi.ts","../package.json"],"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 PermissionManager,\n RepoNotFoundError\n} from '@shaohui_jin/git-cockpit-core';\nimport type { 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';\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\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\n // Health\n app.get('/api/health', async () => ({\n ok: true,\n service: 'git-cockpit',\n version: '0.1.6',\n uptimeMs: process.uptime() * 1000\n }));\n\n // ---------------------------------------------------------------------------\n // 仓库管理\n // ---------------------------------------------------------------------------\n app.get('/api/repos', async () => ({\n repos: runtime.repoManager.list()\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 /** 激活/进入仓库:刷新最近打开排序,并记录一条操作日志(source=web) */\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: { 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 // ---------------------------------------------------------------------------\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.get('/api/settings', async () => ({\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 mr: {\n githubTokenSet: Boolean(runtime.config.mr?.githubToken?.trim())\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 app.put<{\n Body: {\n permissions?: { disabledTools?: string[]; requireApprovalFor?: string[]; dryRunDefault?: boolean };\n mr?: { githubToken?: string };\n };\n }>('/api/settings', async (req, reply) => {\n const patch = req.body ?? {};\n if (patch.permissions === undefined && patch.mr === undefined) {\n return reply.code(400).send({ error: '仅支持更新 permissions 或 mr 段' });\n }\n if (patch.permissions !== undefined) {\n runtime.configStore.update({ permissions: patch.permissions });\n }\n if (patch.mr !== undefined && typeof patch.mr.githubToken === 'string') {\n runtime.configStore.update({ mr: { githubToken: patch.mr.githubToken } });\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: { githubTokenSet: Boolean(runtime.config.mr?.githubToken?.trim()) }\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 runtime.eventBus.on('repo-changed', listener);\n runtime.eventBus.on('log', logListener);\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 });\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 * 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 PermissionManager,\n RepoStore,\n openDatabase\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 /** 全局事件总线:'repo-changed' | 'log' */\n eventBus: EventEmitter;\n}\n\n/** 创建运行时(唯一后端共享);须在进程生命周期内调用一次 */\nexport function createRuntime(options: RuntimeOptions = {}): Runtime {\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, config });\n\n return {\n configStore,\n config,\n db,\n auditLogger,\n permissions,\n repoStore,\n repoManager,\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 } from '@shaohui_jin/git-cockpit-core';\nimport type { GitCockpitConfig } 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 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.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|url/i.test(lower)) out[k] = '[REDACTED]';\n else out[k] = redact(val);\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 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 config: 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.list()[0];\n return record ? this.getByRecord(record) : null;\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 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 /** 激活仓库:刷新最近打开时间,驱动「最近打开」列表重排到首位;仓库不存在返回 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) return { service, record };\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 this.pathToId.set(normalizePath(service.repoPath), record.id);\n this.idToService.set(record.id, service);\n this.attach(service);\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 'post /api/repos/open': { summary: '打开本地仓库', tags: ['仓库'] },\n 'delete /api/repos/{id}': { 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: '提交图数据', tags: ['只读'] },\n 'get /api/repos/{id}/file': { summary: '读取某提交中的文件', tags: ['只读'] },\n 'get /api/repos/{id}/stashes': { summary: 'stash 列表', 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 '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","{\n \"name\": \"@shaohui_jin/git-cockpit-mcp-server\",\n \"version\": \"0.1.9\",\n \"description\": \"Git Cockpit - 基于 MCP 的 Git 可视化操作工具(MCP Server + Web 服务 + CLI)\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"engines\": {\n \"node\": \">=22.5.0\"\n },\n \"bin\": {\n \"git-cockpit\": \"./dist/cli-entry.js\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/cli-entry.js start\",\n \"test\": \"vitest run\",\n \"typecheck\": \"tsc --noEmit -p tsconfig.json\",\n \"prepublishOnly\": \"pnpm run typecheck && pnpm run build && node scripts/copy-web.mjs\"\n },\n \"dependencies\": {\n \"@fastify/cors\": \"^10.0.2\",\n \"@fastify/static\": \"^8.0.3\",\n \"@fastify/swagger\": \"^9.8.1\",\n \"@fastify/swagger-ui\": \"^5.2.6\",\n \"@modelcontextprotocol/sdk\": \"^1.12.0\",\n \"@shaohui_jin/git-cockpit-core\": \"workspace:^\",\n \"fastify\": \"^5.2.1\",\n \"zod\": \"^3.24.1\",\n \"zod-to-json-schema\": \"^3.25.2\"\n },\n \"devDependencies\": {\n \"simple-git\": \"^3.27.0\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.7.2\",\n \"vitest\": \"^3.0.4\"\n }\n}"],"mappings":"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","names":["path","fs","PermissionManager","RepoNotFoundError","EventEmitter","RepoStore","path","record","EventEmitter","RepoStore","path","PermissionManager","RepoNotFoundError"]}
@@ -125,6 +125,13 @@ function formatResultForMcp(exec) {
125
125
  return parts.join("\n\n");
126
126
  }
127
127
 
128
+ // src/tools/index.ts
129
+ import {
130
+ createGithubPullRequest,
131
+ GitOperationError as GitOperationError2,
132
+ githubPullsApiUrl
133
+ } from "@shaohui_jin/git-cockpit-core";
134
+
128
135
  // src/tools/schemas.ts
129
136
  import { z as z2 } from "zod";
130
137
  var repoPath = z2.string().optional().describe("\u4ED3\u5E93\u7EDD\u5BF9\u8DEF\u5F84\u3002\u4E0D\u63D0\u4F9B\u65F6\u4F7F\u7528\u5F53\u524D\u6253\u5F00\u7684\u4ED3\u5E93\u3002");
@@ -167,6 +174,22 @@ var GitGraphSchema = z2.object({
167
174
  ...readonlyBase,
168
175
  maxCount: z2.number().int().min(1).max(5e3).optional().describe("\u6700\u5927\u63D0\u4EA4\u6570")
169
176
  });
177
+ var mergeIntoFrom = {
178
+ into: z2.string().min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours / \u9884\u6F14\u5DE6\u4FA7\uFF09\u3002\u4F8B\u5982 origin/main"),
179
+ from: z2.string().min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs / \u9884\u6F14\u53F3\u4FA7\uFF09\u3002\u4F8B\u5982 feature/x"),
180
+ fetch: z2.boolean().optional().describe("\u662F\u5426\u5148\u975E\u4EA4\u4E92 fetch\u3002\u7F3A\u7701 true\uFF1B\u5931\u8D25\u5219\u7528\u672C\u5730\u5F15\u7528\u5E76\u6807\u660E stale"),
181
+ remote: z2.string().optional().describe("fetch/push \u4F7F\u7528\u7684\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin"),
182
+ path: z2.string().optional().describe("\u4EC5\u5173\u5FC3\u8BE5\u51B2\u7A81\u6587\u4EF6\u8DEF\u5F84")
183
+ };
184
+ var GitMergePreviewSchema = z2.object({
185
+ ...readonlyBase,
186
+ ...mergeIntoFrom
187
+ });
188
+ var GitMergeRehearseSchema = z2.object({
189
+ ...readonlyBase,
190
+ ...mergeIntoFrom,
191
+ maxFiles: z2.number().int().min(1).max(100).optional().describe("\u6700\u591A\u751F\u6210\u51B2\u7A81\u6B63\u6587\u7684\u6587\u4EF6\u6570\uFF0C\u7F3A\u7701 20")
192
+ });
170
193
  var writeBase = { repoPath, dryRun };
171
194
  var pathsField = z2.array(z2.string()).default([]).describe("\u6587\u4EF6\u8DEF\u5F84\u5217\u8868\uFF1B\u7F3A\u7701\u4E3A\u7A7A\u5373\u5168\u90E8\u6587\u4EF6");
172
195
  var GitAddSchema = z2.object({ ...writeBase, paths: pathsField });
@@ -234,6 +257,37 @@ var GitStashPopSchema = z2.object({
234
257
  ...writeBase,
235
258
  index: z2.number().int().min(0).optional().describe("stash \u5E8F\u53F7\uFF0C\u7F3A\u7701\u4E3A\u6700\u65B0\u7684 stash@{0}")
236
259
  });
260
+ var GitApplyResolveSchema = z2.object({
261
+ ...writeBase,
262
+ into: z2.string().min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09\u3002\u4F8B\u5982 origin/main"),
263
+ from: z2.string().min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs\uFF09\u3002\u4F8B\u5982 feature/x"),
264
+ remote: z2.string().optional().describe("push \u4F7F\u7528\u7684\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin"),
265
+ files: z2.array(
266
+ z2.object({
267
+ path: z2.string().describe("\u4ED3\u5E93\u5185\u76F8\u5BF9\u8DEF\u5F84"),
268
+ resolvedContent: z2.string().describe("\u8BE5\u6587\u4EF6\u89E3\u51B3\u540E\u7684\u5B8C\u6574\u5185\u5BB9")
269
+ })
270
+ ).optional().describe("\u5DF2\u89E3\u51B3\u51B2\u7A81\u7684\u6587\u4EF6\u3002\u5E72\u51C0\u5408\u5E76\u53EF\u7701\u7565\u6216\u4F20\u7A7A\u6570\u7EC4"),
271
+ push: z2.boolean().optional().describe("\u662F\u5426\u63A8\u9001\u4E34\u65F6\u5206\u652F\uFF0C\u7F3A\u7701 true"),
272
+ keepLocal: z2.boolean().optional().describe("\u63A8\u9001\u5931\u8D25\u65F6\u4ECD\u4FDD\u7559\u672C\u5730\u4E34\u65F6\u5206\u652F"),
273
+ tempBranch: z2.string().optional().describe("\u81EA\u5B9A\u4E49\u4E34\u65F6\u5206\u652F\u540D\uFF1B\u7F3A\u7701 merge/<from>-into-<into>")
274
+ });
275
+ var mrIntoFrom = {
276
+ into: z2.string().min(1).describe("\u5408\u5165\u76EE\u6807\uFF08\u7EBF\u4E0A / ours\uFF09\u3002\u4F8B\u5982 origin/main"),
277
+ from: z2.string().min(1).describe("\u6211\u7684\u5206\u652F\uFF08theirs\uFF09\u3002\u4F8B\u5982 feature/x"),
278
+ remote: z2.string().optional().describe("\u8FDC\u7A0B\u540D\uFF0C\u7F3A\u7701 origin"),
279
+ sourceBranch: z2.string().optional().describe("PR \u6E90\u5206\u652F\uFF1B\u7F3A\u7701\u5DF2\u6709\u4E34\u65F6\u5206\u652F merge/<from>-into-<into>\uFF0C\u5426\u5219 from")
280
+ };
281
+ var GitMrPrepareSchema = z2.object({
282
+ ...readonlyBase,
283
+ ...mrIntoFrom
284
+ });
285
+ var GitMrCreateSchema = z2.object({
286
+ ...writeBase,
287
+ ...mrIntoFrom,
288
+ title: z2.string().optional().describe("PR \u6807\u9898\uFF1B\u7F3A\u7701 Merge <source> into <target>"),
289
+ body: z2.string().optional().describe("PR \u6B63\u6587")
290
+ });
237
291
  var GitResetHardSchema = z2.object({ ...writeBase, target: z2.string().describe("\u8981\u91CD\u7F6E\u5230\u7684\u63D0\u4EA4") });
238
292
  var GitCleanSchema = z2.object({ ...writeBase });
239
293
  var GitPushForceSchema = z2.object({
@@ -334,6 +388,45 @@ var TOOL_DEFS = [
334
388
  schema: GitGraphSchema,
335
389
  handler: async (args, ctx) => ctx.git.getGraph(args.maxCount)
336
390
  },
391
+ {
392
+ name: "git_merge_preview",
393
+ description: "\u7528 git merge-tree \u9884\u6F14\u628A from \u5408\u5165 into\uFF08\u4E0D\u6539\u5DE5\u4F5C\u533A\uFF09\u3002into=\u5408\u5165\u76EE\u6807/\u7EBF\u4E0A/ours\uFF0Cfrom=\u6211\u7684\u5206\u652F/theirs\u3002\u8FD4\u56DE\u662F\u5426\u53EF\u5E72\u51C0\u5408\u5E76\u53CA\u51B2\u7A81\u6587\u4EF6\u5217\u8868\u3002\u7981\u6B62\u7528 git_merge \u505A\u9884\u6F14\u3002Git >= 2.38\u3002",
394
+ risk: "readonly",
395
+ schema: GitMergePreviewSchema,
396
+ handler: async (args, ctx) => ctx.git.previewMerge({
397
+ into: args.into,
398
+ from: args.from,
399
+ fetch: args.fetch,
400
+ remote: args.remote,
401
+ path: args.path
402
+ })
403
+ },
404
+ {
405
+ name: "git_merge_rehearse",
406
+ description: "\u5B8C\u6574\u5408\u5E76\u9884\u6F14\uFF1A\u51B2\u7A81\u6587\u4EF6 + diff3 \u51B2\u7A81\u6B63\u6587 + ours/theirs/base\uFF08\u4ECD\u4E0D\u6539\u5DE5\u4F5C\u533A\uFF09\u3002\u9009\u8FB9\u540E\u628A files[{path,resolvedContent}] \u4EA4\u7ED9 git_apply_resolve\u3002into/from \u542B\u4E49\u540C git_merge_preview\u3002",
407
+ risk: "readonly",
408
+ schema: GitMergeRehearseSchema,
409
+ handler: async (args, ctx) => ctx.git.rehearseMerge({
410
+ into: args.into,
411
+ from: args.from,
412
+ fetch: args.fetch,
413
+ remote: args.remote,
414
+ path: args.path,
415
+ maxFiles: args.maxFiles
416
+ })
417
+ },
418
+ {
419
+ name: "git_mr_prepare",
420
+ description: "\u53EA\u8BFB\uFF1A\u89E3\u6790\u5F00 PR \u7684\u6E90/\u76EE\u6807\u5206\u652F\u4E0E\u6D4F\u89C8\u5668\u521B\u5EFA\u9875 URL\u3002\u4F18\u5148\u5DF2\u6709\u4E34\u65F6\u5206\u652F merge/<from>-into-<into>\u3002\u4E0D\u8C03\u7528 Token\u3002into/from \u542B\u4E49\u540C git_merge_preview\u3002",
421
+ risk: "readonly",
422
+ schema: GitMrPrepareSchema,
423
+ handler: async (args, ctx) => ctx.git.prepareMr({
424
+ into: args.into,
425
+ from: args.from,
426
+ remote: args.remote,
427
+ sourceBranch: args.sourceBranch
428
+ })
429
+ },
337
430
  // ---------------------------------------------------------------------------
338
431
  // 写操作工具(默认开放,需预览/确认)
339
432
  // ---------------------------------------------------------------------------
@@ -475,6 +568,79 @@ var TOOL_DEFS = [
475
568
  schema: GitStashPopSchema,
476
569
  handler: async (args, ctx) => ctx.git.stashPop({ dryRun: args.dryRun, index: args.index })
477
570
  },
571
+ {
572
+ name: "git_apply_resolve",
573
+ description: "\u5728\u72EC\u7ACB git worktree \u4E2D\u628A from \u5408\u5165 into \u5E76\u63D0\u4EA4\u5230\u4E34\u65F6\u5206\u652F\uFF08\u4E3B\u5DE5\u4F5C\u533A\u4E0D\u5207\u6362\uFF09\u3002\u5E72\u51C0\u5408\u5E76 files \u53EF\u7A7A\uFF1B\u6709\u51B2\u7A81\u5FC5\u987B\u63D0\u4F9B files\u3002\u9ED8\u8BA4 push \u4E34\u65F6\u5206\u652F\u3002\u4E0D\u8981\u7528\u5DE5\u4F5C\u533A git_merge \u4EE3\u66FF\u672C\u5DE5\u5177\u3002",
574
+ risk: "write",
575
+ schema: GitApplyResolveSchema,
576
+ handler: async (args, ctx) => ctx.git.applyResolve({
577
+ into: args.into,
578
+ from: args.from,
579
+ files: args.files,
580
+ remote: args.remote,
581
+ push: args.push,
582
+ keepLocal: args.keepLocal,
583
+ tempBranch: args.tempBranch,
584
+ dryRun: args.dryRun
585
+ })
586
+ },
587
+ {
588
+ name: "git_mr_create",
589
+ description: "\u7528\u8BBE\u7F6E\u91CC\u7684 GitHub Token \u521B\u5EFA PR\uFF08REST\uFF09\u3002\u975E GitHub \u8FDC\u7A0B\u4E0D\u8C03 API\uFF0C\u53EA\u8FD4\u56DE\u6D4F\u89C8\u5668\u521B\u5EFA\u9875\u3002Token \u4E0D\u8FDB\u672C\u5DE5\u5177\u53C2\u6570\u3002dry_run \u53EF\u9884\u89C8\u3002",
590
+ risk: "write",
591
+ schema: GitMrCreateSchema,
592
+ handler: async (args, ctx) => {
593
+ const prep = await ctx.git.prepareMr({
594
+ into: args.into,
595
+ from: args.from,
596
+ remote: args.remote,
597
+ sourceBranch: args.sourceBranch
598
+ });
599
+ const title = (args.title ?? "").trim() || prep.title;
600
+ const body = args.body ?? "";
601
+ const apiUrl = githubPullsApiUrl(prep.remoteUrl);
602
+ const token = ctx.runtime.config.mr?.githubToken ?? "";
603
+ if (prep.platform === "github" && !token.trim()) {
604
+ throw new GitOperationError2("\u672A\u914D\u7F6E GitHub Token\uFF08\u8BBE\u7F6E \u2192 MR \u914D\u7F6E\uFF09", "NO_TOKEN");
605
+ }
606
+ if (args.dryRun) {
607
+ return {
608
+ dryRun: true,
609
+ command: apiUrl ? `POST ${apiUrl}` : `open ${prep.createMrUrl ?? ""}`,
610
+ args: [prep.sourceBranch, prep.targetBranch],
611
+ risk: "medium",
612
+ note: prep.platform === "github" ? `\u5C06\u7528\u8BBE\u7F6E\u4E2D\u7684 GitHub Token \u521B\u5EFA PR\uFF1A${prep.sourceBranch} \u2192 ${prep.targetBranch}\u3002\u6807\u9898\uFF1A${title}` : "\u8FDC\u7A0B\u4E0D\u662F GitHub\uFF08\u6216\u65E0\u6CD5\u8BC6\u522B\uFF09\u3002\u4E0D\u4F1A\u8C03\u7528 Token API\u3002\u786E\u8BA4\u540E\u4EC5\u8FD4\u56DE\u6D4F\u89C8\u5668\u521B\u5EFA\u9875 URL\u3002"
613
+ };
614
+ }
615
+ if (prep.platform !== "github" || !apiUrl) {
616
+ return {
617
+ via: "browser",
618
+ url: prep.createMrUrl,
619
+ sourceBranch: prep.sourceBranch,
620
+ targetBranch: prep.targetBranch,
621
+ title,
622
+ messages: ["\u8FDC\u7A0B\u4E0D\u662F GitHub\uFF0C\u672A\u8C03\u7528 Token\u3002\u8BF7\u7528\u6D4F\u89C8\u5668\u6253\u5F00\u521B\u5EFA\u9875\u3002"]
623
+ };
624
+ }
625
+ const pr = await createGithubPullRequest({
626
+ remoteUrl: prep.remoteUrl,
627
+ token,
628
+ sourceBranch: prep.sourceBranch,
629
+ targetBranch: prep.targetBranch,
630
+ title,
631
+ body
632
+ });
633
+ return {
634
+ via: "token",
635
+ url: pr.url,
636
+ number: pr.number,
637
+ sourceBranch: prep.sourceBranch,
638
+ targetBranch: prep.targetBranch,
639
+ title,
640
+ messages: [`\u5DF2\u521B\u5EFA PR #${pr.number}`]
641
+ };
642
+ }
643
+ },
478
644
  // ---------------------------------------------------------------------------
479
645
  // 高风险工具(默认禁用,需开启/审批;自动备份)
480
646
  // ---------------------------------------------------------------------------
@@ -537,7 +703,11 @@ function createMcpServer(runtime) {
537
703
  "2. \u5199\u64CD\u4F5C\uFF08git_add/git_commit/git_push \u7B49\uFF09\u9ED8\u8BA4\u53EF\u6267\u884C\uFF0C\u4F46\u8BF7\u5148\u4F7F\u7528 dry_run=true \u9884\u89C8\u5F71\u54CD\u8303\u56F4\uFF1B",
538
704
  "3. \u9AD8\u98CE\u9669\u5DE5\u5177\uFF08git_reset_hard/git_clean/git_push_force/git_branch_delete_force/git_rebase\uFF09\u9ED8\u8BA4\u7981\u7528\uFF0C",
539
705
  " \u9700\u7BA1\u7406\u5458\u5728\u914D\u7F6E\u4E2D\u5F00\u542F\uFF1B\u6267\u884C\u65F6\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u5907\u4EFD\u5F53\u524D\u72B6\u6001\uFF1B",
540
- "4. \u672A\u6307\u5B9A repoPath \u65F6\u4F7F\u7528\u6700\u8FD1\u6253\u5F00\u7684\u4ED3\u5E93\u3002"
706
+ "4. \u672A\u6307\u5B9A repoPath \u65F6\u4F7F\u7528\u6700\u8FD1\u6253\u5F00\u7684\u4ED3\u5E93\uFF1B",
707
+ "5. \u5408\u5E76\u9884\u6F14\u7528 git_merge_preview / git_merge_rehearse\uFF08merge-tree\uFF0C\u4E0D\u6539\u5DE5\u4F5C\u533A\uFF09\uFF0C",
708
+ " into=\u5408\u5165\u76EE\u6807/\u7EBF\u4E0A\uFF0Cfrom=\u6211\u7684\u5206\u652F\uFF1B\u7981\u6B62\u7528 git_merge \u505A\u9884\u6F14\u3002",
709
+ "6. \u843D\u76D8\u7528 git_apply_resolve\uFF08\u72EC\u7ACB worktree\uFF0C\u4E3B\u533A\u4E0D\u5207\u6362\uFF09\uFF1B\u51B2\u7A81\u65F6\u628A\u9009\u8FB9\u540E\u7684 files \u4E00\u5E76\u4F20\u5165\u3002",
710
+ "7. \u5F00 GitHub PR \u7528 git_mr_prepare\uFF08\u53EA\u8BFB\uFF09/ git_mr_create\uFF08Token \u5728\u8BBE\u7F6E MR \u914D\u7F6E\uFF0C\u4E0D\u8FDB\u5DE5\u5177\u53C2\u6570\uFF09\uFF1B\u975E GitHub \u53EA\u7ED9\u6D4F\u89C8\u5668\u521B\u5EFA\u9875\u3002"
541
711
  ].join("\n")
542
712
  }
543
713
  );
@@ -619,4 +789,4 @@ export {
619
789
  startMcpStdio,
620
790
  McpHttpHandler
621
791
  };
622
- //# sourceMappingURL=chunk-WCLC5KOG.js.map
792
+ //# sourceMappingURL=chunk-OKXMVKBU.js.map