@piecemaker-legal/piecemaker 1.1.1 → 2.0.2

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 (141) hide show
  1. package/dist/assets/{abnfDiagram-VCTEODGH-DEXWh3LH.js → abnfDiagram-VCTEODGH-BeQS_HnO.js} +1 -1
  2. package/dist/assets/{arc-CoTRLXKW.js → arc-ClYjD3aM.js} +1 -1
  3. package/dist/assets/{architectureDiagram-5GKGNRK7-Cg-jO9oR.js → architectureDiagram-5GKGNRK7-Csdihb1L.js} +1 -1
  4. package/dist/assets/{blockDiagram-I7D4REHJ-ChXwQSIq.js → blockDiagram-I7D4REHJ-BuXey9ht.js} +1 -1
  5. package/dist/assets/{c4Diagram-7LVT6UL2-Bw2VGggd.js → c4Diagram-7LVT6UL2-DdO-ovOy.js} +1 -1
  6. package/dist/assets/channel-DhuuSCHK.js +1 -0
  7. package/dist/assets/{chunk-2Q5K7J3B-C1WdqrLP.js → chunk-2Q5K7J3B-D3hzWzSc.js} +1 -1
  8. package/dist/assets/{chunk-5VM5RSS4-BerUZBa1.js → chunk-5VM5RSS4-DeT3cR6k.js} +1 -1
  9. package/dist/assets/{chunk-F27PBJKO-5W7ax2gB.js → chunk-F27PBJKO-bazGi1ru.js} +1 -1
  10. package/dist/assets/{chunk-IMKFNOWR-JgDN1h4T.js → chunk-IMKFNOWR-BE3ZXrPd.js} +1 -1
  11. package/dist/assets/{chunk-JWPE2WC7-ulNHnEdQ.js → chunk-JWPE2WC7-dTAsgByE.js} +1 -1
  12. package/dist/assets/{chunk-POPQ4Y6H-D4aTDC2T.js → chunk-POPQ4Y6H-DKYrzMSd.js} +1 -1
  13. package/dist/assets/{chunk-SVP7TREG-BA5zX4XF.js → chunk-SVP7TREG-BTtrMvhE.js} +1 -1
  14. package/dist/assets/{chunk-TICWLB2K-DhOULVhX.js → chunk-TICWLB2K-CZPHShPS.js} +1 -1
  15. package/dist/assets/{chunk-XXDRQBXY-CkpCgrVg.js → chunk-XXDRQBXY-B0byFHaU.js} +1 -1
  16. package/dist/assets/classDiagram-ZZMXUADV-CO0E82Dl.js +1 -0
  17. package/dist/assets/classDiagram-v2-VYDZK3BY-CO0E82Dl.js +1 -0
  18. package/dist/assets/{cose-bilkent-JH36ORCC-0xUSfjfV.js → cose-bilkent-JH36ORCC-BbLCzlaU.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-BE6fNu2_.js → cynefin-OW5HDTMX-CQ8ui4YD.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-5FMLGOSQ-rFMnZ9b3.js → cynefinDiagram-5FMLGOSQ-T-GL1aIf.js} +1 -1
  21. package/dist/assets/{dagre-GXQ25YYZ-C1p4ffd7.js → dagre-GXQ25YYZ-CXxVKznH.js} +1 -1
  22. package/dist/assets/{diagram-S7CK7UJ4-BV_3Xj4O.js → diagram-S7CK7UJ4-B_-1Mf_7.js} +1 -1
  23. package/dist/assets/{diagram-UQ7AKVKN-Bb_Evkrm.js → diagram-UQ7AKVKN-Bs-OB8xI.js} +1 -1
  24. package/dist/assets/{diagram-VSXAHHWV-BzgupjxD.js → diagram-VSXAHHWV-BpUqq93o.js} +1 -1
  25. package/dist/assets/{diagram-VX7I27RA-D1c67z76.js → diagram-VX7I27RA-CZ6hSUo6.js} +1 -1
  26. package/dist/assets/{diagram-Z3DM3KII-DsJAJGKE.js → diagram-Z3DM3KII-DVZiDO6S.js} +1 -1
  27. package/dist/assets/{ebnfDiagram-PWID7BFC-B1ge_obq.js → ebnfDiagram-PWID7BFC-D_1izoKK.js} +1 -1
  28. package/dist/assets/{erDiagram-RLTQ6QDP-DrV-tggw.js → erDiagram-RLTQ6QDP-_jSi4sg-.js} +1 -1
  29. package/dist/assets/{flowDiagram-HODETNUW-CVzDiSQ3.js → flowDiagram-HODETNUW-BtBM7ez3.js} +1 -1
  30. package/dist/assets/{ganttDiagram-EL5Y4UJY-CGRdSN9D.js → ganttDiagram-EL5Y4UJY-Bz2nxw7c.js} +1 -1
  31. package/dist/assets/{gitGraphDiagram-WWUBYQGX-CAfvFXzL.js → gitGraphDiagram-WWUBYQGX-8Q3I3hoE.js} +1 -1
  32. package/dist/assets/{index-DaxH81ZJ.js → index-CZUj0Tlu.js} +152 -152
  33. package/dist/assets/index-DcfdIMj-.css +1 -0
  34. package/dist/assets/{infoDiagram-27XIBGKW-DTshEkGK.js → infoDiagram-27XIBGKW-Cc6eKjfU.js} +1 -1
  35. package/dist/assets/{ishikawaDiagram-5VMMS53U-DkbshVxp.js → ishikawaDiagram-5VMMS53U-Bd3w97qM.js} +1 -1
  36. package/dist/assets/{journeyDiagram-3NMN7TZE-v5qE8xpn.js → journeyDiagram-3NMN7TZE-Cy6XbK5X.js} +1 -1
  37. package/dist/assets/{kanban-definition-UXKFOSKX-a9CNMFLt.js → kanban-definition-UXKFOSKX-kyLqWDpr.js} +1 -1
  38. package/dist/assets/{layout-DrW4R1TH.js → layout-DXpPSEft.js} +1 -1
  39. package/dist/assets/{linear-Cs3qpXT_.js → linear-DH_h9cDZ.js} +1 -1
  40. package/dist/assets/{mermaid.core-C7Ti-Fty.js → mermaid.core-BghXiB7p.js} +6 -6
  41. package/dist/assets/{mindmap-definition-YA3MSWOX-B0VDdIcG.js → mindmap-definition-YA3MSWOX-C2xxEXsP.js} +1 -1
  42. package/dist/assets/{pegDiagram-XKGWAZYB-CV1L7MjP.js → pegDiagram-XKGWAZYB-DuppHX4-.js} +1 -1
  43. package/dist/assets/{pieDiagram-E7YTZNPT-DSCuzVTV.js → pieDiagram-E7YTZNPT-BCukPZ2a.js} +1 -1
  44. package/dist/assets/{quadrantDiagram-AXDQQJYC-hzX-9AKo.js → quadrantDiagram-AXDQQJYC-Dm_ojY_7.js} +1 -1
  45. package/dist/assets/{railroadDiagram-O6MQD6OU-BbmzIqcp.js → railroadDiagram-O6MQD6OU-BzdOv7BJ.js} +1 -1
  46. package/dist/assets/{requirementDiagram-BXWQKSXE-CQCRRBSL.js → requirementDiagram-BXWQKSXE-DgewPP_g.js} +1 -1
  47. package/dist/assets/{sankeyDiagram-P5KCCOFB-kiaLcW6X.js → sankeyDiagram-P5KCCOFB-vEIMvCSt.js} +1 -1
  48. package/dist/assets/{sequenceDiagram-WJ2MYXX4-Bgx8ST7Q.js → sequenceDiagram-WJ2MYXX4-BL_31NoY.js} +1 -1
  49. package/dist/assets/{sizeCapture-INFHLROL-BTjJ-ygN.js → sizeCapture-INFHLROL-Ch6u4xo6.js} +1 -1
  50. package/dist/assets/{stateDiagram-D77RDMKH-D48RR1FL.js → stateDiagram-D77RDMKH-ByquMuJv.js} +1 -1
  51. package/dist/assets/{stateDiagram-v2-MP3YSRHH-B-XwSR-z.js → stateDiagram-v2-MP3YSRHH-_QRJP_vn.js} +1 -1
  52. package/dist/assets/{swimlanes-42K2YHIH-BhE-QuEZ.js → swimlanes-42K2YHIH-BZyXEHOb.js} +1 -1
  53. package/dist/assets/swimlanesDiagram-VR7AAH4N-sqZwZwbP.js +8 -0
  54. package/dist/assets/{timeline-definition-24CTP7MA-Cm7EUWWt.js → timeline-definition-24CTP7MA-CLBivayK.js} +1 -1
  55. package/dist/assets/{vennDiagram-4TSXK5OY-X-nC9wZ0.js → vennDiagram-4TSXK5OY-3dJ2PRqR.js} +1 -1
  56. package/dist/assets/{wardleyDiagram-VM6X3IG4-D33YvEQZ.js → wardleyDiagram-VM6X3IG4-C279LT36.js} +1 -1
  57. package/dist/assets/{xychartDiagram-S5SC5T6Z-lziwBKsM.js → xychartDiagram-S5SC5T6Z-FoqPDrei.js} +1 -1
  58. package/dist/index.html +2 -2
  59. package/dist/sw.js +1 -1
  60. package/dist-server/server/index.js +3 -1
  61. package/dist-server/server/index.js.map +1 -1
  62. package/dist-server/server/piecemaker/harness/citations.routes.js +18 -1
  63. package/dist-server/server/piecemaker/harness/citations.routes.js.map +1 -1
  64. package/dist-server/server/piecemaker/harness/legifrance-html.js +112 -0
  65. package/dist-server/server/piecemaker/harness/legifrance-html.js.map +1 -0
  66. package/dist-server/server/piecemaker/index.js +7 -3
  67. package/dist-server/server/piecemaker/index.js.map +1 -1
  68. package/dist-server/server/piecemaker/knowledge/index.js +13 -6
  69. package/dist-server/server/piecemaker/knowledge/index.js.map +1 -1
  70. package/dist-server/server/piecemaker/knowledge/local-routes.js +55 -0
  71. package/dist-server/server/piecemaker/knowledge/local-routes.js.map +1 -0
  72. package/dist-server/server/piecemaker/knowledge/scan-jobs.js +3 -1
  73. package/dist-server/server/piecemaker/knowledge/scan-jobs.js.map +1 -1
  74. package/dist-server/server/piecemaker/library/index.js +14 -41
  75. package/dist-server/server/piecemaker/library/index.js.map +1 -1
  76. package/dist-server/server/piecemaker/library/store.js +11 -3
  77. package/dist-server/server/piecemaker/library/store.js.map +1 -1
  78. package/dist-server/server/piecemaker/library/workspace-installation.js +26 -3
  79. package/dist-server/server/piecemaker/library/workspace-installation.js.map +1 -1
  80. package/dist-server/server/piecemaker/timesheet/service.js +8 -7
  81. package/dist-server/server/piecemaker/timesheet/service.js.map +1 -1
  82. package/dist-server/server/piecemaker/timesheet/store.js +5 -3
  83. package/dist-server/server/piecemaker/timesheet/store.js.map +1 -1
  84. package/package.json +1 -1
  85. package/product.config.json +2 -0
  86. package/scripts/piecemaker/build-app-icon.py +88 -0
  87. package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +2 -2
  88. package/scripts/piecemaker/hooks/proxy-guard.mjs +19 -54
  89. package/scripts/release/assert-piecemaker-version.mjs +25 -0
  90. package/server/index.ts +4 -1
  91. package/server/piecemaker/anonymizer/client-config.cjs +45 -162
  92. package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +127 -0
  93. package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.lock +2396 -0
  94. package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.toml +14 -0
  95. package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +501 -0
  96. package/server/piecemaker/anonymizer/providers.cjs +14 -135
  97. package/server/piecemaker/anonymizer/rewriter-bridge.cjs +221 -0
  98. package/server/piecemaker/anonymizer/service.cjs +144 -28
  99. package/server/piecemaker/harness/citations.routes.ts +23 -1
  100. package/server/piecemaker/harness/legifrance-html.ts +107 -0
  101. package/server/piecemaker/index.ts +8 -3
  102. package/server/piecemaker/knowledge/index.ts +14 -6
  103. package/server/piecemaker/knowledge/local-routes.ts +72 -0
  104. package/server/piecemaker/knowledge/scan-jobs.ts +4 -1
  105. package/server/piecemaker/library/index.ts +14 -34
  106. package/server/piecemaker/library/store.ts +11 -3
  107. package/server/piecemaker/library/workspace-installation.ts +20 -3
  108. package/server/piecemaker/timesheet/service.ts +9 -7
  109. package/server/piecemaker/timesheet/store.ts +5 -3
  110. package/server/piecemaker/vendor/installer/assets/claude-hooks/piecemaker-guard-secrets.mjs +0 -1
  111. package/server/piecemaker/vendor/installer/assets/piecemaker.icns +0 -0
  112. package/server/piecemaker/vendor/installer/assets/piecemaker.ico +0 -0
  113. package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +56 -206
  114. package/server/piecemaker/vendor/installer/lib/commandes.mjs +2 -4
  115. package/server/piecemaker/vendor/installer/lib/conversion-client.mjs +74 -0
  116. package/server/piecemaker/vendor/installer/lib/service.mjs +4 -358
  117. package/server/piecemaker/vendor/installer/lib/state.mjs +0 -6
  118. package/server/piecemaker/vendor/installer/steps/03-python-gliner.mjs +4 -4
  119. package/server/piecemaker/vendor/installer/steps/09-claude-assets.mjs +0 -31
  120. package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +3 -4
  121. package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +1 -49
  122. package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +14 -12
  123. package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +17 -476
  124. package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +2 -2
  125. package/server/piecemaker/vendor/websocket-server/scripts/mineru_improved_1.py +11 -2
  126. package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/scanner_worker.py +13 -37
  127. package/server/piecemaker/vendor/websocket-server/scripts/requirements.txt +1 -7
  128. package/server/piecemaker/vendor/websocket-server/scripts/warmup.py +14 -85
  129. package/dist/assets/channel-DdzrPZ6Y.js +0 -1
  130. package/dist/assets/classDiagram-ZZMXUADV-Dim2o8E5.js +0 -1
  131. package/dist/assets/classDiagram-v2-VYDZK3BY-Dim2o8E5.js +0 -1
  132. package/dist/assets/index-D_Dc_nQv.css +0 -1
  133. package/dist/assets/swimlanesDiagram-VR7AAH4N-DR525d5V.js +0 -8
  134. package/server/piecemaker/anonymizer/proxy.cjs +0 -365
  135. package/server/piecemaker/vendor/installer/assets/PieceMakerApp.swift +0 -189
  136. package/server/piecemaker/vendor/installer/lib/desktop-launcher.mjs +0 -305
  137. package/server/piecemaker/vendor/installer/steps/05-certificats.mjs +0 -171
  138. package/server/piecemaker/vendor/installer/steps/14-mxc-sandbox.mjs +0 -271
  139. package/server/piecemaker/vendor/installer/steps/15-pwa-desktop.mjs +0 -64
  140. package/server/piecemaker/vendor/installer/templates/root-CLAUDE.md +0 -56
  141. package/server/piecemaker/vendor/installer/templates/workspace-CLAUDE.md +0 -52
