@shaohui_jin/git-cockpit-mcp-server 0.1.27 → 0.1.29

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.
Files changed (78) hide show
  1. package/dist/{chunk-3ALI2CBW.js → chunk-DGQHHNPC.js} +3 -3
  2. package/dist/{chunk-G6SEOLMU.js → chunk-ML64BPIL.js} +246 -25
  3. package/dist/chunk-ML64BPIL.js.map +1 -0
  4. package/dist/{chunk-HWO2V3M5.js → chunk-WT4GTWHD.js} +2 -2
  5. package/dist/{chunk-HWO2V3M5.js.map → chunk-WT4GTWHD.js.map} +1 -1
  6. package/dist/cli-entry.js +3 -3
  7. package/dist/cli.d.ts +1 -1
  8. package/dist/cli.js +3 -3
  9. package/dist/index.d.ts +3 -3
  10. package/dist/index.js +3 -3
  11. package/dist/{mcpServer-5C8WQYy6.d.ts → mcpServer-D_ee0o_0.d.ts} +1 -1
  12. package/dist/mcpServer.d.ts +2 -2
  13. package/dist/mcpServer.js +1 -1
  14. package/dist/{runtime-BHX-b-Hx.d.ts → runtime-jfih-vGV.d.ts} +2 -1
  15. package/dist/web/assets/{abnfDiagram-O67JEVCF-Cpmc29Vo.js → abnfDiagram-O67JEVCF-BvLOOVMp.js} +1 -1
  16. package/dist/web/assets/{arc-C4CdJ3cz.js → arc-BTJdMR9U.js} +1 -1
  17. package/dist/web/assets/{architectureDiagram-NJMV4G6O-D-3EH44b.js → architectureDiagram-NJMV4G6O-Cuu0BD8t.js} +1 -1
  18. package/dist/web/assets/{blockDiagram-BEXU5L5S-4o8DaZHq.js → blockDiagram-BEXU5L5S-jSaqNPDC.js} +1 -1
  19. package/dist/web/assets/{c4Diagram-YGBWAQC7-DLufKbff.js → c4Diagram-YGBWAQC7-0PABaygh.js} +1 -1
  20. package/dist/web/assets/channel-CH0mBqDy.js +1 -0
  21. package/dist/web/assets/{chunk-2Q5K7J3B-dEtvq-Sm.js → chunk-2Q5K7J3B-Bx3ShJLs.js} +1 -1
  22. package/dist/web/assets/{chunk-3FUC2YCW-D8IY6i0n.js → chunk-3FUC2YCW-npJuWQgM.js} +2 -2
  23. package/dist/web/assets/{chunk-5VM5RSS4-DTtnT10M.js → chunk-5VM5RSS4-sUx0MLUK.js} +1 -1
  24. package/dist/web/assets/{chunk-GTNCS2PH-CRKiRt85.js → chunk-GTNCS2PH-BO6MsgyZ.js} +1 -1
  25. package/dist/web/assets/{chunk-GWA4HPMP-CapkHNd2.js → chunk-GWA4HPMP-CS_7AIEI.js} +1 -1
  26. package/dist/web/assets/{chunk-JWPE2WC7-D1qG9232.js → chunk-JWPE2WC7-BIEqnMSy.js} +1 -1
  27. package/dist/web/assets/{chunk-NETBCI7D-Cf5bzyqy.js → chunk-NETBCI7D-DWDPVhzE.js} +1 -1
  28. package/dist/web/assets/{chunk-WEXAMYUT-aMK135IE.js → chunk-WEXAMYUT-CZmF6ztf.js} +1 -1
  29. package/dist/web/assets/{chunk-XXDRQBXY-DKObcU_j.js → chunk-XXDRQBXY-C7ux97rA.js} +1 -1
  30. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-BFKjdANY.js → classDiagram-v2-NBCMYWYE-DMFHgo6y.js} +1 -1
  31. package/dist/web/assets/{cose-bilkent-JH36ORCC-Bbl702qf.js → cose-bilkent-JH36ORCC-CHcf0ChD.js} +1 -1
  32. package/dist/web/assets/{cynefin-OW5HDTMX-BwkqOigD.js → cynefin-OW5HDTMX-BVOPFxoX.js} +1 -1
  33. package/dist/web/assets/{cynefinDiagram-VND7K2PF-C032B141.js → cynefinDiagram-VND7K2PF-D3MWGh5_.js} +1 -1
  34. package/dist/web/assets/{dagre-6A5THRUB-mtU2etOs.js → dagre-6A5THRUB-BBXhvs_s.js} +1 -1
  35. package/dist/web/assets/{diagram-22UHCM2B-Bhef_gli.js → diagram-22UHCM2B-BpXFmuft.js} +1 -1
  36. package/dist/web/assets/{diagram-3UASUU5V-Bt5jGMJj.js → diagram-3UASUU5V-BQvfr6_4.js} +1 -1
  37. package/dist/web/assets/{diagram-ATOU4E4O-E9OEgrvO.js → diagram-ATOU4E4O-DpVGu2ct.js} +1 -1
  38. package/dist/web/assets/{diagram-CDSNMT55-Ev4KURRW.js → diagram-CDSNMT55-CMhnvQ9p.js} +1 -1
  39. package/dist/web/assets/{diagram-MLGK6HIB-VyRxPiEM.js → diagram-MLGK6HIB-CMUiUB5V.js} +1 -1
  40. package/dist/web/assets/{diagram-MPIPVDR6-NZUHIzSK.js → diagram-MPIPVDR6-DAEDT1M_.js} +1 -1
  41. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-BWrpXmAB.js → ebnfDiagram-ZINNZB2B--teB4gEh.js} +1 -1
  42. package/dist/web/assets/{elk-276RUBZZ-BGHyKt3X.js → elk-276RUBZZ-DTJ8porF.js} +1 -1
  43. package/dist/web/assets/{erDiagram-OPXOYQCR-GrSAUtcQ.js → erDiagram-OPXOYQCR-Cu8sQHj7.js} +1 -1
  44. package/dist/web/assets/{flowDiagram-KWPJA3E3-B_WVUUVf.js → flowDiagram-KWPJA3E3-C3YlGYFe.js} +1 -1
  45. package/dist/web/assets/{ganttDiagram-FUAMR5RP-DajvFrX3.js → ganttDiagram-FUAMR5RP-InE3Qjdk.js} +1 -1
  46. package/dist/web/assets/{gitGraphDiagram-X574FWY7-CrHV5Rvk.js → gitGraphDiagram-X574FWY7-W9cDxWIt.js} +1 -1
  47. package/dist/web/assets/{index-COls5qGF.css → index-D6wYxAVF.css} +1 -1
  48. package/dist/web/assets/{index-DNSP9H_v.js → index-z64JVqEI.js} +167 -166
  49. package/dist/web/assets/{infoDiagram-VRGFBTTK-BVr7SUMq.js → infoDiagram-VRGFBTTK-Cs_MZzVX.js} +1 -1
  50. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-Clo1T4Ou.js → ishikawaDiagram-OU5B5YK6-CYNBwCgu.js} +1 -1
  51. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-BJJiqW1W.js → journeyDiagram-ZHPQQLJL-M_8xlDc6.js} +1 -1
  52. package/dist/web/assets/{kanban-definition-PNTS6WVX-FIP6aaWx.js → kanban-definition-PNTS6WVX-DJM9NnPd.js} +1 -1
  53. package/dist/web/assets/{linear-gNF2RYPF.js → linear-D2pugD8L.js} +1 -1
  54. package/dist/web/assets/{mindmap-definition-NLK3R4M7-U9ZgUluP.js → mindmap-definition-NLK3R4M7-Ej_zTWtJ.js} +1 -1
  55. package/dist/web/assets/{pegDiagram-GJSIUBJH-kqET45uW.js → pegDiagram-GJSIUBJH-C1eyhBDj.js} +1 -1
  56. package/dist/web/assets/{pieDiagram-5QR66LMP-0Vfqg8YJ.js → pieDiagram-5QR66LMP-ciGhrblI.js} +1 -1
  57. package/dist/web/assets/{quadrantDiagram-O4NWA36T-BwbgrFii.js → quadrantDiagram-O4NWA36T-8MJ_CUFe.js} +1 -1
  58. package/dist/web/assets/{railroadDiagram-XR7U4H2S-AQGvMbOL.js → railroadDiagram-XR7U4H2S-CrBQOwD0.js} +1 -1
  59. package/dist/web/assets/{requirementDiagram-PLB6GJNP-WJNa_ucq.js → requirementDiagram-PLB6GJNP-glZ4Tl5M.js} +1 -1
  60. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-DTOl4R33.js → sankeyDiagram-IPEJSGJF-C87eVSmR.js} +1 -1
  61. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-4xQidS78.js → sequenceDiagram-PO4LG4MO-C4kD0n6_.js} +1 -1
  62. package/dist/web/assets/{sizeCapture-INFHLROL--JObXzuK.js → sizeCapture-INFHLROL-C3egfWS7.js} +1 -1
  63. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-DO54xQHQ.js → stateDiagram-v2-GCMORJYK-miD15x9p.js} +1 -1
  64. package/dist/web/assets/{swimlanes-2SLR337P-D2RfEaDP.js → swimlanes-2SLR337P-RFbVR5iD.js} +1 -1
  65. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-DQeW6-o5.js +8 -0
  66. package/dist/web/assets/{timeline-definition-EJHVYXUP-CjEGHstB.js → timeline-definition-EJHVYXUP-DjpYQNiI.js} +1 -1
  67. package/dist/web/assets/{usecaseDiagram-POWQR4AR-B_QSF3HN.js → usecaseDiagram-POWQR4AR-BIo58LtF.js} +1 -1
  68. package/dist/web/assets/{vennDiagram-UO4OBE2U-DaXBK4lA.js → vennDiagram-UO4OBE2U-D7wQezYC.js} +1 -1
  69. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-DAcGG1ul.js → wardleyDiagram-VNRHLVJA-DwhQUOY8.js} +1 -1
  70. package/dist/web/assets/{xychartDiagram-PMCCYNJV-C9NMkFcl.js → xychartDiagram-PMCCYNJV-B0cVFGsf.js} +1 -1
  71. package/dist/web/index.html +2 -2
  72. package/dist/webServer.d.ts +1 -1
  73. package/dist/webServer.js +2 -2
  74. package/package.json +2 -2
  75. package/dist/chunk-G6SEOLMU.js.map +0 -1
  76. package/dist/web/assets/channel-D3CmON_0.js +0 -1
  77. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-BTVqf00K.js +0 -8
  78. /package/dist/{chunk-3ALI2CBW.js.map → chunk-DGQHHNPC.js.map} +0 -0
