@piecemaker-legal/piecemaker 2.0.8 → 2.0.10
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/dist/assets/FindReplaceDialog-KU5TEIBJ-CQ76sPaJ.js +1 -0
- package/dist/assets/FootnotePropertiesDialog-W3YHM4BV-BpgYQvvd.js +1 -0
- package/dist/assets/HyperlinkDialog-ZYH7ZHRF-qIPBOssF.css +1 -0
- package/dist/assets/HyperlinkDialog-ZYH7ZHRF-z08UbYbL.js +144 -0
- package/dist/assets/ImagePositionDialog-HEWYR6Q3-CFzOnzAt.js +1 -0
- package/dist/assets/ImagePropertiesDialog-RAB47C6M-D-ThGU-b.js +1 -0
- package/dist/assets/PageSetupDialog-AIGIXO3E-_PYIO8eb.js +1 -0
- package/dist/assets/SplitCellDialog-IFBKYCD6-Cb9uQOB8.js +1 -0
- package/dist/assets/TablePropertiesDialog-TMLKGOOW-12vmjb6B.js +1 -0
- package/dist/assets/WatermarkDialog-KRL7WMIS-C-d8rzt2.js +1 -0
- package/dist/assets/{abnfDiagram-VCTEODGH-XuFdW3jA.js → abnfDiagram-VCTEODGH-B4OsX7eF.js} +1 -1
- package/dist/assets/arc-BK1oUIk7.js +1 -0
- package/dist/assets/architectureDiagram-5GKGNRK7-CfaFI6L7.js +36 -0
- package/dist/assets/{blockDiagram-I7D4REHJ-CsgOSDLB.js → blockDiagram-I7D4REHJ-DQEI2azY.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-awKtsGCB.js → c4Diagram-7LVT6UL2-DbravpVi.js} +1 -1
- package/dist/assets/channel-DVyNr2Yg.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-DA5F8ig-.js → chunk-2Q5K7J3B-BLpZ9huC.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DawRFEGW.js → chunk-5VM5RSS4-BLlEdmX9.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-BO6B_U1p.js → chunk-F27PBJKO-D-ZNhI6q.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-Dhrf_k6W.js → chunk-IMKFNOWR-vzSiKnhh.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BTGX4CJ7.js → chunk-JWPE2WC7-DfF0k4nb.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-ByKLZ0wa.js → chunk-POPQ4Y6H-BnwCYsPP.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-DNCtUeow.js → chunk-SVP7TREG-DRxc8fhq.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-bgcAtBKw.js → chunk-TICWLB2K-Dd2Q-md-.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-AD-pwqOj.js → chunk-XXDRQBXY-DBjkEUp_.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-2pGPHl5H.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-2pGPHl5H.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-D23VMTjy.js → cose-bilkent-JH36ORCC-By6PS4oe.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-thDLtHBg.js → cynefin-OW5HDTMX-YxWpjc5W.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-BTG5qDGi.js → cynefinDiagram-5FMLGOSQ-DrtXzz3p.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-yXAeLV32.js → dagre-GXQ25YYZ-BDpTLKqZ.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-v1-YDY3c.js → diagram-S7CK7UJ4-C4LvqeCs.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-DlO9hQ63.js → diagram-UQ7AKVKN-BTpnMeHp.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-B3Bz2C6Y.js → diagram-VSXAHHWV-DucHHWev.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-BosIgZRa.js → diagram-VX7I27RA-DYHZNmmH.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-C1MFbIji.js → diagram-Z3DM3KII-BHIH2BGN.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-B6g9aRAb.js → ebnfDiagram-PWID7BFC-CGiEkwj0.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-BuV5RUQ-.js → erDiagram-RLTQ6QDP-AJMHi2Us.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-OFcS8Psg.js → flowDiagram-HODETNUW-BqzANBh2.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-CxCIs-y4.js → ganttDiagram-EL5Y4UJY-B0xPDY2_.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-DQHfgTRq.js → gitGraphDiagram-WWUBYQGX-CzPX5CNO.js} +1 -1
- package/dist/assets/{index-BPmxqMd5.js → index-Bk2iXKnJ.js} +140 -140
- package/dist/assets/index-Dc9wNuS4.css +1 -0
- package/dist/assets/{infoDiagram-27XIBGKW-UvDaFHGE.js → infoDiagram-27XIBGKW-Cjc44H0O.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-B04u_588.js → ishikawaDiagram-5VMMS53U-BWhr2v7r.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-BYvBbjXl.js → journeyDiagram-3NMN7TZE-Bl4r6RrP.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-DxAJd2LW.js → kanban-definition-UXKFOSKX-FV-T0Vy1.js} +6 -6
- package/dist/assets/layout-CTfb1sAF.js +1 -0
- package/dist/assets/{linear-CmWY0F-m.js → linear-Dfgi6vlo.js} +1 -1
- package/dist/assets/{mermaid.core-ClUcKNvb.js → mermaid.core-DOCGsdlo.js} +7 -7
- package/dist/assets/{mindmap-definition-YA3MSWOX-DZ2GO-J-.js → mindmap-definition-YA3MSWOX-CymQ86ir.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-DF4i_o7r.js → pegDiagram-XKGWAZYB-1VMq_vi0.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-G20Z0KTO.js → pieDiagram-E7YTZNPT-C5GgXtJU.js} +1 -1
- package/dist/assets/processTemplate-GPFDLQMX-B1sVV2O0.js +22 -0
- package/dist/assets/{quadrantDiagram-AXDQQJYC-5MRlN0BO.js → quadrantDiagram-AXDQQJYC-C6zpryYf.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-B4Fp-guo.js → railroadDiagram-O6MQD6OU-D-sv4SWi.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-DFL0eww_.js → requirementDiagram-BXWQKSXE-CkjY-z4B.js} +1 -1
- package/dist/assets/sankeyDiagram-P5KCCOFB-De-d8Q6L.js +40 -0
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-iP9OUxwi.js → sequenceDiagram-WJ2MYXX4-DCsGSJa9.js} +1 -1
- package/dist/assets/{server.browser-BzI1jrZw.js → server.browser-BY3bL-IX.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-C95kgcA-.js → sizeCapture-INFHLROL-xINZhgER.js} +1 -1
- package/dist/assets/stateDiagram-D77RDMKH-CuLUTWSL.js +1 -0
- package/dist/assets/stateDiagram-v2-MP3YSRHH-Cnbs-M92.js +1 -0
- package/dist/assets/{swimlanes-42K2YHIH-BbMY7ECY.js → swimlanes-42K2YHIH-BwxARD7N.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-D7p8qZI2.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-BQs8wPer.js → timeline-definition-24CTP7MA-BIOvnn-l.js} +3 -3
- package/dist/assets/{vendor-codemirror-CjW0lHR5.js → vendor-codemirror-CN-sJePf.js} +2 -2
- package/dist/assets/{vendor-react-D809iwXR.js → vendor-react-B22wqN3J.js} +5 -5
- package/dist/assets/{vennDiagram-4TSXK5OY-Imm0EPbt.js → vennDiagram-4TSXK5OY-BSD6Pfmt.js} +4 -4
- package/dist/assets/{wardleyDiagram-VM6X3IG4-DpMMDayn.js → wardleyDiagram-VM6X3IG4-7Zz2iRkF.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-DdLp1sYL.js → xychartDiagram-S5SC5T6Z-Ctx17Y1D.js} +1 -1
- package/dist/index.html +4 -4
- package/dist/sw.js +1 -1
- package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js +4 -53
- package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js +2 -3
- package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js +30 -0
- package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js +24 -0
- package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js.map +1 -0
- package/dist-server/server/piecemaker/anonymizer/lifecycle.js +2 -3
- package/dist-server/server/piecemaker/anonymizer/lifecycle.js.map +1 -1
- package/dist-server/server/piecemaker/desktop-update/index.js +4 -3
- package/dist-server/server/piecemaker/desktop-update/index.js.map +1 -1
- package/dist-server/server/piecemaker/docx-document.js +50 -0
- package/dist-server/server/piecemaker/docx-document.js.map +1 -0
- package/dist-server/server/piecemaker/index.js +9 -2
- package/dist-server/server/piecemaker/index.js.map +1 -1
- package/dist-server/server/piecemaker/model-discovery/codex-catalog.js +46 -0
- package/dist-server/server/piecemaker/model-discovery/codex-catalog.js.map +1 -0
- package/dist-server/server/piecemaker/model-discovery/index.js +42 -0
- package/dist-server/server/piecemaker/model-discovery/index.js.map +1 -0
- package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js +19 -0
- package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js.map +1 -0
- package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js +61 -0
- package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js.map +1 -0
- package/dist-server/server/piecemaker/project-registry.js +29 -0
- package/dist-server/server/piecemaker/project-registry.js.map +1 -0
- package/package.json +12 -1
- package/scripts/piecemaker/cli/disable-anonymizer.mjs +19 -0
- package/scripts/piecemaker/cli/lib/config.mjs +0 -1
- package/scripts/piecemaker/cli/lib/services.mjs +1 -10
- package/scripts/piecemaker/cli/piecemaker.mjs +0 -3
- package/server/modules/providers/list/mistral/mistral-models.provider.ts +4 -53
- package/server/modules/providers/list/mistral/mistral-runtime.provider.ts +2 -3
- package/server/modules/providers/list/mistral/mistral-vibe-models.ts +43 -0
- package/server/modules/providers/tests/mistral-vibe-models.test.ts +27 -0
- package/server/piecemaker/anonymizer/client-config.cjs +92 -92
- package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +9 -16
- package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.toml +1 -1
- package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +251 -268
- package/server/piecemaker/anonymizer/lifecycle.ts +2 -3
- package/server/piecemaker/anonymizer/rewriter-bridge.cjs +6 -0
- package/server/piecemaker/anonymizer/service.cjs +117 -175
- package/server/piecemaker/desktop-update/index.ts +4 -3
- package/server/piecemaker/docx-document.ts +54 -0
- package/server/piecemaker/harness/decisions.cjs +1 -1
- package/server/piecemaker/harness/index.cjs +2 -3
- package/server/piecemaker/harness/verification.cjs +1 -1
- package/server/piecemaker/index.ts +11 -3
- package/server/piecemaker/model-discovery/codex-catalog.ts +67 -0
- package/server/piecemaker/model-discovery/index.ts +69 -0
- package/server/piecemaker/model-discovery/mistral-catalog.ts +36 -0
- package/server/piecemaker/model-discovery/stdio-rpc.ts +79 -0
- package/server/piecemaker/project-registry.ts +34 -0
- package/server/piecemaker/router.cjs +6 -7
- package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +31 -77
- package/server/piecemaker/vendor/installer/lib/codex-skills.mjs +2 -84
- package/server/piecemaker/vendor/installer/lib/service.mjs +4 -24
- package/server/piecemaker/vendor/installer/steps/06-hooks.mjs +10 -21
- package/server/piecemaker/vendor/installer/steps/09-claude-assets.mjs +0 -11
- package/server/piecemaker/vendor/installer/steps/09-codex-plugin.mjs +6 -36
- package/server/piecemaker/vendor/installer/steps/13-garde-secrets.mjs +30 -12
- package/server/piecemaker/vendor/piecemaker-plugin/README.md +2 -9
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/billing-track.mjs +5 -5
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/classify-ai-documents.mjs +8 -8
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/commit-track.mjs +2 -2
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/compile-recherche.mjs +4 -6
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folders.cjs +71 -38
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/mapping.cjs +8 -38
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/protection.cjs +82 -61
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/protect-originals.mjs +4 -55
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +24 -117
- package/server/piecemaker/vendor/piecemaker-plugin/skills/tamponnage/SKILL.md +2 -2
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +15 -121
- package/server/piecemaker/vendor/websocket-server/case-registry.cjs +16 -32
- package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +0 -73
- package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +0 -7
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/scanner_worker.py +2 -0
- package/server/piecemaker/vendor/websocket-server/stamping-routes.cjs +3 -12
- package/server/piecemaker/vendor/websocket-server/workspace-paths.cjs +6 -6
- package/dist/assets/arc-CsCtfL20.js +0 -1
- package/dist/assets/architectureDiagram-5GKGNRK7-DGxozBGS.js +0 -36
- package/dist/assets/channel-DnynWwYa.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-CHIsGj2B.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-CHIsGj2B.js +0 -1
- package/dist/assets/index-WwknP0l4.css +0 -1
- package/dist/assets/layout-R7w2kLxO.js +0 -1
- package/dist/assets/sankeyDiagram-P5KCCOFB-CpOdmqdy.js +0 -40
- package/dist/assets/stateDiagram-D77RDMKH-D9WHRvlY.js +0 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-D9Qlflkb.js +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-CA64dwpM.js +0 -8
- package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +0 -78
- package/scripts/piecemaker/hooks/proxy-guard.mjs +0 -251
- package/server/piecemaker/anonymizer/providers.cjs +0 -120
- package/server/piecemaker/vendor/installer/steps/08-telegram.mjs +0 -482
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/central-mapping.cjs +0 -0
- package/server/piecemaker/vendor/websocket-server/telegram-admin.cjs +0 -602
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Legal-case folders: every CloudCLI project, as published by the PieceMaker server. */
|
|
2
2
|
const fs = require('node:fs');
|
|
3
|
+
const os = require('node:os');
|
|
3
4
|
const path = require('node:path');
|
|
4
5
|
|
|
6
|
+
const PROJECTS_FILE = 'projects.json';
|
|
7
|
+
|
|
8
|
+
function piecemakerHome() {
|
|
9
|
+
return process.env.PIECEMAKER_HOME || path.join(os.homedir(), '.piecemaker');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function projectsFile() {
|
|
13
|
+
return path.join(piecemakerHome(), PROJECTS_FILE);
|
|
14
|
+
}
|
|
15
|
+
|
|
5
16
|
function realDirectory(value) {
|
|
6
17
|
const requested = String(value || '').trim();
|
|
7
18
|
if (!requested || !path.isAbsolute(requested)) return null;
|
|
@@ -13,23 +24,57 @@ function realDirectory(value) {
|
|
|
13
24
|
}
|
|
14
25
|
}
|
|
15
26
|
|
|
16
|
-
|
|
17
|
-
function registeredCaseFolders(config) {
|
|
18
|
-
const folders = Array.isArray(config?.caseFolders) ? config.caseFolders : [];
|
|
19
|
-
return [...new Set(folders.map(realDirectory).filter(Boolean))];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function isInsideOrEqual(root, candidate) {
|
|
27
|
+
function isInsideOrEqualPath(root, candidate) {
|
|
23
28
|
const relative = path.relative(root, candidate);
|
|
24
29
|
return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
function excludedRoots() {
|
|
33
|
+
return [os.tmpdir(), '/tmp', '/private/tmp', '/var/folders', '/private/var/folders']
|
|
34
|
+
.map(realDirectory)
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isEligibleProjectFolder(folder) {
|
|
39
|
+
const home = realDirectory(os.homedir());
|
|
40
|
+
if (home && isInsideOrEqualPath(folder, home)) return false;
|
|
41
|
+
return !excludedRoots().some((root) => isInsideOrEqualPath(root, folder));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readProjectSources() {
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(fs.readFileSync(projectsFile(), 'utf8'));
|
|
47
|
+
return parsed?.sources && typeof parsed.sources === 'object' && !Array.isArray(parsed.sources) ? parsed.sources : {};
|
|
48
|
+
} catch {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Absolute, existing and de-duplicated project folders, across every publishing server. */
|
|
54
|
+
function registeredProjectFolders() {
|
|
55
|
+
const folders = Object.values(readProjectSources()).flatMap((list) => (Array.isArray(list) ? list : []));
|
|
56
|
+
return [...new Set(folders.map(realDirectory).filter(Boolean))].filter(isEligibleProjectFolder);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function publishProjectSource(sourceId, folders) {
|
|
60
|
+
const file = projectsFile();
|
|
61
|
+
const sources = readProjectSources();
|
|
62
|
+
if (folders === null) delete sources[sourceId];
|
|
63
|
+
else sources[sourceId] = [...new Set(folders.map((folder) => path.resolve(String(folder))))].sort();
|
|
64
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
65
|
+
const temporaryFile = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
66
|
+
try {
|
|
67
|
+
fs.writeFileSync(temporaryFile, `${JSON.stringify({ version: 1, sources }, null, 2)}\n`, 'utf8');
|
|
68
|
+
fs.renameSync(temporaryFile, file);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
fs.rmSync(temporaryFile, { force: true });
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveTarget(target) {
|
|
31
76
|
try {
|
|
32
|
-
|
|
77
|
+
return fs.realpathSync(path.resolve(String(target)));
|
|
33
78
|
} catch {
|
|
34
79
|
// Write/Edit may target a file that does not exist yet. Resolve its nearest
|
|
35
80
|
// existing parent, then append the missing tail without following links.
|
|
@@ -37,22 +82,24 @@ function locateRegisteredCase(folders, target) {
|
|
|
37
82
|
const tail = [];
|
|
38
83
|
while (path.dirname(current) !== current) {
|
|
39
84
|
try {
|
|
40
|
-
|
|
41
|
-
break;
|
|
85
|
+
return path.join(fs.realpathSync(current), ...tail);
|
|
42
86
|
} catch {
|
|
43
87
|
tail.unshift(path.basename(current));
|
|
44
88
|
current = path.dirname(current);
|
|
45
89
|
}
|
|
46
90
|
}
|
|
47
|
-
|
|
91
|
+
return null;
|
|
48
92
|
}
|
|
93
|
+
}
|
|
49
94
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
95
|
+
/** Locate a target inside one of the project folders; a nested project wins over its parent. */
|
|
96
|
+
function locateProjectCase(target) {
|
|
97
|
+
if (!target) return null;
|
|
98
|
+
const absolute = resolveTarget(target);
|
|
99
|
+
if (!absolute) return null;
|
|
100
|
+
const roots = registeredProjectFolders().sort((a, b) => b.length - a.length);
|
|
54
101
|
for (const caseRoot of roots) {
|
|
55
|
-
if (!
|
|
102
|
+
if (!isInsideOrEqualPath(caseRoot, absolute)) continue;
|
|
56
103
|
return {
|
|
57
104
|
casesRoot: path.dirname(caseRoot),
|
|
58
105
|
caseName: path.basename(caseRoot),
|
|
@@ -65,22 +112,8 @@ function locateRegisteredCase(folders, target) {
|
|
|
65
112
|
return null;
|
|
66
113
|
}
|
|
67
114
|
|
|
68
|
-
/**
|
|
69
|
-
* Locate a target inside one of the explicitly registered legal-case folders.
|
|
70
|
-
* There is no longer a workspace-root fallback: a matter is protected only once
|
|
71
|
-
* it is explicitly registered, never by mere location under a common root.
|
|
72
|
-
*/
|
|
73
|
-
function locateConfiguredCase(config, target) {
|
|
74
|
-
return locateRegisteredCase(registeredCaseFolders(config), target);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function configuredWatchPaths(config) {
|
|
78
|
-
return registeredCaseFolders(config);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
115
|
module.exports = {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
registeredCaseFolders,
|
|
116
|
+
locateProjectCase,
|
|
117
|
+
publishProjectSource,
|
|
118
|
+
registeredProjectFolders,
|
|
86
119
|
};
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Mapping d'anonymisation d'un dossier juridique — implémentation unique.
|
|
3
3
|
*
|
|
4
4
|
* Le pipeline admin (`websocket-server/originals-pipeline.cjs`), le serveur
|
|
5
|
-
* Word, l'historique et les vues juridiques la partagent.
|
|
6
|
-
* mapping central produit à partir de ces mappings de dossier.
|
|
5
|
+
* Word, l'historique et les vues juridiques la partagent.
|
|
7
6
|
*
|
|
8
7
|
* Deux sens, jamais symétriques dans leur usage :
|
|
9
8
|
* - `applyMapping` entité → code, sur tout ce que l'IA s'apprête à lire ;
|
|
@@ -16,11 +15,11 @@
|
|
|
16
15
|
const fs = require('node:fs');
|
|
17
16
|
const path = require('node:path');
|
|
18
17
|
|
|
19
|
-
const {
|
|
20
|
-
const {
|
|
18
|
+
const { WORKSPACE_SUBDIR } = require('./protection.cjs');
|
|
19
|
+
const { locateProjectCase } = require('./case-folders.cjs');
|
|
21
20
|
const { isInstitutionalEntity } = require('./institutional-terms.cjs');
|
|
22
21
|
// Le moteur de substitution est extrait dans un module autonome : il est aussi
|
|
23
|
-
// requis par le
|
|
22
|
+
// requis par le proxy PII (`anonymizer/dictionary.cjs`). Une seule
|
|
24
23
|
// implémentation, ré-exportée ici pour les appelants historiques.
|
|
25
24
|
const {
|
|
26
25
|
applyMapping,
|
|
@@ -286,40 +285,13 @@ function readCaseMapping(caseRoot) {
|
|
|
286
285
|
};
|
|
287
286
|
}
|
|
288
287
|
|
|
289
|
-
/**
|
|
290
|
-
* Un mapping d'anonymisation exploitable existe-t-il pour ce dossier ? C'est la
|
|
291
|
-
* condition de la garantie « anonymisé avant envoi » : sans lui, le proxy n'a
|
|
292
|
-
* rien à coder et une surface lisible partirait en clair. `protect-originals`
|
|
293
|
-
* s'en sert pour refuser la lecture d'un dossier PieceMaker pas encore anonymisé.
|
|
294
|
-
* On s'appuie sur `readCaseMapping` (et non sur la seule présence du fichier) pour
|
|
295
|
-
* qu'un `mapping_default.json` illisible compte comme absent — c'est exactement ce
|
|
296
|
-
* que voit la reconstruction du mapping central.
|
|
297
|
-
*/
|
|
298
|
-
function caseHasMapping(caseRoot) {
|
|
299
|
-
return readCaseMapping(caseRoot).exists;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
288
|
// La substitution (`applyMapping` / `revertMapping`) vit dans `substitution.cjs`
|
|
303
289
|
// et est importée en tête de fichier. Elle est ré-exportée ci-dessous pour les
|
|
304
290
|
// appelants historiques de `mapping.cjs`.
|
|
305
291
|
|
|
306
|
-
/**
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
* Telegram). Retourne `null` hors dossier juridique ou sans mapping utilisable :
|
|
310
|
-
* les hooks n'ont alors rien à faire.
|
|
311
|
-
*/
|
|
312
|
-
function resolveCaseMapping(casesRoot, hint) {
|
|
313
|
-
const located = locateCase(casesRoot, hint);
|
|
314
|
-
if (!located) return null;
|
|
315
|
-
const mapping = readCaseMapping(located.caseRoot);
|
|
316
|
-
if (!mapping.exists) return null;
|
|
317
|
-
return { caseRoot: located.caseRoot, caseName: located.caseName, ...mapping };
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/** Resolve a mapping from the explicit folder registry plus legacy workspace. */
|
|
321
|
-
function resolveConfiguredCaseMapping(config, hint) {
|
|
322
|
-
const located = locateConfiguredCase(config, hint);
|
|
292
|
+
/** Resolve the mapping of the project folder containing the hint. */
|
|
293
|
+
function resolveProjectCaseMapping(hint) {
|
|
294
|
+
const located = locateProjectCase(hint);
|
|
323
295
|
if (!located) return null;
|
|
324
296
|
const mapping = readCaseMapping(located.caseRoot);
|
|
325
297
|
if (!mapping.exists) return null;
|
|
@@ -330,15 +302,13 @@ module.exports = {
|
|
|
330
302
|
applyMapping,
|
|
331
303
|
buildEntityRegex,
|
|
332
304
|
byDescendingEntityLength,
|
|
333
|
-
caseHasMapping,
|
|
334
305
|
caseMappingFile,
|
|
335
306
|
escapeWithVariants,
|
|
336
307
|
normalizeMappingDocument,
|
|
337
308
|
normalizeProcedureInfo,
|
|
338
309
|
readCaseMapping,
|
|
339
310
|
readJsonFile,
|
|
340
|
-
|
|
341
|
-
resolveCaseMapping,
|
|
311
|
+
resolveProjectCaseMapping,
|
|
342
312
|
resolveMappedPath,
|
|
343
313
|
revertMapping,
|
|
344
314
|
sortedMapping,
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* L'ancienne règle protégeait le contenu d'un sous-dossier « Pièces
|
|
5
5
|
* originales ». Elle ne protégeait donc rien dans un dossier organisé
|
|
6
|
-
* autrement, ce qui est le cas courant : les PDF
|
|
7
|
-
*
|
|
6
|
+
* autrement, ce qui est le cas courant : les PDF y côtoient le Markdown qu'on
|
|
7
|
+
* en a tiré. La protection est désormais une propriété du fichier,
|
|
8
8
|
* décidée dans l'administration.
|
|
9
9
|
*
|
|
10
10
|
* **Protégés par défaut : les PDF et les images** (`PROTECTED_EXTENSIONS`), qui
|
|
@@ -81,7 +81,7 @@ const PROTECTED_EXTENSIONS = new Set([
|
|
|
81
81
|
* celles rangées sous `ignored` et les variantes d'`extracted_data` ressortiraient
|
|
82
82
|
* telles quelles. Le seul traitement correct est le refus.
|
|
83
83
|
*/
|
|
84
|
-
const FORBIDDEN_JSON_PATTERNS = [/^mapping.*\.json$/i, /_sensitive_map\.json$/i
|
|
84
|
+
const FORBIDDEN_JSON_PATTERNS = [/^mapping.*\.json$/i, /_sensitive_map\.json$/i];
|
|
85
85
|
|
|
86
86
|
/** Vrai pour un mapping de dossier ou un scan PII, où qu'il soit rangé. */
|
|
87
87
|
function isMappingFile(filePath) {
|
|
@@ -172,37 +172,6 @@ function relativeKey(absolutePath, caseRoot) {
|
|
|
172
172
|
return relative.split(path.sep).join('/');
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
/**
|
|
176
|
-
* Situe un chemin dans la racine PieceMaker : chaque enfant direct de cette
|
|
177
|
-
* racine est un dossier juridique indépendant. Retourne `null` hors racine, ce
|
|
178
|
-
* qui est le cas de la grande majorité des appels d'outil — c'est le chemin
|
|
179
|
-
* rapide des hooks.
|
|
180
|
-
*/
|
|
181
|
-
function locateCase(casesRoot, target) {
|
|
182
|
-
if (!casesRoot || !target) return null;
|
|
183
|
-
let root;
|
|
184
|
-
try {
|
|
185
|
-
root = fs.realpathSync(path.resolve(String(casesRoot)));
|
|
186
|
-
} catch {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
// Les deux côtés doivent être résolus : sur macOS `/var/…` est un lien vers
|
|
190
|
-
// `/private/var/…`, si bien qu'une racine résolue et une cible qui ne l'est
|
|
191
|
-
// pas ne se recouvrent jamais — le dossier passait alors pour hors racine et
|
|
192
|
-
// rien n'était protégé.
|
|
193
|
-
const absolute = realpathOrParent(path.resolve(String(target)));
|
|
194
|
-
if (absolute === root || !absolute.startsWith(`${root}${path.sep}`)) return null;
|
|
195
|
-
const [caseName, ...rest] = path.relative(root, absolute).split(path.sep);
|
|
196
|
-
if (!caseName || caseName.startsWith('.')) return null;
|
|
197
|
-
const caseRoot = path.join(root, caseName);
|
|
198
|
-
try {
|
|
199
|
-
if (!fs.statSync(caseRoot).isDirectory()) return null;
|
|
200
|
-
} catch {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
return { casesRoot: root, caseName, caseRoot, absolute, relative: rest.join('/') };
|
|
204
|
-
}
|
|
205
|
-
|
|
206
175
|
/** Normalise une liste de chemins relatifs en Set de clés POSIX. */
|
|
207
176
|
function normalizeKeySet(list) {
|
|
208
177
|
return new Set(
|
|
@@ -244,26 +213,76 @@ function readProtection(caseRoot) {
|
|
|
244
213
|
* que `unprotected` — l'initialisation d'un dossier — n'efface pas les
|
|
245
214
|
* ressources déjà déclarées.
|
|
246
215
|
*/
|
|
247
|
-
function writeProtection(caseRoot,
|
|
216
|
+
function writeProtection(caseRoot, lists = {}) {
|
|
217
|
+
return mutateProtection(caseRoot, () => lists);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const PROTECTION_LOCK_TIMEOUT_MS = 3000;
|
|
221
|
+
const PROTECTION_LOCK_STALE_MS = 10000;
|
|
222
|
+
const PROTECTION_LOCK_RETRY_MS = 25;
|
|
223
|
+
|
|
224
|
+
function sleepSync(milliseconds) {
|
|
225
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function acquireProtectionLock(lockFile) {
|
|
229
|
+
const deadline = Date.now() + PROTECTION_LOCK_TIMEOUT_MS;
|
|
230
|
+
for (;;) {
|
|
231
|
+
try {
|
|
232
|
+
fs.closeSync(fs.openSync(lockFile, 'wx'));
|
|
233
|
+
return;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (error.code !== 'EEXIST') throw error;
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
if (Date.now() - fs.statSync(lockFile).mtimeMs > PROTECTION_LOCK_STALE_MS) {
|
|
239
|
+
fs.rmSync(lockFile, { force: true });
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (Date.now() > deadline) throw new Error(`Le fichier de protection est verrouillé : ${lockFile}`);
|
|
246
|
+
sleepSync(PROTECTION_LOCK_RETRY_MS);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function mutateProtection(caseRoot, mutate) {
|
|
248
251
|
const file = protectionFile(caseRoot);
|
|
249
|
-
const
|
|
250
|
-
const clean = (list, fallback) => [...new Set(
|
|
251
|
-
(Array.isArray(list) ? list : [...fallback])
|
|
252
|
-
.map((entry) => String(entry || '').replaceAll('\\', '/').trim())
|
|
253
|
-
.filter((entry) => entry && !entry.startsWith('../') && !path.isAbsolute(entry))
|
|
254
|
-
)].sort((a, b) => a.localeCompare(b, 'fr'));
|
|
255
|
-
// Une pièce ne peut être à la fois « espace de travail » et « ressource » :
|
|
256
|
-
// `resources` a priorité, on la retire donc de `unprotected`.
|
|
257
|
-
const resourcesList = clean(resources, existing.resources);
|
|
258
|
-
const resourceSet = new Set(resourcesList);
|
|
259
|
-
const unprotectedList = clean(unprotected, existing.unprotected).filter((key) => !resourceSet.has(key));
|
|
252
|
+
const lockFile = `${file}.lock`;
|
|
260
253
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
254
|
+
acquireProtectionLock(lockFile);
|
|
255
|
+
try {
|
|
256
|
+
const existing = readProtection(caseRoot);
|
|
257
|
+
const next = mutate(existing);
|
|
258
|
+
if (!next) return { file, unprotected: existing.unprotected, resources: existing.resources };
|
|
259
|
+
const { unprotected, resources } = next;
|
|
260
|
+
const clean = (list, fallback) => [...new Set(
|
|
261
|
+
(Array.isArray(list) ? list : [...fallback])
|
|
262
|
+
.map((entry) => String(entry || '').replaceAll('\\', '/').trim())
|
|
263
|
+
.filter((entry) => entry && !entry.startsWith('../') && !path.isAbsolute(entry))
|
|
264
|
+
)].sort((a, b) => a.localeCompare(b, 'fr'));
|
|
265
|
+
// Une pièce ne peut être à la fois « espace de travail » et « ressource » :
|
|
266
|
+
// `resources` a priorité, on la retire donc de `unprotected`.
|
|
267
|
+
const resourcesList = clean(resources, existing.resources);
|
|
268
|
+
const resourceSet = new Set(resourcesList);
|
|
269
|
+
const unprotectedList = clean(unprotected, existing.unprotected).filter((key) => !resourceSet.has(key));
|
|
270
|
+
const temporaryFile = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
271
|
+
try {
|
|
272
|
+
fs.writeFileSync(
|
|
273
|
+
temporaryFile,
|
|
274
|
+
`${JSON.stringify({ version: 1, unprotected: unprotectedList, resources: resourcesList }, null, 2)}\n`,
|
|
275
|
+
'utf8'
|
|
276
|
+
);
|
|
277
|
+
fs.renameSync(temporaryFile, file);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
fs.rmSync(temporaryFile, { force: true });
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
return { file, unprotected: new Set(unprotectedList), resources: resourceSet };
|
|
283
|
+
} finally {
|
|
284
|
+
fs.rmSync(lockFile, { force: true });
|
|
285
|
+
}
|
|
267
286
|
}
|
|
268
287
|
|
|
269
288
|
/** Vrai pour un chemin recevable dans une des deux listes d'exceptions. */
|
|
@@ -367,9 +386,9 @@ function markdownCounterpart(absolutePath, caseRoot) {
|
|
|
367
386
|
* de naître au coffre-fort, elle ne pourrait plus se relire.
|
|
368
387
|
*
|
|
369
388
|
* Ne fait rien, et retourne pourquoi, quand le chemin n'est pas une pièce
|
|
370
|
-
* classable : hors dossier,
|
|
371
|
-
* scan PII (jamais déclassés), espace de travail
|
|
372
|
-
* inscrite. `resources` est prioritaire : une ressource n'est jamais
|
|
389
|
+
* classable : hors dossier, extension jamais protégée (seuls PDF et images le
|
|
390
|
+
* sont), dotfile, mapping ou scan PII (jamais déclassés), espace de travail
|
|
391
|
+
* OOXML implicite, ou clé déjà inscrite. `resources` est prioritaire : une ressource n'est jamais
|
|
373
392
|
* rétrogradée en espace de travail. Idempotent, ne lève jamais.
|
|
374
393
|
*/
|
|
375
394
|
function classifyAsWorkspace(absolutePath, caseRoot) {
|
|
@@ -378,12 +397,15 @@ function classifyAsWorkspace(absolutePath, caseRoot) {
|
|
|
378
397
|
if (isMappingFile(absolutePath)) return { classified: false, key: null, reason: 'mapping' };
|
|
379
398
|
const key = exceptionKey(absolutePath, caseRoot);
|
|
380
399
|
if (!key) return { classified: false, key: null, reason: 'non-classable' };
|
|
400
|
+
if (!PROTECTED_EXTENSIONS.has(path.extname(key).toLowerCase())) return { classified: false, key, reason: 'jamais protégée' };
|
|
381
401
|
if (isOoxmlWorkspacePath(absolutePath, caseRoot)) return { classified: false, key, reason: 'ooxml' };
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
402
|
+
let reason = null;
|
|
403
|
+
mutateProtection(caseRoot, ({ unprotected, resources }) => {
|
|
404
|
+
if (resources.has(key)) reason = 'ressource';
|
|
405
|
+
else if (unprotected.has(key)) reason = 'déjà classée';
|
|
406
|
+
return reason ? null : { unprotected: [...unprotected, key] };
|
|
407
|
+
});
|
|
408
|
+
return { classified: !reason, key, reason };
|
|
387
409
|
} catch (error) {
|
|
388
410
|
return { classified: false, key: null, reason: error?.message || 'erreur' };
|
|
389
411
|
}
|
|
@@ -395,7 +417,6 @@ module.exports = {
|
|
|
395
417
|
exceptionKey,
|
|
396
418
|
isMappingFile,
|
|
397
419
|
isSecretFile,
|
|
398
|
-
locateCase,
|
|
399
420
|
isProtectedFile,
|
|
400
421
|
isProtectionBypassed,
|
|
401
422
|
isOoxmlWorkspacePath,
|
|
@@ -21,19 +21,16 @@
|
|
|
21
21
|
import fs from 'node:fs';
|
|
22
22
|
import path from 'node:path';
|
|
23
23
|
import { createRequire } from 'node:module';
|
|
24
|
-
import {
|
|
24
|
+
import { readHookPayload, runHook, noop } from './lib/hook-io.mjs';
|
|
25
25
|
|
|
26
26
|
const require = createRequire(import.meta.url);
|
|
27
|
-
const {
|
|
28
|
-
const { caseHasMapping } = require('./lib/mapping.cjs');
|
|
27
|
+
const { locateProjectCase } = require('./lib/case-folders.cjs');
|
|
29
28
|
const {
|
|
30
29
|
isMappingFile,
|
|
31
30
|
isSecretFile,
|
|
32
31
|
isProtectedFile,
|
|
33
32
|
markdownCounterpart,
|
|
34
33
|
readProtection,
|
|
35
|
-
relativeKey,
|
|
36
|
-
READABLE_EXTENSIONS,
|
|
37
34
|
} = require('./lib/protection.cjs');
|
|
38
35
|
|
|
39
36
|
/** Au-delà, on ne cherche pas de chemin : une commande pareille n'en cite pas un. */
|
|
@@ -81,36 +78,6 @@ function secretReason(absolute) {
|
|
|
81
78
|
return `[PieceMaker] « ${path.basename(absolute)} » est un fichier de secrets d’environnement : il n’est jamais accessible à l’IA.`;
|
|
82
79
|
}
|
|
83
80
|
|
|
84
|
-
/**
|
|
85
|
-
* Refus « dossier non anonymisé ». Sans `mapping_default.json`, le proxy n'a
|
|
86
|
-
* rien à coder : lire une pièce livrerait des données personnelles en clair.
|
|
87
|
-
* On l'explique synthétiquement au modèle, avec l'action qui débloque la lecture.
|
|
88
|
-
*/
|
|
89
|
-
function missingMappingReason(caseName) {
|
|
90
|
-
return `[PieceMaker] « ${caseName} » est un dossier PieceMaker sans mapping d’anonymisation (mapping_default.json absent) : sa lecture est bloquée car son contenu n’est pas encore anonymisable et partirait en clair. Lancez l’anonymisation du dossier dans l’administration PieceMaker avant toute lecture.`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Surface lisible dont le contenu serait normalement codé à la lecture (`.md`,
|
|
95
|
-
* `.json` de pièce) : c'est précisément ce que `missingMappingReason` doit
|
|
96
|
-
* protéger quand aucun mapping n'existe. On écarte les originaux (déjà refusés
|
|
97
|
-
* par `isProtectedFile`), le mapping et les scans (refusés à part), la
|
|
98
|
-
* configuration du dossier (`.piecemaker/…`, dotfiles) et les consignes
|
|
99
|
-
* structurelles `CLAUDE.md`/`AGENTS.md` — jamais des pièces, et l'assistant du
|
|
100
|
-
* dossier doit pouvoir charger sa consigne même avant anonymisation.
|
|
101
|
-
*/
|
|
102
|
-
function isReadableCaseSurface(absolute, caseRoot) {
|
|
103
|
-
const stat = statSafe(absolute);
|
|
104
|
-
if (!stat || stat.isDirectory()) return false;
|
|
105
|
-
const key = relativeKey(absolute, caseRoot);
|
|
106
|
-
if (!key) return false;
|
|
107
|
-
if (key.split('/').some((segment) => segment.startsWith('.'))) return false;
|
|
108
|
-
if (!READABLE_EXTENSIONS.has(path.extname(key).toLowerCase())) return false;
|
|
109
|
-
if (isMappingFile(absolute)) return false;
|
|
110
|
-
const base = path.basename(key);
|
|
111
|
-
return base !== 'CLAUDE.md' && base !== 'AGENTS.md';
|
|
112
|
-
}
|
|
113
|
-
|
|
114
81
|
function statSafe(target) {
|
|
115
82
|
try {
|
|
116
83
|
return fs.statSync(target);
|
|
@@ -189,23 +156,19 @@ function caseRootHasMapping(located) {
|
|
|
189
156
|
async function main() {
|
|
190
157
|
const payload = await readHookPayload(2000);
|
|
191
158
|
if (!payload) return null;
|
|
192
|
-
const config = loadPieceMakerConfig();
|
|
193
159
|
|
|
194
160
|
const toolName = payload.tool_name;
|
|
195
161
|
const cwd = payload.cwd || process.cwd();
|
|
196
162
|
|
|
197
163
|
if (toolName === 'Bash') {
|
|
198
164
|
for (const candidate of commandPaths(payload.tool_input?.command, cwd)) {
|
|
199
|
-
const located =
|
|
165
|
+
const located = locateProjectCase(candidate);
|
|
200
166
|
if (!located) continue;
|
|
201
167
|
if (isMappingFile(candidate)) return deny(mappingReason(candidate));
|
|
202
168
|
if (isSecretFile(candidate)) return deny(secretReason(candidate));
|
|
203
169
|
if (isProtectedFile(candidate, located.caseRoot)) {
|
|
204
170
|
return deny(protectionReason(candidate, located.caseRoot));
|
|
205
171
|
}
|
|
206
|
-
if (isReadableCaseSurface(candidate, located.caseRoot) && !caseHasMapping(located.caseRoot)) {
|
|
207
|
-
return deny(missingMappingReason(located.caseName));
|
|
208
|
-
}
|
|
209
172
|
}
|
|
210
173
|
return null;
|
|
211
174
|
}
|
|
@@ -214,7 +177,7 @@ async function main() {
|
|
|
214
177
|
|
|
215
178
|
const target = absolutePath(payload.tool_input?.file_path || payload.tool_input?.path, cwd);
|
|
216
179
|
if (!target) return null;
|
|
217
|
-
const located =
|
|
180
|
+
const located = locateProjectCase(target);
|
|
218
181
|
if (!located) return null;
|
|
219
182
|
|
|
220
183
|
// Un répertoire n'est pas une pièce : `Grep`/`Glob` en visent un couramment,
|
|
@@ -227,20 +190,6 @@ async function main() {
|
|
|
227
190
|
return deny(protectionReason(target, located.caseRoot));
|
|
228
191
|
}
|
|
229
192
|
|
|
230
|
-
// Dossier PieceMaker sans mapping : la garantie « anonymisé à la lecture »
|
|
231
|
-
// n'existe pas encore. Lire une surface lisible (`.md`/`.json` de pièce) la
|
|
232
|
-
// ferait sortir en clair, et un `Grep` récursif à la racine en ferait autant
|
|
233
|
-
// sur tout le dossier (un `Glob`, qui ne ramène que des noms, reste permis).
|
|
234
|
-
// On calcule l'absence de mapping au plus tard, une fois les refus francs
|
|
235
|
-
// (mapping, original) écartés — c'est le seul chemin qui paie la lecture du
|
|
236
|
-
// mapping.
|
|
237
|
-
if (isReadableCaseSurface(target, located.caseRoot) && !caseHasMapping(located.caseRoot)) {
|
|
238
|
-
return deny(missingMappingReason(located.caseName));
|
|
239
|
-
}
|
|
240
|
-
if (toolName === 'Grep' && located.absolute === located.caseRoot && !caseHasMapping(located.caseRoot)) {
|
|
241
|
-
return deny(missingMappingReason(located.caseName));
|
|
242
|
-
}
|
|
243
|
-
|
|
244
193
|
// Un `Grep` récursif ramène le *contenu* du mapping ; un `Glob` n'en ramène
|
|
245
194
|
// que le nom, qui ne trahit rien — d'où la restriction à Grep ici.
|
|
246
195
|
if (toolName === 'Grep' && caseRootHasMapping(located)) {
|