metheus-governance-mcp-cli 0.2.11 → 0.2.12
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/cli.mjs +178 -5
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -14,12 +14,17 @@ const DEFAULT_SITE_URL = "https://metheus.gesiaplatform.com";
|
|
|
14
14
|
const DEFAULT_BASE_URL = `${DEFAULT_SITE_URL}/governance/mcp`;
|
|
15
15
|
const DEFAULT_SERVER_NAME = "metheus-governance-mcp";
|
|
16
16
|
const AUTH_STORE_RELATIVE_PATH = path.join(".metheus", "governance-mcp-auth.json");
|
|
17
|
+
const SELF_UPDATE_STATE_RELATIVE_PATH = path.join(".metheus", "governance-mcp-cli-update.json");
|
|
17
18
|
const CTXPACK_CACHE_RELATIVE_DIR = path.join(".metheus", "ctxpack-cache");
|
|
18
19
|
const CTXPACK_META_FILENAME = ".metheus_ctxpack_sync.json";
|
|
19
20
|
const CTXPACK_PUSH_TOOL_NAMES = ["ctxpack.push", "ctxpack.update", "ctxpack.save"];
|
|
20
21
|
const CLI_META = loadCLIMeta();
|
|
21
22
|
const CLI_NAME = CLI_META.name || "metheus-governance-mcp-cli";
|
|
22
23
|
const CLI_VERSION = CLI_META.version || "0.0.0";
|
|
24
|
+
const SELF_UPDATE_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
25
|
+
const SELF_UPDATE_TIMEOUT_SECONDS = 6;
|
|
26
|
+
const SELF_UPDATE_ENV_KEY = "METHEUS_CLI_AUTO_UPDATE";
|
|
27
|
+
const SELF_UPDATE_GUARD_ENV_KEY = "METHEUS_CLI_SELF_UPDATE_ATTEMPTED";
|
|
23
28
|
const AUTO_CTXPACK_SYNC_INTERVAL_MS = 60 * 1000;
|
|
24
29
|
const autoCtxpackSyncTracker = new Map();
|
|
25
30
|
|
|
@@ -44,6 +49,7 @@ function printUsage() {
|
|
|
44
49
|
"",
|
|
45
50
|
"Environment:",
|
|
46
51
|
" METHEUS_TOKEN or MCP_AUTH_TOKEN is used first for proxy requests.",
|
|
52
|
+
` ${SELF_UPDATE_ENV_KEY}=0 to disable startup auto-update check.`,
|
|
47
53
|
" If env is missing, stored token file is used:",
|
|
48
54
|
` ${AUTH_STORE_RELATIVE_PATH}`,
|
|
49
55
|
"",
|
|
@@ -69,6 +75,176 @@ function printVersion() {
|
|
|
69
75
|
process.stdout.write(`${CLI_NAME} ${CLI_VERSION}\n`);
|
|
70
76
|
}
|
|
71
77
|
|
|
78
|
+
function resolveHomeFilePath(relativePath) {
|
|
79
|
+
const home = String(process.env.USERPROFILE || process.env.HOME || "").trim();
|
|
80
|
+
if (!home) {
|
|
81
|
+
return path.resolve(process.cwd(), relativePath);
|
|
82
|
+
}
|
|
83
|
+
return path.join(home, relativePath);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function updateStateFilePath() {
|
|
87
|
+
return resolveHomeFilePath(SELF_UPDATE_STATE_RELATIVE_PATH);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function loadSelfUpdateState() {
|
|
91
|
+
const filePath = updateStateFilePath();
|
|
92
|
+
try {
|
|
93
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
94
|
+
const parsed = JSON.parse(raw);
|
|
95
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
96
|
+
return { filePath, checkedAt: "", latestVersion: "", installedVersion: "", updatedAt: "" };
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
filePath,
|
|
100
|
+
checkedAt: String(parsed.checked_at || "").trim(),
|
|
101
|
+
latestVersion: String(parsed.latest_version || "").trim(),
|
|
102
|
+
installedVersion: String(parsed.installed_version || "").trim(),
|
|
103
|
+
updatedAt: String(parsed.updated_at || "").trim(),
|
|
104
|
+
};
|
|
105
|
+
} catch {
|
|
106
|
+
return { filePath, checkedAt: "", latestVersion: "", installedVersion: "", updatedAt: "" };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function saveSelfUpdateState(nextState) {
|
|
111
|
+
const filePath = updateStateFilePath();
|
|
112
|
+
const payload = {
|
|
113
|
+
package_name: CLI_NAME,
|
|
114
|
+
checked_at: String(nextState?.checkedAt || "").trim() || new Date().toISOString(),
|
|
115
|
+
latest_version: String(nextState?.latestVersion || "").trim(),
|
|
116
|
+
installed_version: String(nextState?.installedVersion || "").trim(),
|
|
117
|
+
updated_at: String(nextState?.updatedAt || "").trim(),
|
|
118
|
+
};
|
|
119
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
120
|
+
fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function parseSemver(rawVersion) {
|
|
124
|
+
const match = String(rawVersion || "")
|
|
125
|
+
.trim()
|
|
126
|
+
.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
|
|
127
|
+
if (!match) return null;
|
|
128
|
+
return {
|
|
129
|
+
major: Number.parseInt(match[1], 10),
|
|
130
|
+
minor: Number.parseInt(match[2], 10),
|
|
131
|
+
patch: Number.parseInt(match[3], 10),
|
|
132
|
+
prerelease: String(match[4] || "").trim(),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function compareSemver(leftRaw, rightRaw) {
|
|
137
|
+
const left = parseSemver(leftRaw);
|
|
138
|
+
const right = parseSemver(rightRaw);
|
|
139
|
+
if (!left || !right) {
|
|
140
|
+
return String(leftRaw || "").localeCompare(String(rightRaw || ""));
|
|
141
|
+
}
|
|
142
|
+
if (left.major !== right.major) return left.major - right.major;
|
|
143
|
+
if (left.minor !== right.minor) return left.minor - right.minor;
|
|
144
|
+
if (left.patch !== right.patch) return left.patch - right.patch;
|
|
145
|
+
const leftPre = left.prerelease;
|
|
146
|
+
const rightPre = right.prerelease;
|
|
147
|
+
if (leftPre === rightPre) return 0;
|
|
148
|
+
if (!leftPre) return 1;
|
|
149
|
+
if (!rightPre) return -1;
|
|
150
|
+
return leftPre.localeCompare(rightPre);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function hasNoUpdateFlag(argv) {
|
|
154
|
+
return argv.some((arg) => String(arg || "").trim().toLowerCase() === "--no-update");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function parseIsoMs(raw) {
|
|
158
|
+
const parsed = Date.parse(String(raw || "").trim());
|
|
159
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return 0;
|
|
160
|
+
return parsed;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function fetchLatestCLIVersion(timeoutSeconds = SELF_UPDATE_TIMEOUT_SECONDS) {
|
|
164
|
+
const url = `https://registry.npmjs.org/${encodeURIComponent(CLI_NAME)}/latest`;
|
|
165
|
+
const body = await getJSON(url, timeoutSeconds);
|
|
166
|
+
return String(body?.version || "").trim();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function applyRerunExitResult(runResult) {
|
|
170
|
+
if (runResult?.error) {
|
|
171
|
+
process.stderr.write(`[${CLI_NAME}] restart failed: ${String(runResult.error.message || runResult.error)}\n`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (typeof runResult?.status === "number") {
|
|
175
|
+
process.exit(runResult.status);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (runResult?.signal) {
|
|
179
|
+
process.kill(process.pid, runResult.signal);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
process.exit(0);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function maybeAutoUpdate(commandToken, argv) {
|
|
186
|
+
const command = String(commandToken || "").trim().toLowerCase();
|
|
187
|
+
if (command === "proxy") return;
|
|
188
|
+
if (command === "-v" || command === "--version" || command === "version") return;
|
|
189
|
+
if (command === "-h" || command === "--help") return;
|
|
190
|
+
if (String(process.env[SELF_UPDATE_GUARD_ENV_KEY] || "").trim() === "1") return;
|
|
191
|
+
if (hasNoUpdateFlag(argv)) return;
|
|
192
|
+
if (!boolFromRaw(process.env[SELF_UPDATE_ENV_KEY], true)) return;
|
|
193
|
+
|
|
194
|
+
const nowIso = new Date().toISOString();
|
|
195
|
+
const cached = loadSelfUpdateState();
|
|
196
|
+
let latestVersion = "";
|
|
197
|
+
const checkedMs = parseIsoMs(cached.checkedAt);
|
|
198
|
+
if (checkedMs > 0 && Date.now() - checkedMs < SELF_UPDATE_CHECK_INTERVAL_MS) {
|
|
199
|
+
latestVersion = cached.latestVersion;
|
|
200
|
+
}
|
|
201
|
+
if (!latestVersion) {
|
|
202
|
+
try {
|
|
203
|
+
latestVersion = await fetchLatestCLIVersion(SELF_UPDATE_TIMEOUT_SECONDS);
|
|
204
|
+
} catch {
|
|
205
|
+
latestVersion = "";
|
|
206
|
+
}
|
|
207
|
+
saveSelfUpdateState({
|
|
208
|
+
checkedAt: nowIso,
|
|
209
|
+
latestVersion,
|
|
210
|
+
installedVersion: CLI_VERSION,
|
|
211
|
+
updatedAt: cached.updatedAt,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!latestVersion || compareSemver(latestVersion, CLI_VERSION) <= 0) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
process.stderr.write(`[${CLI_NAME}] update available (${CLI_VERSION} -> ${latestVersion}). Installing...\n`);
|
|
220
|
+
const run = runCLICommand("npm", ["install", "-g", `${CLI_NAME}@latest`], {
|
|
221
|
+
stdio: "inherit",
|
|
222
|
+
});
|
|
223
|
+
if (run.status !== 0) {
|
|
224
|
+
process.stderr.write(`[${CLI_NAME}] auto-update failed. Continue with current version ${CLI_VERSION}.\n`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const updatedAt = new Date().toISOString();
|
|
229
|
+
saveSelfUpdateState({
|
|
230
|
+
checkedAt: updatedAt,
|
|
231
|
+
latestVersion,
|
|
232
|
+
installedVersion: latestVersion,
|
|
233
|
+
updatedAt,
|
|
234
|
+
});
|
|
235
|
+
process.stderr.write(`[${CLI_NAME}] updated to ${latestVersion}. Restarting...\n`);
|
|
236
|
+
|
|
237
|
+
const selfPath = fileURLToPath(import.meta.url);
|
|
238
|
+
const rerun = spawnSync(process.execPath, [selfPath, ...argv], {
|
|
239
|
+
stdio: "inherit",
|
|
240
|
+
env: {
|
|
241
|
+
...process.env,
|
|
242
|
+
[SELF_UPDATE_GUARD_ENV_KEY]: "1",
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
applyRerunExitResult(rerun);
|
|
246
|
+
}
|
|
247
|
+
|
|
72
248
|
function parseArgs(argv) {
|
|
73
249
|
const out = {};
|
|
74
250
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -98,11 +274,7 @@ function normalizeToken(raw) {
|
|
|
98
274
|
}
|
|
99
275
|
|
|
100
276
|
function authStoreFilePath() {
|
|
101
|
-
|
|
102
|
-
if (!home) {
|
|
103
|
-
return path.resolve(process.cwd(), AUTH_STORE_RELATIVE_PATH);
|
|
104
|
-
}
|
|
105
|
-
return path.join(home, AUTH_STORE_RELATIVE_PATH);
|
|
277
|
+
return resolveHomeFilePath(AUTH_STORE_RELATIVE_PATH);
|
|
106
278
|
}
|
|
107
279
|
|
|
108
280
|
function resolveWorkspaceDir(rawPath) {
|
|
@@ -3923,6 +4095,7 @@ async function runBootstrap(flags) {
|
|
|
3923
4095
|
async function main() {
|
|
3924
4096
|
const [, , rawCommand, ...rest] = process.argv;
|
|
3925
4097
|
const command = String(rawCommand || "");
|
|
4098
|
+
await maybeAutoUpdate(command, process.argv.slice(2));
|
|
3926
4099
|
|
|
3927
4100
|
if (command === "-v" || command === "--version" || command === "version") {
|
|
3928
4101
|
printVersion();
|