@@ -4,13 +4,13 @@ import {
4
4
  createRuntime,
5
5
  createWebServer,
6
6
  disposeRuntime
7
- } from "./chunk-G6SEOLMU.js";
7
+ } from "./chunk-ML64BPIL.js";
8
8
  import {
9
9
  formatDaemonUrl,
10
10
  resolveDaemonEndpoint,
11
11
  startMcpStdio,
12
12
  version
13
- } from "./chunk-HWO2V3M5.js";
13
+ } from "./chunk-WT4GTWHD.js";
14
14
 
15
15
  // src/mcpBridge.ts
16
16
  var SECRET_HEADER = "X-Git-Cockpit-Secret";
@@ -403,4 +403,4 @@ async function main(argv) {
403
403
  export {
404
404
  main
405
405
  };
406
- //# sourceMappingURL=chunk-3ALI2CBW.js.map
406
+ //# sourceMappingURL=chunk-DGQHHNPC.js.map
@@ -10,11 +10,11 @@ import {
10
10
  summarizeForAgent,
11
11
  toolSummaries,
12
12
  version
13
- } from "./chunk-HWO2V3M5.js";
13
+ } from "./chunk-WT4GTWHD.js";
14
14
 
15
15
  // src/webServer.ts
16
- import * as path3 from "path";
17
- import * as fs2 from "fs";
16
+ import * as path4 from "path";
17
+ import * as fs3 from "fs";
18
18
  import { fileURLToPath } from "url";
