@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.
Files changed (177) hide show
  1. package/dist/assets/FindReplaceDialog-KU5TEIBJ-BucOHLdm.js +1 -0
  2. package/dist/assets/FootnotePropertiesDialog-W3YHM4BV-D5UqbelV.js +1 -0
  3. package/dist/assets/HyperlinkDialog-ZYH7ZHRF-8yEhLigd.js +144 -0
  4. package/dist/assets/HyperlinkDialog-ZYH7ZHRF-qIPBOssF.css +1 -0
  5. package/dist/assets/ImagePositionDialog-HEWYR6Q3-wGIBYxCm.js +1 -0
  6. package/dist/assets/ImagePropertiesDialog-RAB47C6M-D86PckZ2.js +1 -0
  7. package/dist/assets/PageSetupDialog-AIGIXO3E-DLETP-7v.js +1 -0
  8. package/dist/assets/SplitCellDialog-IFBKYCD6-CwdQBXeb.js +1 -0
  9. package/dist/assets/TablePropertiesDialog-TMLKGOOW-j2nA7KJs.js +1 -0
  10. package/dist/assets/WatermarkDialog-KRL7WMIS-XFJ9gTkC.js +1 -0
  11. package/dist/assets/{abnfDiagram-VCTEODGH-WrsSAHOe.js → abnfDiagram-VCTEODGH-DiCrND_Y.js} +1 -1
  12. package/dist/assets/arc-DOjgbMYz.js +1 -0
  13. package/dist/assets/architectureDiagram-5GKGNRK7-brzIFVhl.js +36 -0
  14. package/dist/assets/{blockDiagram-I7D4REHJ-zGMnF7Ox.js → blockDiagram-I7D4REHJ-C8JhTjQQ.js} +1 -1
  15. package/dist/assets/{c4Diagram-7LVT6UL2-ClWw3bqt.js → c4Diagram-7LVT6UL2-jfYxccyk.js} +1 -1
  16. package/dist/assets/channel-Dajzu2W1.js +1 -0
  17. package/dist/assets/{chunk-2Q5K7J3B-C-9ICPHv.js → chunk-2Q5K7J3B-CrlEShp9.js} +1 -1
  18. package/dist/assets/{chunk-5VM5RSS4-Bawy3teT.js → chunk-5VM5RSS4-DzPlUHGq.js} +1 -1
  19. package/dist/assets/{chunk-F27PBJKO-DQ375RQO.js → chunk-F27PBJKO-Cp9cpeWU.js} +1 -1
  20. package/dist/assets/{chunk-IMKFNOWR-CflNF2f6.js → chunk-IMKFNOWR-BifUldEN.js} +1 -1
  21. package/dist/assets/{chunk-JWPE2WC7-CIXQv0gc.js → chunk-JWPE2WC7-ggPEIkWu.js} +1 -1
  22. package/dist/assets/{chunk-POPQ4Y6H-Cza5j6Qw.js → chunk-POPQ4Y6H-3h_nayRX.js} +1 -1
  23. package/dist/assets/{chunk-SVP7TREG-GQ_VvWZR.js → chunk-SVP7TREG-CH29aG6J.js} +1 -1
  24. package/dist/assets/{chunk-TICWLB2K-BEmU16W5.js → chunk-TICWLB2K-PluJUM5k.js} +1 -1
  25. package/dist/assets/{chunk-XXDRQBXY-C1KpkLYH.js → chunk-XXDRQBXY-DXU39Vwg.js} +1 -1
  26. package/dist/assets/classDiagram-ZZMXUADV-dDEP_giv.js +1 -0
  27. package/dist/assets/classDiagram-v2-VYDZK3BY-dDEP_giv.js +1 -0
  28. package/dist/assets/{cose-bilkent-JH36ORCC-CZE48TRG.js → cose-bilkent-JH36ORCC-Dz6roYrN.js} +1 -1
  29. package/dist/assets/{cynefin-OW5HDTMX-L-tx1eC-.js → cynefin-OW5HDTMX-BLzrpSit.js} +1 -1
  30. package/dist/assets/{cynefinDiagram-5FMLGOSQ-Coa8oJwR.js → cynefinDiagram-5FMLGOSQ-kfxnERDZ.js} +1 -1
  31. package/dist/assets/{dagre-GXQ25YYZ-Bu_3Y75K.js → dagre-GXQ25YYZ-DP57bp-N.js} +1 -1
  32. package/dist/assets/{diagram-S7CK7UJ4-CFiiV4es.js → diagram-S7CK7UJ4-DAlsrQVW.js} +1 -1
  33. package/dist/assets/{diagram-UQ7AKVKN-CCXheGAA.js → diagram-UQ7AKVKN-By5yrMer.js} +1 -1
  34. package/dist/assets/{diagram-VSXAHHWV-B70m4lr_.js → diagram-VSXAHHWV-Dx89kryN.js} +1 -1
  35. package/dist/assets/{diagram-VX7I27RA-CHkiMOVA.js → diagram-VX7I27RA-B9fARitZ.js} +1 -1
  36. package/dist/assets/{diagram-Z3DM3KII-_awpc4Bs.js → diagram-Z3DM3KII-DC15CEH1.js} +1 -1
  37. package/dist/assets/{ebnfDiagram-PWID7BFC-CZzjH86H.js → ebnfDiagram-PWID7BFC-BKQzghGF.js} +1 -1
  38. package/dist/assets/{erDiagram-RLTQ6QDP-9DgiHZg9.js → erDiagram-RLTQ6QDP-CPY6RM7G.js} +1 -1
  39. package/dist/assets/{flowDiagram-HODETNUW-DHY_503b.js → flowDiagram-HODETNUW-4dL4GJcf.js} +1 -1
  40. package/dist/assets/{ganttDiagram-EL5Y4UJY-DPiiYZ_l.js → ganttDiagram-EL5Y4UJY-D4-cVcfb.js} +1 -1
  41. package/dist/assets/{gitGraphDiagram-WWUBYQGX-Ddd_evm7.js → gitGraphDiagram-WWUBYQGX-BMeVIEiX.js} +1 -1
  42. package/dist/assets/{index-tibHq7Qp.js → index-DNtFDOni.js} +140 -140
  43. package/dist/assets/index-Dc9wNuS4.css +1 -0
  44. package/dist/assets/{infoDiagram-27XIBGKW-BPRNhfwb.js → infoDiagram-27XIBGKW-D5qMSUEU.js} +1 -1
  45. package/dist/assets/{ishikawaDiagram-5VMMS53U-BXx6aqkq.js → ishikawaDiagram-5VMMS53U-BDnyKRHm.js} +1 -1
  46. package/dist/assets/{journeyDiagram-3NMN7TZE-BK8TZM29.js → journeyDiagram-3NMN7TZE-dGz5ZGzq.js} +1 -1
  47. package/dist/assets/{kanban-definition-UXKFOSKX-CHioRmxz.js → kanban-definition-UXKFOSKX-D7UAz7o_.js} +6 -6
  48. package/dist/assets/layout-DKrmEGrz.js +1 -0
  49. package/dist/assets/{linear-CTEhUcKG.js → linear-CrTdMY-p.js} +1 -1
  50. package/dist/assets/{mermaid.core-DYUb_UUB.js → mermaid.core-CXQOT5G6.js} +7 -7
  51. package/dist/assets/{mindmap-definition-YA3MSWOX-D7ODZ4oS.js → mindmap-definition-YA3MSWOX-VgEiYfay.js} +1 -1
  52. package/dist/assets/{pegDiagram-XKGWAZYB-8J-_2_kw.js → pegDiagram-XKGWAZYB-9IqT8gGx.js} +1 -1
  53. package/dist/assets/{pieDiagram-E7YTZNPT-ebg0yfMi.js → pieDiagram-E7YTZNPT-D98pRE-t.js} +1 -1
  54. package/dist/assets/processTemplate-GPFDLQMX-B1sVV2O0.js +22 -0
  55. package/dist/assets/{quadrantDiagram-AXDQQJYC-BoWkDiFb.js → quadrantDiagram-AXDQQJYC-b9PwkYzL.js} +1 -1
  56. package/dist/assets/{railroadDiagram-O6MQD6OU-BRJ77ldz.js → railroadDiagram-O6MQD6OU-Dz87hsPE.js} +1 -1
  57. package/dist/assets/{requirementDiagram-BXWQKSXE-Z0JJgktA.js → requirementDiagram-BXWQKSXE-CkcV73dK.js} +1 -1
  58. package/dist/assets/sankeyDiagram-P5KCCOFB-UBN31Yrs.js +40 -0
  59. package/dist/assets/{sequenceDiagram-WJ2MYXX4-BPAjtXwp.js → sequenceDiagram-WJ2MYXX4-CpQxd5Za.js} +1 -1
  60. package/dist/assets/{server.browser-BzI1jrZw.js → server.browser-BY3bL-IX.js} +1 -1
  61. package/dist/assets/{sizeCapture-INFHLROL-BGZPCzM4.js → sizeCapture-INFHLROL-mCsFmyzw.js} +1 -1
  62. package/dist/assets/stateDiagram-D77RDMKH-B0MTBN-3.js +1 -0
  63. package/dist/assets/stateDiagram-v2-MP3YSRHH-DzBPN39W.js +1 -0
  64. package/dist/assets/{swimlanes-42K2YHIH-DvuB15qF.js → swimlanes-42K2YHIH-Dzydjm0e.js} +1 -1
  65. package/dist/assets/swimlanesDiagram-VR7AAH4N-BXQpbUMD.js +8 -0
  66. package/dist/assets/{timeline-definition-24CTP7MA-ZIziYCIF.js → timeline-definition-24CTP7MA-Dz0toOLu.js} +3 -3
  67. package/dist/assets/{vendor-codemirror-CjW0lHR5.js → vendor-codemirror-CN-sJePf.js} +2 -2
  68. package/dist/assets/{vendor-react-D809iwXR.js → vendor-react-B22wqN3J.js} +5 -5
  69. package/dist/assets/{vennDiagram-4TSXK5OY-C5_B9gWa.js → vennDiagram-4TSXK5OY-DOWp7dkf.js} +4 -4
  70. package/dist/assets/{wardleyDiagram-VM6X3IG4-DMlH9Afw.js → wardleyDiagram-VM6X3IG4-CUxsztUB.js} +1 -1
  71. package/dist/assets/{xychartDiagram-S5SC5T6Z-BldhJt7i.js → xychartDiagram-S5SC5T6Z-DS96vNED.js} +1 -1
  72. package/dist/index.html +4 -4
  73. package/dist/sw.js +1 -1
  74. package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js +4 -53
  75. package/dist-server/server/modules/providers/list/mistral/mistral-models.provider.js.map +1 -1
  76. package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js +2 -3
  77. package/dist-server/server/modules/providers/list/mistral/mistral-runtime.provider.js.map +1 -1
  78. package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js +30 -0
  79. package/dist-server/server/modules/providers/list/mistral/mistral-vibe-models.js.map +1 -0
  80. package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js +24 -0
  81. package/dist-server/server/modules/providers/tests/mistral-vibe-models.test.js.map +1 -0
  82. package/dist-server/server/piecemaker/agent-instructions.js +148 -0
  83. package/dist-server/server/piecemaker/agent-instructions.js.map +1 -0
  84. package/dist-server/server/piecemaker/anonymizer/lifecycle.js +2 -3
  85. package/dist-server/server/piecemaker/anonymizer/lifecycle.js.map +1 -1
  86. package/dist-server/server/piecemaker/docx-document.js +50 -0
  87. package/dist-server/server/piecemaker/docx-document.js.map +1 -0
  88. package/dist-server/server/piecemaker/index.js +13 -2
  89. package/dist-server/server/piecemaker/index.js.map +1 -1
  90. package/dist-server/server/piecemaker/model-discovery/codex-catalog.js +46 -0
  91. package/dist-server/server/piecemaker/model-discovery/codex-catalog.js.map +1 -0
  92. package/dist-server/server/piecemaker/model-discovery/index.js +42 -0
  93. package/dist-server/server/piecemaker/model-discovery/index.js.map +1 -0
  94. package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js +19 -0
  95. package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js.map +1 -0
  96. package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js +61 -0
  97. package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js.map +1 -0
  98. package/dist-server/server/piecemaker/project-registry.js +29 -0
  99. package/dist-server/server/piecemaker/project-registry.js.map +1 -0
  100. package/package.json +12 -1
  101. package/scripts/piecemaker/cli/disable-anonymizer.mjs +19 -0
  102. package/scripts/piecemaker/cli/lib/config.mjs +0 -1
  103. package/scripts/piecemaker/cli/lib/plugins.mjs +1 -0
  104. package/scripts/piecemaker/cli/lib/services.mjs +1 -10
  105. package/scripts/piecemaker/cli/piecemaker.mjs +0 -3
  106. package/server/modules/providers/list/mistral/mistral-models.provider.ts +4 -53
  107. package/server/modules/providers/list/mistral/mistral-runtime.provider.ts +2 -3
  108. package/server/modules/providers/list/mistral/mistral-vibe-models.ts +43 -0
  109. package/server/modules/providers/tests/mistral-vibe-models.test.ts +27 -0
  110. package/server/piecemaker/agent-instructions.ts +148 -0
  111. package/server/piecemaker/anonymizer/client-config.cjs +92 -92
  112. package/server/piecemaker/anonymizer/dictionary.cjs +34 -3
  113. package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +9 -16
  114. package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.toml +1 -1
  115. package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +251 -268
  116. package/server/piecemaker/anonymizer/hudsucker-smoke.cjs +343 -0
  117. package/server/piecemaker/anonymizer/lifecycle.ts +2 -3
  118. package/server/piecemaker/anonymizer/rewriter-bridge.cjs +13 -4
  119. package/server/piecemaker/anonymizer/rewriter-worker.cjs +85 -0
  120. package/server/piecemaker/anonymizer/service.cjs +109 -179
  121. package/server/piecemaker/anonymizer/sqlite-dictionary.cjs +12 -1
  122. package/server/piecemaker/docx-document.ts +54 -0
  123. package/server/piecemaker/harness/decisions.cjs +1 -1
  124. package/server/piecemaker/harness/index.cjs +2 -3
  125. package/server/piecemaker/harness/verification.cjs +1 -1
  126. package/server/piecemaker/index.ts +15 -3
  127. package/server/piecemaker/model-discovery/codex-catalog.ts +67 -0
  128. package/server/piecemaker/model-discovery/index.ts +69 -0
  129. package/server/piecemaker/model-discovery/mistral-catalog.ts +36 -0
  130. package/server/piecemaker/model-discovery/stdio-rpc.ts +79 -0
  131. package/server/piecemaker/project-registry.ts +34 -0
  132. package/server/piecemaker/router.cjs +6 -8
  133. package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +31 -77
  134. package/server/piecemaker/vendor/installer/lib/codex-skills.mjs +2 -84
  135. package/server/piecemaker/vendor/installer/lib/service.mjs +4 -24
  136. package/server/piecemaker/vendor/installer/steps/02-dependances-node.mjs +5 -5
  137. package/server/piecemaker/vendor/installer/steps/06-hooks.mjs +10 -21
  138. package/server/piecemaker/vendor/installer/steps/09-claude-assets.mjs +0 -11
  139. package/server/piecemaker/vendor/installer/steps/09-codex-plugin.mjs +6 -36
  140. package/server/piecemaker/vendor/installer/steps/13-garde-secrets.mjs +30 -12
  141. package/server/piecemaker/vendor/piecemaker-plugin/README.md +2 -9
  142. package/server/piecemaker/vendor/piecemaker-plugin/scripts/billing-track.mjs +5 -5
  143. package/server/piecemaker/vendor/piecemaker-plugin/scripts/classify-ai-documents.mjs +8 -8
  144. package/server/piecemaker/vendor/piecemaker-plugin/scripts/commit-track.mjs +2 -2
  145. package/server/piecemaker/vendor/piecemaker-plugin/scripts/compile-recherche.mjs +4 -6
  146. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folder-structure.cjs +2 -90
  147. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folders.cjs +71 -38
  148. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/mapping.cjs +8 -38
  149. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/protection.cjs +82 -61
  150. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +189 -41
  151. package/server/piecemaker/vendor/piecemaker-plugin/scripts/protect-originals.mjs +4 -55
  152. package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +24 -117
  153. package/server/piecemaker/vendor/piecemaker-plugin/skills/ajout-de-fonctionnalites/SKILL.md +4 -18
  154. package/server/piecemaker/vendor/piecemaker-plugin/skills/tamponnage/SKILL.md +2 -2
  155. package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +14 -148
  156. package/server/piecemaker/vendor/websocket-server/case-registry.cjs +16 -42
  157. package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +0 -73
  158. package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +0 -7
  159. package/server/piecemaker/vendor/websocket-server/stamping-routes.cjs +3 -12
  160. package/server/piecemaker/vendor/websocket-server/workspace-paths.cjs +6 -6
  161. package/dist/assets/arc-DAA6wH-C.js +0 -1
  162. package/dist/assets/architectureDiagram-5GKGNRK7-BGNHr7ef.js +0 -36
  163. package/dist/assets/channel-BAQyfMll.js +0 -1
  164. package/dist/assets/classDiagram-ZZMXUADV-BNGCuvrR.js +0 -1
  165. package/dist/assets/classDiagram-v2-VYDZK3BY-BNGCuvrR.js +0 -1
  166. package/dist/assets/index-WwknP0l4.css +0 -1
  167. package/dist/assets/layout-DEnkyfTY.js +0 -1
  168. package/dist/assets/sankeyDiagram-P5KCCOFB-4PW5HTOP.js +0 -40
  169. package/dist/assets/stateDiagram-D77RDMKH-o-vEcKwz.js +0 -1
  170. package/dist/assets/stateDiagram-v2-MP3YSRHH-BsuDaKkN.js +0 -1
  171. package/dist/assets/swimlanesDiagram-VR7AAH4N-DcCAyKlT.js +0 -8
  172. package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +0 -78
  173. package/scripts/piecemaker/hooks/proxy-guard.mjs +0 -251
  174. package/server/piecemaker/anonymizer/providers.cjs +0 -120
  175. package/server/piecemaker/vendor/installer/steps/08-telegram.mjs +0 -482
  176. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/central-mapping.cjs +0 -0
  177. package/server/piecemaker/vendor/websocket-server/telegram-admin.cjs +0 -602
