@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
@@ -6,6 +6,7 @@ import { spawn } from 'node:child_process';
6
6
  import { APP, LOG_DIR, PIECEMAKER_HOME, PORTS } from './config.mjs';
7
7
  import { waitUntil } from './exec.mjs';
8
8
  import { npmPath, runtimeEnv } from './node-runtime.mjs';
9
+ import { freePort } from './ports.mjs';
9
10
 
10
11
  export const APP_LOG = path.join(LOG_DIR, 'application.log');
11
12
  export const APP_PID_FILE = path.join(PIECEMAKER_HOME, 'application.pid');
@@ -32,6 +33,53 @@ export function appClientReachable() {
32
33
  return httpReachable(`http://127.0.0.1:${PORTS.appClient}/`);
33
34
  }
34
35
 
36
+ function anonymizerPort() {
37
+ try {
38
+ const port = Number.parseInt(JSON.parse(fs.readFileSync(path.join(PIECEMAKER_HOME, 'config.json'), 'utf8')).mikePiiPort, 10);
39
+ if (Number.isInteger(port) && port > 0) return port;
40
+ } catch {
41
+ }
42
+ return PORTS.anonymizer;
43
+ }
44
+
45
+ function stopPid(pid) {
46
+ if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
47
+ if (process.platform === 'win32') {
48
+ try { process.kill(pid, 'SIGTERM'); return true; } catch { return false; }
49
+ }
50
+ try { process.kill(-pid, 'SIGTERM'); return true; } catch {
51
+ try { process.kill(pid, 'SIGTERM'); return true; } catch { return false; }
52
+ }
53
+ }
54
+
55
+ export async function stopApplication() {
56
+ const killed = [];
57
+ const occupied = [];
58
+ try {
59
+ const pid = Number.parseInt(fs.readFileSync(APP_PID_FILE, 'utf8'), 10);
60
+ if (stopPid(pid)) killed.push(pid);
61
+ } catch {
62
+ }
63
+
64
+ for (const port of [PORTS.appClient, PORTS.appServer, anonymizerPort()]) {
65
+ const result = await freePort(port);
66
+ killed.push(...result.killed);
67
+ if (!result.released) occupied.push(port);
68
+ }
69
+
70
+ try { fs.rmSync(APP_PID_FILE, { force: true }); } catch { }
71
+ return { killed: [...new Set(killed)], occupied };
72
+ }
73
+
74
+ function readLogTail(file, maxLines = 40) {
75
+ try {
76
+ const lines = fs.readFileSync(file, 'utf8').trimEnd().split(/\n/);
77
+ return lines.slice(-maxLines).join('\n');
78
+ } catch {
79
+ return '';
80
+ }
81
+ }
82
+
35
83
  export async function startApplication(runtime, report) {
36
84
  const alreadyRunning = await appClientReachable() && await appServerReachable();
37
85
  if (alreadyRunning) {
@@ -61,11 +109,24 @@ export async function startApplication(runtime, report) {
61
109
  fs.closeSync(logHandle);
62
110
  }
63
111
 
64
- child.unref();
65
112
  fs.writeFileSync(APP_PID_FILE, String(child.pid), 'utf8');
66
113
 
67
- const serverUp = await waitUntil(appServerReachable, { timeoutMs: 120_000 });
68
- const clientUp = await waitUntil(appClientReachable, { timeoutMs: 120_000 });
114
+ const death = new Promise((resolve) => {
115
+ child.once('error', () => resolve('died'));
116
+ child.once('exit', () => resolve('died'));
117
+ });
118
+
119
+ await Promise.race([
120
+ waitUntil(appServerReachable, { timeoutMs: 120_000 }),
121
+ death,
122
+ ]);
123
+ const serverUp = await appServerReachable();
124
+ if (!serverUp) {
125
+ child.unref();
126
+ return { started: true, pid: child.pid, serverUp: false, clientUp: false, logTail: readLogTail(APP_LOG) };
127
+ }
69
128
 
70
- return { started: true, pid: child.pid, serverUp, clientUp };
129
+ const clientUp = await waitUntil(appClientReachable, { timeoutMs: 120_000 });
130
+ child.unref();
131
+ return { started: true, pid: child.pid, serverUp: true, clientUp, logTail: clientUp ? '' : readLogTail(APP_LOG) };
71
132
  }
@@ -0,0 +1,25 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import test from 'node:test';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ import { BIN_DIR } from './lib/config.mjs';
8
+ import { piecemakerExecutable } from './install-command.mjs';
9
+
10
+ const here = path.dirname(fileURLToPath(import.meta.url));
11
+
12
+ test('the curl bootstrap installs the piecemaker command before launching', () => {
13
+ const posix = fs.readFileSync(path.join(here, 'piecemaker.sh'), 'utf8');
14
+ const windows = fs.readFileSync(path.join(here, 'piecemaker.ps1'), 'utf8');
15
+ assert.match(posix, /install-command\.mjs/);
16
+ assert.match(windows, /install-command\.mjs/);
17
+ });
18
+
19
+ test('the desktop launcher binds to the installed piecemaker binary', () => {
20
+ const executable = piecemakerExecutable();
21
+ assert.equal(path.basename(executable).startsWith('piecemaker'), true);
22
+ if (executable !== 'piecemaker') {
23
+ assert.equal(executable.startsWith(BIN_DIR) || executable.includes(`${path.sep}node${path.sep}` ) || executable.includes('versions/node'), true);
24
+ }
25
+ });
@@ -1,22 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { installPiecemakerCommand, piecemakerExecutable } from './install-command.mjs';
3
4
  import { APP, APP_URL, PORTS } from './lib/config.mjs';
5
+ import { repairClientProxies } from './lib/piecemaker-anonymizer.mjs';
4
6
  import { installComponents } from './lib/composants.mjs';
5
7
  import { installPlugins } from './lib/plugins.mjs';
6
8
  import { gitAvailable, resolveNodeRuntime } from './lib/node-runtime.mjs';
7
- import { freePort } from './lib/ports.mjs';
8
9
  import { ensureDependencies, ensureRepository, rebuildNativeModules } from './lib/repos.mjs';
9
- import { APP_LOG, appClientReachable, appServerReachable, startApplication } from './lib/services.mjs';
10
- import { installApplicationEntry, openApplication, verifyPwaAssets } from './lib/pwa.mjs';
10
+ import { APP_LOG, appClientReachable, appServerReachable, startApplication, stopApplication } from './lib/services.mjs';
11
+ import { applicationWindowIsOpen, installApplicationEntry, openApplication, verifyPwaAssets } from './lib/pwa.mjs';
11
12
  import { banner, blank, c, detail, fail, ok, step, warn } from './lib/ui.mjs';
12
13
 
13
14
  const report = { step, ok, warn, detail };
14
15
 
15
- const HELP = `${c.bold('piecemaker')} — installe, met à jour et lance toute la plateforme.
16
+ const HELP = `${c.bold('piecemaker')} — installe, met à jour, répare et lance toute la plateforme.
16
17
 
17
- piecemaker tout faire : ports, dépôts, dépendances, PWA, serveurs
18
- piecemaker --launch-only ne relance que les serveurs manquants
19
- piecemaker --no-open ne pas ouvrir l application à la fin
18
+ piecemaker reset propre, puis installation et lancement
19
+ piecemaker --launch-only répare et relance seulement si l'application est arrêtée
20
20
  piecemaker --help cette aide
21
21
  `;
22
22
 
@@ -24,27 +24,9 @@ function parseArguments(argv) {
24
24
  return {
25
25
  help: argv.includes('--help') || argv.includes('-h'),
26
26
  launchOnly: argv.includes('--launch-only'),
27
- open: !argv.includes('--no-open'),
28
27
  };
29
28
  }
30
29
 
31
- async function cleanPorts(launchOnly) {
32
- step('Libération des ports');
33
- const targets = launchOnly
34
- ? [PORTS.appServer, PORTS.appClient]
35
- : [PORTS.appClient, PORTS.appServer];
36
-
37
- for (const port of targets) {
38
- const result = await freePort(port);
39
- if (!result.released) {
40
- warn(`port ${port} toujours occupé`);
41
- continue;
42
- }
43
- if (result.killed.length > 0) detail(`port ${port} libéré (${result.killed.join(', ')})`);
44
- }
45
- ok('Ports disponibles');
46
- }
47
-
48
30
  async function synchroniseRepositories(runtime) {
49
31
  if (!gitAvailable()) {
50
32
  throw new Error('git est requis et introuvable.');
@@ -63,32 +45,43 @@ async function synchroniseRepositories(runtime) {
63
45
  rebuildNativeModules(APP, runtime, report, ['better-sqlite3', 'node-pty']);
64
46
  }
65
47
 
66
- async function launchServices(runtime) {
67
- const applicationAlreadyRunning = await appServerReachable() && await appClientReachable();
68
-
69
- if (applicationAlreadyRunning) {
70
- ok(`Application déjà active — ${APP_URL}`);
71
- detail('socle laissé en l état : le routage des clients IA appartient à l application en cours');
72
- return true;
48
+ function printLogTail(logTail) {
49
+ if (!logTail) {
50
+ detail(`journal : ${APP_LOG}`);
51
+ return;
52
+ }
53
+ detail(`journal : ${APP_LOG}`);
54
+ for (const line of logTail.split('\n')) {
55
+ if (line.trim()) detail(line);
73
56
  }
57
+ }
58
+
59
+ async function applicationIsRunning() {
60
+ return await appServerReachable() && await appClientReachable();
61
+ }
62
+
63
+ async function resetAndLaunch(runtime) {
64
+ step('Reset de l application');
65
+ const stopped = await stopApplication();
66
+ if (stopped.killed.length) detail(`arrêtés : ${stopped.killed.join(', ')}`);
67
+ for (const port of stopped.occupied) warn(`port ${port} toujours occupé`);
68
+ ok('Processus précédents arrêtés');
74
69
 
75
70
  const application = await startApplication(runtime, report);
76
71
  if (application.alreadyRunning) {
77
72
  ok(`Application déjà active — ${APP_URL}`);
78
73
  return true;
79
74
  }
80
-
81
75
  if (!application.serverUp) {
82
76
  fail(`Serveur applicatif injoignable sur le port ${PORTS.appServer}`);
83
- detail(`journal : ${APP_LOG}`);
77
+ printLogTail(application.logTail);
84
78
  return false;
85
79
  }
86
80
  if (!application.clientUp) {
87
81
  fail(`Client applicatif injoignable sur le port ${PORTS.appClient}`);
88
- detail(`journal : ${APP_LOG}`);
82
+ printLogTail(application.logTail);
89
83
  return false;
90
84
  }
91
-
92
85
  ok(`Application active — serveur :${PORTS.appServer}, client ${APP_URL}`);
93
86
  return true;
94
87
  }
@@ -111,6 +104,7 @@ async function installPwa() {
111
104
  for (const location of entry.locations || []) {
112
105
  if (location !== entry.location) detail(`également posé : ${location}`);
113
106
  }
107
+ detail(`icône liée à ${piecemakerExecutable()}`);
114
108
  if (!entry.standalone) detail('navigateur Chromium absent : ouverture en onglet classique');
115
109
  if (!entry.verified) warn('installation incomplète : un emplacement attendu est introuvable après coup');
116
110
  }
@@ -127,7 +121,19 @@ async function main() {
127
121
  const runtime = resolveNodeRuntime();
128
122
  detail(`Node ${runtime.version}`);
129
123
 
130
- await cleanPorts(options.launchOnly);
124
+ step('Commande piecemaker');
125
+ const command = installPiecemakerCommand();
126
+ if (command.shims.length) ok(`installée : ${command.executable}`);
127
+ else warn('commande absente du PATH — relancez node scripts/piecemaker/cli/install-command.mjs');
128
+
129
+ await repairClientProxies({ appDir: APP.directory, report });
130
+
131
+ if (!options.launchOnly || !(await applicationIsRunning())) {
132
+ const running = await resetAndLaunch(runtime);
133
+ if (!running) return 1;
134
+ } else {
135
+ ok(`Application déjà active — ${APP_URL}`);
136
+ }
131
137
 
132
138
  if (!options.launchOnly) {
133
139
  await synchroniseRepositories(runtime);
@@ -135,19 +141,14 @@ async function main() {
135
141
  await installPlugins(runtime, report);
136
142
  }
137
143
 
138
- const running = await launchServices(runtime);
139
- if (!running) return 1;
140
-
141
- if (!options.launchOnly) {
142
- await installPwa();
143
- }
144
+ await installPwa();
144
145
 
145
146
  blank();
146
147
  ok(`Application ${c.bold(APP_URL)}`);
147
148
  detail('Configuration du socle : onglet Dossier › Configuration.');
148
149
  blank();
149
150
 
150
- if (options.open) openApplication();
151
+ if (!options.launchOnly && !applicationWindowIsOpen()) openApplication();
151
152
  return 0;
152
153
  }
153
154
 
@@ -59,5 +59,13 @@ if (-not (Test-Path $EntryPath)) {
59
59
  exit 1
60
60
  }
61
61
 
62
+ $Installer = Join-Path $AppDir 'scripts\piecemaker\cli\install-command.mjs'
63
+ if (Test-Path $Installer) {
64
+ & $NodeBin $Installer
65
+ }
66
+
67
+ $BinDir = Join-Path $HOME '.piecemaker\bin'
68
+ $env:PATH = "$(Split-Path $NodeBin -Parent);$BinDir;$env:PATH"
69
+
62
70
  & $NodeBin $EntryPath @args
63
71
  exit $LASTEXITCODE
@@ -54,7 +54,12 @@ if [ ! -f "$APP_DIR/$ENTRY" ]; then
54
54
  exit 1
55
55
  fi
56
56
 
57
- PATH="$(dirname "$NODE_BIN"):$PATH"
57
+ INSTALLER="$APP_DIR/scripts/piecemaker/cli/install-command.mjs"
58
+ if [ -f "$INSTALLER" ]; then
59
+ "$NODE_BIN" "$INSTALLER" || true
60
+ fi
61
+
62
+ PATH="$(dirname "$NODE_BIN"):${HOME}/.piecemaker/bin:${PATH:-}"
58
63
  export PATH
59
64
 
60
65
  exec "$NODE_BIN" "$APP_DIR/$ENTRY" "$@"
package/server/index.ts CHANGED
@@ -30,7 +30,7 @@ import { commandsRoutes } from './modules/commands/index.js';
30
30
  import { settingsRoutes } from './modules/settings/index.js';
31
31
  import { createSystemModule } from './modules/system/index.js';
32
32
  import { createAgentModule } from './modules/agent/index.js';
33
- import { createPieceMakerRouter } from './piecemaker/index.js';
33
+ import { createPieceMakerRouter, stopOriginalsJobs } from './piecemaker/index.js';
34
34
  import projectModuleRoutes from './modules/projects/projects.routes.js';
35
35
  import notificationRoutes from './modules/notifications/notifications.routes.js';
36
36
  import { userRoutes } from './modules/user/index.js';
@@ -378,29 +378,58 @@ async function startServer() {
378
378
  });
379
379
  });
380
380
 
381
- await closeSessionsWatcher();
382
- closeScheduledMessageDispatcher();
383
- // Clean up plugin processes on shutdown
384
- const shutdownRuntimeServices = async () => {
385
- try {
386
- await browserUseService.stopAllSessions();
387
- } catch (err) {
388
- console.error('[Browser] Error stopping sessions during shutdown:', getErrorMessage(err));
389
- }
390
- try {
391
- await stopAllPlugins();
392
- } catch (err) {
393
- console.error('[Plugins] Error stopping plugins during shutdown:', getErrorMessage(err));
394
- }
395
- try {
396
- await removeLocalServerMarker();
397
- } catch (err) {
398
- console.error('[Local Server] Error removing server marker during shutdown:', getErrorMessage(err));
399
- }
400
- process.exit(0);
381
+ let shutdownPromise: Promise<void> | null = null;
382
+ const shutdownRuntimeServices = () => {
383
+ if (shutdownPromise) return shutdownPromise;
384
+ shutdownPromise = (async () => {
385
+ // Stop accepting HTTP work first. Existing requests may finish while
386
+ // the process pipelines and background services are being drained.
387
+ const serverClosed = new Promise<void>((resolve, reject) => {
388
+ server.close((error) => {
389
+ if (error && getErrorCode(error) !== 'ERR_SERVER_NOT_RUNNING') reject(error);
390
+ else resolve();
391
+ });
392
+ }).catch((err) => {
393
+ console.error('[Server] Error closing HTTP server during shutdown:', getErrorMessage(err));
394
+ });
395
+
396
+ try {
397
+ await closeSessionsWatcher();
398
+ } catch (err) {
399
+ console.error('[Sessions] Error stopping watchers during shutdown:', getErrorMessage(err));
400
+ }
401
+ try {
402
+ closeScheduledMessageDispatcher();
403
+ } catch (err) {
404
+ console.error('[Scheduled messages] Error stopping dispatcher during shutdown:', getErrorMessage(err));
405
+ }
406
+ try {
407
+ await stopOriginalsJobs();
408
+ } catch (err) {
409
+ console.error('[PieceMaker] Error stopping pipeline jobs during shutdown:', getErrorMessage(err));
410
+ }
411
+ try {
412
+ await browserUseService.stopAllSessions();
413
+ } catch (err) {
414
+ console.error('[Browser] Error stopping sessions during shutdown:', getErrorMessage(err));
415
+ }
416
+ try {
417
+ await stopAllPlugins();
418
+ } catch (err) {
419
+ console.error('[Plugins] Error stopping plugins during shutdown:', getErrorMessage(err));
420
+ }
421
+ await serverClosed;
422
+ try {
423
+ await removeLocalServerMarker();
424
+ } catch (err) {
425
+ console.error('[Local Server] Error removing server marker during shutdown:', getErrorMessage(err));
426
+ }
427
+ process.exit(0);
428
+ })();
429
+ return shutdownPromise;
401
430
  };
402
- process.on('SIGTERM', () => void shutdownRuntimeServices());
403
- process.on('SIGINT', () => void shutdownRuntimeServices());
431
+ process.on('SIGTERM', () => { void shutdownRuntimeServices(); });
432
+ process.on('SIGINT', () => { void shutdownRuntimeServices(); });
404
433
  } catch (error) {
405
434
  console.error('[ERROR] Failed to start server:', error);
406
435
  process.exit(1);
@@ -15,6 +15,22 @@ function normalizeProjectDisplayName(projectPath: string, customProjectName: str
15
15
  return directoryName || projectPath;
16
16
  }
17
17
 
18
+ function ensureAnonymizationStatusTable(db: ReturnType<typeof getConnection>): void {
19
+ db.exec(`
20
+ CREATE TABLE IF NOT EXISTS piecemaker_anonymization_status (
21
+ project_id TEXT PRIMARY KEY NOT NULL,
22
+ completed_at TEXT NOT NULL,
23
+ FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE
24
+ )
25
+ `);
26
+ const mappingsExist = db.prepare(`SELECT 1 AS ok FROM sqlite_master WHERE type='table' AND name='piecemaker_mappings'`).get() as { ok: number } | undefined;
27
+ if (!mappingsExist) return;
28
+ db.prepare(`
29
+ INSERT OR IGNORE INTO piecemaker_anonymization_status(project_id, completed_at)
30
+ SELECT DISTINCT project_id, ? FROM piecemaker_mappings
31
+ `).run(new Date().toISOString());
32
+ }
33
+
18
34
  export const projectsDb = {
19
35
  createProjectPath(projectPath: string, customProjectName: string | null = null): CreateProjectPathResult {
20
36
  const db = getConnection();
@@ -88,10 +104,13 @@ export const projectsDb = {
88
104
 
89
105
  getProjectPaths(): ProjectRepositoryRow[] {
90
106
  const db = getConnection();
107
+ ensureAnonymizationStatusTable(db);
91
108
  return db.prepare(`
92
- SELECT project_id, project_path, custom_project_name, isStarred, isArchived
93
- FROM projects
94
- WHERE isArchived = 0
109
+ SELECT p.project_id, p.project_path, p.custom_project_name, p.isStarred, p.isArchived,
110
+ CASE WHEN s.project_id IS NULL THEN 0 ELSE 1 END AS anonymization_complete
111
+ FROM projects p
112
+ LEFT JOIN piecemaker_anonymization_status s ON s.project_id = p.project_id
113
+ WHERE p.isArchived = 0
95
114
  `).all() as ProjectRepositoryRow[];
96
115
  },
97
116
 
@@ -101,10 +120,13 @@ export const projectsDb = {
101
120
  */
102
121
  getArchivedProjectPaths(): ProjectRepositoryRow[] {
103
122
  const db = getConnection();
123
+ ensureAnonymizationStatusTable(db);
104
124
  return db.prepare(`
105
- SELECT project_id, project_path, custom_project_name, isStarred, isArchived
106
- FROM projects
107
- WHERE isArchived = 1
125
+ SELECT p.project_id, p.project_path, p.custom_project_name, p.isStarred, p.isArchived,
126
+ CASE WHEN s.project_id IS NULL THEN 0 ELSE 1 END AS anonymization_complete
127
+ FROM projects p
128
+ LEFT JOIN piecemaker_anonymization_status s ON s.project_id = p.project_id
129
+ WHERE p.isArchived = 1
108
130
  `).all() as ProjectRepositoryRow[];
109
131
  },
110
132
 
@@ -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`;