19
19
  import Fastify from "fastify";
20
20
  import { timingSafeEqual } from "crypto";
@@ -38,7 +38,7 @@ import {
38
38
  probeAllMrCli,
39
39
  readCliAuthToken,
40
40
  renderMrTemplate,
41
- RepoNotFoundError as RepoNotFoundError3,
41
+ RepoNotFoundError as RepoNotFoundError4,
42
42
  toHttpsRemoteUrl,
43
43
  upsertMrHost,
44
44
  maskToken,
@@ -55,6 +55,7 @@ import {
55
55
  import { EventEmitter as EventEmitter2 } from "events";
56
56
  import {
57
57
  AuditLogger,
58
+ ChatLogStore,
58
59
  JobEngine,
59
60
  JobStore,
60
61
  PermissionManager,
@@ -335,6 +336,7 @@ function createRuntime(options = {}) {
335
336
  const eventBus = new EventEmitter2();
336
337
  eventBus.setMaxListeners(200);
337
338
  const auditLogger = new AuditLogger(db, config.logging);
339
+ const chatLog = new ChatLogStore(db);
338
340
  const permissions = new PermissionManager(config);
339
341
  const repoStore = new RepoStore2(db);
340
342
  const repoManager = new RepoManager({ repoStore, eventBus, getConfig: () => configStore.get() });
@@ -353,6 +355,7 @@ function createRuntime(options = {}) {
353
355
  config,
354
356
  db,
355
357
  auditLogger,
358
+ chatLog,
356
359
  permissions,
357
360
  repoStore,
358
361
  repoManager,
@@ -378,7 +381,13 @@ import swaggerUi from "@fastify/swagger-ui";
378
381
  import { zodToJsonSchema } from "zod-to-json-schema";
379
382
  var REST_META = {
380
383
  "get /api/health": { summary: "\u63A2\u6D3B", tags: ["\u7CFB\u7EDF"] },
381
- "post /api/shutdown": { summary: "\u4F18\u96C5\u9000\u51FA daemon\uFF08\u672C\u673A\u5BC6\u94A5\uFF1B\u6709 running \u4EFB\u52A1\u65F6 409\uFF09", tags: ["\u7CFB\u7EDF"] },
384
+ "get /api/service-update": { summary: "\u670D\u52A1\u7AEF\u662F\u5426\u6709\u66F4\u65B0\uFF08npm \u6700\u65B0\u7248\uFF0C\u5E26\u7F13\u5B58\uFF09", tags: ["\u7CFB\u7EDF"] },
385
+ "post /api/jobs/desktop-download": { summary: "\u767B\u8BB0\u684C\u9762\u5B89\u88C5\u5305\u4E0B\u8F7D\u4EFB\u52A1\uFF08\u7531\u58F3\u5B50\u56DE\u62A5\u8FDB\u5EA6\uFF09", tags: ["\u4ED3\u5E93"] },
386
+ "post /api/jobs/{id}/desktop-progress": { summary: "\u66F4\u65B0\u684C\u9762\u4E0B\u8F7D\u8FDB\u5EA6\uFF1B\u5B8C\u6210\u540E\u4E0D\u518D\u5360\u7740\u8FDB\u884C\u4E2D", tags: ["\u4ED3\u5E93"] },
387
+ "post /api/shutdown": {
388
+ summary: "\u4F18\u96C5\u9000\u51FA daemon\uFF08\u672C\u673A\u5BC6\u94A5\uFF1B\u6709 running \u4EFB\u52A1\u65F6 409\uFF0Cforce \u786E\u8BA4\u540E\u4ECD\u9000\u51FA\uFF09",
389
+ tags: ["\u7CFB\u7EDF"]
390
+ },
382
391
  "get /api/repos": { summary: "\u5DF2\u6253\u5F00\u4ED3\u5E93\u5217\u8868", tags: ["\u4ED3\u5E93"] },
383
392
  "get /api/repos/overview": { summary: "\u5DF2\u6253\u5F00\u4ED3\u5E93\u8109\u640F\uFF08\u5DE5\u4F5C\u53F0\uFF0C\u5168\u91CF\uFF09", tags: ["\u4ED3\u5E93"] },
384
393
  "get /api/repos/{id}/overview": { summary: "\u5355\u4E2A\u4ED3\u5E93\u8109\u640F\uFF08\u5DE5\u4F5C\u53F0\u589E\u91CF\uFF09", tags: ["\u4ED3\u5E93"] },
@@ -414,6 +423,10 @@ var REST_META = {
414
423
  "post /api/jobs/clone": { summary: "\u540E\u53F0\u514B\u9686\u8FDC\u7A0B\u4ED3\u5E93", tags: ["\u4ED3\u5E93"] },
415
424
  "get /api/tools": { summary: "\u5DE5\u5177\u6CE8\u518C\u8868\uFF08\u542B\u98CE\u9669\u4E0E\u662F\u5426\u542F\u7528\uFF09", tags: ["\u7CFB\u7EDF"] },
416
425
  "get /api/logs": { summary: "\u64CD\u4F5C\u5BA1\u8BA1\u65E5\u5FD7", tags: ["\u7CFB\u7EDF"] },
426
+ "get /api/chat/conversations": { summary: "\u5BF9\u8BDD\u8BB0\u5F55\u5217\u8868", tags: ["\u804A\u5929"] },
427
+ "get /api/chat/conversations/{id}": { summary: "\u4E00\u7EC4\u5BF9\u8BDD\u7684\u5168\u6587", tags: ["\u804A\u5929"] },
428
+ "delete /api/chat/conversations/{id}": { summary: "\u5220\u9664\u4E00\u7EC4\u5BF9\u8BDD", tags: ["\u804A\u5929"] },
429
+ "post /api/chat/conversations/{id}/messages": { summary: "\u8FFD\u52A0\u4E00\u8F6E\u5BF9\u8BDD\uFF08\u542B\u672C\u5730\u9884\u89C8\uFF09", tags: ["\u804A\u5929"] },
417
430
  "get /api/settings": { summary: "\u914D\u7F6E\u4E0E\u6743\u9650", tags: ["\u8BBE\u7F6E"] },
418
431
  "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"] },
419
432
  "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"] },
@@ -421,12 +434,12 @@ var REST_META = {
421
434
  "get /api/events": { summary: "SSE\uFF1A\u4ED3\u5E93\u53D8\u5316\u3001\u65E5\u5FD7\u3001\u540E\u53F0\u4EFB\u52A1\u8FDB\u5EA6", tags: ["\u7CFB\u7EDF"] }
422
435
  };
423
436
  function applyRestMeta(spec) {
424
- for (const [path4, item] of Object.entries(spec.paths ?? {})) {
437
+ for (const [path5, item] of Object.entries(spec.paths ?? {})) {
425
438
  if (!item) continue;
426
439
  for (const method of Object.keys(item)) {
427
440
  const op = item[method];
428
441
  if (!op || typeof op !== "object") continue;
429
- const meta = REST_META[`${method} ${path4}`];
442
+ const meta = REST_META[`${method} ${path5}`];
430
443
  if (meta) {
431
444
  if (!op.summary) op.summary = meta.summary;
432
445
  if (!op.tags) op.tags = meta.tags;
@@ -507,7 +520,12 @@ async function registerApiDocs(app) {
507
520
  // src/chat/chatRoute.ts
508
521
  import { streamText, tool, stepCountIs } from "ai";
509
522
  import { createOpenAI } from "@ai-sdk/openai";
510
- import { applyLlmEnvOverrides, llmBaseUrl } from "@shaohui_jin/git-cockpit-core";
523
+ import {
524
+ applyLlmEnvOverrides,
525
+ isChatLogRole,
526
+ llmBaseUrl,
527
+ RepoNotFoundError as RepoNotFoundError3
528
+ } from "@shaohui_jin/git-cockpit-core";
511
529
 
512
530
  // src/chat/confirmGate.ts
513
531
  import { createHash, randomUUID } from "crypto";
@@ -904,26 +922,205 @@ function registerChatRoutes(app, runtime) {
904
922
  if (!out.ok) return reply.code(409).send(out);
905
923
  return out;
906
924
  });
925
+ app.get("/api/chat/conversations", async (req) => {
926
+ let repoPath = typeof req.query.repoPath === "string" ? req.query.repoPath.trim() : "";
927
+ if (repoPath) repoPath = openedRepoPath(runtime, repoPath) ?? repoPath;
928
+ return {
929
+ conversations: runtime.chatLog.list({
930
+ repoPath: repoPath || void 0,
931
+ limit: clampLimit(req.query.limit)
932
+ })
933
+ };
934
+ });
935
+ app.get("/api/chat/conversations/:id", async (req, reply) => {
936
+ const id = validConversationId(req.params.id);
937
+ if (!id) return reply.code(400).send({ error: "\u5BF9\u8BDD id \u65E0\u6548" });
938
+ const found = runtime.chatLog.get(id);
939
+ if (!found) return reply.code(404).send({ error: "\u5BF9\u8BDD\u4E0D\u5B58\u5728" });
940
+ return found;
941
+ });
942
+ app.delete("/api/chat/conversations/:id", async (req, reply) => {
943
+ const id = validConversationId(req.params.id);
944
+ if (!id) return reply.code(400).send({ error: "\u5BF9\u8BDD id \u65E0\u6548" });
945
+ if (!runtime.chatLog.remove(id)) return reply.code(404).send({ error: "\u5BF9\u8BDD\u4E0D\u5B58\u5728" });
946
+ return { ok: true };
947
+ });
948
+ app.post("/api/chat/conversations/:id/messages", async (req, reply) => {
949
+ const id = validConversationId(req.params.id);
950
+ if (!id) return reply.code(400).send({ error: "\u5BF9\u8BDD id \u65E0\u6548" });
951
+ const requested = typeof req.body?.repoPath === "string" ? req.body.repoPath.trim() : "";
952
+ if (!requested) return reply.code(400).send({ error: "\u8BF7\u5148\u9009\u62E9\u4ED3\u5E93", code: "NO_ACTIVE_REPO" });
953
+ const repoPath = openedRepoPath(runtime, requested);
954
+ if (!repoPath) {
955
+ return reply.code(400).send({ error: "\u8BF7\u5148\u5728\u5DE5\u4F5C\u53F0\u6253\u5F00\u8BE5\u4ED3\u5E93", code: "REPO_NOT_OPEN" });
956
+ }
957
+ const parsed = parseChatLogMessages(req.body?.messages);
958
+ if (!parsed.ok) return reply.code(400).send({ error: parsed.error });
959
+ const saved = runtime.chatLog.append(id, repoPath, parsed.messages);
960
+ if (!saved.ok) {
961
+ return reply.code(409).send({ error: "\u8FD9\u7EC4\u5BF9\u8BDD\u5C5E\u4E8E\u53E6\u4E00\u4E2A\u4ED3\u5E93", code: "CONVERSATION_REPO_MISMATCH" });
962
+ }
963
+ return { conversation: saved.conversation };
964
+ });
965
+ }
966
+ var CONVERSATION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
967
+ var MAX_MESSAGES = 20;
968
+ var MAX_TEXT = 1e5;
969
+ function validConversationId(raw) {
970
+ const id = raw.trim();
971
+ return CONVERSATION_ID.test(id) ? id : null;
972
+ }
973
+ function clampLimit(raw) {
974
+ const n = raw == null || raw === "" ? 100 : Number(raw);
975
+ if (!Number.isFinite(n)) return 100;
976
+ return Math.min(Math.max(Math.floor(n), 1), 200);
977
+ }
978
+ function openedRepoPath(runtime, repoPath) {
979
+ try {
980
+ return runtime.repoManager.getByPath(repoPath).service.repoPath;
981
+ } catch (err) {
982
+ if (err instanceof RepoNotFoundError3) return null;
983
+ throw err;
984
+ }
985
+ }
986
+ function parseChatLogMessages(raw) {
987
+ if (!Array.isArray(raw) || raw.length === 0) return { ok: false, error: "messages \u4E0D\u80FD\u4E3A\u7A7A" };
988
+ if (raw.length > MAX_MESSAGES) return { ok: false, error: "\u4E00\u6B21\u6700\u591A\u8FFD\u52A0 20 \u6761\u6D88\u606F" };
989
+ const messages = [];
990
+ for (const item of raw) {
991
+ if (!item || typeof item !== "object") return { ok: false, error: "\u6D88\u606F\u683C\u5F0F\u65E0\u6548" };
992
+ const row = item;
993
+ if (typeof row.role !== "string" || !isChatLogRole(row.role)) return { ok: false, error: "\u6D88\u606F\u89D2\u8272\u65E0\u6548" };
994
+ if (typeof row.text !== "string" || row.text.length > MAX_TEXT) return { ok: false, error: "\u6D88\u606F\u6B63\u6587\u65E0\u6548" };
995
+ if (row.tool != null && typeof row.tool !== "string") return { ok: false, error: "\u5DE5\u5177\u540D\u65E0\u6548" };
996
+ if (row.success != null && typeof row.success !== "boolean") return { ok: false, error: "\u5DE5\u5177\u7ED3\u679C\u65E0\u6548" };
997
+ messages.push({
998
+ role: row.role,
999
+ text: row.text,
1000
+ tool: typeof row.tool === "string" ? row.tool : null,
1001
+ success: typeof row.success === "boolean" ? row.success : null
1002
+ });
1003
+ }
1004
+ return { ok: true, messages };
1005
+ }
1006
+
1007
+ // src/serviceUpdate.ts
1008
+ import { spawn } from "child_process";
1009
+ import fs2 from "fs";
1010
+ import path3 from "path";
1011
+ var PKG = "@shaohui_jin/git-cockpit-mcp-server";
1012
+ var TTL_MS3 = 30 * 60 * 1e3;
1013
+ var FAIL_TTL_MS = 60 * 1e3;
1014
+ var lookup = npmViewLatest;
1015
+ var cache = null;
1016
+ async function readServiceUpdate(current) {
1017
+ const latest = await cachedLatest();
1018
+ const updateAvailable = Boolean(latest && compareVersions(latest, current) > 0);
1019
+ return {
1020
+ version: current,
1021
+ latestVersion: latest,
1022
+ updateAvailable,
1023
+ command: updateAvailable && latest ? `npm i -g ${PKG}@${latest}` : null
1024
+ };
1025
+ }
1026
+ async function cachedLatest() {
1027
+ const now = Date.now();
1028
+ if (cache && now - cache.at < cache.ttl) return cache.latest;
1029
+ let latest = null;
1030
+ let ttl = FAIL_TTL_MS;
1031
+ try {
1032
+ latest = await lookup();
1033
+ if (latest) ttl = TTL_MS3;
1034
+ } catch {
1035
+ latest = null;
1036
+ }
1037
+ cache = { at: now, latest, ttl };
1038
+ return latest;
1039
+ }
1040
+ function parseVersion(raw) {
1041
+ const matched = raw.trim().match(/(\d+)\.(\d+)\.(\d+)/);
1042
+ if (!matched) return null;
1043
+ return [Number(matched[1]), Number(matched[2]), Number(matched[3])];
1044
+ }
1045
+ function compareVersions(a, b) {
1046
+ const left = parseVersion(a);
1047
+ const right = parseVersion(b);
1048
+ if (!left || !right) return 0;
1049
+ for (let i = 0; i < 3; i += 1) {
1050
+ const av = left[i] ?? 0;
1051
+ const bv = right[i] ?? 0;
1052
+ if (av !== bv) return av - bv;
1053
+ }
1054
+ return 0;
1055
+ }
1056
+ function resolveNpmCli(nodePath) {
1057
+ const dir = path3.dirname(nodePath);
1058
+ const candidates = [
1059
+ path3.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
1060
+ path3.resolve(dir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
1061
+ ];
1062
+ return candidates.find((item) => fs2.existsSync(item)) ?? null;
1063
+ }
1064
+ function parseNpmVersion(raw) {
1065
+ const quoted = raw.match(/"(\d+\.\d+\.\d+)"/);
1066
+ if (quoted?.[1]) return quoted[1];
1067
+ const plain = parseVersion(raw);
1068
+ return plain ? plain.join(".") : null;
1069
+ }
1070
+ function npmViewLatest() {
1071
+ const nodePath = process.execPath;
1072
+ const npmCli = resolveNpmCli(nodePath);
1073
+ const cmd = npmCli ? nodePath : process.platform === "win32" ? "npm.cmd" : "npm";
1074
+ const args = npmCli ? [npmCli, "view", PKG, "version", "--json"] : ["view", PKG, "version", "--json"];
1075
+ return new Promise((resolve3) => {
1076
+ const child = spawn(cmd, args, {
1077
+ windowsHide: true,
1078
+ shell: !npmCli && process.platform === "win32"
1079
+ });
1080
+ let output = "";
1081
+ const timer = setTimeout(() => {
1082
+ child.kill();
1083
+ resolve3(null);
1084
+ }, 15e3);
1085
+ child.stdout?.on("data", (chunk) => {
1086
+ output += chunk.toString();
1087
+ });
1088
+ child.stderr?.on("data", (chunk) => {
1089
+ output += chunk.toString();
1090
+ });
1091
+ child.on("error", () => {
1092
+ clearTimeout(timer);
1093
+ resolve3(null);
1094
+ });
1095
+ child.on("close", (code) => {
1096
+ clearTimeout(timer);
1097
+ if (code !== 0) {
1098
+ resolve3(null);
1099
+ return;
1100
+ }
1101
+ resolve3(parseNpmVersion(output));
1102
+ });
1103
+ });
907
1104
  }
908
1105
 
909
1106
  // src/webServer.ts
910
1107
  function resolveWebDist() {
911
- const here = path3.resolve(path3.dirname(fileURLToPath(import.meta.url)));
1108
+ const here = path4.resolve(path4.dirname(fileURLToPath(import.meta.url)));
912
1109
  const candidates = [
913
- path3.resolve(here, "..", "..", "web", "dist"),
1110
+ path4.resolve(here, "..", "..", "web", "dist"),
914
1111
  // 源码布局:packages/mcp-server/dist → packages/web/dist
915
- path3.resolve(here, "web"),
1112
+ path4.resolve(here, "web"),
916
1113
  // 发布布局:产物由 copy-web.mjs 打入 dist/web(含 index.html)
917
- path3.resolve(here, "web", "dist"),
1114
+ path4.resolve(here, "web", "dist"),
918
1115
  // 兼容旧式 dist/web/dist 布局
919
- path3.resolve(here, "..", "web", "dist"),
1116
+ path4.resolve(here, "..", "web", "dist"),
920
1117
  // 兼容 npm 全局安装目录的旧式布局
921
- path3.resolve(process.cwd(), "dist", "web"),
922
- path3.resolve(process.cwd(), "..", "web", "dist"),
923
- path3.resolve(process.cwd(), "node_modules", "@shaohui_jin", "git-cockpit-web", "dist")
1118
+ path4.resolve(process.cwd(), "dist", "web"),
1119
+ path4.resolve(process.cwd(), "..", "web", "dist"),
1120
+ path4.resolve(process.cwd(), "node_modules", "@shaohui_jin", "git-cockpit-web", "dist")
924
1121
  ];
925
1122
  for (const c of candidates) {
926
- if (fs2.existsSync(path3.join(c, "index.html"))) return c;
1123
+ if (fs3.existsSync(path4.join(c, "index.html"))) return c;
927
1124
  }
928
1125
  return null;
929
1126
  }
@@ -978,7 +1175,7 @@ async function createWebServer(runtime, options = {}) {
978
1175
  await registerApiDocs(app);
979
1176
  registerChatRoutes(app, runtime);
980
1177
  const staticDir = options.staticDir !== void 0 ? options.staticDir : resolveWebDist();
981
- if (staticDir && fs2.existsSync(staticDir)) {
1178
+ if (staticDir && fs3.existsSync(staticDir)) {
982
1179
  await app.register(fastifyStatic, { root: staticDir, prefix: "/" });
983
1180
  }
984
1181
  const mcpHttp = new McpHttpHandler(runtime);
@@ -989,14 +1186,16 @@ async function createWebServer(runtime, options = {}) {
989
1186
  version,
990
1187
  uptimeMs: process.uptime() * 1e3
991
1188
  }));
1189
+ app.get("/api/service-update", async () => readServiceUpdate(version));
992
1190
  app.get("/api/bootstrap", async (req, reply) => {
993
1191
  if (req.headers.origin) {
994
1192
  return reply.code(403).send({ error: "bootstrap \u4E0D\u7ED9\u8DE8\u7AD9\u9875\u9762" });
995
1193
  }
996
1194
  return { secret: runtime.config.auth.localSecret };
997
1195
  });
998
- app.post("/api/shutdown", async (_req, reply) => {
999
- if (jobs.list().some((j) => j.status === "running")) {
1196
+ app.post("/api/shutdown", async (req, reply) => {
1197
+ const force = req.body?.force === true;
1198
+ if (!force && jobs.list().some((j) => j.status === "running")) {
1000
1199
  return reply.code(409).send({ error: "\u6709\u4EFB\u52A1\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u5148\u7B49\u5F85\u7ED3\u675F" });
1001
1200
  }
1002
1201
  await reply.code(204).send();
@@ -1017,6 +1216,28 @@ async function createWebServer(runtime, options = {}) {
1017
1216
  if (!job) return reply.code(404).send({ error: "\u4EFB\u52A1\u4E0D\u5B58\u5728" });
1018
1217
  return { job: jobs.detail(job) };
1019
1218
  });
1219
+ app.post("/api/jobs/desktop-download", async (req, reply) => {
1220
+ const current = req.body?.version?.trim() ?? "";
1221
+ const latest = req.body?.latest?.trim() ?? "";
1222
+ if (!current || !latest) {
1223
+ return reply.code(400).send({ error: "\u9700\u8981 version \u4E0E latest" });
1224
+ }
1225
+ const job = jobs.beginDesktopDownload({ version: current, latest });
1226
+ return { job: jobs.summary(job) };
1227
+ });
1228
+ app.post("/api/jobs/:id/desktop-progress", async (req, reply) => {
1229
+ try {
1230
+ const job = jobs.reportDesktopDownload(req.params.id, {
1231
+ phase: req.body?.phase,
1232
+ percent: req.body?.percent,
1233
+ error: req.body?.error
1234
+ });
1235
+ return { job: jobs.summary(job) };
1236
+ } catch (err) {
1237
+ const msg = err instanceof Error ? err.message : String(err);
1238
+ return reply.code(msg === "\u4EFB\u52A1\u4E0D\u5B58\u5728" ? 404 : 400).send({ error: msg });
1239
+ }
1240
+ });
1020
1241
  app.post("/api/jobs/:id/cancel", async (req, reply) => {
1021
1242
  try {
1022
1243
  const job = jobs.cancel(req.params.id);
@@ -1619,9 +1840,9 @@ data: ${JSON.stringify(payload)}
1619
1840
  if (req.url.startsWith("/api") || req.url.startsWith("/mcp") || req.url.startsWith("/docs")) {
1620
1841
  return reply.code(404).send({ error: "Not Found" });
1621
1842
  }
1622
- const index = path3.join(staticDir, "index.html");
1623
- if (fs2.existsSync(index)) {
1624
- reply.type("text/html").send(fs2.readFileSync(index, "utf8"));
1843
+ const index = path4.join(staticDir, "index.html");
1844
+ if (fs3.existsSync(index)) {
1845
+ reply.type("text/html").send(fs3.readFileSync(index, "utf8"));
1625
1846
  return;
1626
1847
  }
1627
1848
  return reply.code(404).send({ error: "Not Found" });
@@ -1643,7 +1864,7 @@ data: ${JSON.stringify(payload)}
1643
1864
  }
1644
1865
  function toError(err) {
1645
1866
  if (err instanceof GitOperationError) return { code: err.code, message: err.message };
1646
- if (err instanceof RepoNotFoundError3) return { code: "REPO_NOT_FOUND", message: err.message };
1867
+ if (err instanceof RepoNotFoundError4) return { code: "REPO_NOT_FOUND", message: err.message };
1647
1868
  if (err instanceof Error) return { code: "INTERNAL_ERROR", message: err.message.slice(0, 500) };
1648
1869
  return { code: "INTERNAL_ERROR", message: String(err) };
1649
1870
  }
@@ -1794,4 +2015,4 @@ export {
1794
2015
  disposeRuntime,
1795
2016
  createWebServer
1796
2017
  };
1797
- //# sourceMappingURL=chunk-G6SEOLMU.js.map
2018
+ //# sourceMappingURL=chunk-ML64BPIL.js.map