blun-king-cli 9.1.601 → 9.1.603
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -94
- package/LIESMICH.txt +57 -57
- package/README.md +90 -90
- package/bin/agentspine-king-goal-inbox.mjs +127 -127
- package/bin/agentspine-king-goal-intake.mjs +106 -106
- package/bin/agentspine-king-host-runner.mjs +109 -109
- package/bin/agentspine-king-snapshot-policy.cjs +80 -80
- package/bin/agentspine-king-status-policy.mjs +226 -226
- package/bin/agentspine-king-worker-host.mjs +160 -160
- package/bin/core-bootstrap.js +77 -77
- package/bin/launcher-mode.js +93 -93
- package/bin/launcher-restart-policy.cjs +131 -131
- package/bin/launcher-runtime.js +491 -491
- package/bin/managed-node.js +186 -186
- package/bin/managed-plugin-selection.cjs +115 -115
- package/bin/native-module-repair.js +201 -201
- package/bin/node-runtime.js +34 -34
- package/bin/node-version.js +49 -49
- package/bin/plugin-bootstrap.js +162 -162
- package/bin/private-paths.js +1606 -1606
- package/bin/runtime-exit-ledger.cjs +144 -144
- package/bin/runtime-exit-ledger.d.cts +23 -23
- package/bin/standard-tools-bootstrap.js +272 -272
- package/bin/turn-thinking-policy.cjs +82 -82
- package/bin/update-copy.js +200 -200
- package/bin/update-lease.js +597 -597
- package/bin/update-notice.js +1568 -1568
- package/bin/verify-agent-behavior.cjs +122 -122
- package/bin/verify-agent-components.cjs +104 -104
- package/bin/verify-bundled-agent-sources.cjs +83 -83
- package/bin/windows-node-crash-dump.cjs +289 -289
- package/blun.mjs +1709 -1601
- package/codebase-index/codebase_index.py +470 -470
- package/dist-web/assets/{CodeBlockNode-CXnTLMNr.js → CodeBlockNode-TKKkJX8L.js} +1 -1
- package/dist-web/assets/{CodeBlockShell.vue_vue_type_style_index_0_lang-CCF8Ntnq.js → CodeBlockShell.vue_vue_type_style_index_0_lang-CUj2Ds5x.js} +1 -1
- package/dist-web/assets/DesignSystemView-DwK1aQMo.js +12 -0
- package/dist-web/assets/DesignSystemView-szjTj7ns.css +1 -0
- package/dist-web/assets/{Tooltip-CFQEzqC-.js → Tooltip-CTWv7WCU.js} +1 -1
- package/dist-web/assets/{abnfDiagram-N423BO3Z-Z1APZG5m.js → abnfDiagram-N423BO3Z-DA8V8Zjh.js} +1 -1
- package/dist-web/assets/{arc-C4VyWloT.js → arc-YtddKbdY.js} +1 -1
- package/dist-web/assets/{architectureDiagram-T3A2C74G-DpddbUcM.js → architectureDiagram-T3A2C74G-CGEiObmo.js} +1 -1
- package/dist-web/assets/{blockDiagram-VBNYF7ZC-BqccOSMr.js → blockDiagram-VBNYF7ZC-Bd5Vt50e.js} +1 -1
- package/dist-web/assets/{c4Diagram-5PPSVZJV-OHAcJrTy.js → c4Diagram-5PPSVZJV-D2ApPM54.js} +1 -1
- package/dist-web/assets/channel-BnluexuN.js +1 -0
- package/dist-web/assets/{chunk-2GRJ4B5K-DqJHs8Fw.js → chunk-2GRJ4B5K-BDcIEZSl.js} +1 -1
- package/dist-web/assets/{chunk-2Q5K7J3B-B1nCd91o.js → chunk-2Q5K7J3B-6aSabCX7.js} +1 -1
- package/dist-web/assets/{chunk-5RXB4S5H-neG70kZx.js → chunk-5RXB4S5H-BFRQvIJS.js} +1 -1
- package/dist-web/assets/{chunk-5VM5RSS4-l7biGqeH.js → chunk-5VM5RSS4-ChAa-DNY.js} +1 -1
- package/dist-web/assets/{chunk-6Q2QTUOP-BO-F11Gs.js → chunk-6Q2QTUOP-BIfrb2Jc.js} +1 -1
- package/dist-web/assets/{chunk-GF5L2VYU-BtC69fw0.js → chunk-GF5L2VYU-BR0gvtWI.js} +1 -1
- package/dist-web/assets/{chunk-JWPE2WC7-DIOubEqr.js → chunk-JWPE2WC7-BgXKsx5E.js} +1 -1
- package/dist-web/assets/{chunk-KBJHAD2P-DDUeH_Ba.js → chunk-KBJHAD2P-I8mrZhpz.js} +1 -1
- package/dist-web/assets/{chunk-RYQCIY6F-B9gFMVbS.js → chunk-RYQCIY6F-BMPaem92.js} +1 -1
- package/dist-web/assets/{chunk-XXDRQBXY-Bn1Xz6TQ.js → chunk-XXDRQBXY-COy0J-0j.js} +1 -1
- package/dist-web/assets/classDiagram-JCYQIIEL-BliFdSD9.js +1 -0
- package/dist-web/assets/classDiagram-v2-OCEON4UE-BliFdSD9.js +1 -0
- package/dist-web/assets/{cose-bilkent-JH36ORCC-DqigO3T4.js → cose-bilkent-JH36ORCC-MosTTRdo.js} +1 -1
- package/dist-web/assets/{cynefin-VYW2F7L2-rpD93tQ-.js → cynefin-VYW2F7L2-5tjPgrkP.js} +1 -1
- package/dist-web/assets/{cynefinDiagram-MW4NZA55-DbTqnkVJ.js → cynefinDiagram-MW4NZA55-CbDcdInR.js} +1 -1
- package/dist-web/assets/{dagre-VZM6K2ZE-DcM8aEVY.js → dagre-VZM6K2ZE-YaBom1ZH.js} +1 -1
- package/dist-web/assets/{diagram-7IWD3JNH-UymgsBNa.js → diagram-7IWD3JNH-Da4m-Hgz.js} +1 -1
- package/dist-web/assets/{diagram-B4RE2ZJO-BiYu5WPF.js → diagram-B4RE2ZJO-CAUUegxg.js} +1 -1
- package/dist-web/assets/{diagram-LBJQPF4R-BD3kkgOO.js → diagram-LBJQPF4R-o21mWKG4.js} +1 -1
- package/dist-web/assets/{diagram-Q27KOJAE-D1mYQ_cI.js → diagram-Q27KOJAE-BA3z3sXf.js} +1 -1
- package/dist-web/assets/{diagram-UB23O5K3-BtX8u6HG.js → diagram-UB23O5K3-CGRdSSDK.js} +1 -1
- package/dist-web/assets/{ebnfDiagram-BXEA7PRR-CBAnFkRF.js → ebnfDiagram-BXEA7PRR-Bw-iU9FP.js} +1 -1
- package/dist-web/assets/{erDiagram-JOGREHBK-3vKcWsxm.js → erDiagram-JOGREHBK-BwM1yiR0.js} +1 -1
- package/dist-web/assets/{flowDiagram-UKHOOZJN-D9wBbHm2.js → flowDiagram-UKHOOZJN-z05eOR1Q.js} +1 -1
- package/dist-web/assets/{ganttDiagram-PKOTCBZU-Cd_dViF-.js → ganttDiagram-PKOTCBZU-BMUubNTl.js} +1 -1
- package/dist-web/assets/{gitGraphDiagram-DS77QQ5N-BiEEtTo7.js → gitGraphDiagram-DS77QQ5N-Dccbi1wU.js} +1 -1
- package/dist-web/assets/{index-DdEakFJM.js → index-BBbC5LRF.js} +1 -1
- package/dist-web/assets/{index-BmKj7CB1.css → index-C9eDH3u-.css} +1 -1
- package/dist-web/assets/{index-Ba1OvLjf.js → index-D48p0M2S.js} +2 -2
- package/dist-web/assets/{index-CiPA8Kex.js → index-h0KEOmkU.js} +14 -14
- package/dist-web/assets/{index10-CNr4U-Nw.js → index10-Qbuw1N6u.js} +1 -1
- package/dist-web/assets/{index11-Dto_yZNz.js → index11-g8v025wN.js} +1 -1
- package/dist-web/assets/{index5-B2PldaO2.js → index5-BYNcV8BY.js} +1 -1
- package/dist-web/assets/{index6-CBdZmmyx.js → index6-BetANtnV.js} +1 -1
- package/dist-web/assets/{index7-Dz9oCsUF.js → index7-BcPbgCKN.js} +1 -1
- package/dist-web/assets/{index8-B6k25jtU.js → index8-B1lp-RvF.js} +2 -2
- package/dist-web/assets/{infoDiagram-6WML65LV-D4ph2sIH.js → infoDiagram-6WML65LV-BF_pbUnN.js} +1 -1
- package/dist-web/assets/{ishikawaDiagram-WSZJBQD7-DjKjbMm9.js → ishikawaDiagram-WSZJBQD7-Dedv4urS.js} +1 -1
- package/dist-web/assets/{journeyDiagram-NVQOT4AX-DttHDfPQ.js → journeyDiagram-NVQOT4AX-B1Ex3b6o.js} +1 -1
- package/dist-web/assets/{kanban-definition-27J2QSJJ-C5gB-W0d.js → kanban-definition-27J2QSJJ-BtCGliBH.js} +1 -1
- package/dist-web/assets/{linear-BRGe4-d6.js → linear-ByItc8Qr.js} +1 -1
- package/dist-web/assets/{mermaid.core-D-9Sx1No.js → mermaid.core-B1M2O7nN.js} +4 -4
- package/dist-web/assets/{mindmap-definition-FAOFIHXS-Cg_hoVIq.js → mindmap-definition-FAOFIHXS-D0UHKUyj.js} +1 -1
- package/dist-web/assets/{pegDiagram-VL7TDLO6-CSX9hitD.js → pegDiagram-VL7TDLO6-D7zYCVmG.js} +1 -1
- package/dist-web/assets/{pieDiagram-7S7Q4E2Y-0L0mxahZ.js → pieDiagram-7S7Q4E2Y-Bd0i9CHa.js} +1 -1
- package/dist-web/assets/{quadrantDiagram-CIZ2JOQS-U483z0er.js → quadrantDiagram-CIZ2JOQS-MG-Ug47d.js} +1 -1
- package/dist-web/assets/{railroadDiagram-AXF67PYL-D80a2_DJ.js → railroadDiagram-AXF67PYL-AZdl90Gj.js} +1 -1
- package/dist-web/assets/{requirementDiagram-LRYGKXZP-CeyHfq8t.js → requirementDiagram-LRYGKXZP-DjplF9kt.js} +1 -1
- package/dist-web/assets/{sankeyDiagram-W5VNT64P-CCISkkrR.js → sankeyDiagram-W5VNT64P-Bi9Zgfjs.js} +1 -1
- package/dist-web/assets/{sequenceDiagram-SI44F4Z6-jQfm2XS7.js → sequenceDiagram-SI44F4Z6-ChEpdCvb.js} +1 -1
- package/dist-web/assets/{sizeCapture-X5ZJPWSS-lRZsmCoS.js → sizeCapture-X5ZJPWSS-B5RFUdaa.js} +1 -1
- package/dist-web/assets/{stateDiagram-OKZ733FA-ClCgTdUm.js → stateDiagram-OKZ733FA-BkRblSKp.js} +1 -1
- package/dist-web/assets/stateDiagram-v2-UEYNNEHI-fVkQcACW.js +1 -0
- package/dist-web/assets/{swimlanes-SLNWSIFB-BCdOUN05.js → swimlanes-SLNWSIFB-DLXH6Zhc.js} +2 -2
- package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-Bg-BiZJQ.js +8 -0
- package/dist-web/assets/{timeline-definition-Z64GVDOM-DpWutSKa.js → timeline-definition-Z64GVDOM-C_N88siO.js} +1 -1
- package/dist-web/assets/{vennDiagram-T6HMQDX7-DVC5JVsc.js → vennDiagram-T6HMQDX7-DMsnvDt0.js} +1 -1
- package/dist-web/assets/{vue.runtime.esm-bundler-BwwRtQwQ.js → vue.runtime.esm-bundler-B-3uIy85.js} +1 -1
- package/dist-web/assets/{wardleyDiagram-T6FBY63Y-BK2mrq7L.js → wardleyDiagram-T6FBY63Y-C9SJIPzq.js} +1 -1
- package/dist-web/assets/{xychartDiagram-ELKLHX3M-DUJu9F7-.js → xychartDiagram-ELKLHX3M-DZLWA6Go.js} +1 -1
- package/dist-web/index.html +32 -32
- package/dist-web/vis/index.html.gz +0 -0
- package/package.json +1 -1
- package/scripts/fix-node-pty-perms.js +53 -53
- package/standard-skills/blun-app-design-system/SKILL.md +30 -30
- package/standard-skills/blun-app-design-system/provenance.json +17 -17
- package/standard-skills/blun-app-design-system/references/tokens.md +48 -48
- package/standard-skills/blun-app-design-system/scripts/check-blun-design.mjs +49 -49
- package/telegram-plugin/blun.plugin.json +20 -20
- package/telegram-plugin/commands/access.md +37 -37
- package/telegram-plugin/dist/bridge.mjs +81 -81
- package/telegram-plugin/dist/mcp-server.mjs +158 -158
- package/worker-host.mjs +932 -932
- package/dist-web/assets/DesignSystemView-6HgAUEmC.css +0 -1
- package/dist-web/assets/DesignSystemView-BbMOu-XO.js +0 -12
- package/dist-web/assets/channel-B2VOQOI-.js +0 -1
- package/dist-web/assets/classDiagram-JCYQIIEL-BA1TjAxj.js +0 -1
- package/dist-web/assets/classDiagram-v2-OCEON4UE-BA1TjAxj.js +0 -1
- package/dist-web/assets/stateDiagram-v2-UEYNNEHI-fi_lrTAx.js +0 -1
- package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-BUd_uYK3.js +0 -8
|
@@ -1,272 +1,272 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { randomUUID } = require('node:crypto');
|
|
4
|
-
const fs = require('node:fs');
|
|
5
|
-
const path = require('node:path');
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
ensurePrivateDirectory,
|
|
9
|
-
securePrivateFile,
|
|
10
|
-
securePrivateTree,
|
|
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 readCatalogue(packageRoot) {
|
|
19
|
-
const manifestPath = path.join(packageRoot, 'standard-tools', 'manifest.json');
|
|
20
|
-
const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
21
|
-
if (!isObject(parsed) || parsed.version !== 1 || !isObject(parsed.tools)) {
|
|
22
|
-
throw new Error(`Ungueltiger Standard-Tools-Katalog: ${manifestPath}`);
|
|
23
|
-
}
|
|
24
|
-
for (const [name, tool] of Object.entries(parsed.tools)) {
|
|
25
|
-
if (!isObject(tool) || !Array.isArray(tool.detectTokens)
|
|
26
|
-
|| !tool.detectTokens.every((token) => typeof token === 'string' && token.length > 0)) {
|
|
27
|
-
throw new Error(`Ungueltiger Standard-Tool-Eintrag: ${name}`);
|
|
28
|
-
}
|
|
29
|
-
if (tool.platforms !== undefined
|
|
30
|
-
&& (!Array.isArray(tool.platforms)
|
|
31
|
-
|| !tool.platforms.every((platform) => platform === 'darwin' || platform === 'win32'))) {
|
|
32
|
-
throw new Error(`Ungueltige Standard-Tool-Plattform: ${name}`);
|
|
33
|
-
}
|
|
34
|
-
if (tool.owner === 'user-mcp'
|
|
35
|
-
&& (!isObject(tool.config) || typeof tool.config.enabled !== 'boolean')) {
|
|
36
|
-
throw new Error(`Standard-Tool braucht einen Aktivierungszustand: ${name}`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return parsed;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function readMcpDocument(configPath) {
|
|
43
|
-
if (!fs.existsSync(configPath)) return { mcpServers: {} };
|
|
44
|
-
let parsed;
|
|
45
|
-
try {
|
|
46
|
-
parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
47
|
-
} catch (error) {
|
|
48
|
-
throw new Error(`Ungueltige MCP-Konfiguration (JSON): ${configPath}`, { cause: error });
|
|
49
|
-
}
|
|
50
|
-
if (!isObject(parsed) || (parsed.mcpServers !== undefined && !isObject(parsed.mcpServers))) {
|
|
51
|
-
throw new Error(`Ungueltige MCP-Konfiguration: ${configPath}`);
|
|
52
|
-
}
|
|
53
|
-
return parsed.mcpServers === undefined ? { ...parsed, mcpServers: {} } : parsed;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function serverSearchText(name, config) {
|
|
57
|
-
const values = [name];
|
|
58
|
-
if (isObject(config)) {
|
|
59
|
-
if (typeof config.command === 'string') values.push(config.command);
|
|
60
|
-
if (Array.isArray(config.args)) {
|
|
61
|
-
values.push(...config.args.filter((value) => typeof value === 'string'));
|
|
62
|
-
}
|
|
63
|
-
if (typeof config.url === 'string') values.push(config.url);
|
|
64
|
-
}
|
|
65
|
-
return values.join('\n').toLowerCase();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function hasEquivalentServer(servers, name, tool) {
|
|
69
|
-
if (Object.hasOwn(servers, name)) return true;
|
|
70
|
-
const tokens = tool.detectTokens.map((token) => token.toLowerCase());
|
|
71
|
-
return Object.entries(servers).some(([serverName, config]) => {
|
|
72
|
-
const searchText = serverSearchText(serverName, config);
|
|
73
|
-
return tokens.some((token) => searchText.includes(token));
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function sameStringArray(left, right) {
|
|
78
|
-
return Array.isArray(left)
|
|
79
|
-
&& Array.isArray(right)
|
|
80
|
-
&& left.length === right.length
|
|
81
|
-
&& left.every((value, index) => value === right[index]);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function recognizedBundledServer(name, current, expected) {
|
|
85
|
-
if (!isObject(current)) return false;
|
|
86
|
-
const exact = current.transport === expected.transport
|
|
87
|
-
&& current.command === expected.command
|
|
88
|
-
&& sameStringArray(current.args, expected.args);
|
|
89
|
-
if (exact) return true;
|
|
90
|
-
return name === 'context7'
|
|
91
|
-
&& current.transport === 'stdio'
|
|
92
|
-
&& current.command === 'npx'
|
|
93
|
-
&& Array.isArray(current.args)
|
|
94
|
-
&& current.args[0] === '-y'
|
|
95
|
-
&& /^@upstash\/context7-mcp(?:@[^\s]+)?$/u.test(current.args[1] ?? '');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function migrateBundledTool(name, current, expected) {
|
|
99
|
-
if (!recognizedBundledServer(name, current, expected)) return false;
|
|
100
|
-
let changed = false;
|
|
101
|
-
if (current.enabled === false && name !== 'blun-language-guard') {
|
|
102
|
-
current.enabled = true;
|
|
103
|
-
changed = true;
|
|
104
|
-
}
|
|
105
|
-
if (name === 'agent-browser') {
|
|
106
|
-
if (current.startupTimeoutMs === 30_000) {
|
|
107
|
-
current.startupTimeoutMs = expected.startupTimeoutMs;
|
|
108
|
-
changed = true;
|
|
109
|
-
}
|
|
110
|
-
if (current.toolTimeoutMs === 60_000) {
|
|
111
|
-
current.toolTimeoutMs = expected.toolTimeoutMs;
|
|
112
|
-
changed = true;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (name === 'blun-language-guard'
|
|
116
|
-
&& (current.toolTimeoutMs === 30_000 || current.toolTimeoutMs === 60_000)) {
|
|
117
|
-
current.toolTimeoutMs = expected.toolTimeoutMs;
|
|
118
|
-
changed = true;
|
|
119
|
-
}
|
|
120
|
-
return changed;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function writeMcpDocument(configPath, document) {
|
|
124
|
-
const temporaryPath = path.join(
|
|
125
|
-
path.dirname(configPath),
|
|
126
|
-
`.${path.basename(configPath)}.${process.pid}.${randomUUID()}.tmp`,
|
|
127
|
-
);
|
|
128
|
-
try {
|
|
129
|
-
writePrivateFile(temporaryPath, `${JSON.stringify(document, null, 2)}\n`);
|
|
130
|
-
fs.renameSync(temporaryPath, configPath);
|
|
131
|
-
} catch (error) {
|
|
132
|
-
fs.rmSync(temporaryPath, { force: true });
|
|
133
|
-
throw error;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function bundledToolConfig(name, tool, { packageRoot, blunDir, platform, env }) {
|
|
138
|
-
const config = structuredClone(tool.config);
|
|
139
|
-
config.enabled = true;
|
|
140
|
-
if (name === 'blun-language-guard') {
|
|
141
|
-
config.command = platform === 'win32' ? 'python' : 'python3';
|
|
142
|
-
config.args = [
|
|
143
|
-
path.join(
|
|
144
|
-
packageRoot,
|
|
145
|
-
'standard-tools',
|
|
146
|
-
'language-guard',
|
|
147
|
-
'blun_language_guard.py',
|
|
148
|
-
),
|
|
149
|
-
'serve',
|
|
150
|
-
];
|
|
151
|
-
config.env = {
|
|
152
|
-
...(isObject(config.env) ? config.env : {}),
|
|
153
|
-
PYTHONDONTWRITEBYTECODE: '1',
|
|
154
|
-
PYTHONUTF8: '1',
|
|
155
|
-
BLUN_LANGUAGE_GUARD_KEY_FILE: path.join(
|
|
156
|
-
blunDir,
|
|
157
|
-
'secrets',
|
|
158
|
-
'blun-language-guard-signing.key',
|
|
159
|
-
),
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
if (name === 'windows-mcp' && platform === 'win32') {
|
|
163
|
-
const localUvx = typeof env.USERPROFILE === 'string'
|
|
164
|
-
? path.join(env.USERPROFILE, '.local', 'bin', 'uvx.exe')
|
|
165
|
-
: undefined;
|
|
166
|
-
config.command = localUvx !== undefined && fs.existsSync(localUvx) ? localUvx : 'uvx';
|
|
167
|
-
}
|
|
168
|
-
return config;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function availableStandardToolNames({
|
|
172
|
-
packageRoot,
|
|
173
|
-
platform = process.platform,
|
|
174
|
-
}) {
|
|
175
|
-
const catalogue = readCatalogue(packageRoot);
|
|
176
|
-
return Object.entries(catalogue.tools)
|
|
177
|
-
.filter(([, tool]) => tool.owner === 'user-mcp' || tool.owner === 'managed-plugin')
|
|
178
|
-
.filter(([, tool]) => !Array.isArray(tool.platforms) || tool.platforms.includes(platform))
|
|
179
|
-
.map(([name]) => name);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function seedStandardTools({
|
|
183
|
-
packageRoot,
|
|
184
|
-
blunDir,
|
|
185
|
-
names,
|
|
186
|
-
platform = process.platform,
|
|
187
|
-
env = process.env,
|
|
188
|
-
}) {
|
|
189
|
-
const catalogue = readCatalogue(packageRoot);
|
|
190
|
-
if (!Array.isArray(names) || names.length === 0) {
|
|
191
|
-
throw new Error('Mindestens ein Standard-Tool muss ausdruecklich ausgewaehlt werden.');
|
|
192
|
-
}
|
|
193
|
-
const selectedNames = [...new Set(names)];
|
|
194
|
-
for (const name of selectedNames) {
|
|
195
|
-
const tool = catalogue.tools[name];
|
|
196
|
-
if (!isObject(tool) || tool.owner !== 'user-mcp') {
|
|
197
|
-
throw new Error(`Unbekanntes Standard-Tool: ${name}`);
|
|
198
|
-
}
|
|
199
|
-
if (Array.isArray(tool.platforms) && !tool.platforms.includes(platform)) {
|
|
200
|
-
throw new Error(`Standard-Tool ist auf dieser Plattform nicht verfuegbar: ${name}`);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
const configPath = path.join(blunDir, 'mcp.json');
|
|
204
|
-
ensurePrivateDirectory(blunDir);
|
|
205
|
-
if (fs.existsSync(configPath)) securePrivateFile(configPath);
|
|
206
|
-
const document = readMcpDocument(configPath);
|
|
207
|
-
const servers = { ...document.mcpServers };
|
|
208
|
-
const added = [];
|
|
209
|
-
const updated = [];
|
|
210
|
-
|
|
211
|
-
for (const name of selectedNames) {
|
|
212
|
-
const tool = catalogue.tools[name];
|
|
213
|
-
const expected = bundledToolConfig(name, tool, {
|
|
214
|
-
packageRoot,
|
|
215
|
-
blunDir,
|
|
216
|
-
platform,
|
|
217
|
-
env,
|
|
218
|
-
});
|
|
219
|
-
if (Object.hasOwn(servers, name)) {
|
|
220
|
-
if (migrateBundledTool(name, servers[name], expected)) updated.push(name);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
if (hasEquivalentServer(servers, name, tool)) continue;
|
|
224
|
-
servers[name] = expected;
|
|
225
|
-
added.push(name);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (added.length > 0 || updated.length > 0) {
|
|
229
|
-
writeMcpDocument(configPath, { ...document, mcpServers: servers });
|
|
230
|
-
}
|
|
231
|
-
return { added, updated, configPath };
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function seedStandardDesignSkill({ packageRoot, blunDir }) {
|
|
235
|
-
const sourcePath = path.join(
|
|
236
|
-
packageRoot,
|
|
237
|
-
'standard-skills',
|
|
238
|
-
'blun-app-design-system',
|
|
239
|
-
);
|
|
240
|
-
const sourceStat = fs.lstatSync(sourcePath);
|
|
241
|
-
if (!sourceStat.isDirectory() || sourceStat.isSymbolicLink()) {
|
|
242
|
-
throw new Error(`Ungueltiger BLUN-Standard-Skill: ${sourcePath}`);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const skillsDir = path.join(blunDir, 'skills');
|
|
246
|
-
ensurePrivateDirectory(skillsDir);
|
|
247
|
-
const destinationPath = path.join(skillsDir, 'blun-app-design-system');
|
|
248
|
-
const installed = !fs.existsSync(destinationPath);
|
|
249
|
-
if (!installed) {
|
|
250
|
-
const destinationStat = fs.lstatSync(destinationPath);
|
|
251
|
-
if (!destinationStat.isDirectory() || destinationStat.isSymbolicLink()) {
|
|
252
|
-
throw new Error(`Ungueltiges Ziel fuer BLUN-Standard-Skill: ${destinationPath}`);
|
|
253
|
-
}
|
|
254
|
-
} else {
|
|
255
|
-
ensurePrivateDirectory(destinationPath);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
fs.cpSync(sourcePath, destinationPath, {
|
|
259
|
-
recursive: true,
|
|
260
|
-
force: true,
|
|
261
|
-
errorOnExist: false,
|
|
262
|
-
});
|
|
263
|
-
securePrivateTree(destinationPath);
|
|
264
|
-
return { installed, updated: !installed, destinationPath };
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
module.exports = {
|
|
268
|
-
availableStandardToolNames,
|
|
269
|
-
readCatalogue,
|
|
270
|
-
seedStandardDesignSkill,
|
|
271
|
-
seedStandardTools,
|
|
272
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { randomUUID } = require('node:crypto');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
ensurePrivateDirectory,
|
|
9
|
+
securePrivateFile,
|
|
10
|
+
securePrivateTree,
|
|
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 readCatalogue(packageRoot) {
|
|
19
|
+
const manifestPath = path.join(packageRoot, 'standard-tools', 'manifest.json');
|
|
20
|
+
const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
21
|
+
if (!isObject(parsed) || parsed.version !== 1 || !isObject(parsed.tools)) {
|
|
22
|
+
throw new Error(`Ungueltiger Standard-Tools-Katalog: ${manifestPath}`);
|
|
23
|
+
}
|
|
24
|
+
for (const [name, tool] of Object.entries(parsed.tools)) {
|
|
25
|
+
if (!isObject(tool) || !Array.isArray(tool.detectTokens)
|
|
26
|
+
|| !tool.detectTokens.every((token) => typeof token === 'string' && token.length > 0)) {
|
|
27
|
+
throw new Error(`Ungueltiger Standard-Tool-Eintrag: ${name}`);
|
|
28
|
+
}
|
|
29
|
+
if (tool.platforms !== undefined
|
|
30
|
+
&& (!Array.isArray(tool.platforms)
|
|
31
|
+
|| !tool.platforms.every((platform) => platform === 'darwin' || platform === 'win32'))) {
|
|
32
|
+
throw new Error(`Ungueltige Standard-Tool-Plattform: ${name}`);
|
|
33
|
+
}
|
|
34
|
+
if (tool.owner === 'user-mcp'
|
|
35
|
+
&& (!isObject(tool.config) || typeof tool.config.enabled !== 'boolean')) {
|
|
36
|
+
throw new Error(`Standard-Tool braucht einen Aktivierungszustand: ${name}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readMcpDocument(configPath) {
|
|
43
|
+
if (!fs.existsSync(configPath)) return { mcpServers: {} };
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error(`Ungueltige MCP-Konfiguration (JSON): ${configPath}`, { cause: error });
|
|
49
|
+
}
|
|
50
|
+
if (!isObject(parsed) || (parsed.mcpServers !== undefined && !isObject(parsed.mcpServers))) {
|
|
51
|
+
throw new Error(`Ungueltige MCP-Konfiguration: ${configPath}`);
|
|
52
|
+
}
|
|
53
|
+
return parsed.mcpServers === undefined ? { ...parsed, mcpServers: {} } : parsed;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function serverSearchText(name, config) {
|
|
57
|
+
const values = [name];
|
|
58
|
+
if (isObject(config)) {
|
|
59
|
+
if (typeof config.command === 'string') values.push(config.command);
|
|
60
|
+
if (Array.isArray(config.args)) {
|
|
61
|
+
values.push(...config.args.filter((value) => typeof value === 'string'));
|
|
62
|
+
}
|
|
63
|
+
if (typeof config.url === 'string') values.push(config.url);
|
|
64
|
+
}
|
|
65
|
+
return values.join('\n').toLowerCase();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function hasEquivalentServer(servers, name, tool) {
|
|
69
|
+
if (Object.hasOwn(servers, name)) return true;
|
|
70
|
+
const tokens = tool.detectTokens.map((token) => token.toLowerCase());
|
|
71
|
+
return Object.entries(servers).some(([serverName, config]) => {
|
|
72
|
+
const searchText = serverSearchText(serverName, config);
|
|
73
|
+
return tokens.some((token) => searchText.includes(token));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function sameStringArray(left, right) {
|
|
78
|
+
return Array.isArray(left)
|
|
79
|
+
&& Array.isArray(right)
|
|
80
|
+
&& left.length === right.length
|
|
81
|
+
&& left.every((value, index) => value === right[index]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function recognizedBundledServer(name, current, expected) {
|
|
85
|
+
if (!isObject(current)) return false;
|
|
86
|
+
const exact = current.transport === expected.transport
|
|
87
|
+
&& current.command === expected.command
|
|
88
|
+
&& sameStringArray(current.args, expected.args);
|
|
89
|
+
if (exact) return true;
|
|
90
|
+
return name === 'context7'
|
|
91
|
+
&& current.transport === 'stdio'
|
|
92
|
+
&& current.command === 'npx'
|
|
93
|
+
&& Array.isArray(current.args)
|
|
94
|
+
&& current.args[0] === '-y'
|
|
95
|
+
&& /^@upstash\/context7-mcp(?:@[^\s]+)?$/u.test(current.args[1] ?? '');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function migrateBundledTool(name, current, expected) {
|
|
99
|
+
if (!recognizedBundledServer(name, current, expected)) return false;
|
|
100
|
+
let changed = false;
|
|
101
|
+
if (current.enabled === false && name !== 'blun-language-guard') {
|
|
102
|
+
current.enabled = true;
|
|
103
|
+
changed = true;
|
|
104
|
+
}
|
|
105
|
+
if (name === 'agent-browser') {
|
|
106
|
+
if (current.startupTimeoutMs === 30_000) {
|
|
107
|
+
current.startupTimeoutMs = expected.startupTimeoutMs;
|
|
108
|
+
changed = true;
|
|
109
|
+
}
|
|
110
|
+
if (current.toolTimeoutMs === 60_000) {
|
|
111
|
+
current.toolTimeoutMs = expected.toolTimeoutMs;
|
|
112
|
+
changed = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (name === 'blun-language-guard'
|
|
116
|
+
&& (current.toolTimeoutMs === 30_000 || current.toolTimeoutMs === 60_000)) {
|
|
117
|
+
current.toolTimeoutMs = expected.toolTimeoutMs;
|
|
118
|
+
changed = true;
|
|
119
|
+
}
|
|
120
|
+
return changed;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function writeMcpDocument(configPath, document) {
|
|
124
|
+
const temporaryPath = path.join(
|
|
125
|
+
path.dirname(configPath),
|
|
126
|
+
`.${path.basename(configPath)}.${process.pid}.${randomUUID()}.tmp`,
|
|
127
|
+
);
|
|
128
|
+
try {
|
|
129
|
+
writePrivateFile(temporaryPath, `${JSON.stringify(document, null, 2)}\n`);
|
|
130
|
+
fs.renameSync(temporaryPath, configPath);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
fs.rmSync(temporaryPath, { force: true });
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function bundledToolConfig(name, tool, { packageRoot, blunDir, platform, env }) {
|
|
138
|
+
const config = structuredClone(tool.config);
|
|
139
|
+
config.enabled = true;
|
|
140
|
+
if (name === 'blun-language-guard') {
|
|
141
|
+
config.command = platform === 'win32' ? 'python' : 'python3';
|
|
142
|
+
config.args = [
|
|
143
|
+
path.join(
|
|
144
|
+
packageRoot,
|
|
145
|
+
'standard-tools',
|
|
146
|
+
'language-guard',
|
|
147
|
+
'blun_language_guard.py',
|
|
148
|
+
),
|
|
149
|
+
'serve',
|
|
150
|
+
];
|
|
151
|
+
config.env = {
|
|
152
|
+
...(isObject(config.env) ? config.env : {}),
|
|
153
|
+
PYTHONDONTWRITEBYTECODE: '1',
|
|
154
|
+
PYTHONUTF8: '1',
|
|
155
|
+
BLUN_LANGUAGE_GUARD_KEY_FILE: path.join(
|
|
156
|
+
blunDir,
|
|
157
|
+
'secrets',
|
|
158
|
+
'blun-language-guard-signing.key',
|
|
159
|
+
),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (name === 'windows-mcp' && platform === 'win32') {
|
|
163
|
+
const localUvx = typeof env.USERPROFILE === 'string'
|
|
164
|
+
? path.join(env.USERPROFILE, '.local', 'bin', 'uvx.exe')
|
|
165
|
+
: undefined;
|
|
166
|
+
config.command = localUvx !== undefined && fs.existsSync(localUvx) ? localUvx : 'uvx';
|
|
167
|
+
}
|
|
168
|
+
return config;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function availableStandardToolNames({
|
|
172
|
+
packageRoot,
|
|
173
|
+
platform = process.platform,
|
|
174
|
+
}) {
|
|
175
|
+
const catalogue = readCatalogue(packageRoot);
|
|
176
|
+
return Object.entries(catalogue.tools)
|
|
177
|
+
.filter(([, tool]) => tool.owner === 'user-mcp' || tool.owner === 'managed-plugin')
|
|
178
|
+
.filter(([, tool]) => !Array.isArray(tool.platforms) || tool.platforms.includes(platform))
|
|
179
|
+
.map(([name]) => name);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function seedStandardTools({
|
|
183
|
+
packageRoot,
|
|
184
|
+
blunDir,
|
|
185
|
+
names,
|
|
186
|
+
platform = process.platform,
|
|
187
|
+
env = process.env,
|
|
188
|
+
}) {
|
|
189
|
+
const catalogue = readCatalogue(packageRoot);
|
|
190
|
+
if (!Array.isArray(names) || names.length === 0) {
|
|
191
|
+
throw new Error('Mindestens ein Standard-Tool muss ausdruecklich ausgewaehlt werden.');
|
|
192
|
+
}
|
|
193
|
+
const selectedNames = [...new Set(names)];
|
|
194
|
+
for (const name of selectedNames) {
|
|
195
|
+
const tool = catalogue.tools[name];
|
|
196
|
+
if (!isObject(tool) || tool.owner !== 'user-mcp') {
|
|
197
|
+
throw new Error(`Unbekanntes Standard-Tool: ${name}`);
|
|
198
|
+
}
|
|
199
|
+
if (Array.isArray(tool.platforms) && !tool.platforms.includes(platform)) {
|
|
200
|
+
throw new Error(`Standard-Tool ist auf dieser Plattform nicht verfuegbar: ${name}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const configPath = path.join(blunDir, 'mcp.json');
|
|
204
|
+
ensurePrivateDirectory(blunDir);
|
|
205
|
+
if (fs.existsSync(configPath)) securePrivateFile(configPath);
|
|
206
|
+
const document = readMcpDocument(configPath);
|
|
207
|
+
const servers = { ...document.mcpServers };
|
|
208
|
+
const added = [];
|
|
209
|
+
const updated = [];
|
|
210
|
+
|
|
211
|
+
for (const name of selectedNames) {
|
|
212
|
+
const tool = catalogue.tools[name];
|
|
213
|
+
const expected = bundledToolConfig(name, tool, {
|
|
214
|
+
packageRoot,
|
|
215
|
+
blunDir,
|
|
216
|
+
platform,
|
|
217
|
+
env,
|
|
218
|
+
});
|
|
219
|
+
if (Object.hasOwn(servers, name)) {
|
|
220
|
+
if (migrateBundledTool(name, servers[name], expected)) updated.push(name);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (hasEquivalentServer(servers, name, tool)) continue;
|
|
224
|
+
servers[name] = expected;
|
|
225
|
+
added.push(name);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (added.length > 0 || updated.length > 0) {
|
|
229
|
+
writeMcpDocument(configPath, { ...document, mcpServers: servers });
|
|
230
|
+
}
|
|
231
|
+
return { added, updated, configPath };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function seedStandardDesignSkill({ packageRoot, blunDir }) {
|
|
235
|
+
const sourcePath = path.join(
|
|
236
|
+
packageRoot,
|
|
237
|
+
'standard-skills',
|
|
238
|
+
'blun-app-design-system',
|
|
239
|
+
);
|
|
240
|
+
const sourceStat = fs.lstatSync(sourcePath);
|
|
241
|
+
if (!sourceStat.isDirectory() || sourceStat.isSymbolicLink()) {
|
|
242
|
+
throw new Error(`Ungueltiger BLUN-Standard-Skill: ${sourcePath}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const skillsDir = path.join(blunDir, 'skills');
|
|
246
|
+
ensurePrivateDirectory(skillsDir);
|
|
247
|
+
const destinationPath = path.join(skillsDir, 'blun-app-design-system');
|
|
248
|
+
const installed = !fs.existsSync(destinationPath);
|
|
249
|
+
if (!installed) {
|
|
250
|
+
const destinationStat = fs.lstatSync(destinationPath);
|
|
251
|
+
if (!destinationStat.isDirectory() || destinationStat.isSymbolicLink()) {
|
|
252
|
+
throw new Error(`Ungueltiges Ziel fuer BLUN-Standard-Skill: ${destinationPath}`);
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
ensurePrivateDirectory(destinationPath);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
fs.cpSync(sourcePath, destinationPath, {
|
|
259
|
+
recursive: true,
|
|
260
|
+
force: true,
|
|
261
|
+
errorOnExist: false,
|
|
262
|
+
});
|
|
263
|
+
securePrivateTree(destinationPath);
|
|
264
|
+
return { installed, updated: !installed, destinationPath };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
module.exports = {
|
|
268
|
+
availableStandardToolNames,
|
|
269
|
+
readCatalogue,
|
|
270
|
+
seedStandardDesignSkill,
|
|
271
|
+
seedStandardTools,
|
|
272
|
+
};
|