@piecemaker-legal/piecemaker 2.0.8 → 2.0.10
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-CQ76sPaJ.js +1 -0
- package/dist/assets/FootnotePropertiesDialog-W3YHM4BV-BpgYQvvd.js +1 -0
- package/dist/assets/HyperlinkDialog-ZYH7ZHRF-qIPBOssF.css +1 -0
- package/dist/assets/HyperlinkDialog-ZYH7ZHRF-z08UbYbL.js +144 -0
- package/dist/assets/ImagePositionDialog-HEWYR6Q3-CFzOnzAt.js +1 -0
- package/dist/assets/ImagePropertiesDialog-RAB47C6M-D-ThGU-b.js +1 -0
- package/dist/assets/PageSetupDialog-AIGIXO3E-_PYIO8eb.js +1 -0
- package/dist/assets/SplitCellDialog-IFBKYCD6-Cb9uQOB8.js +1 -0
- package/dist/assets/TablePropertiesDialog-TMLKGOOW-12vmjb6B.js +1 -0
- package/dist/assets/WatermarkDialog-KRL7WMIS-C-d8rzt2.js +1 -0
- package/dist/assets/{abnfDiagram-VCTEODGH-XuFdW3jA.js → abnfDiagram-VCTEODGH-B4OsX7eF.js} +1 -1
- package/dist/assets/arc-BK1oUIk7.js +1 -0
- package/dist/assets/architectureDiagram-5GKGNRK7-CfaFI6L7.js +36 -0
- package/dist/assets/{blockDiagram-I7D4REHJ-CsgOSDLB.js → blockDiagram-I7D4REHJ-DQEI2azY.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-awKtsGCB.js → c4Diagram-7LVT6UL2-DbravpVi.js} +1 -1
- package/dist/assets/channel-DVyNr2Yg.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-DA5F8ig-.js → chunk-2Q5K7J3B-BLpZ9huC.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DawRFEGW.js → chunk-5VM5RSS4-BLlEdmX9.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-BO6B_U1p.js → chunk-F27PBJKO-D-ZNhI6q.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-Dhrf_k6W.js → chunk-IMKFNOWR-vzSiKnhh.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BTGX4CJ7.js → chunk-JWPE2WC7-DfF0k4nb.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-ByKLZ0wa.js → chunk-POPQ4Y6H-BnwCYsPP.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-DNCtUeow.js → chunk-SVP7TREG-DRxc8fhq.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-bgcAtBKw.js → chunk-TICWLB2K-Dd2Q-md-.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-AD-pwqOj.js → chunk-XXDRQBXY-DBjkEUp_.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-2pGPHl5H.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-2pGPHl5H.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-D23VMTjy.js → cose-bilkent-JH36ORCC-By6PS4oe.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-thDLtHBg.js → cynefin-OW5HDTMX-YxWpjc5W.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-BTG5qDGi.js → cynefinDiagram-5FMLGOSQ-DrtXzz3p.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-yXAeLV32.js → dagre-GXQ25YYZ-BDpTLKqZ.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-v1-YDY3c.js → diagram-S7CK7UJ4-C4LvqeCs.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-DlO9hQ63.js → diagram-UQ7AKVKN-BTpnMeHp.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-B3Bz2C6Y.js → diagram-VSXAHHWV-DucHHWev.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-BosIgZRa.js → diagram-VX7I27RA-DYHZNmmH.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-C1MFbIji.js → diagram-Z3DM3KII-BHIH2BGN.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-B6g9aRAb.js → ebnfDiagram-PWID7BFC-CGiEkwj0.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-BuV5RUQ-.js → erDiagram-RLTQ6QDP-AJMHi2Us.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-OFcS8Psg.js → flowDiagram-HODETNUW-BqzANBh2.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-CxCIs-y4.js → ganttDiagram-EL5Y4UJY-B0xPDY2_.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-DQHfgTRq.js → gitGraphDiagram-WWUBYQGX-CzPX5CNO.js} +1 -1
- package/dist/assets/{index-BPmxqMd5.js → index-Bk2iXKnJ.js} +140 -140
- package/dist/assets/index-Dc9wNuS4.css +1 -0
- package/dist/assets/{infoDiagram-27XIBGKW-UvDaFHGE.js → infoDiagram-27XIBGKW-Cjc44H0O.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-B04u_588.js → ishikawaDiagram-5VMMS53U-BWhr2v7r.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-BYvBbjXl.js → journeyDiagram-3NMN7TZE-Bl4r6RrP.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-DxAJd2LW.js → kanban-definition-UXKFOSKX-FV-T0Vy1.js} +6 -6
- package/dist/assets/layout-CTfb1sAF.js +1 -0
- package/dist/assets/{linear-CmWY0F-m.js → linear-Dfgi6vlo.js} +1 -1
- package/dist/assets/{mermaid.core-ClUcKNvb.js → mermaid.core-DOCGsdlo.js} +7 -7
- package/dist/assets/{mindmap-definition-YA3MSWOX-DZ2GO-J-.js → mindmap-definition-YA3MSWOX-CymQ86ir.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-DF4i_o7r.js → pegDiagram-XKGWAZYB-1VMq_vi0.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-G20Z0KTO.js → pieDiagram-E7YTZNPT-C5GgXtJU.js} +1 -1
- package/dist/assets/processTemplate-GPFDLQMX-B1sVV2O0.js +22 -0
- package/dist/assets/{quadrantDiagram-AXDQQJYC-5MRlN0BO.js → quadrantDiagram-AXDQQJYC-C6zpryYf.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-B4Fp-guo.js → railroadDiagram-O6MQD6OU-D-sv4SWi.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-DFL0eww_.js → requirementDiagram-BXWQKSXE-CkjY-z4B.js} +1 -1
- package/dist/assets/sankeyDiagram-P5KCCOFB-De-d8Q6L.js +40 -0
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-iP9OUxwi.js → sequenceDiagram-WJ2MYXX4-DCsGSJa9.js} +1 -1
- package/dist/assets/{server.browser-BzI1jrZw.js → server.browser-BY3bL-IX.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-C95kgcA-.js → sizeCapture-INFHLROL-xINZhgER.js} +1 -1
- package/dist/assets/stateDiagram-D77RDMKH-CuLUTWSL.js +1 -0
- package/dist/assets/stateDiagram-v2-MP3YSRHH-Cnbs-M92.js +1 -0
- package/dist/assets/{swimlanes-42K2YHIH-BbMY7ECY.js → swimlanes-42K2YHIH-BwxARD7N.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-D7p8qZI2.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-BQs8wPer.js → timeline-definition-24CTP7MA-BIOvnn-l.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-Imm0EPbt.js → vennDiagram-4TSXK5OY-BSD6Pfmt.js} +4 -4
- package/dist/assets/{wardleyDiagram-VM6X3IG4-DpMMDayn.js → wardleyDiagram-VM6X3IG4-7Zz2iRkF.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-DdLp1sYL.js → xychartDiagram-S5SC5T6Z-Ctx17Y1D.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/anonymizer/lifecycle.js +2 -3
- package/dist-server/server/piecemaker/anonymizer/lifecycle.js.map +1 -1
- package/dist-server/server/piecemaker/desktop-update/index.js +4 -3
- package/dist-server/server/piecemaker/desktop-update/index.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 +9 -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/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/anonymizer/client-config.cjs +92 -92
- 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/lifecycle.ts +2 -3
- package/server/piecemaker/anonymizer/rewriter-bridge.cjs +6 -0
- package/server/piecemaker/anonymizer/service.cjs +117 -175
- package/server/piecemaker/desktop-update/index.ts +4 -3
- 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 +11 -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 -7
- 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/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-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/protect-originals.mjs +4 -55
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +24 -117
- package/server/piecemaker/vendor/piecemaker-plugin/skills/tamponnage/SKILL.md +2 -2
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +15 -121
- package/server/piecemaker/vendor/websocket-server/case-registry.cjs +16 -32
- 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/scripts/presidio-gliner/scanner_worker.py +2 -0
- 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-CsCtfL20.js +0 -1
- package/dist/assets/architectureDiagram-5GKGNRK7-DGxozBGS.js +0 -36
- package/dist/assets/channel-DnynWwYa.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-CHIsGj2B.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-CHIsGj2B.js +0 -1
- package/dist/assets/index-WwknP0l4.css +0 -1
- package/dist/assets/layout-R7w2kLxO.js +0 -1
- package/dist/assets/sankeyDiagram-P5KCCOFB-CpOdmqdy.js +0 -40
- package/dist/assets/stateDiagram-D77RDMKH-D9WHRvlY.js +0 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-D9Qlflkb.js +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-CA64dwpM.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,15 +1,14 @@
|
|
|
1
1
|
type AnonymizerService = {
|
|
2
2
|
start(): Promise<unknown>;
|
|
3
3
|
stop(): Promise<unknown>;
|
|
4
|
-
status(): { enabled: boolean; listening: boolean;
|
|
4
|
+
status(): { enabled: boolean; listening: boolean; reason?: string };
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
export async function startRequiredAnonymizer(service: AnonymizerService) {
|
|
8
8
|
const ensureReady = () => {
|
|
9
9
|
const status = service.status();
|
|
10
|
+
if (status.reason === 'disabled') return;
|
|
10
11
|
if (!status.enabled || !status.listening) throw new Error('PieceMaker : le proxy PII est indisponible. Démarrage IA refusé.');
|
|
11
|
-
const missing = ['claude', 'codex'].filter((provider) => status.coverage[provider]?.state !== 'filtered');
|
|
12
|
-
if (missing.length) throw new Error(`PieceMaker : routage PII non configuré pour ${missing.join(', ')}. Démarrage IA refusé.`);
|
|
13
12
|
};
|
|
14
13
|
try {
|
|
15
14
|
await service.start();
|
|
@@ -85,6 +85,12 @@ function startRewriterBridge({ dictionary, harness = null }) {
|
|
|
85
85
|
const provider = header(request, 'x-piecemaker-provider') || 'claude';
|
|
86
86
|
const current = () => dictionary.get();
|
|
87
87
|
|
|
88
|
+
if (request.method === 'GET' && request.url === '/health') {
|
|
89
|
+
response.writeHead(200);
|
|
90
|
+
response.end('ok');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
88
94
|
if (request.method === 'POST' && request.url === '/v1/request') {
|
|
89
95
|
readBody(request).then((body) => {
|
|
90
96
|
stats.requests += 1;
|
|
@@ -1,40 +1,27 @@
|
|
|
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');
|
|
21
4
|
const path = require('node:path');
|
|
5
|
+
const tls = require('node:tls');
|
|
22
6
|
|
|
23
7
|
const { createSqliteDictionaryLoader } = require('./sqlite-dictionary.cjs');
|
|
24
8
|
const { startRewriterBridge } = require('./rewriter-bridge.cjs');
|
|
25
|
-
const {
|
|
9
|
+
const { removeLegacyProxyConfig } = require('./client-config.cjs');
|
|
26
10
|
const { createHarnessJuridique } = require('../harness/index.cjs');
|
|
27
11
|
|
|
28
|
-
const ENV_VAR = 'ANTHROPIC_BASE_URL';
|
|
29
|
-
const OPENAI_ENV_VAR = 'OPENAI_BASE_URL';
|
|
30
12
|
const DEFAULT_UPSTREAM = 'https://api.anthropic.com';
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
13
|
+
const INTERCEPTED_HOSTS = ['api.anthropic.com', 'api.openai.com', 'chatgpt.com'];
|
|
14
|
+
const COVERAGE = {
|
|
15
|
+
claude: { state: 'filtered', detail: null, file: null },
|
|
16
|
+
codex: { state: 'filtered', detail: null, file: null },
|
|
17
|
+
opencode: { state: 'filtered', detail: null, file: null },
|
|
18
|
+
cursor: { state: 'blocked', detail: 'not-interceptable', file: null },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const CURSOR_REFUSAL = 'PieceMaker : cursor-agent ne peut pas être filtré par le proxy d’anonymisation '
|
|
22
|
+
+ '(son protocole n’accepte aucune base d’URL). Tant qu’un mapping existe, il est bloqué pour éviter '
|
|
23
|
+
+ 'que des noms de parties quittent la machine en clair. Utilisez Claude, Codex ou opencode.';
|
|
36
24
|
|
|
37
|
-
/** Interrupteur : `PIECEMAKER_ANONYMIZER=off`, ou `anonymizer.enabled: false` dans `config.json`. */
|
|
38
25
|
function isDisabled(homeDir) {
|
|
39
26
|
const flag = String(process.env.PIECEMAKER_ANONYMIZER || '').toLowerCase();
|
|
40
27
|
if (flag === 'off' || flag === '0' || flag === 'false') return true;
|
|
@@ -46,13 +33,6 @@ function isDisabled(homeDir) {
|
|
|
46
33
|
}
|
|
47
34
|
}
|
|
48
35
|
|
|
49
|
-
/**
|
|
50
|
-
* Cible du relais Anthropic. Une base distante déjà configurée (passerelle
|
|
51
|
-
* d'entreprise, point de terminaison alternatif) est respectée : on se chaîne
|
|
52
|
-
* derrière elle. Une base en boucle locale ne peut être qu'une instance
|
|
53
|
-
* précédente de ce proxy, et serait un relais mort : on l'ignore au profit de
|
|
54
|
-
* l'API officielle.
|
|
55
|
-
*/
|
|
56
36
|
function resolveUpstream(configured) {
|
|
57
37
|
if (!configured) return DEFAULT_UPSTREAM;
|
|
58
38
|
try {
|
|
@@ -64,35 +44,15 @@ function resolveUpstream(configured) {
|
|
|
64
44
|
}
|
|
65
45
|
}
|
|
66
46
|
|
|
67
|
-
/**
|
|
68
|
-
* Lecture du rapport de `configureProviders` en une couverture par client, la
|
|
69
|
-
* seule chose que l'interface a besoin de montrer.
|
|
70
|
-
*
|
|
71
|
-
* `filtered` : les échanges passent par le proxy et sont anonymisés.
|
|
72
|
-
* `blocked` : le client ne peut pas être filtré et est donc empêché de partir.
|
|
73
|
-
* `unconfigured` : le câblage a échoué ou est en conflit — le client peut
|
|
74
|
-
* encore parler en direct, et c'est précisément ce que le statut doit crier.
|
|
75
|
-
*/
|
|
76
|
-
function summarizeCoverage(report) {
|
|
77
|
-
const coverage = {};
|
|
78
|
-
for (const [name, result] of Object.entries(report || {})) {
|
|
79
|
-
if (result?.blocked) coverage[name] = { state: 'blocked', detail: result.reason, file: result.file || null };
|
|
80
|
-
else if (result?.configured) coverage[name] = { state: 'filtered', detail: null, file: result.file || null };
|
|
81
|
-
else coverage[name] = { state: 'unconfigured', detail: result?.reason || 'unknown', file: result?.file || null };
|
|
82
|
-
}
|
|
83
|
-
return coverage;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
47
|
function hudsuckerBinary() {
|
|
87
48
|
if (process.env.PIECEMAKER_HUDSUCKER_BIN) return process.env.PIECEMAKER_HUDSUCKER_BIN;
|
|
88
49
|
const name = process.platform === 'win32' ? 'piecemaker-hudsucker.exe' : 'piecemaker-hudsucker';
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return packaged;
|
|
50
|
+
const candidates = [
|
|
51
|
+
path.join(__dirname, 'bin', name),
|
|
52
|
+
path.join(__dirname, 'hudsucker-proxy', 'target', 'release', name),
|
|
53
|
+
path.join(__dirname, 'hudsucker-proxy', 'target', 'debug', name),
|
|
54
|
+
];
|
|
55
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) || candidates[0];
|
|
96
56
|
}
|
|
97
57
|
|
|
98
58
|
function certificatePaths() {
|
|
@@ -100,75 +60,95 @@ function certificatePaths() {
|
|
|
100
60
|
return {
|
|
101
61
|
cert: path.join(directory, 'piecemaker-ca.crt'),
|
|
102
62
|
key: path.join(directory, 'piecemaker-ca.key'),
|
|
63
|
+
trustBundle: path.join(directory, 'piecemaker-trust-bundle.pem'),
|
|
103
64
|
};
|
|
104
65
|
}
|
|
105
66
|
|
|
106
|
-
function
|
|
67
|
+
function writeTrustBundle(caFile, bundleFile) {
|
|
68
|
+
const content = [...tls.rootCertificates, fs.readFileSync(caFile, 'utf8').trim()].join('\n');
|
|
69
|
+
const temporary = `${bundleFile}.piecemaker-${process.pid}-${Date.now()}`;
|
|
70
|
+
fs.writeFileSync(temporary, `${content}\n`, { encoding: 'utf8', mode: 0o644 });
|
|
71
|
+
fs.renameSync(temporary, bundleFile);
|
|
72
|
+
return bundleFile;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function removeLegacyCentralMapping(homeDir) {
|
|
76
|
+
let entries = [];
|
|
77
|
+
try {
|
|
78
|
+
entries = fs.readdirSync(homeDir);
|
|
79
|
+
} catch {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
for (const name of entries) {
|
|
83
|
+
if (name.startsWith('central-mapping.json')) fs.rmSync(path.join(homeDir, name), { force: true });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function installCursorGuard({ binDir, mappingFile }) {
|
|
88
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
89
|
+
const file = path.join(binDir, 'cursor-agent');
|
|
90
|
+
const script = `#!/bin/sh
|
|
91
|
+
if [ -s ${JSON.stringify(mappingFile)} ]; then
|
|
92
|
+
printf '%s\\n' ${JSON.stringify(CURSOR_REFUSAL)} >&2
|
|
93
|
+
exit 78
|
|
94
|
+
fi
|
|
95
|
+
PATH=$(printf '%s' "$PATH" | sed -e "s|^${binDir.replace(/[|\\]/g, '\\$&')}:||")
|
|
96
|
+
export PATH
|
|
97
|
+
exec cursor-agent "$@"
|
|
98
|
+
`;
|
|
99
|
+
fs.writeFileSync(file, script, { encoding: 'utf8', mode: 0o755 });
|
|
100
|
+
return { file };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function waitForListening(child, timeoutMs) {
|
|
107
104
|
return new Promise((resolve, reject) => {
|
|
108
105
|
let buffer = '';
|
|
109
|
-
|
|
110
|
-
const finish = (failed, value) => {
|
|
111
|
-
if (settled) return;
|
|
112
|
-
settled = true;
|
|
106
|
+
const finish = (error, port) => {
|
|
113
107
|
clearTimeout(timer);
|
|
114
|
-
child.stdout
|
|
115
|
-
child.stderr?.off('data', take);
|
|
108
|
+
child.stdout.off('data', take);
|
|
116
109
|
child.off('exit', onExit);
|
|
117
|
-
if (
|
|
118
|
-
else resolve(
|
|
110
|
+
if (error) reject(error);
|
|
111
|
+
else resolve(port);
|
|
119
112
|
};
|
|
120
|
-
const timer = setTimeout(() => finish(
|
|
113
|
+
const timer = setTimeout(() => finish(new Error(`hudsucker n'a pas écouté : ${buffer}`)), timeoutMs);
|
|
121
114
|
const take = (chunk) => {
|
|
122
115
|
buffer += chunk.toString();
|
|
123
|
-
|
|
116
|
+
const match = buffer.match(/listening 127\.0\.0\.1:(\d+)/);
|
|
117
|
+
if (match) finish(null, Number(match[1]));
|
|
124
118
|
};
|
|
125
|
-
const onExit = (code) => finish(
|
|
126
|
-
child.stdout
|
|
127
|
-
child.stderr?.on('data', take);
|
|
119
|
+
const onExit = (code) => finish(new Error(`hudsucker sorti (${code}) : ${buffer}`));
|
|
120
|
+
child.stdout.on('data', take);
|
|
128
121
|
child.once('exit', onExit);
|
|
129
122
|
});
|
|
130
123
|
}
|
|
131
124
|
|
|
132
|
-
function proxyEnvironment(proxyUrl, caFile) {
|
|
125
|
+
function proxyEnvironment(proxyUrl, caFile, trustBundle) {
|
|
133
126
|
return {
|
|
134
127
|
HTTPS_PROXY: proxyUrl,
|
|
135
|
-
|
|
136
|
-
ALL_PROXY: proxyUrl,
|
|
128
|
+
https_proxy: proxyUrl,
|
|
137
129
|
NO_PROXY: 'localhost,127.0.0.1,::1',
|
|
138
130
|
NODE_USE_ENV_PROXY: '1',
|
|
139
131
|
NODE_EXTRA_CA_CERTS: caFile,
|
|
140
|
-
SSL_CERT_FILE:
|
|
132
|
+
SSL_CERT_FILE: trustBundle,
|
|
141
133
|
CODEX_CA_CERTIFICATE: caFile,
|
|
142
|
-
REQUESTS_CA_BUNDLE:
|
|
134
|
+
REQUESTS_CA_BUNDLE: trustBundle,
|
|
143
135
|
};
|
|
144
136
|
}
|
|
145
137
|
|
|
146
|
-
function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = console
|
|
138
|
+
function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = console }) {
|
|
147
139
|
const dictionary = createSqliteDictionaryLoader({ databasePath: process.env.DATABASE_PATH || path.join(homeDir, 'auth.db') });
|
|
148
|
-
const legacyMappingFile = path.join(homeDir, 'central-mapping.json');
|
|
149
|
-
// Harnais de citations vérifiées (décisions Légifrance + bloc <CITATIONS>) :
|
|
150
|
-
// son propre interrupteur (`PIECEMAKER_CITATIONS=off`) est géré à
|
|
151
|
-
// l'intérieur, pas ici.
|
|
152
140
|
const harness = createHarnessJuridique({ homeDir, verifyResponses: false });
|
|
153
|
-
// Le shim Cursor vit sous le répertoire de données PieceMaker : il n'a rien à
|
|
154
|
-
// faire dans un répertoire appartenant à un fournisseur.
|
|
155
141
|
const binDir = path.join(homeDir, 'bin');
|
|
156
|
-
const state = {
|
|
157
|
-
enabled: false,
|
|
158
|
-
reason: 'not_started',
|
|
159
|
-
origin: null,
|
|
160
|
-
upstream: null,
|
|
161
|
-
startedAt: null,
|
|
162
|
-
routes: [],
|
|
163
|
-
coverage: {},
|
|
164
|
-
};
|
|
142
|
+
const state = { enabled: false, reason: 'not_started', origin: null, upstream: null, coverage: {} };
|
|
165
143
|
let proxy = null;
|
|
166
144
|
let bridge = null;
|
|
167
145
|
let previousEnv = null;
|
|
146
|
+
const killProxy = () => proxy?.kill('SIGKILL');
|
|
168
147
|
|
|
169
148
|
async function start() {
|
|
170
149
|
if (proxy) return state;
|
|
171
|
-
|
|
150
|
+
removeLegacyProxyConfig({ userHome, homeDir });
|
|
151
|
+
if (isDisabled(homeDir)) {
|
|
172
152
|
state.reason = 'disabled';
|
|
173
153
|
return state;
|
|
174
154
|
}
|
|
@@ -181,14 +161,8 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
181
161
|
return state;
|
|
182
162
|
}
|
|
183
163
|
|
|
184
|
-
const
|
|
185
|
-
const hosts = [
|
|
186
|
-
try {
|
|
187
|
-
const extra = new URL(claudeUpstream).hostname;
|
|
188
|
-
if (extra && !hosts.includes(extra)) hosts.push(extra);
|
|
189
|
-
} catch {
|
|
190
|
-
// l'amont par défaut reste la liste fixe
|
|
191
|
-
}
|
|
164
|
+
const upstream = resolveUpstream(process.env.ANTHROPIC_BASE_URL);
|
|
165
|
+
const hosts = [...new Set([...INTERCEPTED_HOSTS, new URL(upstream).hostname])];
|
|
192
166
|
|
|
193
167
|
try {
|
|
194
168
|
bridge = await startRewriterBridge({ dictionary, harness });
|
|
@@ -198,33 +172,30 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
198
172
|
return state;
|
|
199
173
|
}
|
|
200
174
|
|
|
201
|
-
const listenedPort = DEFAULT_PORT;
|
|
202
|
-
const origin = `http://127.0.0.1:${listenedPort}`;
|
|
203
175
|
const child = spawn(binary, [
|
|
204
|
-
'--listen',
|
|
176
|
+
'--listen', '127.0.0.1:0',
|
|
205
177
|
'--rewriter', `http://127.0.0.1:${bridge.port}`,
|
|
206
178
|
'--ca-cert', certificates.cert,
|
|
207
179
|
'--ca-key', certificates.key,
|
|
208
180
|
'--hosts', hosts.join(','),
|
|
209
181
|
], {
|
|
210
|
-
stdio: ['
|
|
211
|
-
env: {
|
|
212
|
-
...process.env,
|
|
213
|
-
HTTPS_PROXY: '',
|
|
214
|
-
HTTP_PROXY: '',
|
|
215
|
-
ALL_PROXY: '',
|
|
216
|
-
https_proxy: '',
|
|
217
|
-
http_proxy: '',
|
|
218
|
-
all_proxy: '',
|
|
219
|
-
},
|
|
182
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
183
|
+
env: { ...process.env, HTTPS_PROXY: '', HTTP_PROXY: '', ALL_PROXY: '', https_proxy: '', http_proxy: '', all_proxy: '' },
|
|
220
184
|
});
|
|
221
185
|
proxy = child;
|
|
222
|
-
|
|
223
|
-
child.stderr
|
|
186
|
+
process.once('exit', killProxy);
|
|
187
|
+
child.stderr.on('data', (chunk) => logger.warn?.(`[piecemaker] hudsucker : ${String(chunk).trim()}`));
|
|
188
|
+
child.once('exit', (code, signal) => {
|
|
189
|
+
if (proxy === child && state.enabled) logger.warn?.(`[piecemaker] hudsucker arrêté (${code ?? signal}) : requêtes IA refusées`);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
let port;
|
|
224
193
|
try {
|
|
225
|
-
await
|
|
194
|
+
port = await waitForListening(child, 15000);
|
|
195
|
+
child.stdout.resume();
|
|
226
196
|
} catch (error) {
|
|
227
197
|
child.kill('SIGKILL');
|
|
198
|
+
process.off('exit', killProxy);
|
|
228
199
|
proxy = null;
|
|
229
200
|
await bridge.close();
|
|
230
201
|
bridge = null;
|
|
@@ -233,70 +204,44 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
233
204
|
return state;
|
|
234
205
|
}
|
|
235
206
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
} catch (error) {
|
|
245
|
-
logger.warn?.(`[piecemaker] impossible de publier mikePiiPort dans config.json : ${error.message}`);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
previousEnv = {
|
|
249
|
-
[ENV_VAR]: process.env[ENV_VAR],
|
|
250
|
-
[OPENAI_ENV_VAR]: process.env[OPENAI_ENV_VAR],
|
|
251
|
-
PATH: process.env.PATH,
|
|
252
|
-
};
|
|
253
|
-
for (const key of PROXY_ENV_KEYS) previousEnv[key] = process.env[key];
|
|
254
|
-
Object.assign(process.env, proxyEnvironment(origin, certificates.cert));
|
|
207
|
+
const origin = `http://127.0.0.1:${port}`;
|
|
208
|
+
const environment = proxyEnvironment(origin, certificates.cert, writeTrustBundle(certificates.cert, certificates.trustBundle));
|
|
209
|
+
previousEnv = Object.fromEntries(['PATH', 'HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy', ...Object.keys(environment)]
|
|
210
|
+
.map((key) => [key, process.env[key]]));
|
|
211
|
+
for (const key of ['HTTP_PROXY', 'http_proxy', 'ALL_PROXY', 'all_proxy']) delete process.env[key];
|
|
212
|
+
Object.assign(process.env, environment);
|
|
213
|
+
removeLegacyCentralMapping(homeDir);
|
|
214
|
+
installCursorGuard({ binDir, mappingFile: path.join(homeDir, 'central-mapping.json') });
|
|
255
215
|
process.env.PATH = `${binDir}${path.delimiter}${process.env.PATH || ''}`;
|
|
256
216
|
|
|
257
|
-
const report = await configureProviders({
|
|
258
|
-
origin,
|
|
259
|
-
caFile: certificates.cert,
|
|
260
|
-
userHome,
|
|
261
|
-
binDir,
|
|
262
|
-
mappingFile: legacyMappingFile,
|
|
263
|
-
});
|
|
264
217
|
Object.assign(state, {
|
|
265
218
|
enabled: true,
|
|
266
219
|
reason: 'running',
|
|
267
220
|
origin,
|
|
268
|
-
upstream
|
|
269
|
-
|
|
270
|
-
routes: hosts.map((host) => ({ provider: host, upstream: `https://${host}` })),
|
|
271
|
-
coverage: summarizeCoverage(report),
|
|
221
|
+
upstream,
|
|
222
|
+
coverage: COVERAGE,
|
|
272
223
|
});
|
|
273
|
-
|
|
274
|
-
const uncovered = Object.entries(state.coverage)
|
|
275
|
-
.filter(([, value]) => value.state === 'unconfigured')
|
|
276
|
-
.map(([name]) => name);
|
|
277
224
|
logger.log?.(`[piecemaker] anonymisation active sur ${origin} (claude, codex, opencode ; cursor bloqué)`);
|
|
278
|
-
if (uncovered.length) {
|
|
279
|
-
logger.warn?.(`[piecemaker] clients non filtrés, à vérifier : ${uncovered.join(', ')}`);
|
|
280
|
-
}
|
|
281
225
|
return state;
|
|
282
226
|
}
|
|
283
227
|
|
|
284
228
|
async function stop() {
|
|
285
|
-
if (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
229
|
+
if (proxy) {
|
|
230
|
+
const child = proxy;
|
|
231
|
+
proxy = null;
|
|
232
|
+
process.off('exit', killProxy);
|
|
233
|
+
if (child.exitCode === null) {
|
|
234
|
+
child.kill('SIGTERM');
|
|
235
|
+
await Promise.race([
|
|
236
|
+
new Promise((resolve) => child.once('exit', resolve)),
|
|
237
|
+
new Promise((resolve) => setTimeout(resolve, 2000)),
|
|
238
|
+
]);
|
|
239
|
+
if (child.exitCode === null) child.kill('SIGKILL');
|
|
240
|
+
}
|
|
296
241
|
}
|
|
297
242
|
if (bridge) await bridge.close();
|
|
298
243
|
bridge = null;
|
|
299
|
-
|
|
244
|
+
fs.rmSync(path.join(binDir, 'cursor-agent'), { force: true });
|
|
300
245
|
if (previousEnv) {
|
|
301
246
|
for (const [key, value] of Object.entries(previousEnv)) {
|
|
302
247
|
if (value === undefined) delete process.env[key];
|
|
@@ -304,16 +249,15 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
304
249
|
}
|
|
305
250
|
previousEnv = null;
|
|
306
251
|
}
|
|
307
|
-
proxy = null;
|
|
308
252
|
dictionary.close();
|
|
309
|
-
Object.assign(state, { enabled: false, reason: 'stopped', origin: null,
|
|
253
|
+
Object.assign(state, { enabled: false, reason: 'stopped', origin: null, upstream: null, coverage: {} });
|
|
310
254
|
}
|
|
311
255
|
|
|
312
256
|
function status() {
|
|
313
257
|
const current = dictionary.get();
|
|
314
258
|
return {
|
|
315
259
|
...state,
|
|
316
|
-
listening: Boolean(proxy && proxy.exitCode === null &&
|
|
260
|
+
listening: Boolean(proxy && proxy.exitCode === null && proxy.signalCode === null),
|
|
317
261
|
dictionary: {
|
|
318
262
|
file: dictionary.file,
|
|
319
263
|
exists: dictionary.exists(),
|
|
@@ -330,10 +274,8 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
330
274
|
}
|
|
331
275
|
|
|
332
276
|
module.exports = {
|
|
333
|
-
ENV_VAR,
|
|
334
|
-
OPENAI_ENV_VAR,
|
|
335
277
|
createAnonymizerService,
|
|
336
|
-
|
|
278
|
+
installCursorGuard,
|
|
337
279
|
resolveUpstream,
|
|
338
|
-
|
|
280
|
+
writeTrustBundle,
|
|
339
281
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
|
|
3
4
|
import { loadProductConfig } from '../../../shared/product-config.mjs';
|
|
4
5
|
|
|
@@ -23,9 +24,9 @@ function runInstaller(command: string, environment: NodeJS.ProcessEnv) {
|
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
function productRepository(): string | null {
|
|
27
|
+
function productRepository(appRoot: string): string | null {
|
|
27
28
|
try {
|
|
28
|
-
return loadProductConfig().repository;
|
|
29
|
+
return loadProductConfig(process.env.CLOUDCLI_PRODUCT_CONFIG || path.join(appRoot, 'product.config.json')).repository;
|
|
29
30
|
} catch {
|
|
30
31
|
return null;
|
|
31
32
|
}
|
|
@@ -39,7 +40,7 @@ export function createPieceMakerDesktopUpdateRouter(appRoot: string) {
|
|
|
39
40
|
return createDesktopUpdateRouter(createDesktopUpdateService({
|
|
40
41
|
appRoot,
|
|
41
42
|
platform: process.platform,
|
|
42
|
-
repository: productRepository(),
|
|
43
|
+
repository: productRepository(appRoot),
|
|
43
44
|
applicationPid: process.ppid,
|
|
44
45
|
environment: process.env,
|
|
45
46
|
runInstaller,
|
|
@@ -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é
|
|
@@ -4,6 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import type { Router } from 'express';
|
|
5
5
|
|
|
6
6
|
import { providerRuntimeService, sessionsService } from '@/modules/providers/index.js';
|
|
7
|
+
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
7
8
|
import { findApplicationRoot, getModuleDirectory } from '@/shared/utils.js';
|
|
8
9
|
|
|
9
10
|
|
|
@@ -16,7 +17,10 @@ import { createKnowledgeBackend } from './knowledge/index.js';
|
|
|
16
17
|
import { createTimesheetBackend } from './timesheet/index.js';
|
|
17
18
|
import { createCompanySearchRouter } from './company-search.js';
|
|
18
19
|
import { createBodaccSearchRouter } from './bodacc-search.js';
|
|
20
|
+
import { createDocxDocumentRouter } from './docx-document.js';
|
|
19
21
|
import { createShellEnvironmentRouter, resolveDesktopShellEnvironment } from './shell-environment.js';
|
|
22
|
+
import { installModelDiscovery } from './model-discovery/index.js';
|
|
23
|
+
import { startProjectRegistry } from './project-registry.js';
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Point d'entrée PieceMaker. Les modules de `server/piecemaker/vendor/` sont du
|
|
@@ -37,8 +41,9 @@ type PieceMakerRuntimeStatus = {
|
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
type PieceMakerVendorModule = {
|
|
40
|
-
createPieceMakerRouter(options?: { getRuntimeStatus?: () => PieceMakerRuntimeStatus; anonymizer?: unknown }): Router;
|
|
44
|
+
createPieceMakerRouter(options?: { getRuntimeStatus?: () => PieceMakerRuntimeStatus; anonymizer?: unknown; publishProjects?: () => void }): Router;
|
|
41
45
|
piecemakerHome(): string;
|
|
46
|
+
publishProjectSource(sourceId: string, folders: string[] | null): void;
|
|
42
47
|
stopOriginalsJobs(): Promise<void>;
|
|
43
48
|
};
|
|
44
49
|
|
|
@@ -46,13 +51,15 @@ const shellEnvironment = await resolveDesktopShellEnvironment();
|
|
|
46
51
|
const vendor = createRequire(import.meta.url)(routerPath) as PieceMakerVendorModule;
|
|
47
52
|
|
|
48
53
|
export const { piecemakerHome, stopOriginalsJobs } = vendor;
|
|
54
|
+
const publishProjects = startProjectRegistry(vendor.publishProjectSource);
|
|
49
55
|
const { createAnonymizerService } = createRequire(import.meta.url)(path.join(applicationRoot, 'server/piecemaker/anonymizer/service.cjs'));
|
|
50
|
-
const anonymizer = createAnonymizerService({ homeDir: piecemakerHome()
|
|
56
|
+
const anonymizer = createAnonymizerService({ homeDir: piecemakerHome() });
|
|
51
57
|
const ensureProxy = await startRequiredAnonymizer(anonymizer);
|
|
52
58
|
const citations = createCitationStore(piecemakerHome());
|
|
53
59
|
installChatCitationHarness({ runtime: providerRuntimeService, sessions: sessionsService, store: citations, ensureProxy });
|
|
54
60
|
const library = await openLibrary(piecemakerHome(), applicationRoot);
|
|
55
61
|
installLibraryRuntime(providerRuntimeService, sessionsService, library.store);
|
|
62
|
+
installModelDiscovery(providerRegistry);
|
|
56
63
|
const timesheet = createTimesheetBackend(piecemakerHome());
|
|
57
64
|
const knowledge = createKnowledgeBackend(applicationRoot);
|
|
58
65
|
|
|
@@ -61,7 +68,7 @@ export function createPieceMakerLocalRouter() {
|
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
export function createPieceMakerRouter(options: { getRuntimeStatus?: () => PieceMakerRuntimeStatus } = {}) {
|
|
64
|
-
const router = vendor.createPieceMakerRouter({ ...options, anonymizer });
|
|
71
|
+
const router = vendor.createPieceMakerRouter({ ...options, anonymizer, publishProjects });
|
|
65
72
|
router.use(createCitationsRouter(citations, (id) => {
|
|
66
73
|
try { sessionsService.getSessionDetailsById(id); return true; } catch { return false; }
|
|
67
74
|
}));
|
|
@@ -70,6 +77,7 @@ export function createPieceMakerRouter(options: { getRuntimeStatus?: () => Piece
|
|
|
70
77
|
router.use(knowledge.router);
|
|
71
78
|
router.use(createCompanySearchRouter());
|
|
72
79
|
router.use(createBodaccSearchRouter());
|
|
80
|
+
router.use(createDocxDocumentRouter());
|
|
73
81
|
router.use(createShellEnvironmentRouter(shellEnvironment));
|
|
74
82
|
return router;
|
|
75
83
|
}
|