@@ -1,337 +1,10 @@
1
1
  /**
2
- * Local PieceMaker service lifecycle.
3
- *
4
- * The CLI owns the background HTTPS server now that the Electron shell is
5
- * disabled. Runtime files live in ~/.piecemaker so the repository stays
6
- * clean and the same commands work on macOS, Windows and Linux.
7
- */
8
-
9
- import fs from 'node:fs';
10
- import https from 'node:https';
11
- import path from 'node:path';
12
- import { spawn } from 'node:child_process';
13
-
14
- import {
15
- HOME_DIR,
16
- IS_MAC,
17
- IS_WINDOWS,
18
- REPO_ROOT,
19
- commandExists,
20
- ensureDir,
21
- npmBin,
22
- npmEnv,
23
- runCapture,
24
- } from './platform.mjs';
25
- import { loadConfig, readEnv } from './state.mjs';
26
-
27
- export const PID_FILE = path.join(HOME_DIR, 'server.pid');
28
- export const LOG_FILE = path.join(HOME_DIR, 'server.log');
29
- export const SERVER_FILE = path.join(REPO_ROOT, 'websocket-server', 'server.cjs');
30
-
31
- const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
32
-
33
- export function adminUrl(config = loadConfig()) {
34
- return `https://localhost:${Number(config.port) || 43098}/admin/`;
35
- }
36
-
37
- function healthUrl(config = loadConfig()) {
38
- return `https://localhost:${Number(config.port) || 43098}/health`;
39
- }
40
-
41
- export function readServerPid() {
42
- try {
43
- const pid = Number.parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
44
- return Number.isInteger(pid) && pid > 0 ? pid : null;
45
- } catch {
46
- return null;
47
- }
48
- }
49
-
50
- export function isProcessRunning(pid) {
51
- if (!pid) return false;
52
- try {
53
- process.kill(pid, 0);
54
- return true;
55
- } catch (error) {
56
- return error?.code === 'EPERM';
57
- }
58
- }
59
-
60
- function parsePidList(output) {
61
- return [...new Set(
62
- String(output || '')
63
- .split(/\s+/)
64
- .map((value) => Number.parseInt(value, 10))
65
- .filter((pid) => Number.isInteger(pid) && pid > 0 && pid !== process.pid)
66
- )];
67
- }
68
-
69
- /**
70
- * Return every process listening on a TCP port, whether PieceMaker started it
71
- * or not. lsof is available by default on macOS; fuser/ss cover common Linux
72
- * installs, while Windows exposes the owning PID through PowerShell.
2
+ * Maintenance du dépôt installé : mise à jour Git/npm et redémarrage du démon
3
+ * Telegram. Aucun serveur local n'est piloté ici — l'application et son proxy
4
+ * PII sont démarrés par la commande `piecemaker`.
73
5
  */