@@ -1,602 +0,0 @@
1
- const fs = require('node:fs');
2
- const crypto = require('node:crypto');
3
- const os = require('node:os');
4
- const path = require('node:path');
5
- const { spawnSync } = require('node:child_process');
6
- const { isProtectedFile, readProtection } = require('../piecemaker-plugin/scripts/lib/protection.cjs');
7
- const { listConfiguredCases, readRegistryConfig } = require('./case-registry.cjs');
8
-
9
- const ASSISTANT_DIR = 'telegram-piecemaker';
10
- const MONITOR_DIR = 'telegram-piecemaker-lord';
11
- const LEGACY_MONITOR_DIR = 'telegram-lord';
12
- const MONITOR_LABEL = 'com.piecemaker.telegram-monitor';
13
- const DEFAULT_ASSISTANT_NAME = 'Assistant PieceMaker';
14
- const DEFAULT_MONITOR_NAME = 'PieceMaker Monitor';
15
- const RESERVED_DOSSIER_DIRS = new Set([
16
- 'models', 'ressources', '_python_uploads', 'node_modules', 'admin', 'build',
17
- 'certificates', 'docs', 'electron', 'installer', 'orchestrator',
18
- 'output', 'piecemaker-plugin', 'test', 'websocket-server',
19
- ]);
20
- // Arborescences techniques : un dossier juridique finit par en héberger.
21
- const SKIPPED_DIR_NAMES = new Set(['node_modules', 'dist', 'build', 'out', 'coverage', '__pycache__', 'venv']);
22
- const MAX_PROTECTED_NAMES = 100;
23
-
24
- function readJson(file, fallback = {}) {
25
- try {
26
- return fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : fallback;
27
- } catch {
28
- return fallback;
29
- }
30
- }
31
-
32
- function atomicWrite(file, content, mode = undefined) {
33
- fs.mkdirSync(path.dirname(file), { recursive: true });
34
- const temporary = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${Date.now()}.tmp`);
35
- fs.writeFileSync(temporary, content, { encoding: 'utf8', ...(mode ? { mode } : {}) });
36
- fs.renameSync(temporary, file);
37
- if (mode) {
38
- try { fs.chmodSync(file, mode); } catch { /* Windows applique ses ACL. */ }
39
- }
40
- }
41
-
42
- function readToken(stateDir) {
43
- try {
44
- return fs.readFileSync(path.join(stateDir, '.env'), 'utf8')
45
- .match(/^TELEGRAM_BOT_TOKEN=(.+)$/m)?.[1]
46
- ?.trim() || '';
47
- } catch {
48
- return '';
49
- }
50
- }
51
-
52
- function tokenState(token) {
53
- if (!token) return { configured: false, hint: '' };
54
- return { configured: true, hint: token.length > 4 ? `••••${token.slice(-4)}` : 'configuré' };
55
- }
56
-
57
- function validateToken(value) {
58
- const token = String(value || '').trim();
59
- if (!token) return '';
60
- if (token.length > 256 || /\s/.test(token) || !/^\d+:[A-Za-z0-9_-]{20,}$/.test(token)) {
61
- throw new Error('Le token Telegram doit être un token BotFather valide, au format nombre:secret.');
62
- }
63
- return token;
64
- }
65
-
66
- function writeToken(stateDir, token) {
67
- const file = path.join(stateDir, '.env');
68
- const original = fs.existsSync(file) ? fs.readFileSync(file, 'utf8').split(/\r?\n/) : [];
69
- let replaced = false;
70
- const lines = original
71
- .filter((line, index, values) => line || index < values.length - 1)
72
- .map((line) => {
73
- if (/^\s*TELEGRAM_BOT_TOKEN=/.test(line)) {
74
- replaced = true;
75
- return `TELEGRAM_BOT_TOKEN=${token}`;
76
- }
77
- return line;
78
- });
79
- if (!replaced) lines.push(`TELEGRAM_BOT_TOKEN=${token}`);
80
- while (lines.at(-1) === '') lines.pop();
81
- atomicWrite(file, `${lines.join('\n')}\n`, 0o600);
82
- }
83
-
84
- function cleanName(value, fallback) {
85
- const name = String(value || '').replace(/[\x00-\x1f\x7f]/g, ' ').replace(/\s+/g, ' ').trim();
86
- if (!name) return fallback;
87
- if (name.length > 64) throw new Error('Le nom Telegram est limité à 64 caractères.');
88
- return name;
89
- }
90
-
91
- function projectId(value) {
92
- const slug = String(value || '')
93
- .normalize('NFD')
94
- .replace(/[\u0300-\u036f]/g, '')
95
- .toLowerCase()
96
- .replace(/[^a-z0-9]+/g, '-')
97
- .replace(/^-|-$/g, '')
98
- .slice(0, 48) || 'dossier';
99
- return slug === 'piecemaker' ? 'dossier-piecemaker' : slug;
100
- }
101
-
102
- function run(command, args, options = {}) {
103
- return spawnSync(command, args, {
104
- encoding: 'utf8',
105
- timeout: options.timeout || 10_000,
106
- cwd: options.cwd,
107
- env: options.env || process.env,
108
- windowsHide: true,
109
- });
110
- }
111
-
112
- function processExists(pid) {
113
- if (!Number.isInteger(pid) || pid <= 1) return false;
114
- try {
115
- process.kill(pid, 0);
116
- return true;
117
- } catch (error) {
118
- return error.code === 'EPERM';
119
- }
120
- }
121
-
122
- function psValue(pid, field) {
123
- if (process.platform === 'win32') return '';
124
- const executable = process.platform === 'darwin' ? '/bin/ps' : 'ps';
125
- const result = run(executable, ['-p', String(pid), '-o', `${field}=`]);
126
- return result.status === 0 ? String(result.stdout || '').trim() : '';
127
- }
128
-
129
- function readBotProcess(stateDir) {
130
- const pidFile = path.join(stateDir, 'bot.pid');
131
- let pid = 0;
132
- try { pid = Number.parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10); } catch { return null; }
133
- if (!processExists(pid)) return null;
134
-
135
- // Sur macOS/Linux, refuse un PID recyclé ou un processus qui n'est pas le poller Telegram.
136
- if (process.platform !== 'win32') {
137
- const command = psValue(pid, 'command');
138
- const born = Date.parse(psValue(pid, 'lstart'));
139
- let pidFileTime = 0;
140
- try { pidFileTime = fs.statSync(pidFile).mtimeMs; } catch { return null; }
141
- if (!/\bbun\b.*\bserver\.ts\b/.test(command)) return null;
142
- if (!Number.isFinite(born) || born > pidFileTime + 2000) return null;
143
- }
144
- return { pid, command: psValue(pid, 'command') };
145
- }
146
-
147
- function findClaudeParent(botPid) {
148
- let pid = botPid;
149
- for (let depth = 0; depth < 10 && pid > 1; depth++) {
150
- const parent = Number.parseInt(psValue(pid, 'ppid'), 10);
151
- if (!Number.isInteger(parent) || parent <= 1) return null;
152
- const command = psValue(parent, 'command');
153
- if (/\bclaude\b.*--channels\b.*telegram/.test(command)) return parent;
154
- pid = parent;
155
- }
156
- return null;
157
- }
158
-
159
- function monitorStatus(userHome) {
160
- const plist = path.join(userHome, 'Library', 'LaunchAgents', `${MONITOR_LABEL}.plist`);
161
- const installed = fs.existsSync(plist);
162
- if (process.platform !== 'darwin' || typeof process.getuid !== 'function') {
163
- return { installed, loaded: false, running: false, pid: null, autoStart: installed, plist };
164
- }
165
- const service = `gui/${process.getuid()}/${MONITOR_LABEL}`;
166
- const result = run('/bin/launchctl', ['print', service]);
167
- const output = `${result.stdout || ''}\n${result.stderr || ''}`;
168
- const pid = Number.parseInt(output.match(/\bpid\s*=\s*(\d+)/)?.[1] || '', 10) || null;
169
- return { installed, loaded: result.status === 0, running: result.status === 0 && Boolean(pid), pid, autoStart: installed, plist };
170
- }
171
-
172
- function configuredDossiersRoot(repoRoot, homeDir) {
173
- const config = readJson(path.join(homeDir, 'config.json'), {});
174
- const candidate = config.dossiersRoot || config.outputPath || repoRoot;
175
- return path.resolve(String(candidate));
176
- }
177
-
178
- function countMarkdownFiles(root, { maxDepth = 6, maxFiles = 5000 } = {}) {
179
- let count = 0;
180
- function visit(directory, depth) {
181
- if (depth > maxDepth || count >= maxFiles) return;
182
- let entries = [];
183
- try { entries = fs.readdirSync(directory, { withFileTypes: true }); } catch { return; }
184
- for (const entry of entries) {
185
- if (count >= maxFiles) break;
186
- if (entry.isSymbolicLink()) continue;
187
- if (entry.isDirectory()) {
188
- if (!entry.name.startsWith('.') && !SKIPPED_DIR_NAMES.has(entry.name.toLowerCase())) {
189
- visit(path.join(directory, entry.name), depth + 1);
190
- }
191
- } else if (entry.isFile() && entry.name.toLowerCase().endsWith('.md')) {
192
- count += 1;
193
- }
194
- }
195
- }
196
- visit(root, 0);
197
- return count;
198
- }
199
-
200
- function escapeRegExp(value) {
201
- return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
202
- }
203
-
204
- function mappingPairs(mappingFile) {
205
- const document = readJson(mappingFile, null);
206
- if (!document || typeof document !== 'object') return [];
207
- const source = document.mapping || document.mappings || document.anonymizationMapping || document;
208
- if (!source || typeof source !== 'object') return [];
209
- const pairs = [];
210
- const ignoredKeys = new Set(['reverse_mapping', 'reverseMapping', 'extracted_data', 'extractedData']);
211
-
212
- function add(original, replacement) {
213
- const from = String(original || '').trim();
214
- const to = String(replacement || '').trim();
215
- if (from && to && from !== to) pairs.push([from, to]);
216
- }
217
-
218
- function visit(node) {
219
- if (!node || typeof node !== 'object' || Array.isArray(node)) return;
220
- if (typeof node.code === 'string') {
221
- add(node.original, node.code);
222
- add(node.variant, node.code);
223
- return;
224
- }
225
- for (const [key, value] of Object.entries(node)) {
226
- if (ignoredKeys.has(key)) continue;
227
- if (typeof value === 'string') add(key, value);
228
- else visit(value);
229
- }
230
- }
231
-
232
- visit(source);
233
- return pairs.sort((a, b) => b[0].length - a[0].length);
234
- }
235
-
236
- function filteredOriginalName(filename, pairs, index) {
237
- const extension = path.extname(filename).replace(/[^.A-Za-z0-9]/g, '').slice(0, 12);
238
- let filtered = filename;
239
- let replacements = 0;
240
- for (const [original, code] of pairs) {
241
- const fragments = original.split(/[\s._-]+/).filter(Boolean).map(escapeRegExp);
242
- if (!fragments.length) continue;
243
- const pattern = new RegExp(fragments.join('[\\s._-]+'), 'giu');
244
- const before = filtered;
245
- filtered = filtered.replace(pattern, code.replace(/[\\/\x00-\x1f\x7f]/g, '-'));
246
- if (filtered !== before) replacements += 1;
247
- }
248
- if (!replacements) return `Pièce originale ${index + 1}${extension}`;
249
- return filtered.replace(/[\x00-\x1f\x7f]/g, ' ').replace(/[\\/]/g, '-').slice(0, 180);
250
- }
251
-
252
- /**
253
- * Pièces protégées d'un dossier, récursivement. La protection ne dépend plus
254
- * d'un sous-dossier « Pièces originales » mais du fichier lui-même
255
- * (`piecemaker-plugin/scripts/lib/protection.cjs`) : un dossier rangé à plat,
256
- * qui est le cas courant, était auparavant compté comme n'ayant aucune pièce
257
- * isolée.
258
- */
259
- function collectProtectedFiles(root, { maxDepth = 6, maxFiles = 5000 } = {}) {
260
- const protection = readProtection(root);
261
- const files = [];
262
- function visit(directory, depth) {
263
- if (depth > maxDepth || files.length >= maxFiles) return;
264
- let entries = [];
265
- try { entries = fs.readdirSync(directory, { withFileTypes: true }); } catch { return; }
266
- for (const entry of entries) {
267
- if (files.length >= maxFiles) break;
268
- if (entry.isSymbolicLink() || entry.name.startsWith('.')) continue;
269
- const absolute = path.join(directory, entry.name);
270
- if (entry.isDirectory()) {
271
- if (!SKIPPED_DIR_NAMES.has(entry.name.toLowerCase())) visit(absolute, depth + 1);
272
- } else if (entry.isFile() && isProtectedFile(absolute, root, protection)) {
273
- files.push(entry.name);
274
- }
275
- }
276
- }
277
- visit(root, 0);
278
- return files.sort((a, b) => a.localeCompare(b, 'fr'));
279
- }
280
-
281
- function inspectDossier(directory) {
282
- let entries = [];
283
- try { entries = fs.readdirSync(directory, { withFileTypes: true }); } catch { /* état vide */ }
284
- const mappingFiles = entries
285
- .filter((entry) => entry.isFile() && /^mapping.*\.json$/i.test(entry.name))
286
- .map((entry) => path.join(directory, entry.name));
287
- const pairs = mappingFiles.flatMap(mappingPairs);
288
- const protectedFiles = collectProtectedFiles(directory);
289
- // Les noms de fichiers eux-mêmes portent des entités : ils ne sont exposés
290
- // qu'une fois passés par le mapping, jamais bruts.
291
- const mappedOriginalNames = mappingFiles.length
292
- ? protectedFiles.slice(0, MAX_PROTECTED_NAMES).map((name, index) => filteredOriginalName(name, pairs, index))
293
- : [];
294
- return {
295
- mappingConfigured: mappingFiles.length > 0,
296
- originalsProtected: protectedFiles.length > 0,
297
- originalFiles: protectedFiles.length,
298
- mappedOriginalNames,
299
- originalNamesTruncated: mappedOriginalNames.length < protectedFiles.length && mappingFiles.length > 0,
300
- markdownFiles: countMarkdownFiles(directory),
301
- };
302
- }
303
-
304
- function pathsFor({ repoRoot, userHome, homeDir }) {
305
- const channelRoot = path.join(userHome, '.claude', 'channels');
306
- const orchestratorDir = path.join(homeDir, 'orchestrator');
307
- return {
308
- channelRoot,
309
- genericState: path.join(channelRoot, 'telegram'),
310
- assistantState: path.join(channelRoot, ASSISTANT_DIR),
311
- monitorState: path.join(channelRoot, MONITOR_DIR),
312
- legacyMonitorState: path.join(channelRoot, LEGACY_MONITOR_DIR),
313
- projectsFile: path.join(orchestratorDir, 'projects.json'),
314
- launcher: path.join(repoRoot, 'orchestrator', 'launch-telegram.sh'),
315
- dossiersRoot: configuredDossiersRoot(repoRoot, homeDir),
316
- dossierEntries: listConfiguredCases(readRegistryConfig(path.join(homeDir, 'config.json'))),
317
- };
318
- }
319
-
320
- function readMonitorToken(paths) {
321
- return readToken(paths.monitorState) || readToken(paths.legacyMonitorState);
322
- }
323
-
324
- function dossierDirectories(paths, config) {
325
- const projects = Array.isArray(config.projects) ? config.projects : [];
326
- const usedIds = new Set(['piecemaker']);
327
- return paths.dossierEntries
328
- .filter((entry) => !entry.name.startsWith('.') && !entry.name.startsWith('_'))
329
- .filter((entry) => !RESERVED_DOSSIER_DIRS.has(entry.name.toLowerCase()))
330
- .map((entry) => {
331
- const workdir = entry.root;
332
- const configured = projects.find((project) => {
333
- try { return project?.name !== 'piecemaker' && path.resolve(project.workdir) === path.resolve(workdir); }
334
- catch { return false; }
335
- });
336
- const configuredId = /^[a-z0-9][a-z0-9-]{0,63}$/.test(configured?.name || '')
337
- ? configured.name
338
- : '';
339
- let id = configuredId || projectId(entry.name);
340
- if (usedIds.has(id)) {
341
- const suffix = crypto.createHash('sha256').update(workdir).digest('hex').slice(0, 6);
342
- id = `${id.slice(0, 41)}-${suffix}`;
343
- }
344
- usedIds.add(id);
345
- const stateDir = path.join(paths.channelRoot, `telegram-${id}`);
346
- const processInfo = readBotProcess(stateDir);
347
- return {
348
- id,
349
- directoryName: entry.name,
350
- workdir,
351
- name: cleanName(configured?.displayName, entry.name),
352
- linked: Boolean(configured && readToken(stateDir)),
353
- projectConfigured: Boolean(configured),
354
- token: tokenState(readToken(stateDir)),
355
- running: Boolean(processInfo),
356
- pid: processInfo?.pid || null,
357
- ...inspectDossier(workdir),
358
- };
359
- });
360
- }
361
-
362
- function readNames(projectsFile) {
363
- const config = readJson(projectsFile, { projects: [] });
364
- const project = Array.isArray(config.projects)
365
- ? config.projects.find((entry) => entry?.name === 'piecemaker')
366
- : null;
367
- return {
368
- config,
369
- assistantName: cleanName(config.assistantName || project?.displayName, DEFAULT_ASSISTANT_NAME),
370
- monitorName: cleanName(config.daemonName, DEFAULT_MONITOR_NAME),
371
- };
372
- }
373
-
374
- function assertTokenUnique(channelRoot, token, excludedStateDirs = []) {
375
- if (!token || !fs.existsSync(channelRoot)) return;
376
- const excluded = new Set(excludedStateDirs.map((entry) => path.resolve(entry)));
377
- for (const entry of fs.readdirSync(channelRoot, { withFileTypes: true })) {
378
- if (!entry.isDirectory() || !entry.name.startsWith('telegram')) continue;
379
- const stateDir = path.join(channelRoot, entry.name);
380
- if (!excluded.has(path.resolve(stateDir)) && readToken(stateDir) === token) {
381
- throw new Error(`Ce token est déjà utilisé par la configuration « ${entry.name} ». Chaque bot doit avoir son propre token.`);
382
- }
383
- }
384
- }
385
-
386
- function copyGeneralAccess(paths, targetState) {
387
- const target = path.join(targetState, 'access.json');
388
- if (fs.existsSync(target)) return;
389
- const source = path.join(paths.assistantState, 'access.json');
390
- if (!fs.existsSync(source)) return;
391
- atomicWrite(target, fs.readFileSync(source, 'utf8'), 0o600);
392
- }
393
-
394
- function getTelegramState(options) {
395
- const { repoRoot, userHome = os.homedir(), homeDir = path.join(userHome, '.piecemaker') } = options;
396
- const paths = pathsFor({ repoRoot, userHome, homeDir });
397
- const names = readNames(paths.projectsFile);
398
- const assistantProcess = readBotProcess(paths.assistantState);
399
- const monitor = monitorStatus(userHome);
400
- return {
401
- dossiersRoot: paths.dossiersRoot,
402
- assistant: {
403
- name: names.assistantName,
404
- token: tokenState(readToken(paths.assistantState)),
405
- running: Boolean(assistantProcess),
406
- pid: assistantProcess?.pid || null,
407
- autoStart: false,
408
- },
409
- monitor: {
410
- name: names.monitorName,
411
- token: tokenState(readMonitorToken(paths)),
412
- ...monitor,
413
- },
414
- dossiers: dossierDirectories(paths, names.config),
415
- capabilities: {
416
- assistantControl: process.platform === 'darwin' && fs.existsSync(paths.launcher),
417
- monitorControl: process.platform === 'darwin' && monitor.installed,
418
- dossierControl: process.platform === 'darwin' && fs.existsSync(paths.launcher),
419
- },
420
- };
421
- }
422
-
423
- function saveTelegramConfig(options, patch = {}) {
424
- const { repoRoot, userHome = os.homedir(), homeDir = path.join(userHome, '.piecemaker') } = options;
425
- const paths = pathsFor({ repoRoot, userHome, homeDir });
426
- const current = readNames(paths.projectsFile);
427
- const assistantName = cleanName(patch.assistantName, current.assistantName);
428
- const monitorName = cleanName(patch.monitorName, current.monitorName);
429
- const assistantToken = validateToken(patch.assistantToken);
430
- const monitorToken = validateToken(patch.monitorToken);
431
- const savedAssistant = assistantToken || readToken(paths.assistantState);
432
- const savedMonitor = monitorToken || readMonitorToken(paths);
433
- if (savedAssistant && savedMonitor && savedAssistant === savedMonitor) {
434
- throw new Error('L’Assistant et le bot de surveillance doivent utiliser deux tokens différents.');
435
- }
436
- if (assistantToken) assertTokenUnique(paths.channelRoot, assistantToken, [paths.assistantState, paths.genericState]);
437
- if (monitorToken) assertTokenUnique(paths.channelRoot, monitorToken, [paths.monitorState, paths.legacyMonitorState]);
438
- const projects = Array.isArray(current.config.projects) ? [...current.config.projects] : [];
439
- const projectIndex = projects.findIndex((entry) => entry?.name === 'piecemaker');
440
- const existing = projectIndex >= 0 ? projects[projectIndex] : {};
441
- const project = {
442
- ...existing,
443
- name: 'piecemaker',
444
- displayName: assistantName,
445
- workdir: paths.dossiersRoot,
446
- aliases: [...new Set([...(existing.aliases || []), 'pm'])],
447
- permissionMode: existing.permissionMode || 'auto',
448
- };
449
- if (projectIndex >= 0) projects[projectIndex] = project;
450
- else projects.unshift(project);
451
- atomicWrite(paths.projectsFile, `${JSON.stringify({
452
- ...current.config,
453
- assistantName,
454
- daemonName: monitorName,
455
- projects,
456
- }, null, 2)}\n`, 0o600);
457
-
458
- if (assistantToken) writeToken(paths.assistantState, assistantToken);
459
- if (monitorToken || (!readToken(paths.monitorState) && savedMonitor)) {
460
- writeToken(paths.monitorState, savedMonitor);
461
- const legacyAccess = path.join(paths.legacyMonitorState, 'access.json');
462
- const targetAccess = path.join(paths.monitorState, 'access.json');
463
- if (!fs.existsSync(targetAccess) && fs.existsSync(legacyAccess)) {
464
- atomicWrite(targetAccess, fs.readFileSync(legacyAccess, 'utf8'), 0o600);
465
- } else {
466
- copyGeneralAccess(paths, paths.monitorState);
467
- }
468
- }
469
- return getTelegramState({ repoRoot, userHome, homeDir });
470
- }
471
-
472
- function saveDossierBot(options, dossierId, patch = {}) {
473
- const { repoRoot, userHome = os.homedir(), homeDir = path.join(userHome, '.piecemaker') } = options;
474
- const paths = pathsFor({ repoRoot, userHome, homeDir });
475
- const names = readNames(paths.projectsFile);
476
- const dossier = dossierDirectories(paths, names.config).find((entry) => entry.id === dossierId);
477
- if (!dossier) throw new Error('Dossier juridique introuvable dans la racine configurée.');
478
- const name = cleanName(patch.name, dossier.name || dossier.directoryName);
479
- const token = validateToken(patch.token);
480
- const stateDir = path.join(paths.channelRoot, `telegram-${dossier.id}`);
481
- const effectiveToken = token || readToken(stateDir);
482
- if (!effectiveToken) throw new Error('Un token BotFather est requis pour lier ce dossier.');
483
- if (token) assertTokenUnique(paths.channelRoot, token, [stateDir]);
484
-
485
- const projects = Array.isArray(names.config.projects) ? [...names.config.projects] : [];
486
- const index = projects.findIndex((project) => project?.name === dossier.id || (() => {
487
- try { return path.resolve(project?.workdir) === path.resolve(dossier.workdir); } catch { return false; }
488
- })());
489
- const existing = index >= 0 ? projects[index] : {};
490
- const project = {
491
- ...existing,
492
- name: dossier.id,
493
- displayName: name,
494
- workdir: dossier.workdir,
495
- aliases: [...new Set([...(existing.aliases || []), dossier.id])],
496
- permissionMode: existing.permissionMode || 'auto',
497
- };
498
- if (index >= 0) projects[index] = project;
499
- else projects.push(project);
500
- atomicWrite(paths.projectsFile, `${JSON.stringify({ ...names.config, projects }, null, 2)}\n`, 0o600);
501
- if (token) writeToken(stateDir, token);
502
- copyGeneralAccess(paths, stateDir);
503
- return getTelegramState({ repoRoot, userHome, homeDir }).dossiers.find((entry) => entry.id === dossier.id);
504
- }
505
-
506
- function controlProjectSession(options, { projectName, stateDir, label }, action) {
507
- const { repoRoot, userHome = os.homedir() } = options;
508
- const paths = pathsFor({ repoRoot, userHome, homeDir: options.homeDir || path.join(userHome, '.piecemaker') });
509
- if (process.platform !== 'darwin') throw new Error('Le contrôle graphique de la session Telegram est actuellement disponible sur macOS.');
510
- const active = readBotProcess(stateDir);
511
- if (action === 'start') {
512
- if (active) return { ok: true, message: `${label} déjà actif (PID ${active.pid}).` };
513
- if (!readToken(stateDir)) throw new Error(`Configurez d’abord le token de ${label}.`);
514
- const result = run('/bin/bash', [paths.launcher, projectName], {
515
- cwd: repoRoot,
516
- timeout: 30_000,
517
- env: { ...process.env, HOME: userHome },
518
- });
519
- if (result.status !== 0) throw new Error(String(result.stderr || result.stdout || 'Le lancement a échoué.').trim());
520
- return { ok: true, message: String(result.stdout || `${label} lancé.`).trim() };
521
- }
522
- if (action === 'stop') {
523
- if (!active) return { ok: true, message: `${label} déjà arrêté.` };
524
- const claudePid = findClaudeParent(active.pid);
525
- if (!claudePid) throw new Error('Le processus parent Claude n’a pas pu être identifié en sécurité.');
526
- process.kill(claudePid, 'SIGTERM');
527
- return { ok: true, message: `Arrêt demandé à la session Claude (PID ${claudePid}).` };
528
- }
529
- throw new Error('Action Telegram inconnue.');
530
- }
531
-
532
- function controlAssistant(options, action) {
533
- const { repoRoot, userHome = os.homedir(), homeDir = path.join(userHome, '.piecemaker') } = options;
534
- const paths = pathsFor({ repoRoot, userHome, homeDir });
535
- return controlProjectSession({ repoRoot, userHome, homeDir }, {
536
- projectName: 'piecemaker',
537
- stateDir: paths.assistantState,
538
- label: 'L’Assistant général',
539
- }, action);
540
- }
541
-
542
- function controlDossierBot(options, dossierId, action) {
543
- const { repoRoot, userHome = os.homedir(), homeDir = path.join(userHome, '.piecemaker') } = options;
544
- const paths = pathsFor({ repoRoot, userHome, homeDir });
545
- const names = readNames(paths.projectsFile);
546
- const dossier = dossierDirectories(paths, names.config).find((entry) => entry.id === dossierId);
547
- if (!dossier || !dossier.projectConfigured) throw new Error('Ce dossier n’est pas encore lié à un Assistant Telegram.');
548
- return controlProjectSession({ repoRoot, userHome, homeDir }, {
549
- projectName: dossier.id,
550
- stateDir: path.join(paths.channelRoot, `telegram-${dossier.id}`),
551
- label: `L’Assistant « ${dossier.name} »`,
552
- }, action);
553
- }
554
-
555
- function controlMonitor(options, action) {
556
- const { userHome = os.homedir() } = options;
557
- const status = monitorStatus(userHome);
558
- if (process.platform !== 'darwin' || typeof process.getuid !== 'function') {
559
- throw new Error('Le contrôle du service de surveillance est actuellement disponible sur macOS.');
560
- }
561
- if (!status.installed) throw new Error('Le service de surveillance n’est pas installé. Relancez l’étape Telegram de l’installateur.');
562
- const domain = `gui/${process.getuid()}`;
563
- const service = `${domain}/${MONITOR_LABEL}`;
564
- if (action === 'start') {
565
- if (!status.loaded) {
566
- const bootstrap = run('/bin/launchctl', ['bootstrap', domain, status.plist]);
567
- if (bootstrap.status !== 0 && monitorStatus(userHome).loaded === false) {
568
- throw new Error(String(bootstrap.stderr || 'Impossible de charger le service de surveillance.').trim());
569
- }
570
- }
571
- const start = run('/bin/launchctl', ['kickstart', '-k', service]);
572
- if (start.status !== 0) throw new Error(String(start.stderr || 'Impossible de démarrer le service.').trim());
573
- return { ok: true, message: 'Bot de surveillance démarré.' };
574
- }
575
- if (action === 'stop') {
576
- if (!status.running) return { ok: true, message: 'Bot de surveillance déjà arrêté.' };
577
- const stop = run('/bin/launchctl', ['bootout', service]);
578
- if (stop.status !== 0) throw new Error(String(stop.stderr || 'Impossible d’arrêter le service.').trim());
579
- return { ok: true, message: 'Bot de surveillance arrêté jusqu’à la prochaine ouverture de session.' };
580
- }
581
- throw new Error('Action Telegram inconnue.');
582
- }
583
-
584
- function controlTelegram(options, role, action) {
585
- if (!['start', 'stop'].includes(action)) throw new Error('Action Telegram inconnue.');
586
- if (role === 'assistant') return controlAssistant(options, action);
587
- if (role === 'monitor') return controlMonitor(options, action);
588
- throw new Error('Bot Telegram inconnu.');
589
- }
590
-
591
- module.exports = {
592
- ASSISTANT_DIR,
593
- MONITOR_DIR,
594
- getTelegramState,
595
- readToken,
596
- saveDossierBot,
597
- saveTelegramConfig,
598
- tokenState,
599
- validateToken,
600
- controlDossierBot,
601
- controlTelegram,
602
- };