@piecemaker-legal/piecemaker 1.0.6 → 1.0.7
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/{abnfDiagram-VCTEODGH-DF7LFtT-.js → abnfDiagram-VCTEODGH-CR3_ADBu.js} +1 -1
- package/dist/assets/{arc-xOmjWeVH.js → arc-b92imMmc.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-CESzNPwa.js → architectureDiagram-5GKGNRK7-Dfm5Epud.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-BjdN78Ad.js → blockDiagram-I7D4REHJ-B8kLLyAv.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-BcK9EDWT.js → c4Diagram-7LVT6UL2-BWSyAAWX.js} +1 -1
- package/dist/assets/channel-BbcW_Tkh.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-DGUN1JFP.js → chunk-2Q5K7J3B-Ci743mcC.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BG3BBcFr.js → chunk-5VM5RSS4-SdXpal6h.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-CLIOfrF5.js → chunk-F27PBJKO-Rof94ueX.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-D6lGkqij.js → chunk-IMKFNOWR-B3st6ECc.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BmF9vLBT.js → chunk-JWPE2WC7-alXmu5xU.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-C_0SR3ak.js → chunk-POPQ4Y6H-dMs1OCce.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-rT20Wfpp.js → chunk-SVP7TREG-BmM8FsmF.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-Cn2hkmcf.js → chunk-TICWLB2K-CL1LytYG.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-Dm_eP1_v.js → chunk-XXDRQBXY-7U9iRPT1.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-D9_YRZ7i.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-D9_YRZ7i.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-cJeeJsep.js → cose-bilkent-JH36ORCC-DFOMlhj2.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-DX2ePIXe.js → cynefin-OW5HDTMX-CDdJFd9d.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-BvWXN-RR.js → cynefinDiagram-5FMLGOSQ-DPyVNrMW.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-CkwPRhGR.js → dagre-GXQ25YYZ-B3bpHa9D.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-CBGNzYyv.js → diagram-S7CK7UJ4-DeFO2rXB.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-CiwCU0SJ.js → diagram-UQ7AKVKN-CbdGTOVf.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-Aekg4nTk.js → diagram-VSXAHHWV-DFfNQyMd.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-DPFP5K7r.js → diagram-VX7I27RA-DbNZ6_X3.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-BHJkxoey.js → diagram-Z3DM3KII-B8F-ZkNJ.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-2rV1P32T.js → ebnfDiagram-PWID7BFC-BfCCPPAP.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-C4BjyS_H.js → erDiagram-RLTQ6QDP-DcV-3Kzv.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-DL1TBlAG.js → flowDiagram-HODETNUW-BwSkJXCo.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-MKjvzCWz.js → ganttDiagram-EL5Y4UJY-BffHk77T.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-CS_oTGK5.js → gitGraphDiagram-WWUBYQGX-CQ1EWa74.js} +1 -1
- package/dist/assets/{index-XOGQDqU3.js → index-BqK33gaZ.js} +120 -120
- package/dist/assets/index-D_Dc_nQv.css +1 -0
- package/dist/assets/{infoDiagram-27XIBGKW-ihUr2GjN.js → infoDiagram-27XIBGKW-DqIaBXQn.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-D2YAnuir.js → ishikawaDiagram-5VMMS53U-HlhsaZgv.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-Bc2hSZGD.js → journeyDiagram-3NMN7TZE-BYO8-7rN.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-LRMGNonX.js → kanban-definition-UXKFOSKX-DXb8zHGT.js} +1 -1
- package/dist/assets/{layout-CcfWOlPV.js → layout-CREOVuEZ.js} +1 -1
- package/dist/assets/{linear-7JoS2gQO.js → linear-CVCrIRIY.js} +1 -1
- package/dist/assets/{mermaid.core-ijh_SsuN.js → mermaid.core-DFqeWtQR.js} +6 -6
- package/dist/assets/{mindmap-definition-YA3MSWOX-DCE4nzfF.js → mindmap-definition-YA3MSWOX-SUpXz333.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-SjaGa7lE.js → pegDiagram-XKGWAZYB-D-yWpve3.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-1xcRidSZ.js → pieDiagram-E7YTZNPT-BJX4Zp4Z.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-obHAHnYV.js → quadrantDiagram-AXDQQJYC-CHajheTu.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-BsCVcRZb.js → railroadDiagram-O6MQD6OU-DHJWzW5A.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-CUNkhvqb.js → requirementDiagram-BXWQKSXE-DigUIdso.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-DhezXMKm.js → sankeyDiagram-P5KCCOFB-CFUDDbA5.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-CL9VswXH.js → sequenceDiagram-WJ2MYXX4-6tSaXfYD.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-t0g9jiEz.js → sizeCapture-INFHLROL-CXASEDKe.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-BW-g-IOz.js → stateDiagram-D77RDMKH-DWTCAQfZ.js} +1 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-HDQF4MGA.js +1 -0
- package/dist/assets/{swimlanes-42K2YHIH-LsuyeBte.js → swimlanes-42K2YHIH-7-oz5r-e.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-CJbgQFt8.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-B0cFka1R.js → timeline-definition-24CTP7MA-DVH5g4ty.js} +1 -1
- package/dist/assets/{vendor-codemirror-C5qqu4R7.js → vendor-codemirror-CjW0lHR5.js} +5 -5
- package/dist/assets/{vennDiagram-4TSXK5OY-D3HNCDR3.js → vennDiagram-4TSXK5OY-CQvY_ALZ.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-DJ3Co8nb.js → wardleyDiagram-VM6X3IG4-BdlTJgEc.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-Cz5E9New.js → xychartDiagram-S5SC5T6Z-CYn2sV77.js} +1 -1
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +1 -9
- package/dist/generate-icons.js +47 -46
- package/dist/icons/icon-128x128.png +0 -0
- package/dist/icons/icon-128x128.svg +1 -12
- package/dist/icons/icon-144x144.png +0 -0
- package/dist/icons/icon-144x144.svg +1 -12
- package/dist/icons/icon-152x152.png +0 -0
- package/dist/icons/icon-152x152.svg +1 -12
- package/dist/icons/icon-192x192.png +0 -0
- package/dist/icons/icon-192x192.svg +1 -12
- package/dist/icons/icon-384x384.png +0 -0
- package/dist/icons/icon-384x384.svg +1 -12
- package/dist/icons/icon-512x512.png +0 -0
- package/dist/icons/icon-512x512.svg +1 -12
- package/dist/icons/icon-72x72.png +0 -0
- package/dist/icons/icon-72x72.svg +1 -12
- package/dist/icons/icon-96x96.png +0 -0
- package/dist/icons/icon-96x96.svg +1 -12
- package/dist/index.html +3 -3
- package/dist/logo-128.png +0 -0
- package/dist/logo-256.png +0 -0
- package/dist/logo-32.png +0 -0
- package/dist/logo-512.png +0 -0
- package/dist/logo-64.png +0 -0
- package/dist/logo-sources/logo-black.png +0 -0
- package/dist/logo-sources/logo-white.png +0 -0
- package/dist/logo.svg +1 -17
- package/dist/sw.js +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/institutional-terms.js +91 -0
- package/dist-server/plugins/piecemaker-dossier/src/institutional-terms.js.map +1 -0
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js +112 -10
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js.map +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js +5 -1
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js.map +1 -1
- package/dist-server/server/index.js +60 -26
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/database/repositories/projects.db.js +28 -6
- package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -1
- package/dist-server/server/modules/database/tests/projects.db.integration.test.js +58 -1
- package/dist-server/server/modules/database/tests/projects.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/plugins/plugins.routes.js +8 -1
- package/dist-server/server/modules/plugins/plugins.routes.js.map +1 -1
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +2 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
- package/dist-server/server/piecemaker/bodacc-search.js +191 -0
- package/dist-server/server/piecemaker/bodacc-search.js.map +1 -0
- package/dist-server/server/piecemaker/company-search.js +145 -0
- package/dist-server/server/piecemaker/company-search.js.map +1 -0
- package/dist-server/server/piecemaker/company-search.test.js +18 -0
- package/dist-server/server/piecemaker/company-search.test.js.map +1 -0
- package/dist-server/server/piecemaker/index.js +6 -2
- package/dist-server/server/piecemaker/index.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/index.js +9 -4
- package/dist-server/server/piecemaker/knowledge/index.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/pipeline.js +68 -58
- package/dist-server/server/piecemaker/knowledge/pipeline.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/routes.js +2 -4
- package/dist-server/server/piecemaker/knowledge/routes.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/scan-jobs.js +91 -0
- package/dist-server/server/piecemaker/knowledge/scan-jobs.js.map +1 -0
- package/dist-server/server/piecemaker/knowledge/service.js +18 -36
- package/dist-server/server/piecemaker/knowledge/service.js.map +1 -1
- package/dist-server/server/piecemaker/library/connector-installation.js +224 -0
- package/dist-server/server/piecemaker/library/connector-installation.js.map +1 -0
- package/dist-server/server/piecemaker/library/index.js +11 -1
- package/dist-server/server/piecemaker/library/index.js.map +1 -1
- package/dist-server/server/piecemaker/library/marketplace.js +102 -43
- package/dist-server/server/piecemaker/library/marketplace.js.map +1 -1
- package/dist-server/server/piecemaker/library/migrate.js +8 -1
- package/dist-server/server/piecemaker/library/migrate.js.map +1 -1
- package/dist-server/server/piecemaker/library/provider-agents.js +50 -0
- package/dist-server/server/piecemaker/library/provider-agents.js.map +1 -0
- package/dist-server/server/piecemaker/library/provider-connectors.js +80 -0
- package/dist-server/server/piecemaker/library/provider-connectors.js.map +1 -0
- package/dist-server/server/piecemaker/library/provider-skills.js +11 -2
- package/dist-server/server/piecemaker/library/provider-skills.js.map +1 -1
- package/dist-server/server/piecemaker/library/store.js +125 -35
- package/dist-server/server/piecemaker/library/store.js.map +1 -1
- package/dist-server/server/piecemaker/library/workspace-installation.js +53 -29
- package/dist-server/server/piecemaker/library/workspace-installation.js.map +1 -1
- package/dist-server/server/piecemaker/timesheet/service.js +4 -21
- package/dist-server/server/piecemaker/timesheet/service.js.map +1 -1
- package/package.json +3 -2
- package/scripts/piecemaker/cli/install-command.mjs +35 -9
- package/scripts/piecemaker/cli/lib/config.mjs +1 -0
- package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +78 -0
- package/scripts/piecemaker/cli/lib/plugins.mjs +2 -0
- package/scripts/piecemaker/cli/lib/pwa.mjs +46 -7
- package/scripts/piecemaker/cli/lib/services.mjs +65 -4
- package/scripts/piecemaker/cli/piecemaker-command.test.mjs +25 -0
- package/scripts/piecemaker/cli/piecemaker.mjs +45 -44
- package/scripts/piecemaker/cli/piecemaker.ps1 +8 -0
- package/scripts/piecemaker/cli/piecemaker.sh +6 -1
- package/server/index.ts +52 -23
- package/server/modules/database/repositories/projects.db.ts +28 -6
- package/server/modules/database/tests/projects.db.integration.test.ts +63 -1
- package/server/modules/plugins/plugins.routes.ts +8 -1
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +5 -0
- package/server/piecemaker/anonymizer/client-config.cjs +10 -2
- package/server/piecemaker/anonymizer/dictionary.cjs +6 -72
- package/server/piecemaker/bodacc-search.ts +205 -0
- package/server/piecemaker/company-search.test.ts +26 -0
- package/server/piecemaker/company-search.ts +183 -0
- package/server/piecemaker/index.ts +8 -2
- package/server/piecemaker/knowledge/index.ts +9 -4
- package/server/piecemaker/knowledge/pipeline.ts +89 -57
- package/server/piecemaker/knowledge/routes.ts +2 -4
- package/server/piecemaker/knowledge/scan-jobs.ts +111 -0
- package/server/piecemaker/knowledge/service.ts +18 -33
- package/server/piecemaker/library/connector-installation.ts +248 -0
- package/server/piecemaker/library/index.ts +5 -1
- package/server/piecemaker/library/marketplace.ts +104 -39
- package/server/piecemaker/library/migrate.ts +8 -1
- package/server/piecemaker/library/provider-agents.ts +49 -0
- package/server/piecemaker/library/provider-connectors.ts +89 -0
- package/server/piecemaker/library/provider-skills.ts +10 -1
- package/server/piecemaker/library/store.ts +129 -36
- package/server/piecemaker/library/workspace-installation.ts +54 -29
- package/server/piecemaker/router.cjs +2 -1
- package/server/piecemaker/timesheet/service.ts +4 -16
- package/server/piecemaker/vendor/installer/steps/03-python-gliner.mjs +0 -27
- package/server/piecemaker/vendor/installer/steps/14-mxc-sandbox.mjs +1 -1
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +2 -3
- package/server/piecemaker/vendor/piecemaker-plugin/skills/conversion-md/SKILL.md +3 -3
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +3 -10
- package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +135 -4
- package/server/piecemaker/vendor/websocket-server/process-group.cjs +95 -0
- package/server/piecemaker/vendor/websocket-server/scripts/__pycache__/scan_utils.cpython-312.pyc +0 -0
- package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +280 -149
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/model_config.cpython-312.pyc +0 -0
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/scanner_worker.cpython-312.pyc +0 -0
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/scanner_worker.py +35 -29
- package/server/piecemaker/vendor/websocket-server/scripts/requirements.txt +0 -5
- package/server/shared/types.ts +1 -0
- package/dist/assets/channel-Dz3eE5vU.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-BC1eKV2j.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-BC1eKV2j.js +0 -1
- package/dist/assets/index-DhwJpHCq.css +0 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-BXuT4zDG.js +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-B0eYrB4U.js +0 -8
- package/server/piecemaker/vendor/websocket-server/case-instructions.cjs +0 -111
- package/server/piecemaker/vendor/websocket-server/scripts/ADDRESS_ANONYMIZATION.md +0 -216
- package/server/piecemaker/vendor/websocket-server/scripts/DEDUPLICATION_CHANGES.md +0 -134
- package/server/piecemaker/vendor/websocket-server/scripts/GLINER2_README.md +0 -138
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/AUDIT_PERF_QUALITE.md +0 -246
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/MODELE_COREML.md +0 -62
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/coreml_runtime.cpython-312.pyc +0 -0
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/build_coreml.py +0 -118
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/coreml_runtime.py +0 -164
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/presidio-gliner.py +0 -479
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dictionnaire d'anonymisation du proxy PII.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* chokepoint réseau demande : un nom appartenant au dossier B doit rester
|
|
8
|
-
* protégé même s'il est tapé dans une conversation ouverte sur le dossier A.
|
|
9
|
-
* Un proxy par dossier ne saurait pas le faire.
|
|
4
|
+
* Le proxy charge le dictionnaire depuis SQLite (`sqlite-dictionary.cjs`,
|
|
5
|
+
* tables `piecemaker_mappings` / `piecemaker_nodes`). Ce module construit la
|
|
6
|
+
* forme exploitable (`buildDictionary`) et applique la substitution.
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Rechargement à chaud sur `mtime` : une conversion qui ré-écrit le mapping est
|
|
16
|
-
* prise en compte sans redémarrer CloudCLI.
|
|
8
|
+
* Un nom appartenant au dossier B doit rester protégé même s'il est tapé dans
|
|
9
|
+
* une conversation ouverte sur le dossier A. Un proxy par dossier ne saurait
|
|
10
|
+
* pas le faire.
|
|
17
11
|
*/
|
|
18
|
-
const fs = require('node:fs');
|
|
19
|
-
const path = require('node:path');
|
|
20
|
-
|
|
21
12
|
const {
|
|
22
13
|
applyMapping,
|
|
23
14
|
buildEntityRegex,
|
|
@@ -25,8 +16,6 @@ const {
|
|
|
25
16
|
MIN_ENTITY_LENGTH,
|
|
26
17
|
} = require('../vendor/piecemaker-plugin/scripts/lib/substitution.cjs');
|
|
27
18
|
|
|
28
|
-
const CENTRAL_FILENAME = 'central-mapping.json';
|
|
29
|
-
|
|
30
19
|
/** Un dictionnaire vide se comporte comme un proxy transparent. */
|
|
31
20
|
const EMPTY = Object.freeze({
|
|
32
21
|
version: 0,
|
|
@@ -41,14 +30,6 @@ const EMPTY = Object.freeze({
|
|
|
41
30
|
empty: true,
|
|
42
31
|
});
|
|
43
32
|
|
|
44
|
-
function readJson(file) {
|
|
45
|
-
try {
|
|
46
|
-
return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^/, ''));
|
|
47
|
-
} catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
33
|
function plainObject(value) {
|
|
53
34
|
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
54
35
|
}
|
|
@@ -66,7 +47,6 @@ function plainObject(value) {
|
|
|
66
47
|
* moteur refuse de substituer (`buildEntityRegex` nul : ponctuation pure, deux
|
|
67
48
|
* caractères non acronymiques) est écartée des deux listes : surligner ce qui
|
|
68
49
|
* ne part pas codé serait un mensonge à l'écran.
|
|
69
|
-
*
|
|
70
50
|
*/
|
|
71
51
|
function buildDictionary(document, stamp) {
|
|
72
52
|
const mapping = plainObject(document?.mapping);
|
|
@@ -107,50 +87,6 @@ function buildDictionary(document, stamp) {
|
|
|
107
87
|
};
|
|
108
88
|
}
|
|
109
89
|
|
|
110
|
-
/**
|
|
111
|
-
* Chargeur à cache : relit le fichier uniquement quand sa signature
|
|
112
|
-
* (mtime + taille) change. `version` s'incrémente à chaque rechargement effectif,
|
|
113
|
-
* ce qui sert de clé de cache aux consommateurs (client compris).
|
|
114
|
-
*/
|
|
115
|
-
function createDictionaryLoader({ homeDir }) {
|
|
116
|
-
const file = path.join(homeDir, CENTRAL_FILENAME);
|
|
117
|
-
let signature = null;
|
|
118
|
-
let current = EMPTY;
|
|
119
|
-
let generation = 0;
|
|
120
|
-
|
|
121
|
-
function statSignature() {
|
|
122
|
-
try {
|
|
123
|
-
const stats = fs.statSync(file);
|
|
124
|
-
return `${stats.mtimeMs}:${stats.size}`;
|
|
125
|
-
} catch {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function load(force = false) {
|
|
131
|
-
const next = statSignature();
|
|
132
|
-
if (!force && next === signature) return current;
|
|
133
|
-
signature = next;
|
|
134
|
-
if (next === null) {
|
|
135
|
-
current = EMPTY;
|
|
136
|
-
return current;
|
|
137
|
-
}
|
|
138
|
-
const document = readJson(file);
|
|
139
|
-
generation += 1;
|
|
140
|
-
current = document ? buildDictionary(document, generation) : EMPTY;
|
|
141
|
-
return current;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
file,
|
|
146
|
-
/** Le dictionnaire à jour, relu si le fichier a bougé. */
|
|
147
|
-
get: () => load(false),
|
|
148
|
-
/** Relecture inconditionnelle, pour le bouton « rafraîchir ». */
|
|
149
|
-
refresh: () => load(true),
|
|
150
|
-
exists: () => statSignature() !== null,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
90
|
/** Nom réel → code, sur tout ce qui part vers l'API. */
|
|
155
91
|
function anonymize(text, dictionary) {
|
|
156
92
|
return dictionary.empty ? text : applyMapping(text, dictionary.mapping);
|
|
@@ -162,10 +98,8 @@ function deanonymize(text, dictionary) {
|
|
|
162
98
|
}
|
|
163
99
|
|
|
164
100
|
module.exports = {
|
|
165
|
-
CENTRAL_FILENAME,
|
|
166
101
|
EMPTY_DICTIONARY: EMPTY,
|
|
167
102
|
anonymize,
|
|
168
103
|
buildDictionary,
|
|
169
|
-
createDictionaryLoader,
|
|
170
104
|
deanonymize,
|
|
171
105
|
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
import express, { type Request, type Response } from 'express';
|
|
7
|
+
|
|
8
|
+
type McpContent = {
|
|
9
|
+
type?: string;
|
|
10
|
+
text?: string;
|
|
11
|
+
resource?: { text?: string };
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type McpMessage = {
|
|
15
|
+
id?: number;
|
|
16
|
+
error?: { message?: string };
|
|
17
|
+
result?: { content?: McpContent[]; isError?: boolean };
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type BodaccAnnouncement = {
|
|
21
|
+
id: string;
|
|
22
|
+
datePublication: string;
|
|
23
|
+
typeAvis: string;
|
|
24
|
+
familleAvis: string;
|
|
25
|
+
commercant: string;
|
|
26
|
+
ville: string;
|
|
27
|
+
tribunal: string;
|
|
28
|
+
jugement: string;
|
|
29
|
+
acte: string;
|
|
30
|
+
url: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type BodaccSearchResult = {
|
|
34
|
+
siren: string;
|
|
35
|
+
total: number;
|
|
36
|
+
alertes: string[];
|
|
37
|
+
annonces: BodaccAnnouncement[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const BODACC_FAMILY_CODES = new Set([
|
|
41
|
+
'dpc',
|
|
42
|
+
'modification',
|
|
43
|
+
'creation',
|
|
44
|
+
'radiation',
|
|
45
|
+
'collective',
|
|
46
|
+
'vente',
|
|
47
|
+
'immatriculation',
|
|
48
|
+
'divers',
|
|
49
|
+
'conciliation',
|
|
50
|
+
'retablissement_professionnel',
|
|
51
|
+
'inconnue',
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const MCP_TIMEOUT_MS = 45_000;
|
|
55
|
+
|
|
56
|
+
function collectLauncherCandidates(): string[] {
|
|
57
|
+
const candidates: string[] = [];
|
|
58
|
+
const addRoot = (root: string | undefined) => {
|
|
59
|
+
if (root) candidates.push(path.join(root, 'scripts', 'launcher.py'));
|
|
60
|
+
};
|
|
61
|
+
addRoot(process.env.LEGIFRANCE_MCP_ROOT);
|
|
62
|
+
if (process.env.LEGIFRANCE_MCP_LAUNCHER) candidates.push(process.env.LEGIFRANCE_MCP_LAUNCHER);
|
|
63
|
+
const pluginConfigPath = path.join(os.homedir(), '.claude', 'plugins', 'installed_plugins.json');
|
|
64
|
+
if (existsSync(pluginConfigPath)) {
|
|
65
|
+
try {
|
|
66
|
+
const configuration = JSON.parse(readFileSync(pluginConfigPath, 'utf8')) as { plugins?: Record<string, Array<{ installPath?: string }>> };
|
|
67
|
+
for (const [plugin, installations] of Object.entries(configuration.plugins || {})) {
|
|
68
|
+
if (!plugin.includes('mcp-legifrance')) continue;
|
|
69
|
+
for (const installation of installations) addRoot(installation.installPath);
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
return candidates;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const versionsRoot = path.join(os.homedir(), '.claude', 'plugins', 'cache', 'mcp-legifrance', 'piecemaker');
|
|
76
|
+
if (existsSync(versionsRoot)) {
|
|
77
|
+
for (const version of readdirSync(versionsRoot).sort().reverse()) addRoot(path.join(versionsRoot, version));
|
|
78
|
+
}
|
|
79
|
+
return [...new Set(candidates)];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolveLauncher(): string {
|
|
83
|
+
const launcher = collectLauncherCandidates().find((candidate) => existsSync(candidate));
|
|
84
|
+
if (!launcher) throw new Error('Le lanceur du PERS_MORALE_1 Légifrance est introuvable.');
|
|
85
|
+
return launcher;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function mcpCall(tool: string, argumentsValue: Record<string, string>): Promise<McpMessage> {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const processHandle = spawn(process.env.PYTHON_PATH || 'python3', [resolveLauncher()], {
|
|
91
|
+
env: { ...process.env, PYTHONDONTWRITEBYTECODE: '1' },
|
|
92
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
93
|
+
});
|
|
94
|
+
let stdout = '';
|
|
95
|
+
let settled = false;
|
|
96
|
+
const timeout = setTimeout(() => {
|
|
97
|
+
processHandle.kill('SIGTERM');
|
|
98
|
+
reject(new Error('Le PERS_MORALE_1 Légifrance n’a pas répondu à temps.'));
|
|
99
|
+
}, MCP_TIMEOUT_MS);
|
|
100
|
+
const finish = (callback: () => void) => {
|
|
101
|
+
if (settled) return;
|
|
102
|
+
settled = true;
|
|
103
|
+
clearTimeout(timeout);
|
|
104
|
+
callback();
|
|
105
|
+
};
|
|
106
|
+
processHandle.stdout.on('data', (chunk: Buffer) => {
|
|
107
|
+
stdout += chunk.toString();
|
|
108
|
+
for (const line of stdout.split(/\r?\n/).slice(0, -1)) {
|
|
109
|
+
try {
|
|
110
|
+
const message = JSON.parse(line) as McpMessage;
|
|
111
|
+
if (message.id === 3) finish(() => resolve(message));
|
|
112
|
+
} catch {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
stdout = stdout.split(/\r?\n/).at(-1) || '';
|
|
117
|
+
});
|
|
118
|
+
processHandle.once('error', (error) => finish(() => reject(error)));
|
|
119
|
+
processHandle.once('close', (code) => {
|
|
120
|
+
if (!settled) finish(() => reject(new Error(code === null ? 'Le processus Légifrance a été interrompu.' : `Le processus Légifrance s’est arrêté (${code}).`)));
|
|
121
|
+
});
|
|
122
|
+
processHandle.stdin.write(`${JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'piecemaker-dossier', version: '0.1.0' } } })}\n`);
|
|
123
|
+
processHandle.stdin.write('{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}\n');
|
|
124
|
+
processHandle.stdin.write(`${JSON.stringify({ jsonrpc: '2.0', id: 3, method: 'tools/call', params: { name: tool, arguments: argumentsValue } })}\n`);
|
|
125
|
+
processHandle.stdin.end();
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function normalizedSiren(siren: string, siret: string): string {
|
|
130
|
+
const sirenDigits = siren.replace(/\D/g, '');
|
|
131
|
+
if (sirenDigits.length === 9) return sirenDigits;
|
|
132
|
+
const siretDigits = siret.replace(/\D/g, '');
|
|
133
|
+
if (siretDigits.length === 14) return siretDigits.slice(0, 9);
|
|
134
|
+
if (sirenDigits.length === 14) return sirenDigits.slice(0, 9);
|
|
135
|
+
throw new Error('Un SIREN à 9 chiffres ou un SIRET à 14 chiffres est requis.');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function resultContent(message: McpMessage): McpContent[] {
|
|
139
|
+
if (message.error?.message) throw new Error(message.error.message);
|
|
140
|
+
if (message.result?.isError) throw new Error(message.result.content?.find((entry) => entry.type === 'text')?.text || 'Recherche BODACC impossible.');
|
|
141
|
+
return message.result?.content || [];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function readHistory(content: McpContent[], siren: string): BodaccSearchResult {
|
|
145
|
+
const resourceText = content.find((entry) => entry.type === 'resource')?.resource?.text;
|
|
146
|
+
if (!resourceText) throw new Error('La réponse BODACC ne contient pas de données exploitables.');
|
|
147
|
+
const history = JSON.parse(resourceText) as { siren?: string; total_annonces?: number; alertes?: string[]; categories?: Record<string, Array<Record<string, unknown>>> };
|
|
148
|
+
const annonces = Object.values(history.categories || {}).flat().map((annonce) => ({
|
|
149
|
+
id: String(annonce.id || ''),
|
|
150
|
+
datePublication: String(annonce.date_parution || annonce.date || ''),
|
|
151
|
+
typeAvis: String(annonce.type_avis || annonce.type || ''),
|
|
152
|
+
familleAvis: String(annonce.famille_avis || ''),
|
|
153
|
+
commercant: String(annonce.commercant || ''),
|
|
154
|
+
ville: String(annonce.ville || ''),
|
|
155
|
+
tribunal: String(annonce.tribunal || ''),
|
|
156
|
+
jugement: String(annonce.jugement || ''),
|
|
157
|
+
acte: String(annonce.acte || ''),
|
|
158
|
+
url: String(annonce.url || ''),
|
|
159
|
+
}));
|
|
160
|
+
return { siren: history.siren || siren, total: Number(history.total_annonces || annonces.length), alertes: history.alertes || [], annonces };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function familyCode(announcement: BodaccAnnouncement): string {
|
|
164
|
+
const family = announcement.familleAvis.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLocaleLowerCase();
|
|
165
|
+
if (family.includes('depot') && family.includes('compte')) return 'dpc';
|
|
166
|
+
if (family.includes('modification')) return 'modification';
|
|
167
|
+
if (family.includes('creation')) return 'creation';
|
|
168
|
+
if (family.includes('radiation')) return 'radiation';
|
|
169
|
+
if (family.includes('conciliation')) return 'conciliation';
|
|
170
|
+
if (family.includes('retablissement professionnel')) return 'retablissement_professionnel';
|
|
171
|
+
if (family.includes('procedure') && family.includes('collective')) return 'collective';
|
|
172
|
+
if (family.includes('vente') || family.includes('cession')) return 'vente';
|
|
173
|
+
if (family.includes('immatriculation')) return 'immatriculation';
|
|
174
|
+
if (family.includes('divers')) return 'divers';
|
|
175
|
+
return 'inconnue';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function filterFamilies(result: BodaccSearchResult, requestedFamilies: string[] | null): BodaccSearchResult {
|
|
179
|
+
if (requestedFamilies === null) return result;
|
|
180
|
+
const families = [...new Set(requestedFamilies.filter((family) => BODACC_FAMILY_CODES.has(family)))];
|
|
181
|
+
if (families.length === BODACC_FAMILY_CODES.size) return result;
|
|
182
|
+
const annonces = result.annonces.filter((announcement) => families.includes(familyCode(announcement)));
|
|
183
|
+
return { ...result, total: annonces.length, annonces, alertes: result.alertes.filter((alert) => (alert.toLocaleLowerCase().includes('procedure') && families.includes('collective')) || (alert.toLocaleLowerCase().includes('radiation') && families.includes('radiation'))) };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function searchBodacc(siren: string, siret: string, requestedFamilies: string[] | null): Promise<BodaccSearchResult> {
|
|
187
|
+
const normalized = normalizedSiren(siren, siret);
|
|
188
|
+
const response = await mcpCall('Tracking_BODACC', { siren: normalized, type_recherche: 'historique' });
|
|
189
|
+
return filterFamilies(readHistory(resultContent(response), normalized), requestedFamilies);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function createBodaccSearchRouter() {
|
|
193
|
+
const router = express.Router();
|
|
194
|
+
router.post('/bodacc-search', async (request: Request, response: Response) => {
|
|
195
|
+
const siren = typeof request.body?.siren === 'string' ? request.body.siren : '';
|
|
196
|
+
const siret = typeof request.body?.siret === 'string' ? request.body.siret : '';
|
|
197
|
+
const families = Array.isArray(request.body?.families) ? request.body.families.filter((family: unknown): family is string => typeof family === 'string') : null;
|
|
198
|
+
try {
|
|
199
|
+
response.json(await searchBodacc(siren, siret, families));
|
|
200
|
+
} catch (error) {
|
|
201
|
+
response.status(502).json({ error: error instanceof Error ? error.message : 'Recherche BODACC impossible.' });
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
return router;
|
|
205
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { parseSearchLine } from './company-search.js';
|
|
5
|
+
|
|
6
|
+
test('company search accepts registry results without a legal form', () => {
|
|
7
|
+
assert.deepEqual(
|
|
8
|
+
parseSearchLine('- GENSIGHT BIOLOGICS | SIREN 751164757 | en activité | NAF 72.19Z | 74 RUE DU FBG ST ANTOINE 75012 PARIS 12'),
|
|
9
|
+
{
|
|
10
|
+
name: 'GENSIGHT BIOLOGICS',
|
|
11
|
+
siren: '751164757',
|
|
12
|
+
summary: 'en activité · NAF 72.19Z · 74 RUE DU FBG ST ANTOINE 75012 PARIS 12',
|
|
13
|
+
},
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('company search preserves a legal form in registry results', () => {
|
|
18
|
+
assert.deepEqual(
|
|
19
|
+
parseSearchLine('- GENSIGHT BIOLOGICS (SA) | SIREN 751164757 | en activité | NAF 72.19Z | 74 RUE DU FBG ST ANTOINE 75012 PARIS 12'),
|
|
20
|
+
{
|
|
21
|
+
name: 'GENSIGHT BIOLOGICS',
|
|
22
|
+
siren: '751164757',
|
|
23
|
+
summary: 'SA · en activité · NAF 72.19Z · 74 RUE DU FBG ST ANTOINE 75012 PARIS 12',
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import express, { type Request, type Response } from 'express';
|
|
2
|
+
|
|
3
|
+
const REGISTRE_PUBLIC_MCP_URL = 'https://registre-public.com/api/mcp';
|
|
4
|
+
const MCP_PROTOCOL_VERSION = '2025-03-26';
|
|
5
|
+
const MCP_TIMEOUT_MS = 20_000;
|
|
6
|
+
|
|
7
|
+
type JsonRpcPayload = {
|
|
8
|
+
result?: {
|
|
9
|
+
content?: Array<{ type?: string; text?: string }>;
|
|
10
|
+
isError?: boolean;
|
|
11
|
+
};
|
|
12
|
+
error?: { message?: string };
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type CompanyDirector = { name: string; role: string };
|
|
16
|
+
|
|
17
|
+
export type CompanySearchFields = {
|
|
18
|
+
legalName: string;
|
|
19
|
+
legalForm: string;
|
|
20
|
+
status: string;
|
|
21
|
+
siren: string;
|
|
22
|
+
siret: string;
|
|
23
|
+
vat: string;
|
|
24
|
+
legalFormCode: string;
|
|
25
|
+
naf: string;
|
|
26
|
+
creationDate: string;
|
|
27
|
+
category: string;
|
|
28
|
+
address: string;
|
|
29
|
+
directors: CompanyDirector[];
|
|
30
|
+
finances: string[];
|
|
31
|
+
source: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type CompanySearchResult = {
|
|
35
|
+
name: string;
|
|
36
|
+
siren: string;
|
|
37
|
+
summary: string;
|
|
38
|
+
url: string;
|
|
39
|
+
details: string;
|
|
40
|
+
fields: CompanySearchFields;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function parseMcpPayload(body: string): JsonRpcPayload {
|
|
44
|
+
const trimmed = body.trim();
|
|
45
|
+
if (trimmed.startsWith('{')) return JSON.parse(trimmed) as JsonRpcPayload;
|
|
46
|
+
const payloads = trimmed
|
|
47
|
+
.split(/\r?\n/)
|
|
48
|
+
.filter((line) => line.startsWith('data:'))
|
|
49
|
+
.map((line) => line.slice(5).trim())
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.map((line) => JSON.parse(line) as JsonRpcPayload);
|
|
52
|
+
const payload = payloads.at(-1);
|
|
53
|
+
if (!payload) throw new Error('Réponse PERS_MORALE_1 invalide.');
|
|
54
|
+
return payload;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function mcpRequest(id: number, method: string, params: Record<string, unknown>, sessionId?: string): Promise<{ payload: JsonRpcPayload; sessionId?: string }> {
|
|
58
|
+
const response = await fetch(REGISTRE_PUBLIC_MCP_URL, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
Accept: 'application/json, text/event-stream',
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
...(sessionId ? { 'Mcp-Session-Id': sessionId } : {}),
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({ jsonrpc: '2.0', id, method, params }),
|
|
66
|
+
signal: AbortSignal.timeout(MCP_TIMEOUT_MS),
|
|
67
|
+
});
|
|
68
|
+
const body = await response.text();
|
|
69
|
+
if (!response.ok) throw new Error(`Registre Public a répondu ${response.status}.`);
|
|
70
|
+
const payload = parseMcpPayload(body);
|
|
71
|
+
if (payload.error?.message) throw new Error(payload.error.message);
|
|
72
|
+
return { payload, sessionId: response.headers.get('mcp-session-id') || sessionId };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function mcpNotification(method: string, params: Record<string, unknown>, sessionId?: string): Promise<void> {
|
|
76
|
+
const response = await fetch(REGISTRE_PUBLIC_MCP_URL, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: {
|
|
79
|
+
Accept: 'application/json, text/event-stream',
|
|
80
|
+
'Content-Type': 'application/json',
|
|
81
|
+
...(sessionId ? { 'Mcp-Session-Id': sessionId } : {}),
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({ jsonrpc: '2.0', method, params }),
|
|
84
|
+
signal: AbortSignal.timeout(MCP_TIMEOUT_MS),
|
|
85
|
+
});
|
|
86
|
+
if (!response.ok) throw new Error(`Registre Public a répondu ${response.status}.`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function resultText(payload: JsonRpcPayload): string {
|
|
90
|
+
const text = payload.result?.content?.filter((entry) => entry.type === 'text' && typeof entry.text === 'string').map((entry) => entry.text).join('\n').trim();
|
|
91
|
+
if (!text || payload.result?.isError) throw new Error('Registre Public n’a renvoyé aucun résultat exploitable.');
|
|
92
|
+
return text;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function lineValue(text: string, label: string): string {
|
|
96
|
+
const line = text.split('\n').find((entry) => entry.startsWith(label));
|
|
97
|
+
return line ? line.slice(label.length).trim() : '';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function parseSearchLine(line: string): { name: string; siren: string; summary: string } | null {
|
|
101
|
+
const match = /^-\s+(.+?)(?:\s+\(([^)]+)\))?\s+\|\s+SIREN\s+(\d{9})\s+\|\s+(.+?)\s+\|\s+NAF\s+([^|]+)\s+\|\s+(.+)$/.exec(line.trim());
|
|
102
|
+
if (!match) return null;
|
|
103
|
+
return { name: match[1].trim(), siren: match[3], summary: [match[2]?.trim(), match[4].trim(), `NAF ${match[5].trim()}`, match[6].trim()].filter(Boolean).join(' · ') };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function parseDirectors(text: string): CompanyDirector[] {
|
|
107
|
+
const start = text.indexOf('Dirigeants:');
|
|
108
|
+
if (start < 0) return [];
|
|
109
|
+
const block = text.slice(start + 'Dirigeants:'.length).split(/\n(?=Finances\b|$)/)[0];
|
|
110
|
+
return block.split('\n').map((line) => /^-\s+(.+?)\s+\(([^)]+)\)$/.exec(line.trim())).filter((match): match is RegExpExecArray => Boolean(match)).map((match) => ({ name: match[1].trim(), role: match[2].trim() }));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function parseCompanyDetails(text: string, fallback: { name: string; siren: string }): CompanySearchFields {
|
|
114
|
+
const heading = /^#\s+(.+?)\s+\(([^)]+)\)\s+\(([^)]+)\)/m.exec(text);
|
|
115
|
+
const formAndActivity = lineValue(text, 'Forme juridique (code) :');
|
|
116
|
+
const [legalFormCode, nafPart] = formAndActivity.split('|').map((value) => value.trim());
|
|
117
|
+
const creation = lineValue(text, 'Création :');
|
|
118
|
+
const [creationDate, categoryPart] = creation.split('|').map((value) => value.trim());
|
|
119
|
+
return {
|
|
120
|
+
legalName: heading?.[1]?.trim() || fallback.name,
|
|
121
|
+
legalForm: '',
|
|
122
|
+
status: heading?.[3]?.trim() || '',
|
|
123
|
+
siren: lineValue(text, 'SIREN :').split('|')[0].trim() || fallback.siren,
|
|
124
|
+
siret: (lineValue(text, 'SIREN :').match(/SIRET siège\s*:\s*([^|]+)/)?.[1] || '').trim(),
|
|
125
|
+
vat: lineValue(text, 'N° TVA intracommunautaire :'),
|
|
126
|
+
legalFormCode: legalFormCode || '',
|
|
127
|
+
naf: nafPart?.replace(/^NAF\s*:\s*/, '') || '',
|
|
128
|
+
creationDate: creationDate || '',
|
|
129
|
+
category: categoryPart?.replace(/^Catégorie\s*:\s*/, '') || '',
|
|
130
|
+
address: lineValue(text, 'Adresse du siège :'),
|
|
131
|
+
directors: parseDirectors(text),
|
|
132
|
+
finances: text.split('\n').filter((line) => line.startsWith('Finances ')).map((line) => line.trim()),
|
|
133
|
+
source: lineValue(text, 'Fiche web :'),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function callTool(tool: string, args: Record<string, unknown>): Promise<string> {
|
|
138
|
+
let sessionId: string | undefined;
|
|
139
|
+
const initialized = await mcpRequest(1, 'initialize', {
|
|
140
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
141
|
+
capabilities: {},
|
|
142
|
+
clientInfo: { name: 'piecemaker-dossier', version: '0.1.0' },
|
|
143
|
+
});
|
|
144
|
+
sessionId = initialized.sessionId;
|
|
145
|
+
await mcpNotification('notifications/initialized', {}, sessionId);
|
|
146
|
+
const result = await mcpRequest(2, 'tools/call', { name: tool, arguments: args }, sessionId);
|
|
147
|
+
return resultText(result.payload);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function searchCompanies(query: string): Promise<CompanySearchResult[]> {
|
|
151
|
+
const searchText = await callTool('rechercher_entreprise', { query });
|
|
152
|
+
const candidates = searchText.split('\n').map(parseSearchLine).filter((entry): entry is { name: string; siren: string; summary: string } => Boolean(entry));
|
|
153
|
+
const results = await Promise.all(candidates.map(async (candidate) => {
|
|
154
|
+
const details = await callTool('fiche_entreprise', { siren: candidate.siren });
|
|
155
|
+
const fields = parseCompanyDetails(details, candidate);
|
|
156
|
+
return {
|
|
157
|
+
name: fields.legalName || candidate.name,
|
|
158
|
+
siren: fields.siren || candidate.siren,
|
|
159
|
+
summary: candidate.summary,
|
|
160
|
+
url: fields.source,
|
|
161
|
+
details,
|
|
162
|
+
fields,
|
|
163
|
+
};
|
|
164
|
+
}));
|
|
165
|
+
return results;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function createCompanySearchRouter() {
|
|
169
|
+
const router = express.Router();
|
|
170
|
+
router.post('/company-search', async (request: Request, response: Response) => {
|
|
171
|
+
const query = typeof request.body?.query === 'string' ? request.body.query.trim().slice(0, 200) : '';
|
|
172
|
+
if (!query) {
|
|
173
|
+
response.status(400).json({ error: 'Un nom ou un SIREN est requis.' });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
response.json({ query, results: await searchCompanies(query) });
|
|
178
|
+
} catch (error) {
|
|
179
|
+
response.status(502).json({ error: error instanceof Error ? error.message : 'Recherche Registre Public impossible.' });
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
return router;
|
|
183
|
+
}
|
|
@@ -6,6 +6,7 @@ import type { Router } from 'express';
|
|
|
6
6
|
import { providerRuntimeService, sessionsService } from '@/modules/providers/index.js';
|
|
7
7
|
import { findApplicationRoot, getModuleDirectory } from '@/shared/utils.js';
|
|
8
8
|
|
|
9
|
+
|
|
9
10
|
import { startRequiredAnonymizer } from './anonymizer/lifecycle.js';
|
|
10
11
|
import { createCitationStore } from './harness/citation-store.js';
|
|
11
12
|
import { installChatCitationHarness } from './harness/chat-harness.js';
|
|
@@ -13,6 +14,8 @@ import { createCitationsRouter } from './harness/citations.routes.js';
|
|
|
13
14
|
import { startLibraryBackend, installLibraryRuntime } from './library/index.js';
|
|
14
15
|
import { createKnowledgeBackend } from './knowledge/index.js';
|
|
15
16
|
import { createTimesheetBackend } from './timesheet/index.js';
|
|
17
|
+
import { createCompanySearchRouter } from './company-search.js';
|
|
18
|
+
import { createBodaccSearchRouter } from './bodacc-search.js';
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Point d'entrée PieceMaker. Les modules de `server/piecemaker/vendor/` sont du
|
|
@@ -35,11 +38,12 @@ type PieceMakerRuntimeStatus = {
|
|
|
35
38
|
type PieceMakerVendorModule = {
|
|
36
39
|
createPieceMakerRouter(options?: { getRuntimeStatus?: () => PieceMakerRuntimeStatus; anonymizer?: unknown }): Router;
|
|
37
40
|
piecemakerHome(): string;
|
|
41
|
+
stopOriginalsJobs(): Promise<void>;
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
const vendor = createRequire(import.meta.url)(routerPath) as PieceMakerVendorModule;
|
|
41
45
|
|
|
42
|
-
export const { piecemakerHome } = vendor;
|
|
46
|
+
export const { piecemakerHome, stopOriginalsJobs } = vendor;
|
|
43
47
|
const { createAnonymizerService } = createRequire(import.meta.url)(path.join(applicationRoot, 'server/piecemaker/anonymizer/service.cjs'));
|
|
44
48
|
const anonymizer = createAnonymizerService({ homeDir: piecemakerHome(), required: true });
|
|
45
49
|
const ensureProxy = await startRequiredAnonymizer(anonymizer);
|
|
@@ -56,7 +60,9 @@ export function createPieceMakerRouter(options: { getRuntimeStatus?: () => Piece
|
|
|
56
60
|
try { sessionsService.getSessionDetailsById(id); return true; } catch { return false; }
|
|
57
61
|
}));
|
|
58
62
|
router.use(timesheet);
|
|
59
|
-
router.use(knowledge);
|
|
63
|
+
router.use(knowledge.router);
|
|
64
|
+
router.use(createCompanySearchRouter());
|
|
65
|
+
router.use(createBodaccSearchRouter());
|
|
60
66
|
return router;
|
|
61
67
|
}
|
|
62
68
|
export type { PieceMakerRuntimeStatus };
|
|
@@ -3,6 +3,7 @@ import express from 'express';
|
|
|
3
3
|
import { getConnection, projectsDb } from '@/modules/database/index.js';
|
|
4
4
|
|
|
5
5
|
import { KnowledgeStore } from '../../../plugins/piecemaker-dossier/src/knowledge.js';
|
|
6
|
+
|
|
6
7
|
import { createKnowledgePipeline } from './pipeline.js';
|
|
7
8
|
import { createKnowledgeRouter } from './routes.js';
|
|
8
9
|
import { createKnowledgeService } from './service.js';
|
|
@@ -10,13 +11,17 @@ import { createKnowledgeService } from './service.js';
|
|
|
10
11
|
export function createKnowledgeBackend(applicationRoot: string) {
|
|
11
12
|
const router = express.Router();
|
|
12
13
|
let knowledgeRouter: express.Router | null = null;
|
|
14
|
+
let store: KnowledgeStore | null = null;
|
|
15
|
+
const getStore = () => {
|
|
16
|
+
if (!store) store = new KnowledgeStore(getConnection());
|
|
17
|
+
return store;
|
|
18
|
+
};
|
|
13
19
|
router.use((request, response, next) => {
|
|
14
20
|
if (!knowledgeRouter) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
knowledgeRouter = createKnowledgeRouter(createKnowledgeService(store, projectsDb, pipeline));
|
|
21
|
+
const pipeline = createKnowledgePipeline({ applicationRoot, projects: projectsDb, store: getStore() });
|
|
22
|
+
knowledgeRouter = createKnowledgeRouter(createKnowledgeService(getStore(), projectsDb, pipeline));
|
|
18
23
|
}
|
|
19
24
|
knowledgeRouter(request, response, next);
|
|
20
25
|
});
|
|
21
|
-
return router;
|
|
26
|
+
return { router };
|
|
22
27
|
}
|