@mebular/mcp 0.0.0-stage → 0.1.0
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/bin/mebular.mjs +1133 -0
- package/package.json +30 -4
- package/scripts/publish-manifest.mjs +44 -0
- package/src/admin.mjs +1320 -0
- package/src/apply-supervisor.mjs +48 -0
- package/src/build-sha.mjs +36 -0
- package/src/client.mjs +49 -0
- package/src/clients.mjs +1242 -0
- package/src/cloud-switch.mjs +258 -0
- package/src/config-apply.mjs +251 -0
- package/src/config-schema.mjs +690 -0
- package/src/config.mjs +265 -0
- package/src/fleet-events.mjs +207 -0
- package/src/home.mjs +16 -0
- package/src/jointoken.mjs +31 -0
- package/src/lan-host.mjs +36 -0
- package/src/profiles.mjs +681 -0
- package/src/provision.mjs +673 -0
- package/src/qr.mjs +4 -0
- package/src/runtime-file.mjs +91 -0
- package/src/serve.mjs +3478 -0
- package/src/server.mjs +416 -0
- package/src/service-control.mjs +107 -0
- package/src/service-facade.mjs +43 -0
- package/src/task-tools.mjs +45 -0
- package/src/tool-envelope.mjs +50 -0
- package/src/tools.mjs +397 -0
- package/README.md +0 -3
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// G:应用监督进程(分离启动;守护被重启/终止后仍能完成「健康校验 → 回滚兜底」)。
|
|
3
|
+
//
|
|
4
|
+
// 由 serve 在保存需重启配置时分离启动:
|
|
5
|
+
// node apply-supervisor.mjs --home <home> --config <config.json> --old-pid <pid> [--port N] [--fields a,b] [--timeout ms]
|
|
6
|
+
//
|
|
7
|
+
// 退出码:0 = 已生效或已回滚(结果写 <home>/config-apply.result.json);2 = 监督失败。
|
|
8
|
+
|
|
9
|
+
import { runApplySupervisor } from './config-apply.mjs';
|
|
10
|
+
import { resolveBuildSha } from '../../service/dist/index.js';
|
|
11
|
+
|
|
12
|
+
function parseArgs(argv) {
|
|
13
|
+
const out = {};
|
|
14
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
15
|
+
const token = argv[i];
|
|
16
|
+
if (!token.startsWith('--')) continue;
|
|
17
|
+
const key = token.slice(2);
|
|
18
|
+
const next = argv[i + 1];
|
|
19
|
+
if (next === undefined || next.startsWith('--')) out[key] = true;
|
|
20
|
+
else { out[key] = next; i += 1; }
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const flags = parseArgs(process.argv.slice(2));
|
|
26
|
+
const home = typeof flags.home === 'string' ? flags.home : null;
|
|
27
|
+
if (!home) {
|
|
28
|
+
console.error('apply-supervisor: 缺少 --home');
|
|
29
|
+
process.exit(2);
|
|
30
|
+
}
|
|
31
|
+
const configFile = typeof flags.config === 'string' ? flags.config : `${home}/config.json`;
|
|
32
|
+
const oldPid = Number(flags['old-pid'] ?? '0') || 0;
|
|
33
|
+
const port = Number(flags.port ?? '0') || 0;
|
|
34
|
+
const fields = typeof flags.fields === 'string' && flags.fields.length > 0 ? flags.fields.split(',').filter(Boolean) : [];
|
|
35
|
+
const timeoutMs = Number(flags.timeout ?? '0') > 0 ? Number(flags.timeout) : undefined;
|
|
36
|
+
|
|
37
|
+
const result = await runApplySupervisor({
|
|
38
|
+
home,
|
|
39
|
+
configFile,
|
|
40
|
+
oldPid,
|
|
41
|
+
port,
|
|
42
|
+
fields,
|
|
43
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
44
|
+
log: (message) => console.error(`[apply ${new Date().toISOString()}] ${message}`),
|
|
45
|
+
}).catch((error) => ({ status: 'supervisor-error', reason: String(error?.message ?? error) }));
|
|
46
|
+
|
|
47
|
+
console.error(`[apply] 结果:${JSON.stringify(result)} sha=${resolveBuildSha()}`);
|
|
48
|
+
process.exit(result.status === 'supervisor-error' ? 2 : 0);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// 构建 SHA 解析(F-INSTALL-1:**与 cwd 解耦**)。
|
|
2
|
+
//
|
|
3
|
+
// 病灶:旧 `resolveBuildSha()` 默认 `git rev-parse HEAD` 于 `process.cwd()`——全局安装的
|
|
4
|
+
// `mebular service install` 在仓库目录外执行时 sha=unknown,在仓库内执行才是真实 sha。
|
|
5
|
+
// 本模块是 ESM 边界(.mjs,不经 ts-jest),可安全用 import.meta 定位**安装自身**:
|
|
6
|
+
//
|
|
7
|
+
// 1. env 覆盖(MEBULAR_BUILD_SHA / MEBULAR_SERVICE_SHA);
|
|
8
|
+
// 2. 模块邻接构建元数据 `packages/service/dist/version.json`(构建期由
|
|
9
|
+
// scripts/gen-fleet-version.mjs 写入;npm 全局安装 `@mebular/core` 后依然在包内);
|
|
10
|
+
// 3. 模块所在仓库的 git HEAD(仓库内直跑兜底;全局安装不是 git 仓库 → 跳过);
|
|
11
|
+
// 4. `unknown`。
|
|
12
|
+
//
|
|
13
|
+
// 三种来源均与调用者的 cwd 无关。
|
|
14
|
+
|
|
15
|
+
import { execFileSync } from 'node:child_process';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
|
|
19
|
+
export function serviceBuildSha(env = process.env) {
|
|
20
|
+
const fromEnv = env.MEBULAR_BUILD_SHA ?? env.MEBULAR_SERVICE_SHA;
|
|
21
|
+
if (fromEnv) return fromEnv;
|
|
22
|
+
// packages/mcp/src/build-sha.mjs → packages/service/dist/version.json(同一 @mebular/core 包内)
|
|
23
|
+
try {
|
|
24
|
+
const meta = JSON.parse(readFileSync(new URL('../../service/dist/version.json', import.meta.url), 'utf-8'));
|
|
25
|
+
if (typeof meta.sha === 'string' && meta.sha.length > 0 && meta.sha !== 'unknown') return meta.sha;
|
|
26
|
+
} catch {
|
|
27
|
+
// 无构建元数据(未构建)→ 继续
|
|
28
|
+
}
|
|
29
|
+
// packages/mcp/src/build-sha.mjs → 包根(仓库检出时为 git 仓库根)
|
|
30
|
+
try {
|
|
31
|
+
const pkgRoot = fileURLToPath(new URL('../../..', import.meta.url));
|
|
32
|
+
return execFileSync('git', ['rev-parse', 'HEAD'], { cwd: pkgRoot, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
33
|
+
} catch {
|
|
34
|
+
return 'unknown';
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/client.mjs
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// 最小 MCP Streamable HTTP 客户端(CLI 在**守护持锁**时作为本机 MCP 客户端调用 `/mcp`)。
|
|
2
|
+
//
|
|
3
|
+
// 仅覆盖 CLI 所需:initialize → notifications/initialized → tools/call。
|
|
4
|
+
// 复用既有 bearer token 体系(`mebular token`)。
|
|
5
|
+
|
|
6
|
+
async function rpc(endpoint, token, message, sessionId, timeoutMs) {
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
9
|
+
try {
|
|
10
|
+
const headers = {
|
|
11
|
+
'content-type': 'application/json',
|
|
12
|
+
accept: 'application/json, text/event-stream',
|
|
13
|
+
};
|
|
14
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
15
|
+
if (sessionId) headers['mcp-session-id'] = sessionId;
|
|
16
|
+
const res = await fetch(endpoint, { method: 'POST', headers, body: JSON.stringify(message), signal: controller.signal });
|
|
17
|
+
const sid = res.headers.get('mcp-session-id') ?? sessionId ?? null;
|
|
18
|
+
const text = await res.text();
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
throw new Error(`MCP HTTP ${res.status}:${text.slice(0, 200)}`);
|
|
21
|
+
}
|
|
22
|
+
const ct = res.headers.get('content-type') ?? '';
|
|
23
|
+
if (ct.includes('text/event-stream')) {
|
|
24
|
+
const dataLines = text.split('\n').filter((l) => l.startsWith('data:')).map((l) => l.slice(5).trim());
|
|
25
|
+
const last = dataLines[dataLines.length - 1];
|
|
26
|
+
return { sessionId: sid, json: last ? JSON.parse(last) : null };
|
|
27
|
+
}
|
|
28
|
+
return { sessionId: sid, json: text.trim().length > 0 ? JSON.parse(text) : null };
|
|
29
|
+
} finally {
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 调用守护 MCP 的某个工具;返回 MCP result({content, structuredContent})。 */
|
|
35
|
+
export async function callToolViaHttp({ endpoint, token, name, args, timeoutMs = 30000 }) {
|
|
36
|
+
if (typeof endpoint !== 'string' || endpoint.length === 0) throw new Error('缺少守护 MCP 端点');
|
|
37
|
+
const url = `${endpoint.replace(/\/+$/, '')}/mcp`;
|
|
38
|
+
const init = await rpc(url, token, {
|
|
39
|
+
jsonrpc: '2.0',
|
|
40
|
+
id: 1,
|
|
41
|
+
method: 'initialize',
|
|
42
|
+
params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'mebular-cli', version: '0.1.0' } },
|
|
43
|
+
}, null, timeoutMs);
|
|
44
|
+
const sessionId = init.sessionId;
|
|
45
|
+
await rpc(url, token, { jsonrpc: '2.0', method: 'notifications/initialized' }, sessionId, timeoutMs);
|
|
46
|
+
const res = await rpc(url, token, { jsonrpc: '2.0', id: 2, method: 'tools/call', params: { name, arguments: args } }, sessionId, timeoutMs);
|
|
47
|
+
if (res.json?.error) throw new Error(`MCP 错误:${res.json.error.message ?? JSON.stringify(res.json.error)}`);
|
|
48
|
+
return res.json?.result ?? null;
|
|
49
|
+
}
|