74
- export function findListeningPids(port) {
75
- const normalizedPort = Number(port);
76
- if (!Number.isInteger(normalizedPort) || normalizedPort < 1 || normalizedPort > 65_535) {
77
- throw new Error(`Port TCP invalide : ${port}`);
78
- }
79
-
80
- if (IS_WINDOWS) {
81
- const powershell = runCapture('powershell.exe', [
82
- '-NoProfile',
83
- '-NonInteractive',
84
- '-Command',
85
- `$ErrorActionPreference='Stop'; @(Get-NetTCPConnection -State Listen -LocalPort ${normalizedPort}).OwningProcess`,
86
- ]);
87
- if (!powershell.error && powershell.code === 0) return parsePidList(powershell.stdout);
88
-
89
- const netstat = runCapture('netstat', ['-ano', '-p', 'tcp']);
90
- if (!netstat.error && netstat.code === 0) {
91
- const pids = netstat.stdout.split(/\r?\n/).flatMap((line) => {
92
- const columns = line.trim().split(/\s+/);
93
- if (columns.length < 5 || columns[0].toUpperCase() !== 'TCP') return [];
94
- if (!columns[1].endsWith(`:${normalizedPort}`) || columns[3].toUpperCase() !== 'LISTENING') return [];
95
- return parsePidList(columns.at(-1));
96
- });
97
- return [...new Set(pids)];
98
- }
99
-
100
- throw new Error(`Impossible d’identifier le processus qui écoute sur le port ${normalizedPort}.`);
101
- }
102
-
103
- const lsof = runCapture('lsof', [
104
- '-nP',
105
- '-t',
106
- `-iTCP:${normalizedPort}`,
107
- '-sTCP:LISTEN',
108
- ]);
109
- if (!lsof.error && (lsof.code === 0 || lsof.code === 1)) return parsePidList(lsof.stdout);
110
-
111
- const fuser = runCapture('fuser', ['-n', 'tcp', String(normalizedPort)]);
112
- if (!fuser.error && (fuser.code === 0 || fuser.code === 1)) return parsePidList(fuser.stdout);
113
-
114
- const ss = runCapture('ss', ['-H', '-ltnp', 'sport', '=', `:${normalizedPort}`]);
115
- if (!ss.error && ss.code === 0) {
116
- return [...new Set(
117
- [...ss.stdout.matchAll(/pid=(\d+)/g)]
118
- .map((match) => Number.parseInt(match[1], 10))
119
- .filter((pid) => pid > 0 && pid !== process.pid)
120
- )];
121
- }
122
-
123
- throw new Error(`Impossible d’identifier le processus qui écoute sur le port ${normalizedPort}.`);
124
- }
125
-
126
- function signalProcesses(pids, signal) {
127
- const failures = [];
128
- for (const pid of pids) {
129
- try {
130
- process.kill(pid, signal);
131
- } catch (error) {
132
- if (error.code !== 'ESRCH') failures.push(`${pid} (${error.code || error.message})`);
133
- }
134
- }
135
- if (failures.length) {
136
- throw new Error(`Impossible d’arrêter le(s) processus ${failures.join(', ')}.`);
137
- }
138
- }
139
-
140
- async function waitForPortRelease(port, managedPid, timeoutMs) {
141
- const deadline = Date.now() + timeoutMs;
142
- while (Date.now() < deadline) {
143
- const listeners = findListeningPids(port);
144
- if (!listeners.length && (!managedPid || !isProcessRunning(managedPid))) return true;
145
- await delay(200);
146
- }
147
- return false;
148
- }
149
-
150
- function removePid(expectedPid = null) {
151
- const current = readServerPid();
152
- if (expectedPid && current !== expectedPid) return;
153
- try {
154
- fs.unlinkSync(PID_FILE);
155
- } catch (error) {
156
- if (error.code !== 'ENOENT') throw error;
157
- }
158
- }
159
-
160
- export function probeServer(config = loadConfig(), timeoutMs = 1200) {
161
- const url = new URL(healthUrl(config));
162
- return new Promise((resolve) => {
163
- const request = https.get(
164
- {
165
- hostname: '127.0.0.1',
166
- port: url.port,
167
- path: url.pathname,
168
- rejectUnauthorized: false,
169
- timeout: timeoutMs,
170
- },
171
- (response) => {
172
- response.resume();
173
- resolve(response.statusCode === 200);
174
- }
175
- );
176
- request.on('timeout', () => request.destroy());
177
- request.on('error', () => resolve(false));
178
- });
179
- }
180
-
181
- export async function getServerStatus(config = loadConfig()) {
182
- const pid = readServerPid();
183
- const processRunning = isProcessRunning(pid);
184
- if (pid && !processRunning) removePid(pid);
185
- const running = await probeServer(config);
186
- return {
187
- running,
188
- pid: processRunning ? pid : null,
189
- managed: Boolean(processRunning && pid),
190
- url: adminUrl(config),
191
- logFile: LOG_FILE,
192
- };
193
- }
194
-
195
- function serverEnvironment(config) {
196
- const savedEnv = readEnv();
197
- return {
198
- ...process.env,
199
- ...savedEnv,
200
- PORT: String(Number(config.port) || 43098),
201
- PYTHON_PATH: config.pythonPath || savedEnv.PYTHON_PATH || '',
202
- PIECEMAKER_HOST: '127.0.0.1',
203
- };
204
- }
205
-
206
- export async function startServer({ timeoutMs = 15_000 } = {}) {
207
- const config = loadConfig();
208
- const current = await getServerStatus(config);
209
- if (current.running) return { ...current, started: false };
210
-
211
- if (current.pid && current.managed) {
212
- throw new Error(
213
- `Le processus serveur ${current.pid} existe mais ne répond pas. Consultez ${LOG_FILE} ou lancez "piecemaker stop".`
214
- );
215
- }
216
-
217
- const keyFile = path.join(REPO_ROOT, 'websocket-server', 'localhost.key');
218
- const certFile = path.join(REPO_ROOT, 'websocket-server', 'localhost.crt');
219
- if (!fs.existsSync(keyFile) || !fs.existsSync(certFile)) {
220
- throw new Error(
221
- 'Les certificats HTTPS sont absents. Lancez "piecemaker install", puis installez le composant 05 — Certificats HTTPS.'
222
- );
223
- }
224
-
225
- ensureDir(HOME_DIR);
226
- fs.appendFileSync(LOG_FILE, `\n[${new Date().toISOString()}] Démarrage de PieceMaker\n`, 'utf8');
227
- const logFd = fs.openSync(LOG_FILE, 'a');
228
- let child;
229
- try {
230
- child = spawn(process.execPath, [SERVER_FILE], {
231
- cwd: REPO_ROOT,
232
- detached: true,
233
- windowsHide: true,
234
- stdio: ['ignore', logFd, logFd],
235
- env: serverEnvironment(config),
236
- });
237
- } finally {
238
- fs.closeSync(logFd);
239
- }
240
-
241
- child.unref();
242
- fs.writeFileSync(PID_FILE, `${child.pid}\n`, 'utf8');
243
-
244
- const deadline = Date.now() + timeoutMs;
245
- while (Date.now() < deadline) {
246
- if (await probeServer(config)) {
247
- return {
248
- running: true,
249
- pid: child.pid,
250
- managed: true,
251
- url: adminUrl(config),
252
- logFile: LOG_FILE,
253
- started: true,
254
- };
255
- }
256
- if (!isProcessRunning(child.pid)) break;
257
- await delay(250);
258
- }
259
-
260
- removePid(child.pid);
261
- const tail = readLogs(20);
262
- throw new Error(`Le serveur n'a pas démarré. Consultez ${LOG_FILE}.${tail ? `\n${tail}` : ''}`);
263
- }
264
-
265
- export async function stopServer({ timeoutMs = 8_000, forceTimeoutMs = 2_000 } = {}) {
266
- const config = loadConfig();
267
- const status = await getServerStatus(config);
268
- const port = Number(config.port) || 43098;
269
- const listeners = findListeningPids(port);
270
- const targets = [...new Set([status.pid, ...listeners].filter(Boolean))];
271
- if (!targets.length) return { ...status, stopped: false, alreadyStopped: true };
272
-
273
- signalProcesses(targets, 'SIGTERM');
274
- let stopped = await waitForPortRelease(port, status.pid, timeoutMs);
275
-
276
- if (!stopped) {
277
- const remaining = [...new Set([
278
- ...findListeningPids(port),
279
- status.pid && isProcessRunning(status.pid) ? status.pid : null,
280
- ].filter(Boolean))];
281
- signalProcesses(remaining, 'SIGKILL');
282
- stopped = await waitForPortRelease(port, status.pid, forceTimeoutMs);
283
- }
284
-
285
- if (status.pid && !isProcessRunning(status.pid)) removePid(status.pid);
286
- const remaining = findListeningPids(port);
287
- const running = await probeServer(config);
288
- if (!stopped || remaining.length || running) {
289
- const suffix = remaining.length ? ` PID restant(s) : ${remaining.join(', ')}.` : '';
290
- throw new Error(`Le port ${port} n’a pas pu être libéré.${suffix}`);
291
- }
292
- return {
293
- running: false,
294
- pid: null,
295
- managed: false,
296
- stopped: true,
297
- stoppedPids: targets,
298
- url: adminUrl(config),
299
- logFile: LOG_FILE,
300
- };
301
- }
302
-
303
- export function readLogs(lines = 80) {
304
- try {
305
- const content = fs.readFileSync(LOG_FILE, 'utf8').trimEnd();
306
- return content.split(/\r?\n/).slice(-lines).join('\n');
307
- } catch {
308
- return '';
309
- }
310
- }
311
-
312
- export function openExternal(url) {
313
- let command;
314
- let args;
315
- if (IS_WINDOWS) {
316
- command = 'cmd.exe';
317
- args = ['/d', '/s', '/c', 'start', '', url];
318
- } else if (IS_MAC) {
319
- command = 'open';
320
- args = [url];
321
- } else {
322
- command = 'xdg-open';
323
- args = [url];
324
- }
325
6
 
326
- const child = spawn(command, args, { detached: true, stdio: 'ignore', windowsHide: true });
327
- child.unref();
328
- }
329
-
330
- export async function openAdmin() {
331
- const status = await startServer();
332
- openExternal(status.url);
333
- return status;
334
- }
7
+ import { IS_MAC, REPO_ROOT, npmBin, npmEnv, runCapture } from './platform.mjs';
335
8
 
