@imenam/mcp-documentor 1.0.54 → 1.0.56
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.
- package/README.md +1 -0
- package/dist/shared/read-only.d.ts +4 -0
- package/dist/shared/read-only.js +11 -0
- package/dist/src/core/code-ref-resolver.d.ts +14 -0
- package/dist/src/core/code-ref-resolver.js +118 -0
- package/dist/src/gui-worker.js +18 -0
- package/dist/src/index.js +368 -333
- package/dist/src/tools/create-doc.js +8 -26
- package/dist/src/tools/patch-doc.js +4 -1
- package/dist/src/tools/update-doc.js +15 -27
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/gui/dist/assets/{arc-CCHCB-wD.js → arc-hl-9jVa3.js} +1 -1
- package/gui/dist/assets/{architectureDiagram-3BPJPVTR-CY9HTuIq.js → architectureDiagram-3BPJPVTR-Cv0e-QlI.js} +1 -1
- package/gui/dist/assets/{blockDiagram-GPEHLZMM-BOVxufIP.js → blockDiagram-GPEHLZMM-DmqN7ioQ.js} +1 -1
- package/gui/dist/assets/{c4Diagram-AAUBKEIU-D4gw0Vvh.js → c4Diagram-AAUBKEIU-CCmx3y3D.js} +1 -1
- package/gui/dist/assets/channel-Be8bJJxH.js +1 -0
- package/gui/dist/assets/{chunk-2J33WTMH-D9MZap0G.js → chunk-2J33WTMH-DkiBzADG.js} +1 -1
- package/gui/dist/assets/{chunk-4BX2VUAB-DZp7afnZ.js → chunk-4BX2VUAB-DNbC5-R6.js} +1 -1
- package/gui/dist/assets/{chunk-55IACEB6--z45PUIk.js → chunk-55IACEB6-BDo8xvgP.js} +1 -1
- package/gui/dist/assets/{chunk-727SXJPM-BpzsGHwS.js → chunk-727SXJPM-BXbVbdMj.js} +1 -1
- package/gui/dist/assets/{chunk-AQP2D5EJ-BHrif5nf.js → chunk-AQP2D5EJ-BYle6aB3.js} +1 -1
- package/gui/dist/assets/{chunk-FMBD7UC4-BJnvL7mL.js → chunk-FMBD7UC4-BdIrLn1p.js} +1 -1
- package/gui/dist/assets/{chunk-ND2GUHAM-B30rNlK9.js → chunk-ND2GUHAM-BkjdsZf2.js} +1 -1
- package/gui/dist/assets/{chunk-QZHKN3VN-C30k0U0w.js → chunk-QZHKN3VN-pwVXVLDb.js} +1 -1
- package/gui/dist/assets/classDiagram-4FO5ZUOK-DxtDCVWj.js +1 -0
- package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-DxtDCVWj.js +1 -0
- package/gui/dist/assets/{cose-bilkent-S5V4N54A-D_ZPLp5P.js → cose-bilkent-S5V4N54A-COfRIcXU.js} +1 -1
- package/gui/dist/assets/{dagre-BM42HDAG-2XyC0YBj.js → dagre-BM42HDAG-BnD4nd21.js} +1 -1
- package/gui/dist/assets/{diagram-2AECGRRQ-fNv6yQF_.js → diagram-2AECGRRQ-E9oTEn4C.js} +1 -1
- package/gui/dist/assets/{diagram-5GNKFQAL-BUlxr0NW.js → diagram-5GNKFQAL-oSJwag8_.js} +1 -1
- package/gui/dist/assets/{diagram-KO2AKTUF-CtusNGfh.js → diagram-KO2AKTUF-B5ycmD2n.js} +1 -1
- package/gui/dist/assets/{diagram-LMA3HP47-Dkq3FOEe.js → diagram-LMA3HP47-DUo9wv_e.js} +1 -1
- package/gui/dist/assets/{diagram-OG6HWLK6-rjhcMJNr.js → diagram-OG6HWLK6-C-0r9-AW.js} +1 -1
- package/gui/dist/assets/{erDiagram-TEJ5UH35-Bqmogkqv.js → erDiagram-TEJ5UH35-C8w0nNRI.js} +1 -1
- package/gui/dist/assets/{flowDiagram-I6XJVG4X-BcYYq0Bh.js → flowDiagram-I6XJVG4X-CPZzt4CO.js} +1 -1
- package/gui/dist/assets/{ganttDiagram-6RSMTGT7-BGmJltMK.js → ganttDiagram-6RSMTGT7-B0TRw-bC.js} +1 -1
- package/gui/dist/assets/{gitGraphDiagram-PVQCEYII-JIYjcd0T.js → gitGraphDiagram-PVQCEYII-CxuhZqVM.js} +1 -1
- package/gui/dist/assets/{index-BwW5YDD1.js → index-84YuA9I8.js} +75 -75
- package/gui/dist/assets/{infoDiagram-5YYISTIA-CJKrfqe4.js → infoDiagram-5YYISTIA-qLgkATyo.js} +1 -1
- package/gui/dist/assets/{ishikawaDiagram-YF4QCWOH-Bu4wBvlL.js → ishikawaDiagram-YF4QCWOH-Bv8EcedY.js} +1 -1
- package/gui/dist/assets/{journeyDiagram-JHISSGLW-B2CtojGV.js → journeyDiagram-JHISSGLW-CO1wpi9j.js} +1 -1
- package/gui/dist/assets/{kanban-definition-UN3LZRKU-CKbuCL_h.js → kanban-definition-UN3LZRKU-INpnb6ij.js} +1 -1
- package/gui/dist/assets/{linear-BdHWi7h7.js → linear-yh3g20kh.js} +1 -1
- package/gui/dist/assets/{mindmap-definition-RKZ34NQL-Dbf71zzi.js → mindmap-definition-RKZ34NQL-BzSJP5cs.js} +1 -1
- package/gui/dist/assets/{pieDiagram-4H26LBE5-P8RSqdBA.js → pieDiagram-4H26LBE5-DH9i355H.js} +1 -1
- package/gui/dist/assets/{quadrantDiagram-W4KKPZXB-mVsJmQan.js → quadrantDiagram-W4KKPZXB-CNmW8Mck.js} +1 -1
- package/gui/dist/assets/{requirementDiagram-4Y6WPE33-BfHNLWFb.js → requirementDiagram-4Y6WPE33-DIxitFst.js} +1 -1
- package/gui/dist/assets/{sankeyDiagram-5OEKKPKP-CF28kVcG.js → sankeyDiagram-5OEKKPKP-BI4KHeTZ.js} +1 -1
- package/gui/dist/assets/{sequenceDiagram-3UESZ5HK-Bvs2r29Y.js → sequenceDiagram-3UESZ5HK-CbMtHFNi.js} +1 -1
- package/gui/dist/assets/{stateDiagram-AJRCARHV-Cbg-3kIx.js → stateDiagram-AJRCARHV-DWXkOhjJ.js} +1 -1
- package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-Dv9pwdh3.js +1 -0
- package/gui/dist/assets/{timeline-definition-PNZ67QCA-ChYm8r8v.js → timeline-definition-PNZ67QCA-DGsBQPbg.js} +1 -1
- package/gui/dist/assets/{vennDiagram-CIIHVFJN-CPcYOWG6.js → vennDiagram-CIIHVFJN-Dw5U_iYl.js} +1 -1
- package/gui/dist/assets/{wardley-L42UT6IY-KWdiWaFE.js → wardley-L42UT6IY-DGOHyaD_.js} +1 -1
- package/gui/dist/assets/{wardleyDiagram-YWT4CUSO-DAgBnDcZ.js → wardleyDiagram-YWT4CUSO-DpOyo37L.js} +1 -1
- package/gui/dist/assets/{xychartDiagram-2RQKCTM6-DwOk_srp.js → xychartDiagram-2RQKCTM6-BM5vPGiE.js} +1 -1
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/gui/dist/assets/channel-V0oafPxe.js +0 -1
- package/gui/dist/assets/classDiagram-4FO5ZUOK-BZlXnsXv.js +0 -1
- package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-BZlXnsXv.js +0 -1
- package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-Cg1sS-MT.js +0 -1
package/README.md
CHANGED
|
@@ -58,6 +58,7 @@ Add to your MCP client config (e.g. Cursor `mcp.json`):
|
|
|
58
58
|
| `MCP_DOCUMENTOR_OPENAI_MODEL` | No | Embedding model (default: `text-embedding-3-small`) |
|
|
59
59
|
| `PROXY_URL` | No | URL of the proxy server to register and launch the GUI |
|
|
60
60
|
| `MCP_DATA_DIR` | No | Overrides the default data directory (see Data storage) |
|
|
61
|
+
| `READ_ONLY` | No | When set to `1`/`true`/`yes`, disables all create/update/delete tools and GUI write actions (read-only mode) |
|
|
61
62
|
|
|
62
63
|
Optional `.env` at the package root (directory containing `package.json` discovered from the installed binary) is loaded when present.
|
|
63
64
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** True quand la variable d'environnement READ_ONLY est activée. */
|
|
2
|
+
export function isReadOnly() {
|
|
3
|
+
const v = (process.env.READ_ONLY ?? "").trim().toLowerCase();
|
|
4
|
+
return v === "1" || v === "true" || v === "yes";
|
|
5
|
+
}
|
|
6
|
+
/** Outils MCP qui créent / modifient / suppriment de la donnée. */
|
|
7
|
+
export const WRITE_TOOLS = new Set([
|
|
8
|
+
"create_doc", "update_doc", "patch_doc", "move_doc", "delete_doc",
|
|
9
|
+
"restore_doc_version", "refresh_file_snapshot", "reindex_embeddings",
|
|
10
|
+
"prepare_export_bundle", "delete_export_bundle", "upload_image",
|
|
11
|
+
]);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TrackedFile, TrackedFileInput } from "../../shared/schemas/doc.schema.js";
|
|
2
|
+
export interface CodeRefResult {
|
|
3
|
+
content: string;
|
|
4
|
+
additionalTrackedFiles: TrackedFileInput[];
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveCodeRefs(content: string): CodeRefResult;
|
|
7
|
+
export declare function snapshotTrackedFiles(inputs: TrackedFileInput[]): TrackedFile[];
|
|
8
|
+
/**
|
|
9
|
+
* Merges snapshotted tracked files into an existing list.
|
|
10
|
+
* - If a file is not yet tracked: appended.
|
|
11
|
+
* - If a file is already tracked with sections and the new entry also has sections: sections are merged.
|
|
12
|
+
* - Otherwise (whole-file tracking involved): the existing entry is replaced.
|
|
13
|
+
*/
|
|
14
|
+
export declare function mergeTrackedFiles(existing: TrackedFile[], additions: TrackedFileInput[]): TrackedFile[];
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { snapshotSection } from "./section-tracker.js";
|
|
5
|
+
const EXT_TO_LANG = {
|
|
6
|
+
ts: "typescript",
|
|
7
|
+
tsx: "tsx",
|
|
8
|
+
js: "javascript",
|
|
9
|
+
jsx: "jsx",
|
|
10
|
+
py: "python",
|
|
11
|
+
css: "css",
|
|
12
|
+
scss: "scss",
|
|
13
|
+
json: "json",
|
|
14
|
+
md: "markdown",
|
|
15
|
+
html: "html",
|
|
16
|
+
sh: "bash",
|
|
17
|
+
yml: "yaml",
|
|
18
|
+
yaml: "yaml",
|
|
19
|
+
sql: "sql",
|
|
20
|
+
rs: "rust",
|
|
21
|
+
go: "go",
|
|
22
|
+
java: "java",
|
|
23
|
+
cs: "csharp",
|
|
24
|
+
};
|
|
25
|
+
// Syntax: [copy-from-codebase|/absolute/path/to/file.ts|10|50]
|
|
26
|
+
// Using | as separator to avoid conflicts with Windows drive letters (C:\...)
|
|
27
|
+
const CODE_REF_REGEX = /\[copy-from-codebase\|([^|]+)\|(\d+)\|(\d+)\]/g;
|
|
28
|
+
function computeFileHash(absolutePath) {
|
|
29
|
+
return crypto.createHash("sha256").update(fs.readFileSync(absolutePath)).digest("hex");
|
|
30
|
+
}
|
|
31
|
+
export function resolveCodeRefs(content) {
|
|
32
|
+
const additionalTrackedFiles = [];
|
|
33
|
+
const resolved = content.replace(CODE_REF_REGEX, (match, filePath, startStr, endStr) => {
|
|
34
|
+
const start = parseInt(startStr, 10);
|
|
35
|
+
const end = parseInt(endStr, 10);
|
|
36
|
+
if (!fs.existsSync(filePath)) {
|
|
37
|
+
console.warn(`[copy-from-codebase] File not found: ${filePath}`);
|
|
38
|
+
return match;
|
|
39
|
+
}
|
|
40
|
+
const lines = fs.readFileSync(filePath, "utf-8").split("\n");
|
|
41
|
+
const extracted = lines.slice(start - 1, end).join("\n");
|
|
42
|
+
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
43
|
+
const lang = EXT_TO_LANG[ext] ?? "";
|
|
44
|
+
additionalTrackedFiles.push({ path: filePath, lines: [{ start, end }] });
|
|
45
|
+
return `\`\`\`${lang}\n${extracted}\n\`\`\``;
|
|
46
|
+
});
|
|
47
|
+
return { content: resolved, additionalTrackedFiles };
|
|
48
|
+
}
|
|
49
|
+
export function snapshotTrackedFiles(inputs) {
|
|
50
|
+
const byPath = new Map();
|
|
51
|
+
for (const input of inputs) {
|
|
52
|
+
const fp = typeof input === "string" ? input : input.path;
|
|
53
|
+
const absolutePath = path.resolve(process.cwd(), fp);
|
|
54
|
+
const mtime = fs.existsSync(absolutePath)
|
|
55
|
+
? fs.statSync(absolutePath).mtime.toISOString()
|
|
56
|
+
: new Date().toISOString();
|
|
57
|
+
const isWhole = typeof input === "string" || input.lines.length === 0;
|
|
58
|
+
const existing = byPath.get(absolutePath);
|
|
59
|
+
if (isWhole) {
|
|
60
|
+
byPath.set(absolutePath, { mtime, lineRanges: [], wholeFile: true });
|
|
61
|
+
}
|
|
62
|
+
else if (existing) {
|
|
63
|
+
if (!existing.wholeFile) {
|
|
64
|
+
existing.lineRanges.push(...input.lines);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
byPath.set(absolutePath, {
|
|
69
|
+
mtime,
|
|
70
|
+
lineRanges: [...input.lines],
|
|
71
|
+
wholeFile: false,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const tracked = [];
|
|
76
|
+
for (const [absolutePath, { mtime, lineRanges, wholeFile }] of byPath) {
|
|
77
|
+
if (!wholeFile && lineRanges.length > 0 && fs.existsSync(absolutePath)) {
|
|
78
|
+
const sections = lineRanges
|
|
79
|
+
.filter(({ start, end }) => start <= end)
|
|
80
|
+
.map(({ start, end }) => snapshotSection(absolutePath, start, end));
|
|
81
|
+
tracked.push({ path: absolutePath, lastModifiedAt: mtime, sections });
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const contentHash = fs.existsSync(absolutePath) ? computeFileHash(absolutePath) : undefined;
|
|
85
|
+
tracked.push({ path: absolutePath, lastModifiedAt: mtime, ...(contentHash !== undefined && { contentHash }) });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return tracked;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Merges snapshotted tracked files into an existing list.
|
|
92
|
+
* - If a file is not yet tracked: appended.
|
|
93
|
+
* - If a file is already tracked with sections and the new entry also has sections: sections are merged.
|
|
94
|
+
* - Otherwise (whole-file tracking involved): the existing entry is replaced.
|
|
95
|
+
*/
|
|
96
|
+
export function mergeTrackedFiles(existing, additions) {
|
|
97
|
+
if (additions.length === 0)
|
|
98
|
+
return existing;
|
|
99
|
+
const snapshotted = snapshotTrackedFiles(additions);
|
|
100
|
+
const result = [...existing];
|
|
101
|
+
for (const newTf of snapshotted) {
|
|
102
|
+
const idx = result.findIndex(tf => tf.path === newTf.path);
|
|
103
|
+
if (idx < 0) {
|
|
104
|
+
result.push(newTf);
|
|
105
|
+
}
|
|
106
|
+
else if (newTf.sections && result[idx].sections) {
|
|
107
|
+
result[idx] = {
|
|
108
|
+
...result[idx],
|
|
109
|
+
sections: [...result[idx].sections, ...newTf.sections],
|
|
110
|
+
lastModifiedAt: newTf.lastModifiedAt,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
result[idx] = newTf;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
package/dist/src/gui-worker.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Hono } from "hono";
|
|
|
4
4
|
import { streamSSE } from "hono/streaming";
|
|
5
5
|
import { ProxyClient } from "@imenam/mcp-gui-interface";
|
|
6
6
|
import { IPCMessageSchema } from "../shared/schemas/doc.schema.js";
|
|
7
|
+
import { isReadOnly } from "../shared/read-only.js";
|
|
7
8
|
import path from "path";
|
|
8
9
|
import fs from "fs";
|
|
9
10
|
import { execFile } from "child_process";
|
|
@@ -184,6 +185,23 @@ function setupIpcErrorHandlers() {
|
|
|
184
185
|
});
|
|
185
186
|
}
|
|
186
187
|
// --- HTTP Routes ---
|
|
188
|
+
// Bloque les écritures GUI en mode lecture seule (READ_ONLY).
|
|
189
|
+
const READ_ONLY_WRITE_ROUTES = [
|
|
190
|
+
{ method: "POST", prefix: "/api/doc" },
|
|
191
|
+
{ method: "PUT", prefix: "/api/doc/" },
|
|
192
|
+
{ method: "POST", prefix: "/api/images" },
|
|
193
|
+
{ method: "POST", prefix: "/api/configurations/apply" },
|
|
194
|
+
];
|
|
195
|
+
app.use("*", async (c, next) => {
|
|
196
|
+
if (isReadOnly()) {
|
|
197
|
+
const m = c.req.method;
|
|
198
|
+
const p = c.req.path;
|
|
199
|
+
if (READ_ONLY_WRITE_ROUTES.some((r) => r.method === m && p.startsWith(r.prefix))) {
|
|
200
|
+
return c.json({ error: "READ_ONLY mode: write operations are disabled" }, 403);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return next();
|
|
204
|
+
});
|
|
187
205
|
app.get("/proxy/health", (c) => {
|
|
188
206
|
const memory = process.memoryUsage();
|
|
189
207
|
return c.json({
|