@piecemaker-legal/piecemaker 2.0.9 → 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-WrsSAHOe.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-zGMnF7Ox.js → blockDiagram-I7D4REHJ-DQEI2azY.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-ClWw3bqt.js → c4Diagram-7LVT6UL2-DbravpVi.js} +1 -1
- package/dist/assets/channel-DVyNr2Yg.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-C-9ICPHv.js → chunk-2Q5K7J3B-BLpZ9huC.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-Bawy3teT.js → chunk-5VM5RSS4-BLlEdmX9.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-DQ375RQO.js → chunk-F27PBJKO-D-ZNhI6q.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-CflNF2f6.js → chunk-IMKFNOWR-vzSiKnhh.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CIXQv0gc.js → chunk-JWPE2WC7-DfF0k4nb.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-Cza5j6Qw.js → chunk-POPQ4Y6H-BnwCYsPP.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-GQ_VvWZR.js → chunk-SVP7TREG-DRxc8fhq.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-BEmU16W5.js → chunk-TICWLB2K-Dd2Q-md-.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-C1KpkLYH.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-CZE48TRG.js → cose-bilkent-JH36ORCC-By6PS4oe.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-L-tx1eC-.js → cynefin-OW5HDTMX-YxWpjc5W.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-Coa8oJwR.js → cynefinDiagram-5FMLGOSQ-DrtXzz3p.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-Bu_3Y75K.js → dagre-GXQ25YYZ-BDpTLKqZ.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-CFiiV4es.js → diagram-S7CK7UJ4-C4LvqeCs.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-CCXheGAA.js → diagram-UQ7AKVKN-BTpnMeHp.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-B70m4lr_.js → diagram-VSXAHHWV-DucHHWev.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-CHkiMOVA.js → diagram-VX7I27RA-DYHZNmmH.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-_awpc4Bs.js → diagram-Z3DM3KII-BHIH2BGN.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-CZzjH86H.js → ebnfDiagram-PWID7BFC-CGiEkwj0.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-9DgiHZg9.js → erDiagram-RLTQ6QDP-AJMHi2Us.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-DHY_503b.js → flowDiagram-HODETNUW-BqzANBh2.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-DPiiYZ_l.js → ganttDiagram-EL5Y4UJY-B0xPDY2_.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-Ddd_evm7.js → gitGraphDiagram-WWUBYQGX-CzPX5CNO.js} +1 -1
- package/dist/assets/{index-tibHq7Qp.js → index-Bk2iXKnJ.js} +140 -140
- package/dist/assets/index-Dc9wNuS4.css +1 -0
- package/dist/assets/{infoDiagram-27XIBGKW-BPRNhfwb.js → infoDiagram-27XIBGKW-Cjc44H0O.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-BXx6aqkq.js → ishikawaDiagram-5VMMS53U-BWhr2v7r.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-BK8TZM29.js → journeyDiagram-3NMN7TZE-Bl4r6RrP.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-CHioRmxz.js → kanban-definition-UXKFOSKX-FV-T0Vy1.js} +6 -6
- package/dist/assets/layout-CTfb1sAF.js +1 -0
- package/dist/assets/{linear-CTEhUcKG.js → linear-Dfgi6vlo.js} +1 -1
- package/dist/assets/{mermaid.core-DYUb_UUB.js → mermaid.core-DOCGsdlo.js} +7 -7
- package/dist/assets/{mindmap-definition-YA3MSWOX-D7ODZ4oS.js → mindmap-definition-YA3MSWOX-CymQ86ir.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-8J-_2_kw.js → pegDiagram-XKGWAZYB-1VMq_vi0.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-ebg0yfMi.js → pieDiagram-E7YTZNPT-C5GgXtJU.js} +1 -1
- package/dist/assets/processTemplate-GPFDLQMX-B1sVV2O0.js +22 -0
- package/dist/assets/{quadrantDiagram-AXDQQJYC-BoWkDiFb.js → quadrantDiagram-AXDQQJYC-C6zpryYf.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-BRJ77ldz.js → railroadDiagram-O6MQD6OU-D-sv4SWi.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-Z0JJgktA.js → requirementDiagram-BXWQKSXE-CkjY-z4B.js} +1 -1
- package/dist/assets/sankeyDiagram-P5KCCOFB-De-d8Q6L.js +40 -0
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-BPAjtXwp.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-BGZPCzM4.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-DvuB15qF.js → swimlanes-42K2YHIH-BwxARD7N.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-D7p8qZI2.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-ZIziYCIF.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-C5_B9gWa.js → vennDiagram-4TSXK5OY-BSD6Pfmt.js} +4 -4
- package/dist/assets/{wardleyDiagram-VM6X3IG4-DMlH9Afw.js → wardleyDiagram-VM6X3IG4-7Zz2iRkF.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-BldhJt7i.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/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 +103 -174
- 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/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,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,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');
|
|
@@ -23,19 +6,22 @@ const tls = require('node:tls');
|
|
|
23
6
|
|
|
24
7
|
const { createSqliteDictionaryLoader } = require('./sqlite-dictionary.cjs');
|
|
25
8
|
const { startRewriterBridge } = require('./rewriter-bridge.cjs');
|
|
26
|
-
const {
|
|
9
|
+
const { removeLegacyProxyConfig } = require('./client-config.cjs');
|
|
27
10
|
const { createHarnessJuridique } = require('../harness/index.cjs');
|
|
28
11
|
|
|
29
|
-
const ENV_VAR = 'ANTHROPIC_BASE_URL';
|
|
30
|
-
const OPENAI_ENV_VAR = 'OPENAI_BASE_URL';
|
|
31
12
|
const DEFAULT_UPSTREAM = 'https://api.anthropic.com';
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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.';
|
|
37
24
|
|
|
38
|
-
/** Interrupteur : `PIECEMAKER_ANONYMIZER=off`, ou `anonymizer.enabled: false` dans `config.json`. */
|
|
39
25
|
function isDisabled(homeDir) {
|
|
40
26
|
const flag = String(process.env.PIECEMAKER_ANONYMIZER || '').toLowerCase();
|
|
41
27
|
if (flag === 'off' || flag === '0' || flag === 'false') return true;
|
|
@@ -47,13 +33,6 @@ function isDisabled(homeDir) {
|
|
|
47
33
|
}
|
|
48
34
|
}
|
|
49
35
|
|
|
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
36
|
function resolveUpstream(configured) {
|
|
58
37
|
if (!configured) return DEFAULT_UPSTREAM;
|
|
59
38
|
try {
|
|
@@ -65,35 +44,15 @@ function resolveUpstream(configured) {
|
|
|
65
44
|
}
|
|
66
45
|
}
|
|
67
46
|
|
|
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
47
|
function hudsuckerBinary() {
|
|
88
48
|
if (process.env.PIECEMAKER_HUDSUCKER_BIN) return process.env.PIECEMAKER_HUDSUCKER_BIN;
|
|
89
49
|
const name = process.platform === 'win32' ? 'piecemaker-hudsucker.exe' : 'piecemaker-hudsucker';
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
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];
|
|
97
56
|
}
|
|
98
57
|
|
|
99
58
|
function certificatePaths() {
|
|
@@ -113,28 +72,52 @@ function writeTrustBundle(caFile, bundleFile) {
|
|
|
113
72
|
return bundleFile;
|
|
114
73
|
}
|
|
115
74
|
|
|
116
|
-
function
|
|
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) {
|
|
117
104
|
return new Promise((resolve, reject) => {
|
|
118
105
|
let buffer = '';
|
|
119
|
-
|
|
120
|
-
const finish = (failed, value) => {
|
|
121
|
-
if (settled) return;
|
|
122
|
-
settled = true;
|
|
106
|
+
const finish = (error, port) => {
|
|
123
107
|
clearTimeout(timer);
|
|
124
|
-
child.stdout
|
|
125
|
-
child.stderr?.off('data', take);
|
|
108
|
+
child.stdout.off('data', take);
|
|
126
109
|
child.off('exit', onExit);
|
|
127
|
-
if (
|
|
128
|
-
else resolve(
|
|
110
|
+
if (error) reject(error);
|
|
111
|
+
else resolve(port);
|
|
129
112
|
};
|
|
130
|
-
const timer = setTimeout(() => finish(
|
|
113
|
+
const timer = setTimeout(() => finish(new Error(`hudsucker n'a pas écouté : ${buffer}`)), timeoutMs);
|
|
131
114
|
const take = (chunk) => {
|
|
132
115
|
buffer += chunk.toString();
|
|
133
|
-
|
|
116
|
+
const match = buffer.match(/listening 127\.0\.0\.1:(\d+)/);
|
|
117
|
+
if (match) finish(null, Number(match[1]));
|
|
134
118
|
};
|
|
135
|
-
const onExit = (code) => finish(
|
|
136
|
-
child.stdout
|
|
137
|
-
child.stderr?.on('data', take);
|
|
119
|
+
const onExit = (code) => finish(new Error(`hudsucker sorti (${code}) : ${buffer}`));
|
|
120
|
+
child.stdout.on('data', take);
|
|
138
121
|
child.once('exit', onExit);
|
|
139
122
|
});
|
|
140
123
|
}
|
|
@@ -142,8 +125,7 @@ function waitForOutput(child, needle, timeoutMs) {
|
|
|
142
125
|
function proxyEnvironment(proxyUrl, caFile, trustBundle) {
|
|
143
126
|
return {
|
|
144
127
|
HTTPS_PROXY: proxyUrl,
|
|
145
|
-
|
|
146
|
-
ALL_PROXY: proxyUrl,
|
|
128
|
+
https_proxy: proxyUrl,
|
|
147
129
|
NO_PROXY: 'localhost,127.0.0.1,::1',
|
|
148
130
|
NODE_USE_ENV_PROXY: '1',
|
|
149
131
|
NODE_EXTRA_CA_CERTS: caFile,
|
|
@@ -153,32 +135,20 @@ function proxyEnvironment(proxyUrl, caFile, trustBundle) {
|
|
|
153
135
|
};
|
|
154
136
|
}
|
|
155
137
|
|
|
156
|
-
function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = console
|
|
138
|
+
function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = console }) {
|
|
157
139
|
const dictionary = createSqliteDictionaryLoader({ databasePath: process.env.DATABASE_PATH || path.join(homeDir, 'auth.db') });
|
|
158
|
-
const legacyMappingFile = path.join(homeDir, 'central-mapping.json');
|
|
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
140
|
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.
|
|
165
141
|
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
|
-
};
|
|
142
|
+
const state = { enabled: false, reason: 'not_started', origin: null, upstream: null, coverage: {} };
|
|
175
143
|
let proxy = null;
|
|
176
144
|
let bridge = null;
|
|
177
145
|
let previousEnv = null;
|
|
146
|
+
const killProxy = () => proxy?.kill('SIGKILL');
|
|
178
147
|
|
|
179
148
|
async function start() {
|
|
180
149
|
if (proxy) return state;
|
|
181
|
-
|
|
150
|
+
removeLegacyProxyConfig({ userHome, homeDir });
|
|
151
|
+
if (isDisabled(homeDir)) {
|
|
182
152
|
state.reason = 'disabled';
|
|
183
153
|
return state;
|
|
184
154
|
}
|
|
@@ -191,14 +161,8 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
191
161
|
return state;
|
|
192
162
|
}
|
|
193
163
|
|
|
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
|
-
}
|
|
164
|
+
const upstream = resolveUpstream(process.env.ANTHROPIC_BASE_URL);
|
|
165
|
+
const hosts = [...new Set([...INTERCEPTED_HOSTS, new URL(upstream).hostname])];
|
|
202
166
|
|
|
203
167
|
try {
|
|
204
168
|
bridge = await startRewriterBridge({ dictionary, harness });
|
|
@@ -208,33 +172,30 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
208
172
|
return state;
|
|
209
173
|
}
|
|
210
174
|
|
|
211
|
-
const listenedPort = DEFAULT_PORT;
|
|
212
|
-
const origin = `http://127.0.0.1:${listenedPort}`;
|
|
213
175
|
const child = spawn(binary, [
|
|
214
|
-
'--listen',
|
|
176
|
+
'--listen', '127.0.0.1:0',
|
|
215
177
|
'--rewriter', `http://127.0.0.1:${bridge.port}`,
|
|
216
178
|
'--ca-cert', certificates.cert,
|
|
217
179
|
'--ca-key', certificates.key,
|
|
218
180
|
'--hosts', hosts.join(','),
|
|
219
181
|
], {
|
|
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
|
-
},
|
|
182
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
183
|
+
env: { ...process.env, HTTPS_PROXY: '', HTTP_PROXY: '', ALL_PROXY: '', https_proxy: '', http_proxy: '', all_proxy: '' },
|
|
230
184
|
});
|
|
231
185
|
proxy = child;
|
|
232
|
-
|
|
233
|
-
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;
|
|
234
193
|
try {
|
|
235
|
-
await
|
|
194
|
+
port = await waitForListening(child, 15000);
|
|
195
|
+
child.stdout.resume();
|
|
236
196
|
} catch (error) {
|
|
237
197
|
child.kill('SIGKILL');
|
|
198
|
+
process.off('exit', killProxy);
|
|
238
199
|
proxy = null;
|
|
239
200
|
await bridge.close();
|
|
240
201
|
bridge = null;
|
|
@@ -243,72 +204,44 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
243
204
|
return state;
|
|
244
205
|
}
|
|
245
206
|
|
|
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));
|
|
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') });
|
|
266
215
|
process.env.PATH = `${binDir}${path.delimiter}${process.env.PATH || ''}`;
|
|
267
216
|
|
|
268
|
-
const report = await configureProviders({
|
|
269
|
-
origin,
|
|
270
|
-
caFile: certificates.cert,
|
|
271
|
-
trustBundle,
|
|
272
|
-
userHome,
|
|
273
|
-
binDir,
|
|
274
|
-
mappingFile: legacyMappingFile,
|
|
275
|
-
});
|
|
276
217
|
Object.assign(state, {
|
|
277
218
|
enabled: true,
|
|
278
219
|
reason: 'running',
|
|
279
220
|
origin,
|
|
280
|
-
upstream
|
|
281
|
-
|
|
282
|
-
routes: hosts.map((host) => ({ provider: host, upstream: `https://${host}` })),
|
|
283
|
-
coverage: summarizeCoverage(report),
|
|
221
|
+
upstream,
|
|
222
|
+
coverage: COVERAGE,
|
|
284
223
|
});
|
|
285
|
-
|
|
286
|
-
const uncovered = Object.entries(state.coverage)
|
|
287
|
-
.filter(([, value]) => value.state === 'unconfigured')
|
|
288
|
-
.map(([name]) => name);
|
|
289
224
|
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
225
|
return state;
|
|
294
226
|
}
|
|
295
227
|
|
|
296
228
|
async function stop() {
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
+
}
|
|
308
241
|
}
|
|
309
242
|
if (bridge) await bridge.close();
|
|
310
243
|
bridge = null;
|
|
311
|
-
|
|
244
|
+
fs.rmSync(path.join(binDir, 'cursor-agent'), { force: true });
|
|
312
245
|
if (previousEnv) {
|
|
313
246
|
for (const [key, value] of Object.entries(previousEnv)) {
|
|
314
247
|
if (value === undefined) delete process.env[key];
|
|
@@ -316,16 +249,15 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
316
249
|
}
|
|
317
250
|
previousEnv = null;
|
|
318
251
|
}
|
|
319
|
-
proxy = null;
|
|
320
252
|
dictionary.close();
|
|
321
|
-
Object.assign(state, { enabled: false, reason: 'stopped', origin: null,
|
|
253
|
+
Object.assign(state, { enabled: false, reason: 'stopped', origin: null, upstream: null, coverage: {} });
|
|
322
254
|
}
|
|
323
255
|
|
|
324
256
|
function status() {
|
|
325
257
|
const current = dictionary.get();
|
|
326
258
|
return {
|
|
327
259
|
...state,
|
|
328
|
-
listening: Boolean(proxy && proxy.exitCode === null &&
|
|
260
|
+
listening: Boolean(proxy && proxy.exitCode === null && proxy.signalCode === null),
|
|
329
261
|
dictionary: {
|
|
330
262
|
file: dictionary.file,
|
|
331
263
|
exists: dictionary.exists(),
|
|
@@ -342,11 +274,8 @@ function createAnonymizerService({ homeDir, userHome = os.homedir(), logger = co
|
|
|
342
274
|
}
|
|
343
275
|
|
|
344
276
|
module.exports = {
|
|
345
|
-
ENV_VAR,
|
|
346
|
-
OPENAI_ENV_VAR,
|
|
347
277
|
createAnonymizerService,
|
|
348
|
-
|
|
278
|
+
installCursorGuard,
|
|
349
279
|
resolveUpstream,
|
|
350
|
-
summarizeCoverage,
|
|
351
280
|
writeTrustBundle,
|
|
352
281
|
};
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
|
|
3
|
+
import type { ProviderModelOption, ProviderModelsDefinition } from '@/shared/types.js';
|
|
4
|
+
|
|
5
|
+
import { withStdioRpcServer, type RpcCall, type StdioRpcServer } from './stdio-rpc.js';
|
|
6
|
+
|
|
7
|
+
type CodexListedModel = {
|
|
8
|
+
model: string;
|
|
9
|
+
displayName?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
hidden?: boolean;
|
|
12
|
+
isDefault?: boolean;
|
|
13
|
+
defaultReasoningEffort?: string;
|
|
14
|
+
supportedReasoningEfforts?: { reasoningEffort: string; description?: string }[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type CodexModelListPage = {
|
|
18
|
+
data?: CodexListedModel[];
|
|
19
|
+
nextCursor?: string | null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const MAX_PAGES = 10;
|
|
23
|
+
|
|
24
|
+
function bundledCodexAppServer(): StdioRpcServer {
|
|
25
|
+
const launcher = createRequire(import.meta.url).resolve('@openai/codex/bin/codex.js');
|
|
26
|
+
return { command: process.execPath, args: [launcher, 'app-server'] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function toModelOption(listed: CodexListedModel): ProviderModelOption {
|
|
30
|
+
const efforts = listed.supportedReasoningEfforts ?? [];
|
|
31
|
+
return {
|
|
32
|
+
value: listed.model,
|
|
33
|
+
label: listed.displayName || listed.model,
|
|
34
|
+
...(listed.description ? { description: listed.description } : {}),
|
|
35
|
+
...(efforts.length > 0 ? {
|
|
36
|
+
effort: {
|
|
37
|
+
...(listed.defaultReasoningEffort ? { default: listed.defaultReasoningEffort } : {}),
|
|
38
|
+
values: efforts.map(({ reasoningEffort, description }) => ({
|
|
39
|
+
value: reasoningEffort,
|
|
40
|
+
...(description ? { description } : {}),
|
|
41
|
+
})),
|
|
42
|
+
},
|
|
43
|
+
} : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function listCodexModels(call: RpcCall): Promise<ProviderModelsDefinition> {
|
|
48
|
+
const listed: CodexListedModel[] = [];
|
|
49
|
+
let cursor: string | null = null;
|
|
50
|
+
for (let page = 0; page < MAX_PAGES; page += 1) {
|
|
51
|
+
const result = await call('model/list', { cursor, includeHidden: false }) as CodexModelListPage;
|
|
52
|
+
listed.push(...(result.data ?? []));
|
|
53
|
+
cursor = result.nextCursor ?? null;
|
|
54
|
+
if (!cursor) break;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const visible = listed.filter((model) => model.model && !model.hidden);
|
|
58
|
+
if (visible.length === 0) throw new Error('codex app-server listed no models');
|
|
59
|
+
return {
|
|
60
|
+
OPTIONS: visible.map(toModelOption),
|
|
61
|
+
DEFAULT: (visible.find((model) => model.isDefault) ?? visible[0]).model,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function discoverCodexModels(timeoutMs: number): Promise<ProviderModelsDefinition> {
|
|
66
|
+
return withStdioRpcServer(bundledCodexAppServer(), listCodexModels, timeoutMs);
|
|
67
|
+
}
|