336
9
  function runOrThrow(command, args, label) {
337
10
  const result = runCapture(command, args, { cwd: REPO_ROOT });
@@ -405,33 +78,6 @@ export function checkForUpdate() {
405
78
  };
406
79
  }
407
80
 
408
- export const ROOT_CLAUDE_MD = path.join(REPO_ROOT, 'CLAUDE.md');
409
- const ROOT_CLAUDE_MD_TEMPLATE = path.join(REPO_ROOT, 'installer', 'templates', 'root-CLAUDE.md');
410
-
411
- /**
412
- * Deposit the user persona at the repo-root CLAUDE.md, from the versioned
413
- * gabarit installer/templates/root-CLAUDE.md.
414
- *
415
- * The root CLAUDE.md is gitignored: in a dev clone it holds architecture notes,
416
- * and a fresh runtime clone doesn't ship it at all. So it can't be delivered by
417
- * git — the installer deposits it. `git reset --hard` during an update also
418
- * deletes the working-tree file the moment a clone pulls the commit that
419
- * untracked it, so `piecemaker update` must redeposit it too, not just
420
- * `piecemaker install` (step 09).
421
- *
422
- * Rule: absent → write the persona; present → never overwrite. That single rule
423
- * protects both a freshly deposited persona and a dev clone's architecture
424
- * notes (which stay untouched).
425
- *
426
- * Returns { status: 'kept' | 'deposited' | 'missing-template' }.
427
- */
428
- export function depositRootClaudeMd() {
429
- if (fs.existsSync(ROOT_CLAUDE_MD)) return { status: 'kept' };
430
- if (!fs.existsSync(ROOT_CLAUDE_MD_TEMPLATE)) return { status: 'missing-template' };
431
- fs.copyFileSync(ROOT_CLAUDE_MD_TEMPLATE, ROOT_CLAUDE_MD);
432
- return { status: 'deposited' };
433
- }
434
-
435
81
  /**
436
82
  * Apply a pending update: move the working tree to the fetched revision, then
437
83
  * reconcile node_modules with the new package.json. `git` handles both halves
@@ -32,14 +32,8 @@ function writeJson(file, data) {
32
32
 
33
33
  export function loadConfig() {
34
34
  const defaults = {
35
- port: 43098,
36
35
  pythonPath: null,
37
36
  venvPath: path.join(HOME_DIR, 'venv'),
38
- // Confinement OS optionnel (microsoft/mxc). mxcPath pointe le binaire mxc-exec
39
- // une fois construit par l'étape 14 ; mxcEnabled permet de désactiver le
40
- // confinement sans supprimer le binaire.
41
- mxcPath: null,
42
- mxcEnabled: true,
43
37
  };
44
38
  return { ...defaults, ...readJson(CONFIG_FILE, {}) };
45
39
  }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * This is the anonymisation core: a venv is created at config.venvPath,
5
5
  * websocket-server/scripts/requirements.txt is installed into it, then
6
- * warmup.py downloads GLiNER2.5 multilingual (~1.1GB) and spaCy fr/en. An
6
+ * warmup.py downloads GLiNER2.5 multilingual (~1.1GB). An
7
7
  * existing GLiNER2 checkpoint triggers an explicit, mandatory migration prompt.
8
8
  * warmup.py emits JSON status lines on stdout (see log_json in warmup.py) interleaved
9
9
  * with plain emoji lines (log_plain) — both are surfaced through the spinner.
@@ -68,7 +68,7 @@ export function glinerInstallState(status) {
68
68
  export function glinerDownloadQuestion(state) {
69
69
  return state.replacementRequired
70
70
  ? `GLiNER2.5 multilingue remplace obligatoirement l’ancien modèle ${state.legacyModels.join(', ')}. Télécharger et activer ${state.preferredModelId} (environ 1,1 Go) ?`
71
- : `Télécharger et activer ${state.preferredModelId} ainsi que les modèles spaCy fr/en (environ 1,2 Go au total) ?`;
71
+ : `Télécharger et activer ${state.preferredModelId} (environ 1,1 Go) ?`;
72
72
  }
73
73
 
74
74
  function readWarmupStatus(python) {
@@ -158,7 +158,7 @@ export async function install(ctx) {
158
158
  };
159
159
  }
160
160
 
161
- const spin = spinner('Installation des modèles GLiNER2.5 et spaCy...');
161
+ const spin = spinner('Installation du modèle GLiNER2.5...');
162
162
  const code = await run(vp.python, [WARMUP], { cwd: SCRIPTS_DIR, onLine: onWarmupLine(spin) });
163
163
 
164
164
  if (code !== 0) {
@@ -181,7 +181,7 @@ export async function install(ctx) {
181
181
  note: `Les modèles requis ne sont pas prêts (${missingAfterWarmup.join(', ') || installedState.preferredModelId}). Relancez l’étape « 03-python-gliner » pour réessayer.`,
182
182
  };
183
183
  }
184
- spin.succeed('GLiNER2.5 multilingue et modèles spaCy prêts');
184
+ spin.succeed('GLiNER2.5 multilingue prêt');
185
185
 
186
186
  return { status: 'done', note: '' };
187
187
  }
@@ -13,7 +13,6 @@ import { createRequire } from 'node:module';
13
13
 
14
14
  import { log } from '../lib/ui.mjs';
15
15
  import { REPO_ROOT, commandExists } from '../lib/platform.mjs';
16
- import { depositRootClaudeMd } from '../lib/service.mjs';
17
16
  import { loadConfig } from '../lib/state.mjs';
18
17
 
19
18
  const require = createRequire(import.meta.url);
@@ -28,8 +27,6 @@ export const meta = {
28
27
  };
29
28
 
30
29
  const PLUGIN_DIR = path.join(REPO_ROOT, 'piecemaker-plugin');
31
- const CLAUDE_MD = path.join(REPO_ROOT, 'CLAUDE.md');
32
- const CLAUDE_MD_TEMPLATE = path.join(REPO_ROOT, 'installer', 'templates', 'root-CLAUDE.md');
33
30
 
34
31
  function dependencies(overrides = {}) {
35
32
  return {
@@ -42,29 +39,12 @@ function dependencies(overrides = {}) {
42
39
  syncClaudeAssets,
43
40
  claudeHooksStatus,
44
41
  installClaudeHooks,
45
- depositRootClaudeMd,
46
42
  loadConfig,
47
43
  refreshRegisteredCaseRules,
48
44
  ...overrides,
49
45
  };
50
46
  }
51
47
 
52
- /**
53
- * Dépose la persona utilisateur si le CLAUDE.md racine est absent. Un fichier
54
- * existant n'est jamais remplacé, afin de préserver les repères d'un clone de
55
- * développement.
56
- */
57
- function reconcileClaudeMd(ops) {
58
- const result = ops.depositRootClaudeMd();
59
- if (result.status === 'missing-template') {
60
- return { status: 'partial', note: `CLAUDE.md absent et gabarit introuvable (${CLAUDE_MD_TEMPLATE}).` };
61
- }
62
- if (result.status === 'deposited') {
63
- return { status: 'done', note: 'CLAUDE.md (persona utilisateur) déposé depuis le gabarit.' };
64
- }
65
- return { status: 'done', note: '' };
66
- }
67
-
68
48
  export async function install(ctx, overrides = {}) {
69
49
  const ops = dependencies(overrides);
70
50
  if (!ops.existsSync(PLUGIN_DIR)) {
@@ -84,7 +64,6 @@ export async function install(ctx, overrides = {}) {
84
64
  if (ctx.dryRun) {
85
65
  ops.log.info(`[simulation] enregistrement de ${assets.length} skill(s)/agent(s) PieceMaker dans ~/.claude`);
86
66
  ops.log.info('[simulation] fusion des hooks PieceMaker dans ~/.claude/settings.json');
87
- ops.log.info('[simulation] dépôt de CLAUDE.md (racine) depuis le gabarit si absent');
88
67
  ops.log.info('[simulation] actualisation des instructions des dossiers juridiques enregistrés');
89
68
  return { status: 'skipped', note: 'Mode simulation — aucune modification effectuée.' };
90
69
  }
@@ -106,8 +85,6 @@ export async function install(ctx, overrides = {}) {
106
85
  ops.log.warn(`Instructions non actualisées pour ${failure.folder} : ${failure.error}`);
107
86
  }
108
87
 
109
- const claudeMd = reconcileClaudeMd(ops);
110
- if (claudeMd.status !== 'done') return claudeMd;
111
88
  if (result.conflicts.length) {
112
89
  return {
113
90
  status: 'partial',
@@ -130,16 +107,8 @@ export async function check(_ctx, overrides = {}) {
130
107
  .filter((asset) => !['linked', 'copied'].includes(
131
108
  ops.claudeAssetStatus(REPO_ROOT, ops.userHome, asset)?.state,
132
109
  ));
133
- const claudeMdOk = ops.existsSync(CLAUDE_MD);
134
110
  const hooksOk = ops.claudeHooksStatus(REPO_ROOT, ops.userHome).ok;
135
111
 
136
- if (!claudeMdOk && unregistered.length) {
137
- return {
138
- status: 'partial',
139
- note: `CLAUDE.md absent et ${unregistered.length} skill(s)/agent(s) non enregistré(s) dans ~/.claude.`,
140
- };
141
- }
142
- if (!claudeMdOk) return { status: 'partial', note: 'CLAUDE.md absent.' };
143
112
  if (!hooksOk) return { status: 'partial', note: 'Hooks PieceMaker non enregistrés dans ~/.claude/settings.json.' };
144
113
  if (unregistered.length) {
145
114
  return {
@@ -39,11 +39,10 @@ function validPort(value) {
39
39
  function readRouting() {
40
40
  try {
41
41
  const settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf8'));
42
- const value = settings?.env?.ANTHROPIC_BASE_URL;
42
+ const value = settings?.env?.HTTPS_PROXY;
43
43
  if (typeof value !== 'string' || !value) return false;
44
44
  const url = new URL(value);
45
- return ['127.0.0.1', 'localhost'].includes(url.hostname)
46
- && url.pathname.replace(/\/$/, '') === '/anthropic';
45
+ return ['127.0.0.1', 'localhost'].includes(url.hostname);
47
46
  } catch {
48
47
  return false;
49
48
  }
@@ -111,7 +110,7 @@ function colore(texte, couleur) {
111
110
 
112
111
  function badge(routed, actif) {
113
112
  if (routed && actif) return colore('🔒 Anonymisation PieceMaker active ✓', VERT);
114
- if (routed) return colore('⚠️ Anonymisation PieceMaker inactive — proxy arrêté (piecemaker start)', ROUGE);
113
+ if (routed) return colore('⚠️ Anonymisation PieceMaker inactive — proxy arrêté (piecemaker)', ROUGE);
115
114
  return colore('⚠️ Anonymisation PieceMaker inactive — accès direct', ROUGE);
116
115
  }
117
116
 
@@ -32,13 +32,10 @@ const {
32
32
  worktreeDetails,
33
33
  } = require('../piecemaker-plugin/scripts/lib/commits.cjs');
34
34
  const {
35
- cancelOriginalsJob,
36
- getJob,
37
35
  listOriginals,
38
36
  readCaseMapping,
39
37
  rebuildCaseMapping,
40
38
  saveCaseMapping,
41
- startOriginalsJob,
42
39
  writeCaseMapping,
43
40
  } = require('./originals-pipeline.cjs');
44
41
  const { invalidateOriginals, listOriginalsCached } = require('../../originals-cache.cjs');
@@ -1619,8 +1616,7 @@ const REVEAL_TARGETS = new Set(['files', 'terminal']);
1619
1616
  /**
1620
1617
  * Résout un chemin reçu du navigateur (relatif à la racine d'un dossier
1621
1618
  * juridique) vers un chemin absolu, sans jamais remonter hors de cette
1622
- * racine. Même garde que `startOriginalsJob` (originals-pipeline.cjs) pour
1623
- * les pièces sélectionnées : `path.resolve` puis vérification du préfixe.
1619
+ * racine : `path.resolve` puis vérification du préfixe.
1624
1620
  */
1625
1621
  function resolveCasePath(caseRoot, relativePath) {
1626
1622
  const relative = String(relativePath || '').replaceAll('\\', '/').replace(/^\.\//, '');
@@ -2095,9 +2091,6 @@ function createAdminRouter({
2095
2091
  ok: true,
2096
2092
  version: pkg.version || 'inconnue',
2097
2093
  repoRoot,
2098
- certificatesReady:
2099
- fs.existsSync(path.join(repoRoot, 'websocket-server', 'localhost.crt')) &&
2100
- fs.existsSync(path.join(repoRoot, 'websocket-server', 'localhost.key')),
2101
2094
  files: {
2102
2095
  skills: files.filter((file) => file.kind === 'skill').length,
2103
2096
  agents: files.filter((file) => file.kind === 'agent').length,
@@ -2718,47 +2711,6 @@ function createAdminRouter({
2718
2711
  }
2719
2712
  });
2720
2713
 
2721
- // ── Pièces originales : conversion Markdown et pipeline d'anonymisation ──
2722
- // Les deux traitements sont longs (OCR, modèles NER) : la route rend la main
2723
- // avec un identifiant de travail que l'administration interroge ensuite.
2724
-
2725
- router.post('/originals/pipeline', async (req, res) => {
2726
- try {
2727
- const legalCase = selectedCase(req.body?.case);
2728
- const job = await startOriginalsJob({
2729
- casesRoot: legalCase.casesRoot,
2730
- caseName: legalCase.caseName,
2731
- homeDir,
2732
- action: String(req.body?.action || ''),
2733
- files: Array.isArray(req.body?.files) ? req.body.files : [],
2734
- options: {
2735
- // Sans `force`, un travail sans sélection ne refait que les pièces
2736
- // dont le Markdown ou le scan PII manque.
2737
- force: req.body?.force === true,
2738
- engine: String(req.body?.engine || '').trim() || undefined,
2739
- mode: String(req.body?.mode || '').trim() || undefined,
2740
- lang: String(req.body?.lang || '').trim() || undefined,
2741
- },
2742
- });
2743
- job.reference = legalCase.id;
2744
- res.status(202).json({ ok: true, job });
2745
- } catch (error) {
2746
- res.status(400).json({ error: error.message });
2747
- }
2748
- });
2749
-
2750
- router.get('/originals/job', (req, res) => {
2751
- const job = getJob(req.query.id);
2752
- if (!job) return res.status(404).json({ error: 'Travail inconnu ou expiré.' });
2753
- res.json({ job });
2754
- });
2755
-
2756
- router.delete('/originals/job', (req, res) => {
2757
- const job = cancelOriginalsJob(req.query.id);
2758
- if (!job) return res.status(404).json({ error: 'Aucun traitement en cours pour cet identifiant.' });
2759
- res.json({ ok: true, job });
2760
- });
2761
-
2762
2714
  // ── Protection des pièces ────────────────────────────────────────────────
2763
2715
  // La protection est une propriété du fichier, pas de son emplacement : c'est
2764
2716
  // ici qu'on la décide, et les hooks Claude Code l'appliquent