@piecemaker-legal/piecemaker 1.0.6 → 1.0.8

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 (217) hide show
  1. package/dist/assets/{abnfDiagram-VCTEODGH-DF7LFtT-.js → abnfDiagram-VCTEODGH-K8VucbQ5.js} +1 -1
  2. package/dist/assets/{arc-xOmjWeVH.js → arc-DUmCdRoC.js} +1 -1
  3. package/dist/assets/{architectureDiagram-5GKGNRK7-CESzNPwa.js → architectureDiagram-5GKGNRK7-DYhs5vqD.js} +1 -1
  4. package/dist/assets/{blockDiagram-I7D4REHJ-BjdN78Ad.js → blockDiagram-I7D4REHJ-DaXJ9nve.js} +1 -1
  5. package/dist/assets/{c4Diagram-7LVT6UL2-BcK9EDWT.js → c4Diagram-7LVT6UL2-BLbi-zPT.js} +1 -1
  6. package/dist/assets/channel-CJIq1vqf.js +1 -0
  7. package/dist/assets/{chunk-2Q5K7J3B-DGUN1JFP.js → chunk-2Q5K7J3B-Bzs3gxaq.js} +1 -1
  8. package/dist/assets/{chunk-5VM5RSS4-BG3BBcFr.js → chunk-5VM5RSS4-CQNzml7k.js} +1 -1
  9. package/dist/assets/{chunk-F27PBJKO-CLIOfrF5.js → chunk-F27PBJKO-B8xedWWH.js} +1 -1
  10. package/dist/assets/{chunk-IMKFNOWR-D6lGkqij.js → chunk-IMKFNOWR-CjBGlNOu.js} +1 -1
  11. package/dist/assets/{chunk-JWPE2WC7-BmF9vLBT.js → chunk-JWPE2WC7-vfnYEpgA.js} +1 -1
  12. package/dist/assets/{chunk-POPQ4Y6H-C_0SR3ak.js → chunk-POPQ4Y6H-Btl98tE8.js} +1 -1
  13. package/dist/assets/{chunk-SVP7TREG-rT20Wfpp.js → chunk-SVP7TREG-Bp6-pLjR.js} +1 -1
  14. package/dist/assets/{chunk-TICWLB2K-Cn2hkmcf.js → chunk-TICWLB2K-DvmznW2q.js} +1 -1
  15. package/dist/assets/{chunk-XXDRQBXY-Dm_eP1_v.js → chunk-XXDRQBXY-QSHJTsI3.js} +1 -1
  16. package/dist/assets/classDiagram-ZZMXUADV-BYkEWggn.js +1 -0
  17. package/dist/assets/classDiagram-v2-VYDZK3BY-BYkEWggn.js +1 -0
  18. package/dist/assets/{cose-bilkent-JH36ORCC-cJeeJsep.js → cose-bilkent-JH36ORCC-DxpyZoe2.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-DX2ePIXe.js → cynefin-OW5HDTMX-B9GRp2Gn.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-5FMLGOSQ-BvWXN-RR.js → cynefinDiagram-5FMLGOSQ-Q-2tz5GW.js} +1 -1
  21. package/dist/assets/{dagre-GXQ25YYZ-CkwPRhGR.js → dagre-GXQ25YYZ-3Rl3NqNe.js} +1 -1
  22. package/dist/assets/{diagram-S7CK7UJ4-CBGNzYyv.js → diagram-S7CK7UJ4-CreeWCO1.js} +1 -1
  23. package/dist/assets/{diagram-UQ7AKVKN-CiwCU0SJ.js → diagram-UQ7AKVKN-DNBoIyDJ.js} +1 -1
  24. package/dist/assets/{diagram-VSXAHHWV-Aekg4nTk.js → diagram-VSXAHHWV-ZkcE_cRk.js} +1 -1
  25. package/dist/assets/{diagram-VX7I27RA-DPFP5K7r.js → diagram-VX7I27RA-Dzdf7N2Q.js} +1 -1
  26. package/dist/assets/{diagram-Z3DM3KII-BHJkxoey.js → diagram-Z3DM3KII-M6b-nr1u.js} +1 -1
  27. package/dist/assets/{ebnfDiagram-PWID7BFC-2rV1P32T.js → ebnfDiagram-PWID7BFC-DunIw64n.js} +1 -1
  28. package/dist/assets/{erDiagram-RLTQ6QDP-C4BjyS_H.js → erDiagram-RLTQ6QDP-kN0Se5uc.js} +1 -1
  29. package/dist/assets/{flowDiagram-HODETNUW-DL1TBlAG.js → flowDiagram-HODETNUW-24YCovut.js} +1 -1
  30. package/dist/assets/{ganttDiagram-EL5Y4UJY-MKjvzCWz.js → ganttDiagram-EL5Y4UJY-DTUUEEy6.js} +1 -1
  31. package/dist/assets/{gitGraphDiagram-WWUBYQGX-CS_oTGK5.js → gitGraphDiagram-WWUBYQGX-BitrqhMr.js} +1 -1
  32. package/dist/assets/{index-XOGQDqU3.js → index-CG9hW1GU.js} +120 -120
  33. package/dist/assets/index-D_Dc_nQv.css +1 -0
  34. package/dist/assets/{infoDiagram-27XIBGKW-ihUr2GjN.js → infoDiagram-27XIBGKW-BNHN5Mby.js} +1 -1
  35. package/dist/assets/{ishikawaDiagram-5VMMS53U-D2YAnuir.js → ishikawaDiagram-5VMMS53U-De3IQKso.js} +1 -1
  36. package/dist/assets/{journeyDiagram-3NMN7TZE-Bc2hSZGD.js → journeyDiagram-3NMN7TZE-CWlkZ25T.js} +1 -1
  37. package/dist/assets/{kanban-definition-UXKFOSKX-LRMGNonX.js → kanban-definition-UXKFOSKX-2o_ax-Nk.js} +1 -1
  38. package/dist/assets/{layout-CcfWOlPV.js → layout-Btga9ZYP.js} +1 -1
  39. package/dist/assets/{linear-7JoS2gQO.js → linear-BoGGmqPG.js} +1 -1
  40. package/dist/assets/{mermaid.core-ijh_SsuN.js → mermaid.core-M308jnLQ.js} +6 -6
  41. package/dist/assets/{mindmap-definition-YA3MSWOX-DCE4nzfF.js → mindmap-definition-YA3MSWOX-BJ6C_grh.js} +1 -1
  42. package/dist/assets/{pegDiagram-XKGWAZYB-SjaGa7lE.js → pegDiagram-XKGWAZYB-qcdYX0Xe.js} +1 -1
  43. package/dist/assets/{pieDiagram-E7YTZNPT-1xcRidSZ.js → pieDiagram-E7YTZNPT-XPrSlt1M.js} +1 -1
  44. package/dist/assets/{quadrantDiagram-AXDQQJYC-obHAHnYV.js → quadrantDiagram-AXDQQJYC-COhHhra5.js} +1 -1
  45. package/dist/assets/{railroadDiagram-O6MQD6OU-BsCVcRZb.js → railroadDiagram-O6MQD6OU-Mw_KF6gb.js} +1 -1
  46. package/dist/assets/{requirementDiagram-BXWQKSXE-CUNkhvqb.js → requirementDiagram-BXWQKSXE-DPbt7jUM.js} +1 -1
  47. package/dist/assets/{sankeyDiagram-P5KCCOFB-DhezXMKm.js → sankeyDiagram-P5KCCOFB-B6HzL6Gh.js} +1 -1
  48. package/dist/assets/{sequenceDiagram-WJ2MYXX4-CL9VswXH.js → sequenceDiagram-WJ2MYXX4-B_dXXCC0.js} +1 -1
  49. package/dist/assets/{sizeCapture-INFHLROL-t0g9jiEz.js → sizeCapture-INFHLROL-DwODubWN.js} +1 -1
  50. package/dist/assets/{stateDiagram-D77RDMKH-BW-g-IOz.js → stateDiagram-D77RDMKH-5AUnOL3S.js} +1 -1
  51. package/dist/assets/stateDiagram-v2-MP3YSRHH-CFM_nDL9.js +1 -0
  52. package/dist/assets/{swimlanes-42K2YHIH-LsuyeBte.js → swimlanes-42K2YHIH-BLnIwbnd.js} +1 -1
  53. package/dist/assets/swimlanesDiagram-VR7AAH4N-BRQJYnDN.js +8 -0
  54. package/dist/assets/{timeline-definition-24CTP7MA-B0cFka1R.js → timeline-definition-24CTP7MA-B1Yc7-na.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-BFRtl-NI.js} +1 -1
  57. package/dist/assets/{wardleyDiagram-VM6X3IG4-DJ3Co8nb.js → wardleyDiagram-VM6X3IG4-BsR8iaD_.js} +1 -1
  58. package/dist/assets/{xychartDiagram-S5SC5T6Z-Cz5E9New.js → xychartDiagram-S5SC5T6Z-Crh1SwrA.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/cli/desktop-shortcut.js +80 -0
  97. package/dist-server/server/modules/cli/desktop-shortcut.js.map +1 -0
  98. package/dist-server/server/modules/cli/pwa-launcher.js +27 -4
  99. package/dist-server/server/modules/cli/pwa-launcher.js.map +1 -1
  100. package/dist-server/server/modules/database/repositories/projects.db.js +28 -6
  101. package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -1
  102. package/dist-server/server/modules/database/tests/projects.db.integration.test.js +58 -1
  103. package/dist-server/server/modules/database/tests/projects.db.integration.test.js.map +1 -1
  104. package/dist-server/server/modules/plugins/plugins.routes.js +8 -1
  105. package/dist-server/server/modules/plugins/plugins.routes.js.map +1 -1
  106. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +2 -0
  107. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
  108. package/dist-server/server/piecemaker/bodacc-search.js +191 -0
  109. package/dist-server/server/piecemaker/bodacc-search.js.map +1 -0
  110. package/dist-server/server/piecemaker/company-search.js +145 -0
  111. package/dist-server/server/piecemaker/company-search.js.map +1 -0
  112. package/dist-server/server/piecemaker/company-search.test.js +18 -0
  113. package/dist-server/server/piecemaker/company-search.test.js.map +1 -0
  114. package/dist-server/server/piecemaker/index.js +6 -2
  115. package/dist-server/server/piecemaker/index.js.map +1 -1
  116. package/dist-server/server/piecemaker/knowledge/index.js +9 -4
  117. package/dist-server/server/piecemaker/knowledge/index.js.map +1 -1
  118. package/dist-server/server/piecemaker/knowledge/pipeline.js +68 -58
  119. package/dist-server/server/piecemaker/knowledge/pipeline.js.map +1 -1
  120. package/dist-server/server/piecemaker/knowledge/routes.js +2 -4
  121. package/dist-server/server/piecemaker/knowledge/routes.js.map +1 -1
  122. package/dist-server/server/piecemaker/knowledge/scan-jobs.js +91 -0
  123. package/dist-server/server/piecemaker/knowledge/scan-jobs.js.map +1 -0
  124. package/dist-server/server/piecemaker/knowledge/service.js +18 -36
  125. package/dist-server/server/piecemaker/knowledge/service.js.map +1 -1
  126. package/dist-server/server/piecemaker/library/connector-installation.js +224 -0
  127. package/dist-server/server/piecemaker/library/connector-installation.js.map +1 -0
  128. package/dist-server/server/piecemaker/library/index.js +11 -1
  129. package/dist-server/server/piecemaker/library/index.js.map +1 -1
  130. package/dist-server/server/piecemaker/library/marketplace.js +102 -43
  131. package/dist-server/server/piecemaker/library/marketplace.js.map +1 -1
  132. package/dist-server/server/piecemaker/library/migrate.js +8 -1
  133. package/dist-server/server/piecemaker/library/migrate.js.map +1 -1
  134. package/dist-server/server/piecemaker/library/provider-agents.js +50 -0
  135. package/dist-server/server/piecemaker/library/provider-agents.js.map +1 -0
  136. package/dist-server/server/piecemaker/library/provider-connectors.js +80 -0
  137. package/dist-server/server/piecemaker/library/provider-connectors.js.map +1 -0
  138. package/dist-server/server/piecemaker/library/provider-skills.js +11 -2
  139. package/dist-server/server/piecemaker/library/provider-skills.js.map +1 -1
  140. package/dist-server/server/piecemaker/library/store.js +125 -35
  141. package/dist-server/server/piecemaker/library/store.js.map +1 -1
  142. package/dist-server/server/piecemaker/library/workspace-installation.js +53 -29
  143. package/dist-server/server/piecemaker/library/workspace-installation.js.map +1 -1
  144. package/dist-server/server/piecemaker/timesheet/service.js +4 -21
  145. package/dist-server/server/piecemaker/timesheet/service.js.map +1 -1
  146. package/package.json +3 -2
  147. package/product.config.json +1 -0
  148. package/scripts/piecemaker/cli/install-command.mjs +35 -9
  149. package/scripts/piecemaker/cli/lib/app-window.mjs +47 -0
  150. package/scripts/piecemaker/cli/lib/config.mjs +1 -0
  151. package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +78 -0
  152. package/scripts/piecemaker/cli/lib/plugins.mjs +2 -0
  153. package/scripts/piecemaker/cli/lib/pwa.mjs +49 -13
  154. package/scripts/piecemaker/cli/lib/services.mjs +65 -4
  155. package/scripts/piecemaker/cli/piecemaker-command.test.mjs +25 -0
  156. package/scripts/piecemaker/cli/piecemaker.mjs +60 -45
  157. package/scripts/piecemaker/cli/piecemaker.ps1 +8 -0
  158. package/scripts/piecemaker/cli/piecemaker.sh +6 -1
  159. package/server/index.ts +52 -23
  160. package/server/modules/cli/desktop-shortcut.ts +89 -0
  161. package/server/modules/cli/pwa-launcher.ts +29 -4
  162. package/server/modules/database/repositories/projects.db.ts +28 -6
  163. package/server/modules/database/tests/projects.db.integration.test.ts +63 -1
  164. package/server/modules/plugins/plugins.routes.ts +8 -1
  165. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +5 -0
  166. package/server/piecemaker/anonymizer/client-config.cjs +10 -2
  167. package/server/piecemaker/anonymizer/dictionary.cjs +6 -72
  168. package/server/piecemaker/bodacc-search.ts +205 -0
  169. package/server/piecemaker/company-search.test.ts +26 -0
  170. package/server/piecemaker/company-search.ts +183 -0
  171. package/server/piecemaker/index.ts +8 -2
  172. package/server/piecemaker/knowledge/index.ts +9 -4
  173. package/server/piecemaker/knowledge/pipeline.ts +89 -57
  174. package/server/piecemaker/knowledge/routes.ts +2 -4
  175. package/server/piecemaker/knowledge/scan-jobs.ts +111 -0
  176. package/server/piecemaker/knowledge/service.ts +18 -33
  177. package/server/piecemaker/library/connector-installation.ts +248 -0
  178. package/server/piecemaker/library/index.ts +5 -1
  179. package/server/piecemaker/library/marketplace.ts +104 -39
  180. package/server/piecemaker/library/migrate.ts +8 -1
  181. package/server/piecemaker/library/provider-agents.ts +49 -0
  182. package/server/piecemaker/library/provider-connectors.ts +89 -0
  183. package/server/piecemaker/library/provider-skills.ts +10 -1
  184. package/server/piecemaker/library/store.ts +129 -36
  185. package/server/piecemaker/library/workspace-installation.ts +54 -29
  186. package/server/piecemaker/router.cjs +2 -1
  187. package/server/piecemaker/timesheet/service.ts +4 -16
  188. package/server/piecemaker/vendor/installer/steps/03-python-gliner.mjs +0 -27
  189. package/server/piecemaker/vendor/installer/steps/14-mxc-sandbox.mjs +1 -1
  190. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +2 -3
  191. package/server/piecemaker/vendor/piecemaker-plugin/skills/conversion-md/SKILL.md +3 -3
  192. package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +3 -10
  193. package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +135 -4
  194. package/server/piecemaker/vendor/websocket-server/process-group.cjs +95 -0
  195. package/server/piecemaker/vendor/websocket-server/scripts/__pycache__/scan_utils.cpython-312.pyc +0 -0
  196. package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +280 -149
  197. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/model_config.cpython-312.pyc +0 -0
  198. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/scanner_worker.cpython-312.pyc +0 -0
  199. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/scanner_worker.py +35 -29
  200. package/server/piecemaker/vendor/websocket-server/scripts/requirements.txt +0 -5
  201. package/server/shared/types.ts +1 -0
  202. package/dist/assets/channel-Dz3eE5vU.js +0 -1
  203. package/dist/assets/classDiagram-ZZMXUADV-BC1eKV2j.js +0 -1
  204. package/dist/assets/classDiagram-v2-VYDZK3BY-BC1eKV2j.js +0 -1
  205. package/dist/assets/index-DhwJpHCq.css +0 -1
  206. package/dist/assets/stateDiagram-v2-MP3YSRHH-BXuT4zDG.js +0 -1
  207. package/dist/assets/swimlanesDiagram-VR7AAH4N-B0eYrB4U.js +0 -8
  208. package/server/piecemaker/vendor/websocket-server/case-instructions.cjs +0 -111
  209. package/server/piecemaker/vendor/websocket-server/scripts/ADDRESS_ANONYMIZATION.md +0 -216
  210. package/server/piecemaker/vendor/websocket-server/scripts/DEDUPLICATION_CHANGES.md +0 -134
  211. package/server/piecemaker/vendor/websocket-server/scripts/GLINER2_README.md +0 -138
  212. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/AUDIT_PERF_QUALITE.md +0 -246
  213. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/MODELE_COREML.md +0 -62
  214. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/coreml_runtime.cpython-312.pyc +0 -0
  215. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/build_coreml.py +0 -118
  216. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/coreml_runtime.py +0 -164
  217. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/presidio-gliner.py +0 -479
@@ -4,7 +4,7 @@ import { tmpdir } from 'node:os';
4
4
  import path from 'node:path';
5
5
  import test from 'node:test';
6
6
 
7
- import { closeConnection } from '@/modules/database/connection.js';
7
+ import { closeConnection, getConnection } from '@/modules/database/connection.js';
8
8
  import { initializeDatabase } from '@/modules/database/init-db.js';
9
9
  import { projectsDb } from '@/modules/database/repositories/projects.db.js';
10
10
 
@@ -70,3 +70,65 @@ test('projectsDb.createProjectPath returns active_conflict for active duplicates
70
70
  assert.equal(conflict.project?.isArchived, 0);
71
71
  });
