blun-king-cli 9.1.624 → 9.1.626

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 (142) hide show
  1. package/CHANGELOG.md +62 -213
  2. package/LIESMICH.txt +57 -74
  3. package/README.md +76 -119
  4. package/agent-spine-plugin/scripts/check-install.js +4 -4
  5. package/agent-spine-plugin/src/hook.js +0 -4
  6. package/agent-spine-plugin/src/lib/documents.js +1 -1
  7. package/agent-spine-plugin/src/lib/hook-context.js +0 -1
  8. package/agent-spine-plugin/src/lib/host-instruction-budget.js +2 -1
  9. package/agent-spine-plugin/src/lib/preflight.js +9 -38
  10. package/agent-spine-plugin/src/lib/source-roots.js +1 -1
  11. package/bin/agentspine-king-goal-inbox.mjs +127 -127
  12. package/bin/agentspine-king-goal-intake.mjs +106 -106
  13. package/bin/agentspine-king-host-runner.mjs +109 -109
  14. package/bin/agentspine-king-snapshot-policy.cjs +80 -80
  15. package/bin/agentspine-king-status-policy.mjs +226 -226
  16. package/bin/agentspine-king-worker-host.mjs +160 -160
  17. package/bin/core-bootstrap.js +83 -97
  18. package/bin/launcher-mode.js +93 -93
  19. package/bin/launcher-restart-policy.cjs +131 -131
  20. package/bin/launcher-runtime.js +91 -254
  21. package/bin/managed-node.js +186 -186
  22. package/bin/managed-plugin-selection.cjs +109 -116
  23. package/bin/native-module-repair.js +201 -201
  24. package/bin/node-runtime.js +34 -34
  25. package/bin/node-version.js +49 -49
  26. package/bin/plugin-bootstrap.js +149 -172
  27. package/bin/private-paths.js +1578 -1624
  28. package/bin/runtime-exit-ledger.cjs +144 -144
  29. package/bin/runtime-exit-ledger.d.cts +23 -23
  30. package/bin/standard-tools-bootstrap.js +277 -272
  31. package/bin/turn-thinking-policy.cjs +82 -82
  32. package/bin/update-copy.js +200 -200
  33. package/bin/update-lease.js +597 -599
  34. package/bin/update-notice.js +1568 -1568
  35. package/bin/verify-agent-behavior.cjs +122 -122
  36. package/bin/verify-agent-components.cjs +104 -104
  37. package/bin/verify-bundled-agent-sources.cjs +91 -96
  38. package/bin/windows-node-crash-dump.cjs +289 -289
  39. package/blun.mjs +187640 -180475
  40. package/bundled-agent-sources.json +26 -30
  41. package/codebase-index/codebase_index.py +462 -470
  42. package/dist-web/assets/{CodeBlockNode-TKKkJX8L.js → CodeBlockNode-BMbuG85x.js} +1 -1
  43. package/dist-web/assets/{CodeBlockShell.vue_vue_type_style_index_0_lang-CUj2Ds5x.js → CodeBlockShell.vue_vue_type_style_index_0_lang-C1fv7l_Z.js} +1 -1
  44. package/dist-web/assets/DesignSystemView-6HgAUEmC.css +1 -0
  45. package/dist-web/assets/DesignSystemView-CEEBqXSX.js +12 -0
  46. package/dist-web/assets/{Tooltip-CTWv7WCU.js → Tooltip--YoauJ24.js} +1 -1
  47. package/dist-web/assets/{abnfDiagram-N423BO3Z-DA8V8Zjh.js → abnfDiagram-N423BO3Z-DZvBWkRg.js} +1 -1
  48. package/dist-web/assets/{arc-YtddKbdY.js → arc-B04JATeX.js} +1 -1
  49. package/dist-web/assets/{architectureDiagram-T3A2C74G-CGEiObmo.js → architectureDiagram-T3A2C74G-DmYnMep2.js} +1 -1
  50. package/dist-web/assets/{blockDiagram-VBNYF7ZC-Bd5Vt50e.js → blockDiagram-VBNYF7ZC-DJ1gPS1C.js} +1 -1
  51. package/dist-web/assets/{c4Diagram-5PPSVZJV-D2ApPM54.js → c4Diagram-5PPSVZJV-DmjxvUbd.js} +1 -1
  52. package/dist-web/assets/channel-472fcEvw.js +1 -0
  53. package/dist-web/assets/{chunk-2GRJ4B5K-BDcIEZSl.js → chunk-2GRJ4B5K-AB0raj3I.js} +1 -1
  54. package/dist-web/assets/{chunk-2Q5K7J3B-6aSabCX7.js → chunk-2Q5K7J3B-Drf039vM.js} +1 -1
  55. package/dist-web/assets/{chunk-5RXB4S5H-BFRQvIJS.js → chunk-5RXB4S5H-D5ILttmz.js} +1 -1
  56. package/dist-web/assets/{chunk-5VM5RSS4-ChAa-DNY.js → chunk-5VM5RSS4-DrLSdi8y.js} +1 -1
  57. package/dist-web/assets/{chunk-6Q2QTUOP-BIfrb2Jc.js → chunk-6Q2QTUOP-LiaqdEOQ.js} +1 -1
  58. package/dist-web/assets/{chunk-GF5L2VYU-BR0gvtWI.js → chunk-GF5L2VYU-AIYkb--o.js} +1 -1
  59. package/dist-web/assets/{chunk-JWPE2WC7-BgXKsx5E.js → chunk-JWPE2WC7-CogdNl0i.js} +1 -1
  60. package/dist-web/assets/{chunk-KBJHAD2P-I8mrZhpz.js → chunk-KBJHAD2P-DHjpNtZv.js} +1 -1
  61. package/dist-web/assets/{chunk-RYQCIY6F-BMPaem92.js → chunk-RYQCIY6F-BUSOqH2X.js} +1 -1
  62. package/dist-web/assets/{chunk-XXDRQBXY-COy0J-0j.js → chunk-XXDRQBXY-BpoMV0bh.js} +1 -1
  63. package/dist-web/assets/classDiagram-JCYQIIEL-BTZeLngj.js +1 -0
  64. package/dist-web/assets/classDiagram-v2-OCEON4UE-BTZeLngj.js +1 -0
  65. package/dist-web/assets/{cose-bilkent-JH36ORCC-MosTTRdo.js → cose-bilkent-JH36ORCC-BJr2I61o.js} +1 -1
  66. package/dist-web/assets/{cynefin-VYW2F7L2-5tjPgrkP.js → cynefin-VYW2F7L2-HtysQZ4A.js} +1 -1
  67. package/dist-web/assets/{cynefinDiagram-MW4NZA55-CbDcdInR.js → cynefinDiagram-MW4NZA55-CcpC_2uv.js} +1 -1
  68. package/dist-web/assets/{dagre-VZM6K2ZE-YaBom1ZH.js → dagre-VZM6K2ZE-D8Ao0LUg.js} +1 -1
  69. package/dist-web/assets/{diagram-7IWD3JNH-Da4m-Hgz.js → diagram-7IWD3JNH-BITiLhjX.js} +1 -1
  70. package/dist-web/assets/{diagram-B4RE2ZJO-CAUUegxg.js → diagram-B4RE2ZJO-CJtwfy9f.js} +1 -1
  71. package/dist-web/assets/{diagram-LBJQPF4R-o21mWKG4.js → diagram-LBJQPF4R-BsVTh6w8.js} +1 -1
  72. package/dist-web/assets/{diagram-Q27KOJAE-BA3z3sXf.js → diagram-Q27KOJAE-pA_MUCUf.js} +1 -1
  73. package/dist-web/assets/{diagram-UB23O5K3-CGRdSSDK.js → diagram-UB23O5K3-CIY8z7vu.js} +1 -1
  74. package/dist-web/assets/{ebnfDiagram-BXEA7PRR-Bw-iU9FP.js → ebnfDiagram-BXEA7PRR-C3FMioqp.js} +1 -1
  75. package/dist-web/assets/{erDiagram-JOGREHBK-BwM1yiR0.js → erDiagram-JOGREHBK-CzoyKmKc.js} +1 -1
  76. package/dist-web/assets/{flowDiagram-UKHOOZJN-z05eOR1Q.js → flowDiagram-UKHOOZJN-DOqfushy.js} +1 -1
  77. package/dist-web/assets/{ganttDiagram-PKOTCBZU-BMUubNTl.js → ganttDiagram-PKOTCBZU-Cf9XQSWl.js} +1 -1
  78. package/dist-web/assets/{gitGraphDiagram-DS77QQ5N-Dccbi1wU.js → gitGraphDiagram-DS77QQ5N-DHUDofE5.js} +1 -1
  79. package/dist-web/assets/{index-h0KEOmkU.js → index-B6qEMQMx.js} +14 -14
  80. package/dist-web/assets/{index-C9eDH3u-.css → index-BmKj7CB1.css} +1 -1
  81. package/dist-web/assets/{index-BBbC5LRF.js → index-DokxDX1w.js} +1 -1
  82. package/dist-web/assets/{index-D48p0M2S.js → index-auojtiAA.js} +2 -2
  83. package/dist-web/assets/{index10-Qbuw1N6u.js → index10-Drb2n5e5.js} +1 -1
  84. package/dist-web/assets/{index11-g8v025wN.js → index11-Cx-aV4G2.js} +1 -1
  85. package/dist-web/assets/{index5-BYNcV8BY.js → index5-Dg3qGTyH.js} +1 -1
  86. package/dist-web/assets/{index6-BetANtnV.js → index6-BgvtIICP.js} +1 -1
  87. package/dist-web/assets/{index7-BcPbgCKN.js → index7-uEnniPQG.js} +1 -1
  88. package/dist-web/assets/{index8-B1lp-RvF.js → index8-h4IKc0ek.js} +2 -2
  89. package/dist-web/assets/{infoDiagram-6WML65LV-BF_pbUnN.js → infoDiagram-6WML65LV-D-3tvEiJ.js} +1 -1
  90. package/dist-web/assets/{ishikawaDiagram-WSZJBQD7-Dedv4urS.js → ishikawaDiagram-WSZJBQD7-DIIBNv7a.js} +1 -1
  91. package/dist-web/assets/{journeyDiagram-NVQOT4AX-B1Ex3b6o.js → journeyDiagram-NVQOT4AX-DP4r1Vlx.js} +1 -1
  92. package/dist-web/assets/{kanban-definition-27J2QSJJ-BtCGliBH.js → kanban-definition-27J2QSJJ-C3_Ztgxv.js} +1 -1
  93. package/dist-web/assets/{linear-ByItc8Qr.js → linear-DnqhIw-y.js} +1 -1
  94. package/dist-web/assets/{mermaid.core-B1M2O7nN.js → mermaid.core-xYUgBa3T.js} +4 -4
  95. package/dist-web/assets/{mindmap-definition-FAOFIHXS-D0UHKUyj.js → mindmap-definition-FAOFIHXS-l-dq-zfr.js} +1 -1
  96. package/dist-web/assets/{pegDiagram-VL7TDLO6-D7zYCVmG.js → pegDiagram-VL7TDLO6-C8vXQiaJ.js} +1 -1
  97. package/dist-web/assets/{pieDiagram-7S7Q4E2Y-Bd0i9CHa.js → pieDiagram-7S7Q4E2Y-CyNRSsI6.js} +1 -1
  98. package/dist-web/assets/{quadrantDiagram-CIZ2JOQS-MG-Ug47d.js → quadrantDiagram-CIZ2JOQS-D-w8AD_g.js} +1 -1
  99. package/dist-web/assets/{railroadDiagram-AXF67PYL-AZdl90Gj.js → railroadDiagram-AXF67PYL-BLkyQCWy.js} +1 -1
  100. package/dist-web/assets/{requirementDiagram-LRYGKXZP-DjplF9kt.js → requirementDiagram-LRYGKXZP-CEWO7XfT.js} +1 -1
  101. package/dist-web/assets/{sankeyDiagram-W5VNT64P-Bi9Zgfjs.js → sankeyDiagram-W5VNT64P-ByGoAaGj.js} +1 -1
  102. package/dist-web/assets/{sequenceDiagram-SI44F4Z6-ChEpdCvb.js → sequenceDiagram-SI44F4Z6-Bhn9GQtd.js} +1 -1
  103. package/dist-web/assets/{sizeCapture-X5ZJPWSS-B5RFUdaa.js → sizeCapture-X5ZJPWSS-jVKR9CTu.js} +1 -1
  104. package/dist-web/assets/{stateDiagram-OKZ733FA-BkRblSKp.js → stateDiagram-OKZ733FA-wW3XbegA.js} +1 -1
  105. package/dist-web/assets/stateDiagram-v2-UEYNNEHI-B8aids-Z.js +1 -0
  106. package/dist-web/assets/{swimlanes-SLNWSIFB-DLXH6Zhc.js → swimlanes-SLNWSIFB-B8QyFS7G.js} +2 -2
  107. package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-B8XAkdSm.js +8 -0
  108. package/dist-web/assets/{timeline-definition-Z64GVDOM-C_N88siO.js → timeline-definition-Z64GVDOM-DpvxS-1A.js} +1 -1
  109. package/dist-web/assets/{vennDiagram-T6HMQDX7-DMsnvDt0.js → vennDiagram-T6HMQDX7-BzGM0ihY.js} +1 -1
  110. package/dist-web/assets/{vue.runtime.esm-bundler-B-3uIy85.js → vue.runtime.esm-bundler-BOcpwIX1.js} +1 -1
  111. package/dist-web/assets/{wardleyDiagram-T6FBY63Y-C9SJIPzq.js → wardleyDiagram-T6FBY63Y-2DKAOs5e.js} +1 -1
  112. package/dist-web/assets/{xychartDiagram-ELKLHX3M-DZLWA6Go.js → xychartDiagram-ELKLHX3M-B0SP0TXd.js} +1 -1
  113. package/dist-web/index.html +32 -32
  114. package/dist-web/vis/index.html.gz +0 -0
  115. package/package.json +1 -1
  116. package/scripts/fix-node-pty-perms.js +53 -53
  117. package/standard-skills/blun-app-design-system/SKILL.md +30 -30
  118. package/standard-skills/blun-app-design-system/provenance.json +17 -17
  119. package/standard-skills/blun-app-design-system/references/tokens.md +48 -48
  120. package/standard-skills/blun-app-design-system/scripts/check-blun-design.mjs +49 -49
  121. package/standard-skills/translate-native/README.md +1 -1
  122. package/standard-skills/translate-native/scripts/blun_language_guard.py +2 -0
  123. package/standard-skills/translate-native/scripts/language_gateway.py +2 -0
  124. package/standard-skills/translate-native/scripts/pre_output_guard.py +2 -0
  125. package/standard-tools/language-guard/blun_language_guard.py +2 -0
  126. package/standard-tools/language-guard/language_gateway.py +2 -0
  127. package/standard-tools/language-guard/pre_output_guard.py +2 -0
  128. package/telegram-plugin/blun.plugin.json +20 -20
  129. package/telegram-plugin/commands/access.md +37 -37
  130. package/telegram-plugin/dist/bridge.mjs +310 -187
  131. package/telegram-plugin/dist/mcp-server.mjs +174 -261
  132. package/telegram-plugin/dist/noise.mjs +1 -19
  133. package/worker-host.mjs +13747 -16824
  134. package/bin/profile-handoff.cjs +0 -152
  135. package/bin/profile-launch.cjs +0 -120
  136. package/dist-web/assets/DesignSystemView-DwK1aQMo.js +0 -12
  137. package/dist-web/assets/DesignSystemView-szjTj7ns.css +0 -1
  138. package/dist-web/assets/channel-BnluexuN.js +0 -1
  139. package/dist-web/assets/classDiagram-JCYQIIEL-BliFdSD9.js +0 -1
  140. package/dist-web/assets/classDiagram-v2-OCEON4UE-BliFdSD9.js +0 -1
  141. package/dist-web/assets/stateDiagram-v2-UEYNNEHI-fVkQcACW.js +0 -1
  142. package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-Bg-BiZJQ.js +0 -8
