@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.
Files changed (209) hide show
  1. package/dist/assets/{abnfDiagram-VCTEODGH-DF7LFtT-.js → abnfDiagram-VCTEODGH-CR3_ADBu.js} +1 -1
  2. package/dist/assets/{arc-xOmjWeVH.js → arc-b92imMmc.js} +1 -1
  3. package/dist/assets/{architectureDiagram-5GKGNRK7-CESzNPwa.js → architectureDiagram-5GKGNRK7-Dfm5Epud.js} +1 -1
  4. package/dist/assets/{blockDiagram-I7D4REHJ-BjdN78Ad.js → blockDiagram-I7D4REHJ-B8kLLyAv.js} +1 -1
  5. package/dist/assets/{c4Diagram-7LVT6UL2-BcK9EDWT.js → c4Diagram-7LVT6UL2-BWSyAAWX.js} +1 -1
  6. package/dist/assets/channel-BbcW_Tkh.js +1 -0
  7. package/dist/assets/{chunk-2Q5K7J3B-DGUN1JFP.js → chunk-2Q5K7J3B-Ci743mcC.js} +1 -1
  8. package/dist/assets/{chunk-5VM5RSS4-BG3BBcFr.js → chunk-5VM5RSS4-SdXpal6h.js} +1 -1
  9. package/dist/assets/{chunk-F27PBJKO-CLIOfrF5.js → chunk-F27PBJKO-Rof94ueX.js} +1 -1
  10. package/dist/assets/{chunk-IMKFNOWR-D6lGkqij.js → chunk-IMKFNOWR-B3st6ECc.js} +1 -1
  11. package/dist/assets/{chunk-JWPE2WC7-BmF9vLBT.js → chunk-JWPE2WC7-alXmu5xU.js} +1 -1
  12. package/dist/assets/{chunk-POPQ4Y6H-C_0SR3ak.js → chunk-POPQ4Y6H-dMs1OCce.js} +1 -1
  13. package/dist/assets/{chunk-SVP7TREG-rT20Wfpp.js → chunk-SVP7TREG-BmM8FsmF.js} +1 -1
  14. package/dist/assets/{chunk-TICWLB2K-Cn2hkmcf.js → chunk-TICWLB2K-CL1LytYG.js} +1 -1
  15. package/dist/assets/{chunk-XXDRQBXY-Dm_eP1_v.js → chunk-XXDRQBXY-7U9iRPT1.js} +1 -1
  16. package/dist/assets/classDiagram-ZZMXUADV-D9_YRZ7i.js +1 -0
  17. package/dist/assets/classDiagram-v2-VYDZK3BY-D9_YRZ7i.js +1 -0
  18. package/dist/assets/{cose-bilkent-JH36ORCC-cJeeJsep.js → cose-bilkent-JH36ORCC-DFOMlhj2.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-DX2ePIXe.js → cynefin-OW5HDTMX-CDdJFd9d.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-5FMLGOSQ-BvWXN-RR.js → cynefinDiagram-5FMLGOSQ-DPyVNrMW.js} +1 -1
  21. package/dist/assets/{dagre-GXQ25YYZ-CkwPRhGR.js → dagre-GXQ25YYZ-B3bpHa9D.js} +1 -1
  22. package/dist/assets/{diagram-S7CK7UJ4-CBGNzYyv.js → diagram-S7CK7UJ4-DeFO2rXB.js} +1 -1
  23. package/dist/assets/{diagram-UQ7AKVKN-CiwCU0SJ.js → diagram-UQ7AKVKN-CbdGTOVf.js} +1 -1
  24. package/dist/assets/{diagram-VSXAHHWV-Aekg4nTk.js → diagram-VSXAHHWV-DFfNQyMd.js} +1 -1
  25. package/dist/assets/{diagram-VX7I27RA-DPFP5K7r.js → diagram-VX7I27RA-DbNZ6_X3.js} +1 -1
  26. package/dist/assets/{diagram-Z3DM3KII-BHJkxoey.js → diagram-Z3DM3KII-B8F-ZkNJ.js} +1 -1
  27. package/dist/assets/{ebnfDiagram-PWID7BFC-2rV1P32T.js → ebnfDiagram-PWID7BFC-BfCCPPAP.js} +1 -1
  28. package/dist/assets/{erDiagram-RLTQ6QDP-C4BjyS_H.js → erDiagram-RLTQ6QDP-DcV-3Kzv.js} +1 -1
  29. package/dist/assets/{flowDiagram-HODETNUW-DL1TBlAG.js → flowDiagram-HODETNUW-BwSkJXCo.js} +1 -1
  30. package/dist/assets/{ganttDiagram-EL5Y4UJY-MKjvzCWz.js → ganttDiagram-EL5Y4UJY-BffHk77T.js} +1 -1
  31. package/dist/assets/{gitGraphDiagram-WWUBYQGX-CS_oTGK5.js → gitGraphDiagram-WWUBYQGX-CQ1EWa74.js} +1 -1
  32. package/dist/assets/{index-XOGQDqU3.js → index-BqK33gaZ.js} +120 -120
  33. package/dist/assets/index-D_Dc_nQv.css +1 -0
  34. package/dist/assets/{infoDiagram-27XIBGKW-ihUr2GjN.js → infoDiagram-27XIBGKW-DqIaBXQn.js} +1 -1
  35. package/dist/assets/{ishikawaDiagram-5VMMS53U-D2YAnuir.js → ishikawaDiagram-5VMMS53U-HlhsaZgv.js} +1 -1
  36. package/dist/assets/{journeyDiagram-3NMN7TZE-Bc2hSZGD.js → journeyDiagram-3NMN7TZE-BYO8-7rN.js} +1 -1
  37. package/dist/assets/{kanban-definition-UXKFOSKX-LRMGNonX.js → kanban-definition-UXKFOSKX-DXb8zHGT.js} +1 -1
  38. package/dist/assets/{layout-CcfWOlPV.js → layout-CREOVuEZ.js} +1 -1
  39. package/dist/assets/{linear-7JoS2gQO.js → linear-CVCrIRIY.js} +1 -1
  40. package/dist/assets/{mermaid.core-ijh_SsuN.js → mermaid.core-DFqeWtQR.js} +6 -6
  41. package/dist/assets/{mindmap-definition-YA3MSWOX-DCE4nzfF.js → mindmap-definition-YA3MSWOX-SUpXz333.js} +1 -1
  42. package/dist/assets/{pegDiagram-XKGWAZYB-SjaGa7lE.js → pegDiagram-XKGWAZYB-D-yWpve3.js} +1 -1
  43. package/dist/assets/{pieDiagram-E7YTZNPT-1xcRidSZ.js → pieDiagram-E7YTZNPT-BJX4Zp4Z.js} +1 -1
  44. package/dist/assets/{quadrantDiagram-AXDQQJYC-obHAHnYV.js → quadrantDiagram-AXDQQJYC-CHajheTu.js} +1 -1
  45. package/dist/assets/{railroadDiagram-O6MQD6OU-BsCVcRZb.js → railroadDiagram-O6MQD6OU-DHJWzW5A.js} +1 -1
  46. package/dist/assets/{requirementDiagram-BXWQKSXE-CUNkhvqb.js → requirementDiagram-BXWQKSXE-DigUIdso.js} +1 -1
  47. package/dist/assets/{sankeyDiagram-P5KCCOFB-DhezXMKm.js → sankeyDiagram-P5KCCOFB-CFUDDbA5.js} +1 -1
  48. package/dist/assets/{sequenceDiagram-WJ2MYXX4-CL9VswXH.js → sequenceDiagram-WJ2MYXX4-6tSaXfYD.js} +1 -1
  49. package/dist/assets/{sizeCapture-INFHLROL-t0g9jiEz.js → sizeCapture-INFHLROL-CXASEDKe.js} +1 -1
  50. package/dist/assets/{stateDiagram-D77RDMKH-BW-g-IOz.js → stateDiagram-D77RDMKH-DWTCAQfZ.js} +1 -1
  51. package/dist/assets/stateDiagram-v2-MP3YSRHH-HDQF4MGA.js +1 -0
  52. package/dist/assets/{swimlanes-42K2YHIH-LsuyeBte.js → swimlanes-42K2YHIH-7-oz5r-e.js} +1 -1
  53. package/dist/assets/swimlanesDiagram-VR7AAH4N-CJbgQFt8.js +8 -0
  54. package/dist/assets/{timeline-definition-24CTP7MA-B0cFka1R.js → timeline-definition-24CTP7MA-DVH5g4ty.js} +1 -1
  55. package/dist/assets/{vendor-codemirror-C5qqu4R7.js → vendor-codemirror-CjW0lHR5.js} +5 -5
  56. package/dist/assets/{vennDiagram-4TSXK5OY-D3HNCDR3.js → vennDiagram-4TSXK5OY-CQvY_ALZ.js} +1 -1
  57. package/dist/assets/{wardleyDiagram-VM6X3IG4-DJ3Co8nb.js → wardleyDiagram-VM6X3IG4-BdlTJgEc.js} +1 -1
  58. package/dist/assets/{xychartDiagram-S5SC5T6Z-Cz5E9New.js → xychartDiagram-S5SC5T6Z-CYn2sV77.js} +1 -1
  59. package/dist/favicon.png +0 -0
  60. package/dist/favicon.svg +1 -9
  61. package/dist/generate-icons.js +47 -46
  62. package/dist/icons/icon-128x128.png +0 -0
  63. package/dist/icons/icon-128x128.svg +1 -12
  64. package/dist/icons/icon-144x144.png +0 -0
  65. package/dist/icons/icon-144x144.svg +1 -12
  66. package/dist/icons/icon-152x152.png +0 -0
  67. package/dist/icons/icon-152x152.svg +1 -12
  68. package/dist/icons/icon-192x192.png +0 -0
  69. package/dist/icons/icon-192x192.svg +1 -12
  70. package/dist/icons/icon-384x384.png +0 -0
  71. package/dist/icons/icon-384x384.svg +1 -12
  72. package/dist/icons/icon-512x512.png +0 -0
  73. package/dist/icons/icon-512x512.svg +1 -12
  74. package/dist/icons/icon-72x72.png +0 -0
  75. package/dist/icons/icon-72x72.svg +1 -12
  76. package/dist/icons/icon-96x96.png +0 -0
  77. package/dist/icons/icon-96x96.svg +1 -12
  78. package/dist/index.html +3 -3
  79. package/dist/logo-128.png +0 -0
  80. package/dist/logo-256.png +0 -0
  81. package/dist/logo-32.png +0 -0
  82. package/dist/logo-512.png +0 -0
  83. package/dist/logo-64.png +0 -0
  84. package/dist/logo-sources/logo-black.png +0 -0
  85. package/dist/logo-sources/logo-white.png +0 -0
  86. package/dist/logo.svg +1 -17
  87. package/dist/sw.js +1 -1
  88. package/dist-server/plugins/piecemaker-dossier/src/institutional-terms.js +91 -0
  89. package/dist-server/plugins/piecemaker-dossier/src/institutional-terms.js.map +1 -0
  90. package/dist-server/plugins/piecemaker-dossier/src/knowledge.js +112 -10
  91. package/dist-server/plugins/piecemaker-dossier/src/knowledge.js.map +1 -1
  92. package/dist-server/plugins/piecemaker-dossier/src/scan-result.js +5 -1
  93. package/dist-server/plugins/piecemaker-dossier/src/scan-result.js.map +1 -1
  94. package/dist-server/server/index.js +60 -26
  95. package/dist-server/server/index.js.map +1 -1
  96. package/dist-server/server/modules/database/repositories/projects.db.js +28 -6
  97. package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -1
  98. package/dist-server/server/modules/database/tests/projects.db.integration.test.js +58 -1
  99. package/dist-server/server/modules/database/tests/projects.db.integration.test.js.map +1 -1
  100. package/dist-server/server/modules/plugins/plugins.routes.js +8 -1
  101. package/dist-server/server/modules/plugins/plugins.routes.js.map +1 -1
  102. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +2 -0
  103. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
  104. package/dist-server/server/piecemaker/bodacc-search.js +191 -0
  105. package/dist-server/server/piecemaker/bodacc-search.js.map +1 -0
  106. package/dist-server/server/piecemaker/company-search.js +145 -0
  107. package/dist-server/server/piecemaker/company-search.js.map +1 -0
  108. package/dist-server/server/piecemaker/company-search.test.js +18 -0
  109. package/dist-server/server/piecemaker/company-search.test.js.map +1 -0
  110. package/dist-server/server/piecemaker/index.js +6 -2
  111. package/dist-server/server/piecemaker/index.js.map +1 -1
  112. package/dist-server/server/piecemaker/knowledge/index.js +9 -4
  113. package/dist-server/server/piecemaker/knowledge/index.js.map +1 -1
  114. package/dist-server/server/piecemaker/knowledge/pipeline.js +68 -58
  115. package/dist-server/server/piecemaker/knowledge/pipeline.js.map +1 -1
  116. package/dist-server/server/piecemaker/knowledge/routes.js +2 -4
  117. package/dist-server/server/piecemaker/knowledge/routes.js.map +1 -1
  118. package/dist-server/server/piecemaker/knowledge/scan-jobs.js +91 -0
  119. package/dist-server/server/piecemaker/knowledge/scan-jobs.js.map +1 -0
  120. package/dist-server/server/piecemaker/knowledge/service.js +18 -36
  121. package/dist-server/server/piecemaker/knowledge/service.js.map +1 -1
  122. package/dist-server/server/piecemaker/library/connector-installation.js +224 -0
  123. package/dist-server/server/piecemaker/library/connector-installation.js.map +1 -0
  124. package/dist-server/server/piecemaker/library/index.js +11 -1
  125. package/dist-server/server/piecemaker/library/index.js.map +1 -1
  126. package/dist-server/server/piecemaker/library/marketplace.js +102 -43
  127. package/dist-server/server/piecemaker/library/marketplace.js.map +1 -1
  128. package/dist-server/server/piecemaker/library/migrate.js +8 -1
  129. package/dist-server/server/piecemaker/library/migrate.js.map +1 -1
  130. package/dist-server/server/piecemaker/library/provider-agents.js +50 -0
  131. package/dist-server/server/piecemaker/library/provider-agents.js.map +1 -0
  132. package/dist-server/server/piecemaker/library/provider-connectors.js +80 -0
  133. package/dist-server/server/piecemaker/library/provider-connectors.js.map +1 -0
  134. package/dist-server/server/piecemaker/library/provider-skills.js +11 -2
  135. package/dist-server/server/piecemaker/library/provider-skills.js.map +1 -1
  136. package/dist-server/server/piecemaker/library/store.js +125 -35
  137. package/dist-server/server/piecemaker/library/store.js.map +1 -1
  138. package/dist-server/server/piecemaker/library/workspace-installation.js +53 -29
  139. package/dist-server/server/piecemaker/library/workspace-installation.js.map +1 -1
  140. package/dist-server/server/piecemaker/timesheet/service.js +4 -21
  141. package/dist-server/server/piecemaker/timesheet/service.js.map +1 -1
  142. package/package.json +3 -2
  143. package/scripts/piecemaker/cli/install-command.mjs +35 -9
  144. package/scripts/piecemaker/cli/lib/config.mjs +1 -0
  145. package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +78 -0
  146. package/scripts/piecemaker/cli/lib/plugins.mjs +2 -0
  147. package/scripts/piecemaker/cli/lib/pwa.mjs +46 -7
  148. package/scripts/piecemaker/cli/lib/services.mjs +65 -4
  149. package/scripts/piecemaker/cli/piecemaker-command.test.mjs +25 -0
  150. package/scripts/piecemaker/cli/piecemaker.mjs +45 -44
  151. package/scripts/piecemaker/cli/piecemaker.ps1 +8 -0
  152. package/scripts/piecemaker/cli/piecemaker.sh +6 -1
  153. package/server/index.ts +52 -23
  154. package/server/modules/database/repositories/projects.db.ts +28 -6
  155. package/server/modules/database/tests/projects.db.integration.test.ts +63 -1
  156. package/server/modules/plugins/plugins.routes.ts +8 -1
  157. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +5 -0
  158. package/server/piecemaker/anonymizer/client-config.cjs +10 -2
  159. package/server/piecemaker/anonymizer/dictionary.cjs +6 -72
  160. package/server/piecemaker/bodacc-search.ts +205 -0
  161. package/server/piecemaker/company-search.test.ts +26 -0
  162. package/server/piecemaker/company-search.ts +183 -0
  163. package/server/piecemaker/index.ts +8 -2
  164. package/server/piecemaker/knowledge/index.ts +9 -4
  165. package/server/piecemaker/knowledge/pipeline.ts +89 -57
  166. package/server/piecemaker/knowledge/routes.ts +2 -4
  167. package/server/piecemaker/knowledge/scan-jobs.ts +111 -0
  168. package/server/piecemaker/knowledge/service.ts +18 -33
  169. package/server/piecemaker/library/connector-installation.ts +248 -0
  170. package/server/piecemaker/library/index.ts +5 -1
  171. package/server/piecemaker/library/marketplace.ts +104 -39
  172. package/server/piecemaker/library/migrate.ts +8 -1
  173. package/server/piecemaker/library/provider-agents.ts +49 -0
  174. package/server/piecemaker/library/provider-connectors.ts +89 -0
  175. package/server/piecemaker/library/provider-skills.ts +10 -1
  176. package/server/piecemaker/library/store.ts +129 -36
  177. package/server/piecemaker/library/workspace-installation.ts +54 -29
  178. package/server/piecemaker/router.cjs +2 -1
  179. package/server/piecemaker/timesheet/service.ts +4 -16
  180. package/server/piecemaker/vendor/installer/steps/03-python-gliner.mjs +0 -27
  181. package/server/piecemaker/vendor/installer/steps/14-mxc-sandbox.mjs +1 -1
  182. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +2 -3
  183. package/server/piecemaker/vendor/piecemaker-plugin/skills/conversion-md/SKILL.md +3 -3
  184. package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +3 -10
  185. package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +135 -4
  186. package/server/piecemaker/vendor/websocket-server/process-group.cjs +95 -0
  187. package/server/piecemaker/vendor/websocket-server/scripts/__pycache__/scan_utils.cpython-312.pyc +0 -0
  188. package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +280 -149
  189. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/model_config.cpython-312.pyc +0 -0
  190. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/scanner_worker.cpython-312.pyc +0 -0
  191. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/scanner_worker.py +35 -29
  192. package/server/piecemaker/vendor/websocket-server/scripts/requirements.txt +0 -5
  193. package/server/shared/types.ts +1 -0
  194. package/dist/assets/channel-Dz3eE5vU.js +0 -1
  195. package/dist/assets/classDiagram-ZZMXUADV-BC1eKV2j.js +0 -1
  196. package/dist/assets/classDiagram-v2-VYDZK3BY-BC1eKV2j.js +0 -1
  197. package/dist/assets/index-DhwJpHCq.css +0 -1
  198. package/dist/assets/stateDiagram-v2-MP3YSRHH-BXuT4zDG.js +0 -1
  199. package/dist/assets/swimlanesDiagram-VR7AAH4N-B0eYrB4U.js +0 -8
  200. package/server/piecemaker/vendor/websocket-server/case-instructions.cjs +0 -111
  201. package/server/piecemaker/vendor/websocket-server/scripts/ADDRESS_ANONYMIZATION.md +0 -216
  202. package/server/piecemaker/vendor/websocket-server/scripts/DEDUPLICATION_CHANGES.md +0 -134
  203. package/server/piecemaker/vendor/websocket-server/scripts/GLINER2_README.md +0 -138
  204. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/AUDIT_PERF_QUALITE.md +0 -246
  205. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/MODELE_COREML.md +0 -62
  206. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/coreml_runtime.cpython-312.pyc +0 -0
  207. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/build_coreml.py +0 -118
  208. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/coreml_runtime.py +0 -164
  209. 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
- * Source unique : le mapping central écrit par `central-mapping.cjs` lors de
5
- * l'enregistrement d'un mapping de dossier. Il réunit tous les dossiers
6
- * enregistrés avec des codes dé-conflictés, ce qui est exactement ce qu'un
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
- * Le fichier contient des noms réels (mode 0600). Il n'est jamais servi tel
12
- * quel : `routes.cjs` n'en expose que ce dont l'interface a besoin pour
13
- * surligner, et seulement derrière l'authentification CloudCLI.
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 store = new KnowledgeStore(getConnection());
16
- const pipeline = createKnowledgePipeline({ applicationRoot, projects: projectsDb, store });
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
  }