72
72
  });
73
+
74
+ test('projectsDb.getProjectPaths reads anonymization completion directly from SQLite', async () => {
75
+ await withIsolatedDatabase(() => {
76
+ const created = projectsDb.createProjectPath('/workspace/anonymized-project');
77
+ assert.ok(created.project);
78
+
79
+ projectsDb.getProjectPaths();
80
+ getConnection().prepare(`
81
+ INSERT INTO piecemaker_anonymization_status (project_id, completed_at)
82
+ VALUES (?, ?)
83
+ `).run(created.project.project_id, new Date().toISOString());
84
+
85
+ const project = projectsDb.getProjectPaths()[0];
86
+ assert.equal(project.anonymization_complete, 1);
87
+ });
88
+ });
89
+
90
+ test('projectsDb.getProjectPaths backfills anonymization completion from existing mappings', async () => {
91
+ await withIsolatedDatabase(() => {
92
+ const created = projectsDb.createProjectPath('/workspace/legacy-scanned-project');
93
+ assert.ok(created.project);
94
+ const db = getConnection();
95
+ db.exec(`
96
+ CREATE TABLE IF NOT EXISTS piecemaker_nodes (
97
+ project_id TEXT NOT NULL,
98
+ id TEXT NOT NULL,
99
+ kind TEXT NOT NULL,
100
+ label TEXT NOT NULL,
101
+ search_text TEXT NOT NULL DEFAULT '',
102
+ aliases_json TEXT NOT NULL DEFAULT '[]',
103
+ data_json TEXT NOT NULL DEFAULT '{}',
104
+ origin TEXT NOT NULL DEFAULT 'gliner',
105
+ created_at TEXT NOT NULL,
106
+ updated_at TEXT NOT NULL,
107
+ PRIMARY KEY (project_id, id)
108
+ );
109
+ CREATE TABLE IF NOT EXISTS piecemaker_mappings (
110
+ project_id TEXT NOT NULL,
111
+ node_id TEXT NOT NULL,
112
+ real_value TEXT NOT NULL,
113
+ masked_value TEXT NOT NULL,
114
+ search_text TEXT NOT NULL DEFAULT '',
115
+ data_json TEXT NOT NULL DEFAULT '{}',
116
+ origin TEXT NOT NULL DEFAULT 'gliner',
117
+ created_at TEXT NOT NULL,
118
+ updated_at TEXT NOT NULL,
119
+ PRIMARY KEY (project_id, node_id, real_value)
120
+ );
121
+ `);
122
+ db.prepare(`
123
+ INSERT INTO piecemaker_nodes(project_id,id,kind,label,search_text,aliases_json,data_json,origin,created_at,updated_at)
124
+ VALUES (?, 'person-1', 'person', 'Alice', 'alice', '[]', '{}', 'gliner', ?, ?)
125
+ `).run(created.project.project_id, new Date().toISOString(), new Date().toISOString());
126
+ db.prepare(`
127
+ INSERT INTO piecemaker_mappings(project_id,node_id,real_value,masked_value,search_text,data_json,origin,created_at,updated_at)
128
+ VALUES (?, 'person-1', 'Alice', 'PERSONNE_PHYSIQUE_01', 'alice', '{}', 'gliner', ?, ?)
129
+ `).run(created.project.project_id, new Date().toISOString(), new Date().toISOString());
130
+
131
+ const project = projectsDb.getProjectPaths()[0];
132
+ assert.equal(project.anonymization_complete, 1);
133
+ });
134
+ });
@@ -26,8 +26,15 @@ export function createPluginsRouter(service: ReturnType<typeof createPluginsServ
26
26
  router.get('/:name/assets/*', async (req, res, next) => {
27
27
  try {
28
28
  const asset = service.resolveAsset(routeParameter(req.params.name), wildcardPath(req));
29
+ const stats = fs.statSync(asset.path);
30
+ const etag = `"${stats.mtimeMs.toString(16)}-${stats.size.toString(16)}"`;
29
31
  res.setHeader('Content-Type', asset.contentType);
30
- res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
32
+ res.setHeader('ETag', etag);
33
+ res.setHeader('Cache-Control', 'private, max-age=0, must-revalidate');
34
+ if (req.headers['if-none-match'] === etag) {
35
+ res.status(304).end();
36
+ return;
37
+ }
31
38
  const stream = fs.createReadStream(asset.path);
32
39
  stream.on('error', next);
33
40
  stream.pipe(res);
@@ -29,6 +29,7 @@ export type ProjectListItem = {
29
29
  displayName: string;
30
30
  fullPath: string;
31
31
  isStarred: boolean;
32
+ anonymizationComplete: boolean;
32
33
  sessions: SessionSummary[];
33
34
  sessionMeta: {
34
35
  hasMore: boolean;
@@ -189,6 +190,7 @@ export async function getProjectsWithSessions(
189
190
  project_path: string;
190
191
  custom_project_name?: string | null;
191
192
  isStarred?: number;
193
+ anonymization_complete?: number;
192
194
  }>;
193
195
  const totalProjects = projectRows.length;
194
196
  const projects: ProjectListItem[] = [];
@@ -223,6 +225,7 @@ export async function getProjectsWithSessions(
223
225
  displayName,
224
226
  fullPath: projectPath,
225
227
  isStarred: Boolean(row.isStarred),
228
+ anonymizationComplete: Boolean(row.anonymization_complete),
226
229
  sessions: sessionsPage.sessions,
227
230
  sessionMeta: {
228
231
  hasMore: sessionsPage.hasMore,
@@ -257,6 +260,7 @@ export async function getArchivedProjectsWithSessions(
257
260
  project_path: string;
258
261
  custom_project_name?: string | null;
259
262
  isStarred?: number;
263
+ anonymization_complete?: number;
260
264
  }>;
261
265
 
262
266
  const archivedProjects: ArchivedProjectListItem[] = [];
@@ -275,6 +279,7 @@ export async function getArchivedProjectsWithSessions(
275
279
  displayName,
276
280
  fullPath: row.project_path,
277
281
  isStarred: Boolean(row.isStarred),
282
+ anonymizationComplete: Boolean(row.anonymization_complete),
278
283
  isArchived: true,
279
284
  sessions: sessionsPage.sessions,
280
285
  sessionMeta: {
@@ -138,10 +138,18 @@ function configureCodexProxy({
138
138
 
139
139
  const managedTable = lines.findIndex((line) => line.trim() === `[model_providers.${CODEX_PROVIDER_ID}]`);
140
140
  const managedStart = lines.findIndex((line) => line.trim() === CODEX_BLOCK_START);
141
- const managedEnd = lines.findIndex((line) => line.trim() === CODEX_BLOCK_END);
142
- if (managedTable >= 0 && (managedStart < 0 || managedEnd < managedTable)) {
141
+ let managedEnd = lines.findIndex((line) => line.trim() === CODEX_BLOCK_END);
142
+ if (managedTable >= 0 && managedStart < 0) {
143
143
  return { configured: false, changed: false, conflict: true, file: configFile, reason: 'provider-table-conflict' };
144
144
  }
145
+ if (managedStart >= 0 && managedEnd < managedStart) {
146
+ let end = managedTable >= managedStart ? managedTable : managedStart;
147
+ for (let index = end + 1; index < lines.length; index += 1) {
148
+ if (/^\s*\[/.test(lines[index])) break;
149
+ end = index;
150
+ }
151
+ managedEnd = end;
152
+ }
145
153
 
146
154
  if (provider) {
147
155
  lines[provider.index] = `model_provider = ${tomlString(CODEX_PROVIDER_ID)} # géré par PieceMaker`;
@@ -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
+ });