@@ -1,185 +1,162 @@
1
- 'use strict';
2
-
3
- const { randomUUID } = require('node:crypto');
4
- const fs = require('node:fs');
5
- const path = require('node:path');
1
+ 'use strict';
2
+
3
+ const { randomUUID } = require('node:crypto');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+ const { selectedAgentSpineRoot, describeAgentSpineSelection, copySelectedAgentSpinePlugin } = require('./managed-plugin-selection.cjs');
7
+
6
8
  const {
7
- selectedAgentSpineRoot,
8
- describeAgentSpineSelection,
9
- copySelectedAgentSpinePlugin,
10
- snapshotPluginTree,
11
- digestPluginEntries,
12
- } = require('./managed-plugin-selection.cjs');
13
-
14
- const {
15
- ensurePrivateDirectory,
16
- securePrivateFile,
17
- writePrivateFile,
18
- } = require('./private-paths');
19
-
20
- function isObject(value) {
21
- return typeof value === 'object' && value !== null && !Array.isArray(value);
22
- }
23
-
24
- function readInstalledPlugins(filePath) {
25
- if (!fs.existsSync(filePath)) return { version: 1, plugins: [] };
26
-
27
- const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
28
- if (!isObject(parsed) || parsed.version !== 1 || !Array.isArray(parsed.plugins)
29
- || !parsed.plugins.every(isObject)) {
30
- throw new Error(`Ungültige Plugin-Konfiguration: ${filePath}`);
31
- }
32
- return parsed;
33
- }
34
-
35
- function updatedInstalledPlugins(installed, pluginRoot, now, pluginId = 'telegram') {
36
- const previous = installed.plugins.find((plugin) => plugin.id === pluginId);
37
- const previousCapabilities = isObject(previous?.capabilities) ? previous.capabilities : {};
38
- const previousMcpServers = isObject(previousCapabilities.mcpServers)
39
- ? previousCapabilities.mcpServers : {};
40
- const previousTelegramMcp = isObject(previousMcpServers[pluginId])
41
- ? previousMcpServers[pluginId] : {};
42
- const telegram = {
43
- ...previous,
44
- id: pluginId,
45
- root: pluginRoot,
46
- source: 'local-path',
47
- enabled: true,
48
- installedAt: typeof previous?.installedAt === 'string' && previous.installedAt.length > 0
49
- ? previous.installedAt : now,
50
- updatedAt: now,
51
- capabilities: {
52
- ...previousCapabilities,
53
- mcpServers: {
54
- ...previousMcpServers,
55
- [pluginId]: { ...previousTelegramMcp, enabled: previousTelegramMcp.enabled === false && pluginId === 'agent-spine' ? false : true },
56
- },
57
- },
58
- };
59
-
60
- const plugins = [];
61
- let replaced = false;
62
- for (const plugin of installed.plugins) {
63
- if (plugin.id !== pluginId) {
64
- plugins.push(plugin);
65
- } else if (!replaced) {
66
- plugins.push(telegram);
67
- replaced = true;
68
- }
69
- }
70
- if (!replaced) plugins.push(telegram);
71
- return { ...installed, version: 1, plugins };
72
- }
73
-
74
- function writeInstalledPlugins(filePath, installed) {
75
- const temporaryPath = path.join(
76
- path.dirname(filePath),
77
- `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,
78
- );
79
- try {
80
- writePrivateFile(temporaryPath, `${JSON.stringify(installed, null, 2)}\n`);
81
- fs.renameSync(temporaryPath, filePath);
82
- } catch (error) {
83
- fs.rmSync(temporaryPath, { force: true });
84
- throw error;
85
- }
86
- }
87
-
88
- function installManagedTelegramPlugin({ packageRoot, blunDir, now = new Date().toISOString() }) {
89
- const sourceRoot = path.join(packageRoot, 'telegram-plugin');
90
- const pluginsDir = path.join(blunDir, 'plugins');
91
- const managedDir = path.join(pluginsDir, 'managed');
92
- const pluginRoot = path.join(managedDir, 'telegram');
93
- const installedFile = path.join(pluginsDir, 'installed.json');
94
-
95
- ensurePrivateDirectory(pluginsDir);
96
- if (fs.existsSync(installedFile)) securePrivateFile(installedFile);
9
+ ensurePrivateDirectory,
10
+ securePrivateFile,
11
+ writePrivateFile,
12
+ } = require('./private-paths');
13
+
14
+ function isObject(value) {
15
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
16
+ }
17
+
18
+ function readInstalledPlugins(filePath) {
19
+ if (!fs.existsSync(filePath)) return { version: 1, plugins: [] };
20
+
21
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
22
+ if (!isObject(parsed) || parsed.version !== 1 || !Array.isArray(parsed.plugins)
23
+ || !parsed.plugins.every(isObject)) {
24
+ throw new Error(`Ungültige Plugin-Konfiguration: ${filePath}`);
25
+ }
26
+ return parsed;
27
+ }
28
+
29
+ function updatedInstalledPlugins(installed, pluginRoot, now, pluginId = 'telegram') {
30
+ const previous = installed.plugins.find((plugin) => plugin.id === pluginId);
31
+ const previousCapabilities = isObject(previous?.capabilities) ? previous.capabilities : {};
32
+ const previousMcpServers = isObject(previousCapabilities.mcpServers)
33
+ ? previousCapabilities.mcpServers : {};
34
+ const previousTelegramMcp = isObject(previousMcpServers[pluginId])
35
+ ? previousMcpServers[pluginId] : {};
36
+ const telegram = {
37
+ ...previous,
38
+ id: pluginId,
39
+ root: pluginRoot,
40
+ source: 'local-path',
41
+ enabled: true,
42
+ installedAt: typeof previous?.installedAt === 'string' && previous.installedAt.length > 0
43
+ ? previous.installedAt : now,
44
+ updatedAt: now,
45
+ capabilities: {
46
+ ...previousCapabilities,
47
+ mcpServers: {
48
+ ...previousMcpServers,
49
+ [pluginId]: { ...previousTelegramMcp, enabled: previousTelegramMcp.enabled === false && pluginId === 'agent-spine' ? false : true },
50
+ },
51
+ },
52
+ };
53
+
54
+ const plugins = [];
55
+ let replaced = false;
56
+ for (const plugin of installed.plugins) {
57
+ if (plugin.id !== pluginId) {
58
+ plugins.push(plugin);
59
+ } else if (!replaced) {
60
+ plugins.push(telegram);
61
+ replaced = true;
62
+ }
63
+ }
64
+ if (!replaced) plugins.push(telegram);
65
+ return { ...installed, version: 1, plugins };
66
+ }
67
+
68
+ function writeInstalledPlugins(filePath, installed) {
69
+ const temporaryPath = path.join(
70
+ path.dirname(filePath),
71
+ `.${path.basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,
72
+ );
73
+ try {
74
+ writePrivateFile(temporaryPath, `${JSON.stringify(installed, null, 2)}\n`);
75
+ fs.renameSync(temporaryPath, filePath);
76
+ } catch (error) {
77
+ fs.rmSync(temporaryPath, { force: true });
78
+ throw error;
79
+ }
80
+ }
81
+
82
+ function installManagedTelegramPlugin({ packageRoot, blunDir, now = new Date().toISOString() }) {
83
+ const sourceRoot = path.join(packageRoot, 'telegram-plugin');
84
+ const pluginsDir = path.join(blunDir, 'plugins');
85
+ const managedDir = path.join(pluginsDir, 'managed');
86
+ const pluginRoot = path.join(managedDir, 'telegram');
87
+ const installedFile = path.join(pluginsDir, 'installed.json');
88
+
89
+ ensurePrivateDirectory(pluginsDir);
90
+ if (fs.existsSync(installedFile)) securePrivateFile(installedFile);
97
91
  const installed = readInstalledPlugins(installedFile);
98
92
  const updatedInstalled = updatedInstalledPlugins(installed, pluginRoot, now);
99
93
  ensurePrivateDirectory(managedDir);
100
- const expectedEntries = snapshotPluginTree(sourceRoot);
101
- const expectedDigest = digestPluginEntries(expectedEntries);
102
- let hadPreviousPlugin = false;
103
- if (fs.existsSync(pluginRoot)) {
104
- ensurePrivateDirectory(pluginRoot);
105
- hadPreviousPlugin = true;
106
- const existingEntries = snapshotPluginTree(pluginRoot, expectedEntries);
107
- if (existingEntries !== null && digestPluginEntries(existingEntries) === expectedDigest) {
108
- const previous = installed.plugins.find((plugin) => plugin.id === 'telegram');
109
- const previousUpdate = typeof previous?.updatedAt === 'string' && previous.updatedAt.length > 0
110
- ? previous.updatedAt : now;
111
- const reusedInstalled = updatedInstalledPlugins(installed, pluginRoot, previousUpdate);
112
- if (JSON.stringify(reusedInstalled) !== JSON.stringify(installed)) {
113
- writeInstalledPlugins(installedFile, reusedInstalled);
114
- }
115
- return { installedFile, pluginRoot, reused: true };
116
- }
117
- }
118
94
  const nonce = `${process.pid}-${randomUUID()}`;
119
95
  const stagingRoot = path.join(managedDir, `.telegram-${nonce}.tmp`);
120
96
  const backupRoot = path.join(managedDir, `.telegram-${nonce}.bak`);
97
+ let hadPreviousPlugin = false;
121
98
  let installedNewPlugin = false;
122
99
  try {
100
+ if (fs.existsSync(pluginRoot)) {
101
+ ensurePrivateDirectory(pluginRoot);
102
+ hadPreviousPlugin = true;
103
+ }
123
104
  fs.cpSync(sourceRoot, stagingRoot, { recursive: true, force: true });
124
- const stagedEntries = snapshotPluginTree(stagingRoot, expectedEntries);
125
- if (stagedEntries === null || digestPluginEntries(stagedEntries) !== expectedDigest) {
126
- throw new Error('Telegram source changed during installation');
105
+ if (hadPreviousPlugin) fs.renameSync(pluginRoot, backupRoot);
106
+ try {
107
+ fs.renameSync(stagingRoot, pluginRoot);
108
+ installedNewPlugin = true;
109
+ writeInstalledPlugins(installedFile, updatedInstalled);
110
+ } catch (error) {
111
+ if (installedNewPlugin) fs.rmSync(pluginRoot, { recursive: true, force: true });
112
+ if (hadPreviousPlugin && fs.existsSync(backupRoot)) {
113
+ fs.renameSync(backupRoot, pluginRoot);
114
+ }
115
+ throw error;
127
116
  }
128
- if (hadPreviousPlugin) fs.renameSync(pluginRoot, backupRoot);
129
- try {
130
- fs.renameSync(stagingRoot, pluginRoot);
131
- installedNewPlugin = true;
132
- writeInstalledPlugins(installedFile, updatedInstalled);
133
- } catch (error) {
134
- if (installedNewPlugin) fs.rmSync(pluginRoot, { recursive: true, force: true });
135
- if (hadPreviousPlugin && fs.existsSync(backupRoot)) {
136
- fs.renameSync(backupRoot, pluginRoot);
137
- }
138
- throw error;
139
- }
140
- if (hadPreviousPlugin) fs.rmSync(backupRoot, { recursive: true, force: true });
141
- } finally {
142
- fs.rmSync(stagingRoot, { recursive: true, force: true });
143
- }
144
-
117
+ if (hadPreviousPlugin) fs.rmSync(backupRoot, { recursive: true, force: true });
118
+ } finally {
119
+ fs.rmSync(stagingRoot, { recursive: true, force: true });
120
+ }
121
+
122
+ return { installedFile, pluginRoot };
123
+ }
124
+
125
+ function installManagedAgentSpinePlugin({ packageRoot, blunDir, now = new Date().toISOString() }) {
126
+ const installedFile = path.join(blunDir, 'plugins', 'installed.json');
127
+ ensurePrivateDirectory(path.dirname(installedFile));
128
+ if (fs.existsSync(installedFile)) securePrivateFile(installedFile);
129
+ const installed = readInstalledPlugins(installedFile);
130
+ const records = installed.plugins.filter(plugin => plugin.id === 'agent-spine');
131
+ if (records.length > 1) throw new Error('Ambiguous AgentSpine registration');
132
+ const selected = selectedAgentSpineRoot({ installed, blunDir });
133
+ if (selected) return { installedFile, pluginRoot: selected, reused: true, selected: true };
134
+ if (records[0]?.enabled === false) {
135
+ return { installedFile, pluginRoot: records[0].root, reused: true, disabled: true };
136
+ }
137
+ if (records[0]) {
138
+ const currentRoot = records[0].root;
139
+ if (typeof currentRoot !== 'string' || !path.isAbsolute(currentRoot)) {
140
+ throw new Error('Invalid AgentSpine registration root');
141
+ }
142
+ const managed = path.resolve(blunDir, 'plugins', 'managed');
143
+ const sameDirectory = (left, right) => process.platform === 'win32'
144
+ ? left.toLowerCase() === right.toLowerCase() : left === right;
145
+ if (!sameDirectory(path.dirname(path.resolve(currentRoot)), managed)
146
+ || !/^agent-spine-[a-f0-9]{64}$/.test(path.basename(currentRoot))) {
147
+ return { installedFile, pluginRoot: currentRoot, reused: true, custom: true };
148
+ }
149
+ // An upgrade must not conceal changes to the previous immutable default.
150
+ describeAgentSpineSelection({ blunDir, pluginRoot: currentRoot });
151
+ }
152
+ const pluginRoot = copySelectedAgentSpinePlugin({ blunDir, sourceRoot: path.join(packageRoot, 'agent-spine-plugin') });
153
+ if (records[0]?.root === pluginRoot) return { installedFile, pluginRoot, reused: true };
154
+ writeInstalledPlugins(installedFile, updatedInstalledPlugins(installed, pluginRoot, now, 'agent-spine'));
145
155
  return { installedFile, pluginRoot, reused: false };
146
156
  }
147
-
148
- function installManagedAgentSpinePlugin({ packageRoot, blunDir, now = new Date().toISOString() }) {
149
- const installedFile = path.join(blunDir, 'plugins', 'installed.json');
150
- ensurePrivateDirectory(path.dirname(installedFile));
151
- if (fs.existsSync(installedFile)) securePrivateFile(installedFile);
152
- const installed = readInstalledPlugins(installedFile);
153
- const records = installed.plugins.filter(plugin => plugin.id === 'agent-spine');
154
- if (records.length > 1) throw new Error('Ambiguous AgentSpine registration');
155
- const selected = selectedAgentSpineRoot({ installed, blunDir });
156
- if (selected) return { installedFile, pluginRoot: selected, reused: true, selected: true };
157
- if (records[0]?.enabled === false) {
158
- return { installedFile, pluginRoot: records[0].root, reused: true, disabled: true };
159
- }
160
- if (records[0]) {
161
- const currentRoot = records[0].root;
162
- if (typeof currentRoot !== 'string' || !path.isAbsolute(currentRoot)) {
163
- throw new Error('Invalid AgentSpine registration root');
164
- }
165
- const managed = path.resolve(blunDir, 'plugins', 'managed');
166
- const sameDirectory = (left, right) => process.platform === 'win32'
167
- ? left.toLowerCase() === right.toLowerCase() : left === right;
168
- if (!sameDirectory(path.dirname(path.resolve(currentRoot)), managed)
169
- || !/^agent-spine-[a-f0-9]{64}$/.test(path.basename(currentRoot))) {
170
- return { installedFile, pluginRoot: currentRoot, reused: true, custom: true };
171
- }
172
- // An upgrade must not conceal changes to the previous immutable default.
173
- describeAgentSpineSelection({ blunDir, pluginRoot: currentRoot });
174
- }
175
- const pluginRoot = copySelectedAgentSpinePlugin({ blunDir, sourceRoot: path.join(packageRoot, 'agent-spine-plugin') });
176
- if (records[0]?.root === pluginRoot) return { installedFile, pluginRoot, reused: true };
177
- writeInstalledPlugins(installedFile, updatedInstalledPlugins(installed, pluginRoot, now, 'agent-spine'));
178
- return { installedFile, pluginRoot, reused: false };
179
- }
180
-
181
- module.exports = {
182
- installManagedAgentSpinePlugin,
183
- installManagedTelegramPlugin,
184
- readInstalledPlugins,
185
- };
157
+
158
+ module.exports = {
159
+ installManagedAgentSpinePlugin,
160
+ installManagedTelegramPlugin,
161
+ readInstalledPlugins,
162
+ };