@piecemaker-legal/piecemaker 2.0.9 → 2.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/FindReplaceDialog-KU5TEIBJ-BucOHLdm.js +1 -0
- package/dist/assets/FootnotePropertiesDialog-W3YHM4BV-D5UqbelV.js +1 -0
- package/dist/assets/HyperlinkDialog-ZYH7ZHRF-8yEhLigd.js +144 -0
- package/dist/assets/HyperlinkDialog-ZYH7ZHRF-qIPBOssF.css +1 -0
- package/dist/assets/ImagePositionDialog-HEWYR6Q3-wGIBYxCm.js +1 -0
- package/dist/assets/ImagePropertiesDialog-RAB47C6M-D86PckZ2.js +1 -0
- package/dist/assets/PageSetupDialog-AIGIXO3E-DLETP-7v.js +1 -0
- package/dist/assets/SplitCellDialog-IFBKYCD6-CwdQBXeb.js +1 -0
- package/dist/assets/TablePropertiesDialog-TMLKGOOW-j2nA7KJs.js +1 -0
- package/dist/assets/WatermarkDialog-KRL7WMIS-XFJ9gTkC.js +1 -0
- package/dist/assets/{abnfDiagram-VCTEODGH-WrsSAHOe.js → abnfDiagram-VCTEODGH-DiCrND_Y.js} +1 -1
- package/dist/assets/arc-DOjgbMYz.js +1 -0
- package/dist/assets/architectureDiagram-5GKGNRK7-brzIFVhl.js +36 -0
- package/dist/assets/{blockDiagram-I7D4REHJ-zGMnF7Ox.js → blockDiagram-I7D4REHJ-C8JhTjQQ.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-ClWw3bqt.js → c4Diagram-7LVT6UL2-jfYxccyk.js} +1 -1
- package/dist/assets/channel-Dajzu2W1.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-C-9ICPHv.js → chunk-2Q5K7J3B-CrlEShp9.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-Bawy3teT.js → chunk-5VM5RSS4-DzPlUHGq.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-DQ375RQO.js → chunk-F27PBJKO-Cp9cpeWU.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-CflNF2f6.js → chunk-IMKFNOWR-BifUldEN.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CIXQv0gc.js → chunk-JWPE2WC7-ggPEIkWu.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-Cza5j6Qw.js → chunk-POPQ4Y6H-3h_nayRX.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-GQ_VvWZR.js → chunk-SVP7TREG-CH29aG6J.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-BEmU16W5.js → chunk-TICWLB2K-PluJUM5k.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-C1KpkLYH.js → chunk-XXDRQBXY-DXU39Vwg.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-dDEP_giv.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-dDEP_giv.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-CZE48TRG.js → cose-bilkent-JH36ORCC-Dz6roYrN.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-L-tx1eC-.js → cynefin-OW5HDTMX-BLzrpSit.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-Coa8oJwR.js → cynefinDiagram-5FMLGOSQ-kfxnERDZ.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-Bu_3Y75K.js → dagre-GXQ25YYZ-DP57bp-N.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-CFiiV4es.js → diagram-S7CK7UJ4-DAlsrQVW.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-CCXheGAA.js → diagram-UQ7AKVKN-By5yrMer.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-B70m4lr_.js → diagram-VSXAHHWV-Dx89kryN.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-CHkiMOVA.js → diagram-VX7I27RA-B9fARitZ.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-_awpc4Bs.js → diagram-Z3DM3KII-DC15CEH1.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-CZzjH86H.js → ebnfDiagram-PWID7BFC-BKQzghGF.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-9DgiHZg9.js → erDiagram-RLTQ6QDP-CPY6RM7G.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-DHY_503b.js → flowDiagram-HODETNUW-4dL4GJcf.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-DPiiYZ_l.js → ganttDiagram-EL5Y4UJY-D4-cVcfb.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-Ddd_evm7.js → gitGraphDiagram-WWUBYQGX-BMeVIEiX.js} +1 -1
- package/dist/assets/{index-tibHq7Qp.js → index-DNtFDOni.js} +140 -140
- package/dist/assets/index-Dc9wNuS4.css +1 -0
- package/dist/assets/{infoDiagram-27XIBGKW-BPRNhfwb.js → infoDiagram-27XIBGKW-D5qMSUEU.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-BXx6aqkq.js → ishikawaDiagram-5VMMS53U-BDnyKRHm.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-BK8TZM29.js → journeyDiagram-3NMN7TZE-dGz5ZGzq.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-CHioRmxz.js → kanban-definition-UXKFOSKX-D7UAz7o_.js} +6 -6
- package/dist/assets/layout-DKrmEGrz.js +1 -0
- package/dist/assets/{linear-CTEhUcKG.js → linear-CrTdMY-p.js} +1 -1
- package/dist/assets/{mermaid.core-DYUb_UUB.js → mermaid.core-CXQOT5G6.js} +7 -7
- package/dist/assets/{mindmap-definition-YA3MSWOX-D7ODZ4oS.js → mindmap-definition-YA3MSWOX-VgEiYfay.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-8J-_2_kw.js → pegDiagram-XKGWAZYB-9IqT8gGx.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-ebg0yfMi.js → pieDiagram-E7YTZNPT-D98pRE-t.js} +1 -1
- package/dist/assets/processTemplate-GPFDLQMX-B1sVV2O0.js +22 -0
- package/dist/assets/{quadrantDiagram-AXDQQJYC-BoWkDiFb.js → quadrantDiagram-AXDQQJYC-b9PwkYzL.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-BRJ77ldz.js → railroadDiagram-O6MQD6OU-Dz87hsPE.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-Z0JJgktA.js → requirementDiagram-BXWQKSXE-CkcV73dK.js} +1 -1
- package/dist/assets/sankeyDiagram-P5KCCOFB-UBN31Yrs.js +40 -0
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-BPAjtXwp.js → sequenceDiagram-WJ2MYXX4-CpQxd5Za.js} +1 -1
- package/dist/assets/{server.browser-BzI1jrZw.js → server.browser-BY3bL-IX.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-BGZPCzM4.js → sizeCapture-INFHLROL-mCsFmyzw.js} +1 -1
- package/dist/assets/stateDiagram-D77RDMKH-B0MTBN-3.js +1 -0
- package/dist/assets/stateDiagram-v2-MP3YSRHH-DzBPN39W.js +1 -0
- package/dist/assets/{swimlanes-42K2YHIH-DvuB15qF.js → swimlanes-42K2YHIH-Dzydjm0e.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-BXQpbUMD.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-ZIziYCIF.js → timeline-definition-24CTP7MA-Dz0toOLu.js} +3 -3
- package/dist/assets/{vendor-codemirror-CjW0lHR5.js → vendor-codemirror-CN-sJePf.js} +2 -2
- package/dist/assets/{vendor-react-D809iwXR.js → vendor-react-B22wqN3J.js} +5 -5
- package/dist/assets/{vennDiagram-4TSXK5OY-C5_B9gWa.js → vennDiagram-4TSXK5OY-DOWp7dkf.js} +4 -4
- package/dist/assets/{wardleyDiagram-VM6X3IG4-DMlH9Afw.js → wardleyDiagram-VM6X3IG4-CUxsztUB.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-BldhJt7i.js → xychartDiagram-S5SC5T6Z-DS96vNED.js} +1 -1
- package/dist/index.html +4 -4
- package/dist/sw.js +1 -1
- package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js +4 -53
- package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js +2 -3
- package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js +30 -0
- package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js +24 -0
- package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js.map +1 -0
- package/dist-server/server/piecemaker/agent-instructions.js +148 -0
- package/dist-server/server/piecemaker/agent-instructions.js.map +1 -0
- package/dist-server/server/piecemaker/anonymizer/lifecycle.js +2 -3
- package/dist-server/server/piecemaker/anonymizer/lifecycle.js.map +1 -1
- package/dist-server/server/piecemaker/docx-document.js +50 -0
- package/dist-server/server/piecemaker/docx-document.js.map +1 -0
- package/dist-server/server/piecemaker/index.js +13 -2
- package/dist-server/server/piecemaker/index.js.map +1 -1
- package/dist-server/server/piecemaker/model-discovery/codex-catalog.js +46 -0
- package/dist-server/server/piecemaker/model-discovery/codex-catalog.js.map +1 -0
- package/dist-server/server/piecemaker/model-discovery/index.js +42 -0
- package/dist-server/server/piecemaker/model-discovery/index.js.map +1 -0
- package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js +19 -0
- package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js.map +1 -0
- package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js +61 -0
- package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js.map +1 -0
- package/dist-server/server/piecemaker/project-registry.js +29 -0
- package/dist-server/server/piecemaker/project-registry.js.map +1 -0
- package/package.json +12 -1
- package/scripts/piecemaker/cli/disable-anonymizer.mjs +19 -0
- package/scripts/piecemaker/cli/lib/config.mjs +0 -1
- package/scripts/piecemaker/cli/lib/plugins.mjs +1 -0
- package/scripts/piecemaker/cli/lib/services.mjs +1 -10
- package/scripts/piecemaker/cli/piecemaker.mjs +0 -3
- package/server/modules/providers/list/mistral/mistral-models.provider.ts +4 -53
- package/server/modules/providers/list/mistral/mistral-runtime.provider.ts +2 -3
- package/server/modules/providers/list/mistral/mistral-vibe-models.ts +43 -0
- package/server/modules/providers/tests/mistral-vibe-models.test.ts +27 -0
- package/server/piecemaker/agent-instructions.ts +148 -0
- package/server/piecemaker/anonymizer/client-config.cjs +92 -92
- package/server/piecemaker/anonymizer/dictionary.cjs +34 -3
- package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +9 -16
- package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.toml +1 -1
- package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +251 -268
- package/server/piecemaker/anonymizer/hudsucker-smoke.cjs +343 -0
- package/server/piecemaker/anonymizer/lifecycle.ts +2 -3
- package/server/piecemaker/anonymizer/rewriter-bridge.cjs +13 -4
- package/server/piecemaker/anonymizer/rewriter-worker.cjs +85 -0
- package/server/piecemaker/anonymizer/service.cjs +109 -179
- package/server/piecemaker/anonymizer/sqlite-dictionary.cjs +12 -1
- package/server/piecemaker/docx-document.ts +54 -0
- package/server/piecemaker/harness/decisions.cjs +1 -1
- package/server/piecemaker/harness/index.cjs +2 -3
- package/server/piecemaker/harness/verification.cjs +1 -1
- package/server/piecemaker/index.ts +15 -3
- package/server/piecemaker/model-discovery/codex-catalog.ts +67 -0
- package/server/piecemaker/model-discovery/index.ts +69 -0
- package/server/piecemaker/model-discovery/mistral-catalog.ts +36 -0
- package/server/piecemaker/model-discovery/stdio-rpc.ts +79 -0
- package/server/piecemaker/project-registry.ts +34 -0
- package/server/piecemaker/router.cjs +6 -8
- package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +31 -77
- package/server/piecemaker/vendor/installer/lib/codex-skills.mjs +2 -84
- package/server/piecemaker/vendor/installer/lib/service.mjs +4 -24
- package/server/piecemaker/vendor/installer/steps/02-dependances-node.mjs +5 -5
- package/server/piecemaker/vendor/installer/steps/06-hooks.mjs +10 -21
- package/server/piecemaker/vendor/installer/steps/09-claude-assets.mjs +0 -11
- package/server/piecemaker/vendor/installer/steps/09-codex-plugin.mjs +6 -36
- package/server/piecemaker/vendor/installer/steps/13-garde-secrets.mjs +30 -12
- package/server/piecemaker/vendor/piecemaker-plugin/README.md +2 -9
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/billing-track.mjs +5 -5
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/classify-ai-documents.mjs +8 -8
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/commit-track.mjs +2 -2
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/compile-recherche.mjs +4 -6
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folder-structure.cjs +2 -90
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folders.cjs +71 -38
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/mapping.cjs +8 -38
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/protection.cjs +82 -61
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +189 -41
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/protect-originals.mjs +4 -55
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +24 -117
- package/server/piecemaker/vendor/piecemaker-plugin/skills/ajout-de-fonctionnalites/SKILL.md +4 -18
- package/server/piecemaker/vendor/piecemaker-plugin/skills/tamponnage/SKILL.md +2 -2
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +14 -148
- package/server/piecemaker/vendor/websocket-server/case-registry.cjs +16 -42
- package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +0 -73
- package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +0 -7
- package/server/piecemaker/vendor/websocket-server/stamping-routes.cjs +3 -12
- package/server/piecemaker/vendor/websocket-server/workspace-paths.cjs +6 -6
- package/dist/assets/arc-DAA6wH-C.js +0 -1
- package/dist/assets/architectureDiagram-5GKGNRK7-BGNHr7ef.js +0 -36
- package/dist/assets/channel-BAQyfMll.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-BNGCuvrR.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-BNGCuvrR.js +0 -1
- package/dist/assets/index-WwknP0l4.css +0 -1
- package/dist/assets/layout-DEnkyfTY.js +0 -1
- package/dist/assets/sankeyDiagram-P5KCCOFB-4PW5HTOP.js +0 -40
- package/dist/assets/stateDiagram-D77RDMKH-o-vEcKwz.js +0 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-BsuDaKkN.js +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-DcCAyKlT.js +0 -8
- package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +0 -78
- package/scripts/piecemaker/hooks/proxy-guard.mjs +0 -251
- package/server/piecemaker/anonymizer/providers.cjs +0 -120
- package/server/piecemaker/vendor/installer/steps/08-telegram.mjs +0 -482
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/central-mapping.cjs +0 -0
- package/server/piecemaker/vendor/websocket-server/telegram-admin.cjs +0 -602
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cycle de vie du proxy PII et point d'injection dans CloudCLI.
|
|
3
|
-
*
|
|
4
|
-
* Deux leviers, aucun ne touchant un fichier upstream.
|
|
5
|
-
*
|
|
6
|
-
* **L'environnement du serveur.** Les quatre chemins de lancement de CloudCLI —
|
|
7
|
-
* chat Claude, Codex, Cursor, opencode — étalent `process.env` dans le
|
|
8
|
-
* sous-processus qu'ils créent, tout comme le terminal intégré. Poser des
|
|
9
|
-
* variables ici les atteint donc tous les cinq sans ajouter une ligne ailleurs.
|
|
10
|
-
*
|
|
11
|
-
* **Les configurations utilisateur des clients.** Codex ne lit pas de variable
|
|
12
|
-
* de base d'URL, opencode préfère sa configuration ; `providers.cjs` s'en
|
|
13
|
-
* charge, et pose le garde-fou de Cursor, seul client non interceptable.
|
|
14
|
-
*
|
|
15
|
-
* Le proxy vit et meurt avec le serveur : à l'arrêt, les bases écrites sont
|
|
16
|
-
* retirées pour ne pas laisser les clients pointer un port fermé.
|
|
17
|
-
*/
|
|
18
1
|
const { spawn } = require('node:child_process');
|
|
19
2
|
const fs = require('node:fs');
|
|
20
3
|
const os = require('node:os');
|
|
@@ -22,20 +5,22 @@ const path = require('node:path');
|
|
|
22
5
|
const tls = require('node:tls');
|
|
23
6
|
|
|
24
7
|
const { createSqliteDictionaryLoader } = require('./sqlite-dictionary.cjs');
|
|
25
|
-
const {
|
|
26
|
-
const {
|
|
27
|
-
const { createHarnessJuridique } = require('../harness/index.cjs');
|
|
8
|
+
const { startRewriterWorker } = require('./rewriter-worker.cjs');
|
|
9
|
+
const { removeLegacyProxyConfig } = require('./client-config.cjs');
|
|
28
10
|
|
|
29
|
-
const ENV_VAR = 'ANTHROPIC_BASE_URL';
|
|
30
|
-
const OPENAI_ENV_VAR = 'OPENAI_BASE_URL';
|
|
31
11
|
const DEFAULT_UPSTREAM = 'https://api.anthropic.com';
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
12
|
+
const INTERCEPTED_HOSTS = ['api.anthropic.com', 'api.openai.com', 'chatgpt.com'];
|
|
13
|
+
const COVERAGE = {
|
|
14
|
+
claude: { state: 'filtered', detail: null, file: null },
|
|
15
|
+
codex: { state: 'filtered', detail: null, file: null },
|
|
16
|
+
opencode: { state: 'filtered', detail: null, file: null },
|
|
17
|
+
cursor: { state: 'blocked', detail: 'not-interceptable', file: null },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const CURSOR_REFUSAL = 'PieceMaker : cursor-agent ne peut pas être filtré par le proxy d’anonymisation '
|
|
21
|
+
+ '(son protocole n’accepte aucune base d’URL). Tant qu’un mapping existe, il est bloqué pour éviter '
|
|
22
|
+
+ 'que des noms de parties quittent la machine en clair. Utilisez Claude, Codex ou opencode.';
|
|
37
23
|
|
|
38
|
-
/** Interrupteur : `PIECEMAKER_ANONYMIZER=off`, ou `anonymizer.enabled: false` dans `config.json`. */
|
|
39
24
|
function isDisabled(homeDir) {
|
|
40
25
|
const flag = String(process.env.PIECEMAKER_ANONYMIZER || '').toLowerCase();
|
|
41
26
|
if (flag === 'off' || flag === '0' || flag === 'false') return true;
|
|
@@ -47,13 +32,6 @@ function isDisabled(homeDir) {
|
|
|
47
32
|
}
|
|
48
33
|
}
|
|
49
34
|
|
|
50
|
-
/**
|
|
51
|
-
* Cible du relais Anthropic. Une base distante déjà configurée (passerelle
|
|
52
|
-
* d'entreprise, point de terminaison alternatif) est respectée : on se chaîne
|
|
53
|
-
* derrière elle. Une base en boucle locale ne peut être qu'une instance
|
|
54
|
-
* précédente de ce proxy, et serait un relais mort : on l'ignore au profit de
|
|
55
|
-
* l'API officielle.
|
|
56
|
-
*/
|
|
57
35
|
function resolveUpstream(configured) {
|
|
58
36
|
if (!configured) return DEFAULT_UPSTREAM;
|
|
59
37
|
try {
|
|
@@ -65,35 +43,17 @@ function resolveUpstream(configured) {
|
|
|
65
43
|
}
|
|
66
44
|
}
|
|
67
45
|
|
|
68
|
-
/**
|
|
69
|
-
* Lecture du rapport de `configureProviders` en une couverture par client, la
|
|
70
|
-
* seule chose que l'interface a besoin de montrer.
|
|
71
|
-
*
|
|
72
|
-
* `filtered` : les échanges passent par le proxy et sont anonymisés.
|
|
73
|
-
* `blocked` : le client ne peut pas être filtré et est donc empêché de partir.
|
|
74
|
-
* `unconfigured` : le câblage a échoué ou est en conflit — le client peut
|
|
75
|
-
* encore parler en direct, et c'est précisément ce que le statut doit crier.
|
|
76
|
-
*/
|
|
77
|
-
function summarizeCoverage(report) {
|
|
78
|
-
const coverage = {};
|
|
79
|
-
for (const [name, result] of Object.entries(report || {})) {
|
|
80
|
-
if (result?.blocked) coverage[name] = { state: 'blocked', detail: result.reason, file: result.file || null };
|
|
81
|
-
else if (result?.configured) coverage[name] = { state: 'filtered', detail: null, file: result.file || null };
|
|
82
|
-
else coverage[name] = { state: 'unconfigured', detail: result?.reason || 'unknown', file: result?.file || null };
|
|
83
|
-
}
|
|
84
|
-
return coverage;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
46
|
function hudsuckerBinary() {
|
|
88
47
|
if (process.env.PIECEMAKER_HUDSUCKER_BIN) return process.env.PIECEMAKER_HUDSUCKER_BIN;
|
|
89
48
|
const name = process.platform === 'win32' ? 'piecemaker-hudsucker.exe' : 'piecemaker-hudsucker';
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return
|
|
49
|
+
const candidates = [
|
|
50
|
+
path.join(__dirname, 'bin', name),
|
|
51
|
+
path.join(__dirname, 'hudsucker-proxy', 'target', 'release', name),
|
|
52
|
+
path.join(__dirname, 'hudsucker-proxy', 'target', 'debug', name),
|
|
53
|
+
];
|
|
54
|
+
const present = candidates.filter((candidate) => fs.existsSync(candidate));
|
|
55
|
+
if (!present.length) return candidates[0];
|
|
56
|
+
return present.reduce((newest, candidate) => (fs.statSync(candidate).mtimeMs > fs.statSync(newest).mtimeMs ? candidate : newest));
|
|
97
57
|
}
|
|
98
58
|
|
|
99
59
|
function certificatePaths() {
|
|
@@ -113,28 +73,52 @@ function writeTrustBundle(caFile, bundleFile) {
|
|
|
113
73
|
return bundleFile;
|
|
114
74
|
}
|
|
115
75
|
|
|
116
|
-
function
|
|
76
|
+
function removeLegacyCentralMapping(homeDir) {
|
|
77
|
+
let entries = [];
|
|
78
|
+
try {
|
|
79
|
+
entries = fs.readdirSync(homeDir);
|
|
80
|
+
} catch {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
for (const name of entries) {
|
|
84
|
+
if (name.startsWith('central-mapping.json')) fs.rmSync(path.join(homeDir, name), { force: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function installCursorGuard({ binDir, mappingFile }) {
|
|
89
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
90
|
+
const file = path.join(binDir, 'cursor-agent');
|
|
91
|
+
const script = `#!/bin/sh
|
|
92
|
+
if [ -s ${JSON.stringify(mappingFile)} ]; then
|
|
93
|
+
printf '%s\\n' ${JSON.stringify(CURSOR_REFUSAL)} >&2
|
|
94
|
+
exit 78
|
|
95
|
+
fi
|
|
96
|
+
PATH=$(printf '%s' "$PATH" | sed -e "s|^${binDir.replace(/[|\\]/g, '\\$&')}:||")
|
|
97
|
+
export PATH
|
|
98
|
+
exec cursor-agent "$@"
|
|
99
|
+
`;
|
|
100
|
+
fs.writeFileSync(file, script, { encoding: 'utf8', mode: 0o755 });
|
|
101
|
+
return { file };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function waitForListening(child, timeoutMs) {
|
|
117
105
|
return new Promise((resolve, reject) => {
|
|
118
106
|
let buffer = '';
|
|
119
|
-
|
|
120
|
-
const finish = (failed, value) => {
|
|
121
|
-
if (settled) return;
|
|
122
|
-
settled = true;
|
|
107
|
+
const finish = (error, port) => {
|
|
123
108
|
clearTimeout(timer);
|
|
124
|
-
child.stdout
|
|
125
|
-
child.stderr?.off('data', take);
|
|
109
|
+
child.stdout.off('data', take);
|
|
126
110
|
child.off('exit', onExit);
|
|
127
|
-
if (
|
|
128
|
-
else resolve(
|
|
111
|
+
if (error) reject(error);
|
|
112
|
+
else resolve(port);
|
|
129
113
|
};
|
|
130
|
-
const timer = setTimeout(() => finish(
|
|
114
|
+
const timer = setTimeout(() => finish(new Error(`hudsucker n'a pas écouté : ${buffer}`)), timeoutMs);
|
|
131
115
|
const take = (chunk) => {
|
|
132
116
|
buffer += chunk.toString();
|
|
133
|
-
|
|
117
|
+
const match = buffer.match(/listening 127\.0\.0\.1:(\d+)/);
|
|
118
|
+
if (match) finish(null, Number(match[1]));
|
|
134
119
|
};
|
|
135
|
-
const onExit = (code) => finish(
|
|
136
|
-
child.stdout
|
|
137
|
-
child.stderr?.on('data', take);
|
|
120
|
+
const onExit = (code) => finish(new Error(`hudsucker sorti (${code}) : ${buffer}`));
|
|
121
|
+
child.stdout.on('data', take);
|
|
138
122
|
child.once('exit', onExit);
|
|
139
123
|
});
|
|
140
124
|
}
|
|
@@ -142,8 +126,7 @@ function waitForOutput(child, needle, timeoutMs) {
|
|
|
142
126
|
function proxyEnvironment(proxyUrl, caFile, trustBundle) {
|
|
143
127
|
return {
|
|
144
128
|
HTTPS_PROXY: proxyUrl,
|
|
145
|
-
|
|
146
|
-
ALL_PROXY: proxyUrl,
|
|
129
|
+
https_proxy: proxyUrl,
|
|
147
130
|
NO_PROXY: 'localhost,127.0.0.1,::1',
|
|
148
131
|
NODE_USE_ENV_PROXY: '1',
|
|
149
132
|
NODE_EXTRA_CA_CERTS: caFile,
|
|
@@ -153,32 +136,20 @@ function proxyEnvironment(proxyUrl, caFile, trustBundle) {
|
|
|
153
136
|
};
|
|
154
137
|
}
|
|
155
138
|
|
|
156
|
-
function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = console
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
// Harnais de citations vérifiées (décisions Légifrance + bloc <CITATIONS>) :
|
|
160
|
-
// son propre interrupteur (`PIECEMAKER_CITATIONS=off`) est géré à
|
|
161
|
-
// l'intérieur, pas ici.
|
|
162
|
-
const harness = createHarnessJuridique({ homeDir, verifyResponses: false });
|
|
163
|
-
// Le shim Cursor vit sous le répertoire de données PieceMaker : il n'a rien à
|
|
164
|
-
// faire dans un répertoire appartenant à un fournisseur.
|
|
139
|
+
function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = console }) {
|
|
140
|
+
const databasePath = process.env.DATABASE_PATH || path.join(homeDir, 'auth.db');
|
|
141
|
+
const dictionary = createSqliteDictionaryLoader({ databasePath });
|
|
165
142
|
const binDir = path.join(homeDir, 'bin');
|
|
166
|
-
const state = {
|
|
167
|
-
enabled: false,
|
|
168
|
-
reason: 'not_started',
|
|
169
|
-
origin: null,
|
|
170
|
-
upstream: null,
|
|
171
|
-
startedAt: null,
|
|
172
|
-
routes: [],
|
|
173
|
-
coverage: {},
|
|
174
|
-
};
|
|
143
|
+
const state = { enabled: false, reason: 'not_started', origin: null, upstream: null, coverage: {} };
|
|
175
144
|
let proxy = null;
|
|
176
145
|
let bridge = null;
|
|
177
146
|
let previousEnv = null;
|
|
147
|
+
const killProxy = () => proxy?.kill('SIGKILL');
|
|
178
148
|
|
|
179
149
|
async function start() {
|
|
180
150
|
if (proxy) return state;
|
|
181
|
-
|
|
151
|
+
removeLegacyProxyConfig({ userHome, homeDir });
|
|
152
|
+
if (isDisabled(homeDir)) {
|
|
182
153
|
state.reason = 'disabled';
|
|
183
154
|
return state;
|
|
184
155
|
}
|
|
@@ -191,50 +162,41 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
191
162
|
return state;
|
|
192
163
|
}
|
|
193
164
|
|
|
194
|
-
const
|
|
195
|
-
const hosts = [
|
|
196
|
-
try {
|
|
197
|
-
const extra = new URL(claudeUpstream).hostname;
|
|
198
|
-
if (extra && !hosts.includes(extra)) hosts.push(extra);
|
|
199
|
-
} catch {
|
|
200
|
-
// l'amont par défaut reste la liste fixe
|
|
201
|
-
}
|
|
165
|
+
const upstream = resolveUpstream(process.env.ANTHROPIC_BASE_URL);
|
|
166
|
+
const hosts = [...new Set([...INTERCEPTED_HOSTS, new URL(upstream).hostname])];
|
|
202
167
|
|
|
203
168
|
try {
|
|
204
|
-
bridge = await
|
|
169
|
+
bridge = await startRewriterWorker({ databasePath, homeDir });
|
|
205
170
|
} catch (error) {
|
|
206
171
|
state.reason = `listen_failed: ${error.message}`;
|
|
207
172
|
logger.warn?.(`[piecemaker] réécriture PII non démarrée : ${error.message}`);
|
|
208
173
|
return state;
|
|
209
174
|
}
|
|
210
175
|
|
|
211
|
-
const listenedPort = DEFAULT_PORT;
|
|
212
|
-
const origin = `http://127.0.0.1:${listenedPort}`;
|
|
213
176
|
const child = spawn(binary, [
|
|
214
|
-
'--listen',
|
|
177
|
+
'--listen', '127.0.0.1:0',
|
|
215
178
|
'--rewriter', `http://127.0.0.1:${bridge.port}`,
|
|
216
179
|
'--ca-cert', certificates.cert,
|
|
217
180
|
'--ca-key', certificates.key,
|
|
218
181
|
'--hosts', hosts.join(','),
|
|
219
182
|
], {
|
|
220
|
-
stdio: ['
|
|
221
|
-
env: {
|
|
222
|
-
...process.env,
|
|
223
|
-
HTTPS_PROXY: '',
|
|
224
|
-
HTTP_PROXY: '',
|
|
225
|
-
ALL_PROXY: '',
|
|
226
|
-
https_proxy: '',
|
|
227
|
-
http_proxy: '',
|
|
228
|
-
all_proxy: '',
|
|
229
|
-
},
|
|
183
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
184
|
+
env: { ...process.env, HTTPS_PROXY: '', HTTP_PROXY: '', ALL_PROXY: '', https_proxy: '', http_proxy: '', all_proxy: '' },
|
|
230
185
|
});
|
|
231
186
|
proxy = child;
|
|
232
|
-
|
|
233
|
-
child.stderr
|
|
187
|
+
process.once('exit', killProxy);
|
|
188
|
+
child.stderr.on('data', (chunk) => logger.warn?.(`[piecemaker] hudsucker : ${String(chunk).trim()}`));
|
|
189
|
+
child.once('exit', (code, signal) => {
|
|
190
|
+
if (proxy === child && state.enabled) logger.warn?.(`[piecemaker] hudsucker arrêté (${code ?? signal}) : requêtes IA refusées`);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
let port;
|
|
234
194
|
try {
|
|
235
|
-
await
|
|
195
|
+
port = await waitForListening(child, 15000);
|
|
196
|
+
child.stdout.resume();
|
|
236
197
|
} catch (error) {
|
|
237
198
|
child.kill('SIGKILL');
|
|
199
|
+
process.off('exit', killProxy);
|
|
238
200
|
proxy = null;
|
|
239
201
|
await bridge.close();
|
|
240
202
|
bridge = null;
|
|
@@ -243,72 +205,44 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
243
205
|
return state;
|
|
244
206
|
}
|
|
245
207
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
} catch (error) {
|
|
255
|
-
logger.warn?.(`[piecemaker] impossible de publier mikePiiPort dans config.json : ${error.message}`);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
previousEnv = {
|
|
259
|
-
[ENV_VAR]: process.env[ENV_VAR],
|
|
260
|
-
[OPENAI_ENV_VAR]: process.env[OPENAI_ENV_VAR],
|
|
261
|
-
PATH: process.env.PATH,
|
|
262
|
-
};
|
|
263
|
-
for (const key of PROXY_ENV_KEYS) previousEnv[key] = process.env[key];
|
|
264
|
-
const trustBundle = writeTrustBundle(certificates.cert, certificates.trustBundle);
|
|
265
|
-
Object.assign(process.env, proxyEnvironment(origin, certificates.cert, trustBundle));
|
|
208
|
+
const origin = `http://127.0.0.1:${port}`;
|
|
209
|
+
const environment = proxyEnvironment(origin, certificates.cert, writeTrustBundle(certificates.cert, certificates.trustBundle));
|
|
210
|
+
previousEnv = Object.fromEntries(['PATH', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy', ...Object.keys(environment)]
|
|
211
|
+
.map((key) => [key, process.env[key]]));
|
|
212
|
+
for (const key of ['HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete process.env[key];
|
|
213
|
+
Object.assign(process.env, environment);
|
|
214
|
+
removeLegacyCentralMapping(homeDir);
|
|
215
|
+
installCursorGuard({ binDir, mappingFile: path.join(homeDir, 'central-mapping.json') });
|
|
266
216
|
process.env.PATH = `${binDir}${path.delimiter}${process.env.PATH || ''}`;
|
|
267
217
|
|
|
268
|
-
const report = await configureProviders({
|
|
269
|
-
origin,
|
|
270
|
-
caFile: certificates.cert,
|
|
271
|
-
trustBundle,
|
|
272
|
-
userHome,
|
|
273
|
-
binDir,
|
|
274
|
-
mappingFile: legacyMappingFile,
|
|
275
|
-
});
|
|
276
218
|
Object.assign(state, {
|
|
277
219
|
enabled: true,
|
|
278
220
|
reason: 'running',
|
|
279
221
|
origin,
|
|
280
|
-
upstream
|
|
281
|
-
|
|
282
|
-
routes: hosts.map((host) => ({ provider: host, upstream: `https://${host}` })),
|
|
283
|
-
coverage: summarizeCoverage(report),
|
|
222
|
+
upstream,
|
|
223
|
+
coverage: COVERAGE,
|
|
284
224
|
});
|
|
285
|
-
|
|
286
|
-
const uncovered = Object.entries(state.coverage)
|
|
287
|
-
.filter(([, value]) => value.state === 'unconfigured')
|
|
288
|
-
.map(([name]) => name);
|
|
289
225
|
logger.log?.(`[piecemaker] anonymisation active sur ${origin} (claude, codex, opencode ; cursor bloqué)`);
|
|
290
|
-
if (uncovered.length) {
|
|
291
|
-
logger.warn?.(`[piecemaker] clients non filtrés, à vérifier : ${uncovered.join(', ')}`);
|
|
292
|
-
}
|
|
293
226
|
return state;
|
|
294
227
|
}
|
|
295
228
|
|
|
296
229
|
async function stop() {
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
230
|
+
if (proxy) {
|
|
231
|
+
const child = proxy;
|
|
232
|
+
proxy = null;
|
|
233
|
+
process.off('exit', killProxy);
|
|
234
|
+
if (child.exitCode === null) {
|
|
235
|
+
child.kill('SIGTERM');
|
|
236
|
+
await Promise.race([
|
|
237
|
+
new Promise((resolve) => child.once('exit', resolve)),
|
|
238
|
+
new Promise((resolve) => setTimeout(resolve, 2000)),
|
|
239
|
+
]);
|
|
240
|
+
if (child.exitCode === null) child.kill('SIGKILL');
|
|
241
|
+
}
|
|
308
242
|
}
|
|
309
243
|
if (bridge) await bridge.close();
|
|
310
244
|
bridge = null;
|
|
311
|
-
|
|
245
|
+
fs.rmSync(path.join(binDir, 'cursor-agent'), { force: true });
|
|
312
246
|
if (previousEnv) {
|
|
313
247
|
for (const [key, value] of Object.entries(previousEnv)) {
|
|
314
248
|
if (value === undefined) delete process.env[key];
|
|
@@ -316,16 +250,15 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
316
250
|
}
|
|
317
251
|
previousEnv = null;
|
|
318
252
|
}
|
|
319
|
-
proxy = null;
|
|
320
253
|
dictionary.close();
|
|
321
|
-
Object.assign(state, { enabled: false, reason: 'stopped', origin: null,
|
|
254
|
+
Object.assign(state, { enabled: false, reason: 'stopped', origin: null, upstream: null, coverage: {} });
|
|
322
255
|
}
|
|
323
256
|
|
|
324
257
|
function status() {
|
|
325
258
|
const current = dictionary.get();
|
|
326
259
|
return {
|
|
327
260
|
...state,
|
|
328
|
-
listening: Boolean(proxy && proxy.exitCode === null &&
|
|
261
|
+
listening: Boolean(proxy && proxy.exitCode === null && proxy.signalCode === null),
|
|
329
262
|
dictionary: {
|
|
330
263
|
file: dictionary.file,
|
|
331
264
|
exists: dictionary.exists(),
|
|
@@ -342,11 +275,8 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
342
275
|
}
|
|
343
276
|
|
|
344
277
|
module.exports = {
|
|
345
|
-
ENV_VAR,
|
|
346
|
-
OPENAI_ENV_VAR,
|
|
347
278
|
createAnonymizerService,
|
|
348
|
-
|
|
279
|
+
installCursorGuard,
|
|
349
280
|
resolveUpstream,
|
|
350
|
-
summarizeCoverage,
|
|
351
281
|
writeTrustBundle,
|
|
352
282
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
1
2
|
const path = require('node:path');
|
|
2
3
|
|
|
3
4
|
const Database = require('better-sqlite3');
|
|
@@ -62,6 +63,7 @@ function createSqliteDictionaryLoader({ databasePath }) {
|
|
|
62
63
|
let mappingSignature = null;
|
|
63
64
|
let generation = 0;
|
|
64
65
|
let current = EMPTY_DICTIONARY;
|
|
66
|
+
let failure = null;
|
|
65
67
|
|
|
66
68
|
function connection() {
|
|
67
69
|
if (!database) {
|
|
@@ -97,15 +99,24 @@ function createSqliteDictionaryLoader({ databasePath }) {
|
|
|
97
99
|
generation += 1;
|
|
98
100
|
current = buildDictionary(buildMappingDocument(rows), generation);
|
|
99
101
|
dataVersion = nextDataVersion;
|
|
102
|
+
failure = null;
|
|
100
103
|
return current;
|
|
101
|
-
} catch {
|
|
104
|
+
} catch (error) {
|
|
105
|
+
failure = fs.existsSync(file) ? error : null;
|
|
102
106
|
return current;
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
|
|
110
|
+
function loadForOutbound() {
|
|
111
|
+
const dictionary = load(false);
|
|
112
|
+
if (failure) throw new Error(`dictionnaire illisible : ${failure.message}`);
|
|
113
|
+
return dictionary;
|
|
114
|
+
}
|
|
115
|
+
|
|
106
116
|
return {
|
|
107
117
|
file,
|
|
108
118
|
get: () => load(false),
|
|
119
|
+
getForOutbound: loadForOutbound,
|
|
109
120
|
refresh: () => load(true),
|
|
110
121
|
exists: () => !load(false).empty,
|
|
111
122
|
close: () => {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { rename, rm, stat, writeFile } from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import express, { type Request, type Response } from 'express';
|
|
6
|
+
|
|
7
|
+
import { projectsDb } from '@/modules/database/index.js';
|
|
8
|
+
|
|
9
|
+
const MAX_DOCX_BYTES = '100mb';
|
|
10
|
+
|
|
11
|
+
function resolveDocxPath(request: Request): string | null {
|
|
12
|
+
const projectId = typeof request.query.projectId === 'string' ? request.query.projectId : '';
|
|
13
|
+
const filePath = typeof request.query.path === 'string' ? request.query.path : '';
|
|
14
|
+
const projectRoot = projectId ? projectsDb.getProjectPathById(projectId) : null;
|
|
15
|
+
if (!projectRoot || !filePath.toLowerCase().endsWith('.docx')) return null;
|
|
16
|
+
const resolved = path.resolve(projectRoot, filePath);
|
|
17
|
+
return resolved.startsWith(path.resolve(projectRoot) + path.sep) ? resolved : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function readVersion(filePath: string): Promise<number> {
|
|
21
|
+
return (await stat(filePath)).mtimeMs;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createDocxDocumentRouter() {
|
|
25
|
+
const router = express.Router();
|
|
26
|
+
|
|
27
|
+
router.get('/docx-document/version', async (request: Request, response: Response) => {
|
|
28
|
+
const filePath = resolveDocxPath(request);
|
|
29
|
+
if (!filePath) return response.status(400).json({ error: 'Invalid docx path' });
|
|
30
|
+
try {
|
|
31
|
+
response.json({ version: await readVersion(filePath) });
|
|
32
|
+
} catch {
|
|
33
|
+
response.status(404).json({ error: 'File not found' });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
router.put('/docx-document', express.raw({ type: () => true, limit: MAX_DOCX_BYTES }), async (request: Request, response: Response) => {
|
|
38
|
+
const filePath = resolveDocxPath(request);
|
|
39
|
+
if (!filePath || !Buffer.isBuffer(request.body) || request.body.length === 0) {
|
|
40
|
+
return response.status(400).json({ error: 'Invalid docx payload' });
|
|
41
|
+
}
|
|
42
|
+
const temporaryPath = `${filePath}.${randomUUID()}.tmp`;
|
|
43
|
+
try {
|
|
44
|
+
await writeFile(temporaryPath, request.body);
|
|
45
|
+
await rename(temporaryPath, filePath);
|
|
46
|
+
response.json({ version: await readVersion(filePath) });
|
|
47
|
+
} catch (error) {
|
|
48
|
+
await rm(temporaryPath, { force: true });
|
|
49
|
+
response.status(500).json({ error: error instanceof Error ? error.message : String(error) });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return router;
|
|
54
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Capture des décisions Légifrance lues via l'outil MCP `consulter_decision`,
|
|
5
5
|
* repérées dans un corps de requête SORTANTE déjà analysé en JSON par le
|
|
6
|
-
* proxy PII (`server/piecemaker/anonymizer/
|
|
6
|
+
* proxy PII (`server/piecemaker/anonymizer/rewriter-bridge.cjs`).
|
|
7
7
|
*
|
|
8
8
|
* Équivalent serveur du hook `decision-cache.mjs` de l'autre dépôt
|
|
9
9
|
* (`piecemaker-plugin/scripts/decision-cache.mjs`, PostToolUse Claude Code) :
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Façade du harnais de citations vérifiées,
|
|
5
|
-
*
|
|
6
|
-
* tâche ultérieure, ce module ne modifie rien de `proxy.cjs`.
|
|
4
|
+
* Façade du harnais de citations vérifiées, branchée sur le réécrivain du
|
|
5
|
+
* proxy PII (`server/piecemaker/anonymizer/rewriter-bridge.cjs`).
|
|
7
6
|
*
|
|
8
7
|
* Deux observateurs, symétriques au sens et au retour du proxy lui-même :
|
|
9
8
|
* - `observerRequete` regarde ce qui PART vers le fournisseur (avant
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* (`piecemaker-plugin/scripts/verify-citations.mjs`).
|
|
7
7
|
*
|
|
8
8
|
* Ce dépôt n'a pas de hook Claude Code : tout le trafic IA passe par le
|
|
9
|
-
* proxy PII local (`server/piecemaker/anonymizer/
|
|
9
|
+
* proxy PII local (`server/piecemaker/anonymizer/rewriter-bridge.cjs`), qui est donc
|
|
10
10
|
* l'unique point d'observation possible pour un texte produit par le modèle.
|
|
11
11
|
* Contrairement au hook d'origine, ce module n'a pas le pouvoir de bloquer
|
|
12
12
|
* un tour (il n'y a pas de conversation Claude Code à interrompre côté
|
|
@@ -3,10 +3,13 @@ import path from 'path';
|
|
|
3
3
|
|
|
4
4
|
import type { Router } from 'express';
|
|
5
5
|
|
|
6
|
+
import { projectsDb } from '@/modules/database/index.js';
|
|
6
7
|
import { providerRuntimeService, sessionsService } from '@/modules/providers/index.js';
|
|
8
|
+
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
7
9
|
import { findApplicationRoot, getModuleDirectory } from '@/shared/utils.js';
|
|
8
10
|
|
|
9
11
|
|
|
12
|
+
import { createAgentInstructionsRouter, startAgentInstructionsMirror } from './agent-instructions.js';
|
|
10
13
|
import { startRequiredAnonymizer } from './anonymizer/lifecycle.js';
|
|
11
14
|
import { createCitationStore } from './harness/citation-store.js';
|
|
12
15
|
import { installChatCitationHarness } from './harness/chat-harness.js';
|
|
@@ -16,7 +19,10 @@ import { createKnowledgeBackend } from './knowledge/index.js';
|
|
|
16
19
|
import { createTimesheetBackend } from './timesheet/index.js';
|
|
17
20
|
import { createCompanySearchRouter } from './company-search.js';
|
|
18
21
|
import { createBodaccSearchRouter } from './bodacc-search.js';
|
|
22
|
+
import { createDocxDocumentRouter } from './docx-document.js';
|
|
19
23
|
import { createShellEnvironmentRouter, resolveDesktopShellEnvironment } from './shell-environment.js';
|
|
24
|
+
import { installModelDiscovery } from './model-discovery/index.js';
|
|
25
|
+
import { startProjectRegistry } from './project-registry.js';
|
|
20
26
|
|
|
21
27
|
/**
|
|
22
28
|
* Point d'entrée PieceMaker. Les modules de `server/piecemaker/vendor/` sont du
|
|
@@ -37,8 +43,9 @@ type PieceMakerRuntimeStatus = {
|
|
|
37
43
|
};
|
|
38
44
|
|
|
39
45
|
type PieceMakerVendorModule = {
|
|
40
|
-
createPieceMakerRouter(options?: { getRuntimeStatus?: () => PieceMakerRuntimeStatus; anonymizer?: unknown }): Router;
|
|
46
|
+
createPieceMakerRouter(options?: { getRuntimeStatus?: () => PieceMakerRuntimeStatus; anonymizer?: unknown; publishProjects?: () => void }): Router;
|
|
41
47
|
piecemakerHome(): string;
|
|
48
|
+
publishProjectSource(sourceId: string, folders: string[] | null): void;
|
|
42
49
|
stopOriginalsJobs(): Promise<void>;
|
|
43
50
|
};
|
|
44
51
|
|
|
@@ -46,22 +53,25 @@ const shellEnvironment = await resolveDesktopShellEnvironment();
|
|
|
46
53
|
const vendor = createRequire(import.meta.url)(routerPath) as PieceMakerVendorModule;
|
|
47
54
|
|
|
48
55
|
export const { piecemakerHome, stopOriginalsJobs } = vendor;
|
|
56
|
+
const publishProjects = startProjectRegistry(vendor.publishProjectSource);
|
|
49
57
|
const { createAnonymizerService } = createRequire(import.meta.url)(path.join(applicationRoot, 'server/piecemaker/anonymizer/service.cjs'));
|
|
50
|
-
const anonymizer = createAnonymizerService({ homeDir: piecemakerHome()
|
|
58
|
+
const anonymizer = createAnonymizerService({ homeDir: piecemakerHome() });
|
|
51
59
|
const ensureProxy = await startRequiredAnonymizer(anonymizer);
|
|
52
60
|
const citations = createCitationStore(piecemakerHome());
|
|
53
61
|
installChatCitationHarness({ runtime: providerRuntimeService, sessions: sessionsService, store: citations, ensureProxy });
|
|
54
62
|
const library = await openLibrary(piecemakerHome(), applicationRoot);
|
|
55
63
|
installLibraryRuntime(providerRuntimeService, sessionsService, library.store);
|
|
64
|
+
installModelDiscovery(providerRegistry);
|
|
56
65
|
const timesheet = createTimesheetBackend(piecemakerHome());
|
|
57
66
|
const knowledge = createKnowledgeBackend(applicationRoot);
|
|
67
|
+
startAgentInstructionsMirror(() => projectsDb.getProjectPaths().map((row) => row.project_path));
|
|
58
68
|
|
|
59
69
|
export function createPieceMakerLocalRouter() {
|
|
60
70
|
return knowledge.localRouter;
|
|
61
71
|
}
|
|
62
72
|
|
|
63
73
|
export function createPieceMakerRouter(options: { getRuntimeStatus?: () => PieceMakerRuntimeStatus } = {}) {
|
|
64
|
-
const router = vendor.createPieceMakerRouter({ ...options, anonymizer });
|
|
74
|
+
const router = vendor.createPieceMakerRouter({ ...options, anonymizer, publishProjects });
|
|
65
75
|
router.use(createCitationsRouter(citations, (id) => {
|
|
66
76
|
try { sessionsService.getSessionDetailsById(id); return true; } catch { return false; }
|
|
67
77
|
}));
|
|
@@ -70,6 +80,8 @@ export function createPieceMakerRouter(options: { getRuntimeStatus?: () => Piece
|
|
|
70
80
|
router.use(knowledge.router);
|
|
71
81
|
router.use(createCompanySearchRouter());
|
|
72
82
|
router.use(createBodaccSearchRouter());
|
|
83
|
+
router.use(createDocxDocumentRouter());
|
|
84
|
+
router.use(createAgentInstructionsRouter(path.join(piecemakerHome(), 'CLAUDE.md')));
|
|
73
85
|
router.use(createShellEnvironmentRouter(shellEnvironment));
|
|
74
86
|
return router;
|
|
75
87
|
}
|