@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.
Files changed (165) hide show
  1. package/dist/assets/FindReplaceDialog-KU5TEIBJ-CQ76sPaJ.js +1 -0
  2. package/dist/assets/FootnotePropertiesDialog-W3YHM4BV-BpgYQvvd.js +1 -0
  3. package/dist/assets/HyperlinkDialog-ZYH7ZHRF-qIPBOssF.css +1 -0
  4. package/dist/assets/HyperlinkDialog-ZYH7ZHRF-z08UbYbL.js +144 -0
  5. package/dist/assets/ImagePositionDialog-HEWYR6Q3-CFzOnzAt.js +1 -0
  6. package/dist/assets/ImagePropertiesDialog-RAB47C6M-D-ThGU-b.js +1 -0
  7. package/dist/assets/PageSetupDialog-AIGIXO3E-_PYIO8eb.js +1 -0
  8. package/dist/assets/SplitCellDialog-IFBKYCD6-Cb9uQOB8.js +1 -0
  9. package/dist/assets/TablePropertiesDialog-TMLKGOOW-12vmjb6B.js +1 -0
  10. package/dist/assets/WatermarkDialog-KRL7WMIS-C-d8rzt2.js +1 -0
  11. package/dist/assets/{abnfDiagram-VCTEODGH-WrsSAHOe.js → abnfDiagram-VCTEODGH-B4OsX7eF.js} +1 -1
  12. package/dist/assets/arc-BK1oUIk7.js +1 -0
  13. package/dist/assets/architectureDiagram-5GKGNRK7-CfaFI6L7.js +36 -0
  14. package/dist/assets/{blockDiagram-I7D4REHJ-zGMnF7Ox.js → blockDiagram-I7D4REHJ-DQEI2azY.js} +1 -1
  15. package/dist/assets/{c4Diagram-7LVT6UL2-ClWw3bqt.js → c4Diagram-7LVT6UL2-DbravpVi.js} +1 -1
  16. package/dist/assets/channel-DVyNr2Yg.js +1 -0
  17. package/dist/assets/{chunk-2Q5K7J3B-C-9ICPHv.js → chunk-2Q5K7J3B-BLpZ9huC.js} +1 -1
  18. package/dist/assets/{chunk-5VM5RSS4-Bawy3teT.js → chunk-5VM5RSS4-BLlEdmX9.js} +1 -1
  19. package/dist/assets/{chunk-F27PBJKO-DQ375RQO.js → chunk-F27PBJKO-D-ZNhI6q.js} +1 -1
  20. package/dist/assets/{chunk-IMKFNOWR-CflNF2f6.js → chunk-IMKFNOWR-vzSiKnhh.js} +1 -1
  21. package/dist/assets/{chunk-JWPE2WC7-CIXQv0gc.js → chunk-JWPE2WC7-DfF0k4nb.js} +1 -1
  22. package/dist/assets/{chunk-POPQ4Y6H-Cza5j6Qw.js → chunk-POPQ4Y6H-BnwCYsPP.js} +1 -1
  23. package/dist/assets/{chunk-SVP7TREG-GQ_VvWZR.js → chunk-SVP7TREG-DRxc8fhq.js} +1 -1
  24. package/dist/assets/{chunk-TICWLB2K-BEmU16W5.js → chunk-TICWLB2K-Dd2Q-md-.js} +1 -1
  25. package/dist/assets/{chunk-XXDRQBXY-C1KpkLYH.js → chunk-XXDRQBXY-DBjkEUp_.js} +1 -1
  26. package/dist/assets/classDiagram-ZZMXUADV-2pGPHl5H.js +1 -0
  27. package/dist/assets/classDiagram-v2-VYDZK3BY-2pGPHl5H.js +1 -0
  28. package/dist/assets/{cose-bilkent-JH36ORCC-CZE48TRG.js → cose-bilkent-JH36ORCC-By6PS4oe.js} +1 -1
  29. package/dist/assets/{cynefin-OW5HDTMX-L-tx1eC-.js → cynefin-OW5HDTMX-YxWpjc5W.js} +1 -1
  30. package/dist/assets/{cynefinDiagram-5FMLGOSQ-Coa8oJwR.js → cynefinDiagram-5FMLGOSQ-DrtXzz3p.js} +1 -1
  31. package/dist/assets/{dagre-GXQ25YYZ-Bu_3Y75K.js → dagre-GXQ25YYZ-BDpTLKqZ.js} +1 -1
  32. package/dist/assets/{diagram-S7CK7UJ4-CFiiV4es.js → diagram-S7CK7UJ4-C4LvqeCs.js} +1 -1
  33. package/dist/assets/{diagram-UQ7AKVKN-CCXheGAA.js → diagram-UQ7AKVKN-BTpnMeHp.js} +1 -1
  34. package/dist/assets/{diagram-VSXAHHWV-B70m4lr_.js → diagram-VSXAHHWV-DucHHWev.js} +1 -1
  35. package/dist/assets/{diagram-VX7I27RA-CHkiMOVA.js → diagram-VX7I27RA-DYHZNmmH.js} +1 -1
  36. package/dist/assets/{diagram-Z3DM3KII-_awpc4Bs.js → diagram-Z3DM3KII-BHIH2BGN.js} +1 -1
  37. package/dist/assets/{ebnfDiagram-PWID7BFC-CZzjH86H.js → ebnfDiagram-PWID7BFC-CGiEkwj0.js} +1 -1
  38. package/dist/assets/{erDiagram-RLTQ6QDP-9DgiHZg9.js → erDiagram-RLTQ6QDP-AJMHi2Us.js} +1 -1
  39. package/dist/assets/{flowDiagram-HODETNUW-DHY_503b.js → flowDiagram-HODETNUW-BqzANBh2.js} +1 -1
  40. package/dist/assets/{ganttDiagram-EL5Y4UJY-DPiiYZ_l.js → ganttDiagram-EL5Y4UJY-B0xPDY2_.js} +1 -1
  41. package/dist/assets/{gitGraphDiagram-WWUBYQGX-Ddd_evm7.js → gitGraphDiagram-WWUBYQGX-CzPX5CNO.js} +1 -1
  42. package/dist/assets/{index-tibHq7Qp.js → index-Bk2iXKnJ.js} +140 -140
  43. package/dist/assets/index-Dc9wNuS4.css +1 -0
  44. package/dist/assets/{infoDiagram-27XIBGKW-BPRNhfwb.js → infoDiagram-27XIBGKW-Cjc44H0O.js} +1 -1
  45. package/dist/assets/{ishikawaDiagram-5VMMS53U-BXx6aqkq.js → ishikawaDiagram-5VMMS53U-BWhr2v7r.js} +1 -1
  46. package/dist/assets/{journeyDiagram-3NMN7TZE-BK8TZM29.js → journeyDiagram-3NMN7TZE-Bl4r6RrP.js} +1 -1
  47. package/dist/assets/{kanban-definition-UXKFOSKX-CHioRmxz.js → kanban-definition-UXKFOSKX-FV-T0Vy1.js} +6 -6
  48. package/dist/assets/layout-CTfb1sAF.js +1 -0
  49. package/dist/assets/{linear-CTEhUcKG.js → linear-Dfgi6vlo.js} +1 -1
  50. package/dist/assets/{mermaid.core-DYUb_UUB.js → mermaid.core-DOCGsdlo.js} +7 -7
  51. package/dist/assets/{mindmap-definition-YA3MSWOX-D7ODZ4oS.js → mindmap-definition-YA3MSWOX-CymQ86ir.js} +1 -1
  52. package/dist/assets/{pegDiagram-XKGWAZYB-8J-_2_kw.js → pegDiagram-XKGWAZYB-1VMq_vi0.js} +1 -1
  53. package/dist/assets/{pieDiagram-E7YTZNPT-ebg0yfMi.js → pieDiagram-E7YTZNPT-C5GgXtJU.js} +1 -1
  54. package/dist/assets/processTemplate-GPFDLQMX-B1sVV2O0.js +22 -0
  55. package/dist/assets/{quadrantDiagram-AXDQQJYC-BoWkDiFb.js → quadrantDiagram-AXDQQJYC-C6zpryYf.js} +1 -1
  56. package/dist/assets/{railroadDiagram-O6MQD6OU-BRJ77ldz.js → railroadDiagram-O6MQD6OU-D-sv4SWi.js} +1 -1
  57. package/dist/assets/{requirementDiagram-BXWQKSXE-Z0JJgktA.js → requirementDiagram-BXWQKSXE-CkjY-z4B.js} +1 -1
  58. package/dist/assets/sankeyDiagram-P5KCCOFB-De-d8Q6L.js +40 -0
  59. package/dist/assets/{sequenceDiagram-WJ2MYXX4-BPAjtXwp.js → sequenceDiagram-WJ2MYXX4-DCsGSJa9.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-xINZhgER.js} +1 -1
  62. package/dist/assets/stateDiagram-D77RDMKH-CuLUTWSL.js +1 -0
  63. package/dist/assets/stateDiagram-v2-MP3YSRHH-Cnbs-M92.js +1 -0
  64. package/dist/assets/{swimlanes-42K2YHIH-DvuB15qF.js → swimlanes-42K2YHIH-BwxARD7N.js} +1 -1
  65. package/dist/assets/swimlanesDiagram-VR7AAH4N-D7p8qZI2.js +8 -0
  66. package/dist/assets/{timeline-definition-24CTP7MA-ZIziYCIF.js → timeline-definition-24CTP7MA-BIOvnn-l.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-BSD6Pfmt.js} +4 -4
  70. package/dist/assets/{wardleyDiagram-VM6X3IG4-DMlH9Afw.js → wardleyDiagram-VM6X3IG4-7Zz2iRkF.js} +1 -1
  71. package/dist/assets/{xychartDiagram-S5SC5T6Z-BldhJt7i.js → xychartDiagram-S5SC5T6Z-Ctx17Y1D.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/anonymizer/lifecycle.js +2 -3
  83. package/dist-server/server/piecemaker/anonymizer/lifecycle.js.map +1 -1
  84. package/dist-server/server/piecemaker/docx-document.js +50 -0
  85. package/dist-server/server/piecemaker/docx-document.js.map +1 -0
  86. package/dist-server/server/piecemaker/index.js +9 -2
  87. package/dist-server/server/piecemaker/index.js.map +1 -1
  88. package/dist-server/server/piecemaker/model-discovery/codex-catalog.js +46 -0
  89. package/dist-server/server/piecemaker/model-discovery/codex-catalog.js.map +1 -0
  90. package/dist-server/server/piecemaker/model-discovery/index.js +42 -0
  91. package/dist-server/server/piecemaker/model-discovery/index.js.map +1 -0
  92. package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js +19 -0
  93. package/dist-server/server/piecemaker/model-discovery/mistral-catalog.js.map +1 -0
  94. package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js +61 -0
  95. package/dist-server/server/piecemaker/model-discovery/stdio-rpc.js.map +1 -0
  96. package/dist-server/server/piecemaker/project-registry.js +29 -0
  97. package/dist-server/server/piecemaker/project-registry.js.map +1 -0
  98. package/package.json +12 -1
  99. package/scripts/piecemaker/cli/disable-anonymizer.mjs +19 -0
  100. package/scripts/piecemaker/cli/lib/config.mjs +0 -1
  101. package/scripts/piecemaker/cli/lib/services.mjs +1 -10
  102. package/scripts/piecemaker/cli/piecemaker.mjs +0 -3
  103. package/server/modules/providers/list/mistral/mistral-models.provider.ts +4 -53
  104. package/server/modules/providers/list/mistral/mistral-runtime.provider.ts +2 -3
  105. package/server/modules/providers/list/mistral/mistral-vibe-models.ts +43 -0
  106. package/server/modules/providers/tests/mistral-vibe-models.test.ts +27 -0
  107. package/server/piecemaker/anonymizer/client-config.cjs +92 -92
  108. package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +9 -16
  109. package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.toml +1 -1
  110. package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +251 -268
  111. package/server/piecemaker/anonymizer/lifecycle.ts +2 -3
  112. package/server/piecemaker/anonymizer/rewriter-bridge.cjs +6 -0
  113. package/server/piecemaker/anonymizer/service.cjs +103 -174
  114. package/server/piecemaker/docx-document.ts +54 -0
  115. package/server/piecemaker/harness/decisions.cjs +1 -1
  116. package/server/piecemaker/harness/index.cjs +2 -3
  117. package/server/piecemaker/harness/verification.cjs +1 -1
  118. package/server/piecemaker/index.ts +11 -3
  119. package/server/piecemaker/model-discovery/codex-catalog.ts +67 -0
  120. package/server/piecemaker/model-discovery/index.ts +69 -0
  121. package/server/piecemaker/model-discovery/mistral-catalog.ts +36 -0
  122. package/server/piecemaker/model-discovery/stdio-rpc.ts +79 -0
  123. package/server/piecemaker/project-registry.ts +34 -0
  124. package/server/piecemaker/router.cjs +6 -7
  125. package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +31 -77
  126. package/server/piecemaker/vendor/installer/lib/codex-skills.mjs +2 -84
  127. package/server/piecemaker/vendor/installer/lib/service.mjs +4 -24
  128. package/server/piecemaker/vendor/installer/steps/06-hooks.mjs +10 -21
  129. package/server/piecemaker/vendor/installer/steps/09-claude-assets.mjs +0 -11
  130. package/server/piecemaker/vendor/installer/steps/09-codex-plugin.mjs +6 -36
  131. package/server/piecemaker/vendor/installer/steps/13-garde-secrets.mjs +30 -12
  132. package/server/piecemaker/vendor/piecemaker-plugin/README.md +2 -9
  133. package/server/piecemaker/vendor/piecemaker-plugin/scripts/billing-track.mjs +5 -5
  134. package/server/piecemaker/vendor/piecemaker-plugin/scripts/classify-ai-documents.mjs +8 -8
  135. package/server/piecemaker/vendor/piecemaker-plugin/scripts/commit-track.mjs +2 -2
  136. package/server/piecemaker/vendor/piecemaker-plugin/scripts/compile-recherche.mjs +4 -6
  137. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folders.cjs +71 -38
  138. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/mapping.cjs +8 -38
  139. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/protection.cjs +82 -61
  140. package/server/piecemaker/vendor/piecemaker-plugin/scripts/protect-originals.mjs +4 -55
  141. package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +24 -117
  142. package/server/piecemaker/vendor/piecemaker-plugin/skills/tamponnage/SKILL.md +2 -2
  143. package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +15 -121
  144. package/server/piecemaker/vendor/websocket-server/case-registry.cjs +16 -32
  145. package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +0 -73
  146. package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +0 -7
  147. package/server/piecemaker/vendor/websocket-server/stamping-routes.cjs +3 -12
  148. package/server/piecemaker/vendor/websocket-server/workspace-paths.cjs +6 -6
  149. package/dist/assets/arc-DAA6wH-C.js +0 -1
  150. package/dist/assets/architectureDiagram-5GKGNRK7-BGNHr7ef.js +0 -36
  151. package/dist/assets/channel-BAQyfMll.js +0 -1
  152. package/dist/assets/classDiagram-ZZMXUADV-BNGCuvrR.js +0 -1
  153. package/dist/assets/classDiagram-v2-VYDZK3BY-BNGCuvrR.js +0 -1
  154. package/dist/assets/index-WwknP0l4.css +0 -1
  155. package/dist/assets/layout-DEnkyfTY.js +0 -1
  156. package/dist/assets/sankeyDiagram-P5KCCOFB-4PW5HTOP.js +0 -40
  157. package/dist/assets/stateDiagram-D77RDMKH-o-vEcKwz.js +0 -1
  158. package/dist/assets/stateDiagram-v2-MP3YSRHH-BsuDaKkN.js +0 -1
  159. package/dist/assets/swimlanesDiagram-VR7AAH4N-DcCAyKlT.js +0 -8
  160. package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +0 -78
  161. package/scripts/piecemaker/hooks/proxy-guard.mjs +0 -251
  162. package/server/piecemaker/anonymizer/providers.cjs +0 -120
  163. package/server/piecemaker/vendor/installer/steps/08-telegram.mjs +0 -482
  164. package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/central-mapping.cjs +0 -0
  165. package/server/piecemaker/vendor/websocket-server/telegram-admin.cjs +0 -602
@@ -1,120 +0,0 @@
1
- /**
2
- * Branchement des clients IA sur le proxy d'anonymisation.
3
- *
4
- * Le serveur pose `HTTPS_PROXY` et le certificat de l'autorité locale.
5
- * Les URL des fournisseurs ne sont pas réécrites.
6
- *
7
- * Cursor ignore tout proxy HTTP. Tant qu'un mapping existe, un shim en tête
8
- * de `PATH` refuse de lancer `cursor-agent`.
9
- */
10
- const fs = require('node:fs');
11
- const os = require('node:os');
12
- const path = require('node:path');
13
-
14
- const {
15
- configureClaudeCodeProxy,
16
- bypassClaudeCodeProxy,
17
- configureCodexProxy,
18
- bypassCodexProxy,
19
- } = require('./client-config.cjs');
20
-
21
- function configureOpencode() {
22
- return { configured: true, changed: false, conflict: false, file: null };
23
- }
24
-
25
- function bypassOpencode() {
26
- return { bypassed: true, changed: false, conflict: false, file: null };
27
- }
28
-
29
- /** Première ligne du garde-fou Cursor : ce que le shim écrit avant de renoncer. */
30
- const CURSOR_REFUSAL = 'PieceMaker : cursor-agent ne peut pas être filtré par le proxy d’anonymisation '
31
- + '(son protocole n’accepte aucune base d’URL). Tant qu’un mapping existe, il est bloqué pour éviter '
32
- + 'que des noms de parties quittent la machine en clair. Utilisez Claude, Codex ou opencode.';
33
-
34
- /**
35
- * Écrit le shim `cursor-agent` dans un répertoire que le service place en tête
36
- * de `PATH`. Le shim ne lit jamais le contenu du mapping — sa seule existence
37
- * décide —, et se retire du `PATH` avant de relayer le vrai binaire pour ne pas
38
- * se rappeler lui-même.
39
- */
40
- function installCursorGuard({ binDir, mappingFile }) {
41
- fs.mkdirSync(binDir, { recursive: true });
42
- const file = path.join(binDir, 'cursor-agent');
43
- const script = `#!/bin/sh
44
- # Généré par PieceMaker — ne pas modifier. Voir server/piecemaker/anonymizer/providers.cjs
45
- if [ -s ${JSON.stringify(mappingFile)} ]; then
46
- printf '%s\\n' ${JSON.stringify(CURSOR_REFUSAL)} >&2
47
- exit 78
48
- fi
49
- PATH=$(printf '%s' "$PATH" | sed -e "s|^${binDir.replace(/[|\\]/g, '\\$&')}:||")
50
- export PATH
51
- exec cursor-agent "$@"
52
- `;
53
- fs.writeFileSync(file, script, { encoding: 'utf8', mode: 0o755 });
54
- return { file, binDir };
55
- }
56
-
57
- /**
58
- * Câble tous les fournisseurs sur `origin` et rend un rapport par client.
59
- * Aucun échec n'est fatal : un client mal configuré est signalé « non couvert »
60
- * dans le statut, ce qui est l'information utile, plutôt que d'empêcher le
61
- * serveur de démarrer.
62
- */
63
- async function configureProviders({ origin, caFile, trustBundle, userHome = os.homedir(), binDir, mappingFile }) {
64
- const report = {};
65
-
66
- const attempt = (name, run) => {
67
- try {
68
- report[name] = run();
69
- } catch (error) {
70
- report[name] = { configured: false, changed: false, conflict: true, reason: error.message };
71
- }
72
- };
73
-
74
- attempt('claude', () => configureClaudeCodeProxy({ proxyUrl: origin, caFile, trustBundle, userHome }));
75
- attempt('codex', () => configureCodexProxy({
76
- codexHome: process.env.CODEX_HOME || path.join(userHome, '.codex'),
77
- }));
78
- attempt('opencode', () => configureOpencode());
79
- attempt('cursor', () => ({
80
- configured: false,
81
- changed: false,
82
- conflict: false,
83
- blocked: true,
84
- reason: 'not-interceptable',
85
- ...installCursorGuard({ binDir, mappingFile }),
86
- }));
87
-
88
- return report;
89
- }
90
-
91
- /** Défait tout ce que `configureProviders` a posé : l'arrêt du serveur ne doit pas laisser de base morte. */
92
- async function bypassProviders({ userHome = os.homedir(), binDir }) {
93
- const report = {};
94
- const attempt = (name, run) => {
95
- try {
96
- report[name] = run();
97
- } catch (error) {
98
- report[name] = { bypassed: false, conflict: true, reason: error.message };
99
- }
100
- };
101
-
102
- attempt('claude', () => bypassClaudeCodeProxy({ userHome }));
103
- attempt('codex', () => bypassCodexProxy());
104
- attempt('opencode', () => bypassOpencode());
105
- attempt('cursor', () => {
106
- const file = path.join(binDir, 'cursor-agent');
107
- if (fs.existsSync(file)) fs.rmSync(file);
108
- return { bypassed: true, file };
109
- });
110
- return report;
111
- }
112
-
113
- module.exports = {
114
- CURSOR_REFUSAL,
115
- bypassOpencode,
116
- bypassProviders,
117
- configureOpencode,
118
- configureProviders,
119
- installCursorGuard,
120
- };
@@ -1,482 +0,0 @@
1
- /**
2
- * Étape Telegram unique.
3
- *
4
- * Deux bots Telegram, deux rôles volontairement séparés :
5
- * 1. l'Assistant Bot ouvre une vraie session Claude dans la racine PieceMaker ;
6
- * 2. le daemon de surveillance ne contient aucun assistant/LLM. Il observe et
7
- * pilote la session avec des commandes déterministes (/status, /restart…).
8
- *
9
- * Écrit :
10
- * ~/.claude/channels/telegram-piecemaker/ état de l'Assistant Bot
11
- * ~/.claude/channels/telegram-piecemaker-lord/ état du daemon (chemin historique)
12
- * ~/.piecemaker/orchestrator/projects.json racine et nom du daemon
13
- * ~/Library/LaunchAgents/com.piecemaker.telegram-monitor.plist (macOS)
14
- */
15
-
16
- import fs from 'node:fs';
17
- import os from 'node:os';
18
- import path from 'node:path';
19
-
20
- import { log, spinner } from '../lib/ui.mjs';
21
- import { ask, confirm, secret, nonInteractive } from '../lib/prompt.mjs';
22
- import {
23
- commandExists,
24
- ensureDir,
25
- HOME_DIR,
26
- IS_MAC,
27
- REPO_ROOT,
28
- run,
29
- runCapture,
30
- } from '../lib/platform.mjs';
31
-
32
- export const meta = {
33
- id: '08-telegram',
34
- label: 'Telegram — Assistant Bot et daemon',
35
- description: 'Configure le bot conversationnel PieceMaker et son daemon de surveillance séparé',
36
- };
37
-
38
- const OFFICIAL_MARKETPLACE = 'claude-plugins-official';
39
- const OFFICIAL_MARKETPLACE_REPO = 'anthropics/claude-plugins-official';
40
- const PLUGIN_SPEC = `telegram@${OFFICIAL_MARKETPLACE}`;
41
-
42
- const CHANNEL_ROOT = path.join(os.homedir(), '.claude', 'channels');
43
- const GENERIC_STATE_DIR = path.join(CHANNEL_ROOT, 'telegram');
44
- const ASSISTANT_STATE_DIR = path.join(CHANNEL_ROOT, 'telegram-piecemaker');
45
- // Conservé pour migrer sans casser les installations « Lord of the bots ».
46
- const DAEMON_STATE_DIR = path.join(CHANNEL_ROOT, 'telegram-piecemaker-lord');
47
- const LEGACY_DAEMON_STATE_DIR = path.join(CHANNEL_ROOT, 'telegram-lord');
48
-
49
- const ORCHESTRATOR_SRC = path.join(REPO_ROOT, 'orchestrator');
50
- const ORCHESTRATOR_DIR = path.join(HOME_DIR, 'orchestrator');
51
- const PROJECTS_FILE = path.join(ORCHESTRATOR_DIR, 'projects.json');
52
- const ASSISTANT_LAUNCHER = path.join(ORCHESTRATOR_SRC, 'launch-telegram.sh');
53
- const DAEMON_ENTRY = path.join(ORCHESTRATOR_SRC, 'piecemaker-daemon.mjs');
54
-
55
- const DAEMON_LABEL = 'com.piecemaker.telegram-monitor';
56
- const DAEMON_PLIST = path.join(os.homedir(), 'Library', 'LaunchAgents', `${DAEMON_LABEL}.plist`);
57
- const DAEMON_LOG = path.join(ORCHESTRATOR_DIR, 'telegram-monitor.log');
58
- const DEFAULT_ASSISTANT_NAME = 'Assistant PieceMaker';
59
- const DEFAULT_DAEMON_NAME = 'PieceMaker Monitor';
60
-
61
- function parseJson(raw, fallback = null) {
62
- try {
63
- return JSON.parse(raw);
64
- } catch {
65
- return fallback;
66
- }
67
- }
68
-
69
- function listMarketplaces() {
70
- const result = runCapture('claude', ['plugin', 'marketplace', 'list', '--json']);
71
- return result.code === 0 ? parseJson(result.stdout) : null;
72
- }
73
-
74
- function listPlugins() {
75
- const result = runCapture('claude', ['plugin', 'list', '--json']);
76
- return result.code === 0 ? parseJson(result.stdout) : null;
77
- }
78
-
79
- function isTelegramInstalled(plugins) {
80
- return Array.isArray(plugins) && plugins.some((plugin) => (
81
- plugin.id === PLUGIN_SPEC && plugin.enabled !== false
82
- ));
83
- }
84
-
85
- async function ensureOfficialPlugin() {
86
- const marketplaces = listMarketplaces();
87
- const marketplaceInstalled = Array.isArray(marketplaces)
88
- && marketplaces.some((marketplace) => marketplace.name === OFFICIAL_MARKETPLACE);
89
-
90
- if (!marketplaceInstalled) {
91
- const spin = spinner(`Enregistrement du marketplace officiel (${OFFICIAL_MARKETPLACE_REPO})...`);
92
- const code = await run('claude', ['plugin', 'marketplace', 'add', OFFICIAL_MARKETPLACE_REPO]);
93
- if (code !== 0) {
94
- spin.fail('Échec de l\'enregistrement du marketplace officiel.');
95
- return false;
96
- }
97
- spin.succeed('Marketplace officiel enregistré.');
98
- } else {
99
- log.ok(`Marketplace officiel « ${OFFICIAL_MARKETPLACE} » déjà enregistré.`);
100
- }
101
-
102
- const plugins = listPlugins();
103
- if (isTelegramInstalled(plugins)) {
104
- log.ok(`Plugin « ${PLUGIN_SPEC} » déjà installé et activé.`);
105
- return true;
106
- }
107
-
108
- const spin = spinner(`Installation du plugin officiel (${PLUGIN_SPEC})...`);
109
- const code = await run('claude', ['plugin', 'install', PLUGIN_SPEC]);
110
- if (code !== 0) {
111
- spin.fail('Échec de l\'installation du plugin Telegram.');
112
- return false;
113
- }
114
- spin.succeed('Plugin Telegram installé.');
115
- return true;
116
- }
117
-
118
- function readToken(stateDir) {
119
- try {
120
- return fs.readFileSync(path.join(stateDir, '.env'), 'utf8')
121
- .match(/^TELEGRAM_BOT_TOKEN=(.+)$/m)?.[1]
122
- ?.trim() || '';
123
- } catch {
124
- return '';
125
- }
126
- }
127
-
128
- function writeToken(stateDir, token) {
129
- ensureDir(stateDir);
130
- const file = path.join(stateDir, '.env');
131
- fs.writeFileSync(file, `TELEGRAM_BOT_TOKEN=${token}\n`, { encoding: 'utf8', mode: 0o600 });
132
- try {
133
- fs.chmodSync(file, 0o600);
134
- } catch {
135
- // Les ACL du profil utilisateur s'appliquent sur Windows.
136
- }
137
- }
138
-
139
- function readAccess(stateDir) {
140
- try {
141
- const parsed = JSON.parse(fs.readFileSync(path.join(stateDir, 'access.json'), 'utf8'));
142
- return parsed && typeof parsed === 'object' ? parsed : {};
143
- } catch {
144
- return {};
145
- }
146
- }
147
-
148
- function writeAccess(stateDir, ids, { pairing = false } = {}) {
149
- ensureDir(stateDir);
150
- const existing = readAccess(stateDir);
151
- const access = {
152
- ...existing,
153
- dmPolicy: pairing && !ids.length ? 'pairing' : 'allowlist',
154
- allowFrom: ids,
155
- groups: existing.groups || {},
156
- pending: existing.pending || {},
157
- };
158
- fs.writeFileSync(
159
- path.join(stateDir, 'access.json'),
160
- `${JSON.stringify(access, null, 2)}\n`,
161
- 'utf8',
162
- );
163
- }
164
-
165
- function configuredIds() {
166
- for (const stateDir of [ASSISTANT_STATE_DIR, DAEMON_STATE_DIR, LEGACY_DAEMON_STATE_DIR, GENERIC_STATE_DIR]) {
167
- const ids = readAccess(stateDir).allowFrom;
168
- if (Array.isArray(ids) && ids.length) return ids.map(String);
169
- }
170
- return [];
171
- }
172
-
173
- function readOrchestratorConfig() {
174
- try {
175
- const parsed = JSON.parse(fs.readFileSync(PROJECTS_FILE, 'utf8'));
176
- if (Array.isArray(parsed)) return { projects: parsed };
177
- if (parsed && typeof parsed === 'object') return parsed;
178
- } catch {
179
- // Une configuration saine sera créée ci-dessous.
180
- }
181
- return { projects: [] };
182
- }
183
-
184
- function normalizeDaemonName(value) {
185
- return String(value || DEFAULT_DAEMON_NAME).replace(/[\r\n]+/g, ' ').trim().slice(0, 64)
186
- || DEFAULT_DAEMON_NAME;
187
- }
188
-
189
- function normalizeAssistantName(value) {
190
- return String(value || DEFAULT_ASSISTANT_NAME).replace(/[\r\n]+/g, ' ').trim().slice(0, 64)
191
- || DEFAULT_ASSISTANT_NAME;
192
- }
193
-
194
- function writeOrchestratorConfig(assistantName, daemonName, assistantRoot) {
195
- const config = readOrchestratorConfig();
196
- const projects = Array.isArray(config.projects) ? [...config.projects] : [];
197
- const index = projects.findIndex((project) => project?.name === 'piecemaker');
198
- const existing = index >= 0 ? projects[index] : {};
199
- const piecemaker = {
200
- ...existing,
201
- name: 'piecemaker',
202
- displayName: assistantName,
203
- workdir: assistantRoot,
204
- aliases: [...new Set([...(existing.aliases || []), 'pm'])],
205
- permissionMode: existing.permissionMode || 'auto',
206
- };
207
-
208
- if (index >= 0) projects[index] = piecemaker;
209
- else projects.unshift(piecemaker);
210
-
211
- ensureDir(ORCHESTRATOR_DIR);
212
- fs.writeFileSync(
213
- PROJECTS_FILE,
214
- `${JSON.stringify({ ...config, assistantName, daemonName, projects }, null, 2)}\n`,
215
- 'utf8',
216
- );
217
- return projects;
218
- }
219
-
220
- function xml(value) {
221
- return String(value)
222
- .replaceAll('&', '&')
223
- .replaceAll('<', '&lt;')
224
- .replaceAll('>', '&gt;')
225
- .replaceAll('"', '&quot;')
226
- .replaceAll("'", '&apos;');
227
- }
228
-
229
- function daemonPlist() {
230
- const values = [process.execPath, DAEMON_ENTRY].map((value) => (
231
- ` <string>${xml(value)}</string>`
232
- )).join('\n');
233
-
234
- return `<?xml version="1.0" encoding="UTF-8"?>
235
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
236
- <plist version="1.0">
237
- <dict>
238
- <key>Label</key>
239
- <string>${DAEMON_LABEL}</string>
240
- <key>ProgramArguments</key>
241
- <array>
242
- ${values}
243
- </array>
244
- <key>WorkingDirectory</key>
245
- <string>${xml(REPO_ROOT)}</string>
246
- <key>RunAtLoad</key>
247
- <true/>
248
- <key>KeepAlive</key>
249
- <true/>
250
- <key>ThrottleInterval</key>
251
- <integer>10</integer>
252
- <key>StandardOutPath</key>
253
- <string>${xml(DAEMON_LOG)}</string>
254
- <key>StandardErrorPath</key>
255
- <string>${xml(DAEMON_LOG)}</string>
256
- </dict>
257
- </plist>
258
- `;
259
- }
260
-
261
- function installDaemonService() {
262
- if (!IS_MAC || typeof process.getuid !== 'function') return false;
263
-
264
- ensureDir(path.dirname(DAEMON_PLIST));
265
- ensureDir(ORCHESTRATOR_DIR);
266
- fs.writeFileSync(DAEMON_PLIST, daemonPlist(), 'utf8');
267
-
268
- const domain = `gui/${process.getuid()}`;
269
- // bootout échoue normalement lors de la première installation.
270
- runCapture('launchctl', ['bootout', domain, DAEMON_PLIST]);
271
- const bootstrap = runCapture('launchctl', ['bootstrap', domain, DAEMON_PLIST]);
272
- if (bootstrap.code !== 0) {
273
- log.warn(`Le service n'a pas démarré : ${bootstrap.stderr || 'erreur launchctl'}`);
274
- return false;
275
- }
276
- const start = runCapture('launchctl', ['kickstart', '-k', `${domain}/${DAEMON_LABEL}`]);
277
- if (start.code !== 0) {
278
- log.warn(`Le daemon est installé mais n'a pas démarré : ${start.stderr || 'erreur launchctl'}`);
279
- return false;
280
- }
281
- return true;
282
- }
283
-
284
- function daemonServiceRunning() {
285
- if (!IS_MAC || typeof process.getuid !== 'function') return false;
286
- return runCapture('launchctl', ['print', `gui/${process.getuid()}/${DAEMON_LABEL}`]).code === 0;
287
- }
288
-
289
- async function configureToken({ title, purpose, stateDir, migrateFrom = null }) {
290
- log.step(title);
291
- log.detail(purpose);
292
-
293
- let token = readToken(stateDir);
294
- if (!token && migrateFrom) {
295
- const previous = readToken(migrateFrom);
296
- if (previous && await confirm('Réutiliser le bot Telegram déjà configuré ?', true)) {
297
- writeToken(stateDir, previous);
298
- token = previous;
299
- log.ok('Token existant réutilisé dans la configuration dédiée PieceMaker.');
300
- }
301
- }
302
-
303
- if (token) {
304
- log.ok('Token déjà configuré.');
305
- if (!nonInteractive && await confirm('Remplacer ce token ?', false)) {
306
- const replacement = await secret(`Nouveau token — ${title}`);
307
- if (replacement) {
308
- writeToken(stateDir, replacement);
309
- token = replacement;
310
- }
311
- }
312
- return token;
313
- }
314
-
315
- if (nonInteractive) return '';
316
- const value = await secret(`Token BotFather — ${title}`);
317
- if (value) {
318
- writeToken(stateDir, value);
319
- log.ok(`Token enregistré en 0600 dans ${stateDir}`);
320
- }
321
- return value;
322
- }
323
-
324
- async function launchAssistant(assistantRoot) {
325
- if (!IS_MAC) {
326
- log.info('Démarrage manuel de l’Assistant Bot :');
327
- log.detail(`cd "${assistantRoot}" && TELEGRAM_STATE_DIR="${ASSISTANT_STATE_DIR}" claude --channels plugin:${PLUGIN_SPEC}`);
328
- return true;
329
- }
330
-
331
- if (!await confirm(`Ouvrir maintenant la session de l’Assistant général dans ${assistantRoot} ?`, true)) {
332
- log.info(`Lancement reporté : /bin/bash ${ASSISTANT_LAUNCHER} piecemaker`);
333
- return true;
334
- }
335
-
336
- const result = runCapture('/bin/bash', [ASSISTANT_LAUNCHER, 'piecemaker'], { timeout: 30000 });
337
- if (result.code === 0) {
338
- log.ok(result.stdout || `Assistant général ouvert dans ${assistantRoot}`);
339
- return true;
340
- }
341
- log.warn(result.stderr || result.stdout || 'Impossible d’ouvrir la session de l’Assistant Bot.');
342
- return false;
343
- }
344
-
345
- export async function install(ctx) {
346
- if (!commandExists('claude', ['--version'])) {
347
- return {
348
- status: 'skipped',
349
- note: 'CLI « claude » introuvable — installez Claude Code puis relancez cette étape.',
350
- };
351
- }
352
- if (!fs.existsSync(ORCHESTRATOR_SRC)) {
353
- return { status: 'failed', note: `Dossier orchestrator/ introuvable : ${ORCHESTRATOR_SRC}` };
354
- }
355
- const assistantRoot = path.resolve(ctx.config?.dossiersRoot || ctx.config?.outputPath || REPO_ROOT);
356
-
357
- if (ctx.dryRun) {
358
- log.info(`[simulation] Installation de ${PLUGIN_SPEC}`);
359
- log.info(`[simulation] Assistant général dans ${ASSISTANT_STATE_DIR}, session ouverte dans ${assistantRoot}`);
360
- log.info(`[simulation] Daemon de surveillance nommé et configuré dans ${DAEMON_STATE_DIR}`);
361
- if (IS_MAC) log.info(`[simulation] Service utilisateur ${DAEMON_PLIST}`);
362
- return { status: 'skipped', note: 'Mode simulation — aucune modification effectuée.' };
363
- }
364
-
365
- if (!await ensureOfficialPlugin()) {
366
- return {
367
- status: 'failed',
368
- note: `Impossible d'installer le plugin officiel « ${PLUGIN_SPEC} ».
369
- Vérifiez la connexion réseau et le marketplace Claude Code.`,
370
- };
371
- }
372
-
373
- log.step('Deux bots Telegram distincts sont nécessaires');
374
- log.detail('Dans @BotFather, utilisez /newbot deux fois : un bot conversationnel et un bot de surveillance.');
375
- log.detail('Leurs tokens doivent être différents : le daemon ne dialogue jamais avec Claude et ne consomme aucun LLM.');
376
-
377
- const existingConfig = readOrchestratorConfig();
378
- const currentAssistantName = normalizeAssistantName(existingConfig.assistantName);
379
- const assistantName = nonInteractive
380
- ? currentAssistantName
381
- : normalizeAssistantName(await ask('Nom de l’Assistant général', { def: currentAssistantName }));
382
-
383
- const assistantToken = await configureToken({
384
- title: `1/2 — ${assistantName}, Assistant général conversationnel`,
385
- purpose: `Sa session Claude est ouverte à la racine des dossiers : ${assistantRoot}`,
386
- stateDir: ASSISTANT_STATE_DIR,
387
- migrateFrom: GENERIC_STATE_DIR,
388
- });
389
-
390
- const currentName = normalizeDaemonName(existingConfig.daemonName);
391
- const daemonName = nonInteractive
392
- ? currentName
393
- : normalizeDaemonName(await ask('Nom du daemon de surveillance', { def: currentName }));
394
-
395
- const daemonToken = await configureToken({
396
- title: `2/2 — ${daemonName}, daemon de surveillance`,
397
- purpose: 'Outil déterministe sans assistant : état, usage, lancement, arrêt et redémarrage de la session.',
398
- stateDir: DAEMON_STATE_DIR,
399
- migrateFrom: LEGACY_DAEMON_STATE_DIR,
400
- });
401
-
402
- const previousIds = configuredIds();
403
- const idsRaw = nonInteractive
404
- ? previousIds.join(', ')
405
- : await ask('Identifiants Telegram autorisés pour les deux bots', { def: previousIds.join(', ') });
406
- const ids = [...new Set(idsRaw.split(',').map((id) => id.trim()).filter(Boolean))];
407
-
408
- if (assistantToken) writeAccess(ASSISTANT_STATE_DIR, ids, { pairing: true });
409
- if (daemonToken) writeAccess(DAEMON_STATE_DIR, ids);
410
- if (ids.length) log.ok(`Allowlist commune enregistrée : ${ids.join(', ')}`);
411
- else log.warn('Aucun identifiant autorisé : le daemon refusera tous les messages.');
412
-
413
- ensureDir(assistantRoot);
414
- const projects = writeOrchestratorConfig(assistantName, daemonName, assistantRoot);
415
- log.ok(`Assistant général « ${assistantName} » fixé à la racine : ${assistantRoot}`);
416
-
417
- const issues = [];
418
- if (!assistantToken) issues.push('token de l’Assistant Bot absent');
419
- if (!daemonToken) issues.push('token du daemon absent');
420
- if (assistantToken && daemonToken && assistantToken === daemonToken) {
421
- issues.push('les deux bots utilisent le même token');
422
- log.warn('Un token Telegram ne peut pas être interrogé simultanément par l’Assistant Bot et le daemon.');
423
- }
424
- if (!ids.length) issues.push('allowlist du daemon vide');
425
-
426
- let daemonStarted = false;
427
- if (daemonToken && ids.length && assistantToken !== daemonToken) {
428
- if (IS_MAC) {
429
- daemonStarted = installDaemonService();
430
- if (daemonStarted) log.ok(`Daemon « ${daemonName} » installé et démarré (sans LLM).`);
431
- else issues.push('service du daemon non démarré');
432
- } else {
433
- log.info(`Démarrage manuel du daemon : node ${DAEMON_ENTRY}`);
434
- }
435
- }
436
-
437
- let assistantStarted = true;
438
- if (assistantToken) assistantStarted = await launchAssistant(assistantRoot);
439
- if (!assistantStarted) issues.push('session de l’Assistant Bot non ouverte');
440
-
441
- if (issues.length) return { status: 'partial', note: issues.join(' ; ') };
442
- return {
443
- status: 'done',
444
- note: `${projects.length} projet(s) ; Assistant général « ${assistantName} » à la racine ; daemon « ${daemonName} »${daemonStarted ? ' actif' : ' configuré'}.`,
445
- };
446
- }
447
-
448
- export async function check(ctx = {}) {
449
- if (!commandExists('claude', ['--version'])) {
450
- return { status: 'skipped', note: 'CLI « claude » introuvable.' };
451
- }
452
-
453
- const syntax = runCapture(process.execPath, ['--check', DAEMON_ENTRY]);
454
- if (syntax.code !== 0) {
455
- return { status: 'failed', note: `Le daemon ne compile pas : ${syntax.stderr.split('\n')[0]}` };
456
- }
457
-
458
- const pluginInstalled = isTelegramInstalled(listPlugins());
459
- const assistantToken = readToken(ASSISTANT_STATE_DIR);
460
- const daemonToken = readToken(DAEMON_STATE_DIR);
461
- const config = readOrchestratorConfig();
462
- const assistantRoot = path.resolve(ctx.config?.dossiersRoot || ctx.config?.outputPath || REPO_ROOT);
463
- const rootConfigured = Array.isArray(config.projects) && config.projects.some((project) => (
464
- project?.name === 'piecemaker'
465
- && typeof project.workdir === 'string'
466
- && path.resolve(project.workdir) === assistantRoot
467
- ));
468
- const ids = readAccess(DAEMON_STATE_DIR).allowFrom || [];
469
- const serviceReady = !IS_MAC || daemonServiceRunning();
470
-
471
- const missing = [];
472
- if (!pluginInstalled) missing.push('plugin Telegram absent');
473
- if (!assistantToken) missing.push('Assistant Bot non configuré');
474
- if (!daemonToken) missing.push('daemon non configuré');
475
- if (assistantToken && daemonToken && assistantToken === daemonToken) missing.push('tokens identiques');
476
- if (!rootConfigured) missing.push('racine PieceMaker non configurée');
477
- if (!ids.length) missing.push('allowlist du daemon vide');
478
- if (!serviceReady) missing.push('service du daemon arrêté');
479
-
480
- if (missing.length) return { status: 'partial', note: missing.join(' ; ') };
481
- return { status: 'done', note: `Assistant général « ${normalizeAssistantName(config.assistantName)} » + daemon « ${normalizeDaemonName(config.daemonName)} »` };
482
- }