@yemi33/minions 0.1.1924 → 0.1.1925
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/dashboard.js +64 -53
- package/engine/copilot-models.json +5 -0
- package/package.json +1 -1
package/dashboard.js
CHANGED
|
@@ -1190,21 +1190,24 @@ function getDiskVersion() {
|
|
|
1190
1190
|
}
|
|
1191
1191
|
|
|
1192
1192
|
// ── MCP server discovery ─────────────────────────────────────────────────────
|
|
1193
|
-
//
|
|
1194
|
-
// • `
|
|
1195
|
-
// •
|
|
1193
|
+
// Reads MCP server registrations directly from each CLI's config file:
|
|
1194
|
+
// • `~/.claude.json` → `mcpServers` (Claude user scope)
|
|
1195
|
+
// • `~/.copilot/mcp-config.json` → `mcpServers` (Copilot user scope)
|
|
1196
1196
|
// • Each registered project's `<localPath>/.mcp.json` → workspace scope
|
|
1197
1197
|
//
|
|
1198
|
-
//
|
|
1199
|
-
//
|
|
1200
|
-
//
|
|
1198
|
+
// We used to shell out to `claude mcp list` + `copilot mcp list --json`, but
|
|
1199
|
+
// `claude mcp list` blocks on a TTY check when the dashboard daemon has no
|
|
1200
|
+
// parent console (bin/minions.js spawns it with windowsHide:true), so every
|
|
1201
|
+
// refresh timed out and the cache stayed empty. File reads are zero-spawn and
|
|
1202
|
+
// the config files are the authoritative source for registration.
|
|
1203
|
+
//
|
|
1204
|
+
// Cost: we lose live connection status (✓ Connected / ! Needs auth) and the
|
|
1205
|
+
// Claude plugin: <plugin>:<server> rows. The UI shows status only in a hover
|
|
1206
|
+
// tooltip, and plugin servers are rare; reliability matters more.
|
|
1201
1207
|
|
|
1202
1208
|
const _MCP_CACHE_TTL = 5 * 60 * 1000;
|
|
1203
|
-
const _MCP_CLAUDE_TIMEOUT = 30000;
|
|
1204
|
-
const _MCP_COPILOT_TIMEOUT = 15000;
|
|
1205
1209
|
let _mcpServersCache = null;
|
|
1206
1210
|
let _mcpServersCacheTs = 0;
|
|
1207
|
-
let _mcpRefreshInflight = null;
|
|
1208
1211
|
|
|
1209
1212
|
function _parseClaudeMcpListLine(line) {
|
|
1210
1213
|
const trimmed = (line || '').trim();
|
|
@@ -1248,19 +1251,43 @@ function _parseCopilotMcpListJson(raw) {
|
|
|
1248
1251
|
} catch { return []; }
|
|
1249
1252
|
}
|
|
1250
1253
|
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1254
|
+
// Read Claude's user-scope MCP servers from `~/.claude.json` directly.
|
|
1255
|
+
// Shelling out to `claude mcp list` is unreliable from the dashboard daemon —
|
|
1256
|
+
// the CLI's MCP health probes block on a TTY check when the parent has no
|
|
1257
|
+
// console (bin/minions.js launches the dashboard with windowsHide:true), and
|
|
1258
|
+
// neither exec() nor spawn(..., {detached:true}) reliably sidesteps it.
|
|
1259
|
+
// Config-file reads are zero-spawn, instant, and the file is the authoritative
|
|
1260
|
+
// source for which servers are registered.
|
|
1261
|
+
function _readClaudeUserMcpServers() {
|
|
1262
|
+
try {
|
|
1263
|
+
const data = safeJsonObj(path.join(os.homedir(), '.claude.json'));
|
|
1264
|
+
const servers = data?.mcpServers || {};
|
|
1265
|
+
return Object.entries(servers).map(([name, cfg]) => ({
|
|
1266
|
+
name,
|
|
1267
|
+
source: 'Claude user',
|
|
1268
|
+
command: cfg.command || cfg.url || '',
|
|
1269
|
+
args: Array.isArray(cfg.args) ? cfg.args.join(' ') : (cfg.args || ''),
|
|
1270
|
+
}));
|
|
1271
|
+
} catch { return []; }
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// Copilot stores its MCP config at `~/.copilot/mcp-config.json` (may have a
|
|
1275
|
+
// UTF-8 BOM — Windows tooling tends to write one).
|
|
1276
|
+
function _readCopilotUserMcpServers() {
|
|
1277
|
+
try {
|
|
1278
|
+
const configPath = path.join(os.homedir(), '.copilot', 'mcp-config.json');
|
|
1279
|
+
if (!fs.existsSync(configPath)) return [];
|
|
1280
|
+
let raw = fs.readFileSync(configPath, 'utf8');
|
|
1281
|
+
if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1);
|
|
1282
|
+
const data = JSON.parse(raw);
|
|
1283
|
+
const servers = data?.mcpServers || {};
|
|
1284
|
+
return Object.entries(servers).map(([name, cfg]) => ({
|
|
1285
|
+
name,
|
|
1286
|
+
source: 'Copilot',
|
|
1287
|
+
command: cfg.command || cfg.url || '',
|
|
1288
|
+
args: Array.isArray(cfg.args) ? cfg.args.join(' ') : (cfg.args || ''),
|
|
1289
|
+
}));
|
|
1290
|
+
} catch { return []; }
|
|
1264
1291
|
}
|
|
1265
1292
|
|
|
1266
1293
|
function _readWorkspaceMcpServers(projects) {
|
|
@@ -1293,42 +1320,26 @@ function _dedupeMcpServers(entries) {
|
|
|
1293
1320
|
return out;
|
|
1294
1321
|
}
|
|
1295
1322
|
|
|
1296
|
-
async function _refreshMcpServersCache() {
|
|
1297
|
-
if (_mcpRefreshInflight) return _mcpRefreshInflight;
|
|
1298
|
-
_mcpRefreshInflight = (async () => {
|
|
1299
|
-
try {
|
|
1300
|
-
const [claudeOut, copilotOut] = await Promise.all([
|
|
1301
|
-
_runCliAsync('claude', ['mcp', 'list'], _MCP_CLAUDE_TIMEOUT),
|
|
1302
|
-
_runCliAsync('copilot', ['mcp', 'list', '--json'], _MCP_COPILOT_TIMEOUT),
|
|
1303
|
-
]);
|
|
1304
|
-
const claudeEntries = (claudeOut || '').split(/\r?\n/).map(_parseClaudeMcpListLine).filter(Boolean);
|
|
1305
|
-
const copilotEntries = _parseCopilotMcpListJson(copilotOut || '{}');
|
|
1306
|
-
const workspaceEntries = _readWorkspaceMcpServers(PROJECTS);
|
|
1307
|
-
_mcpServersCache = _dedupeMcpServers([...claudeEntries, ...copilotEntries, ...workspaceEntries]);
|
|
1308
|
-
_mcpServersCacheTs = Date.now();
|
|
1309
|
-
} catch (e) {
|
|
1310
|
-
console.error('[mcp-discovery] refresh failed:', e.message?.split('\n')?.[0]);
|
|
1311
|
-
if (!_mcpServersCache) _mcpServersCache = [];
|
|
1312
|
-
_mcpServersCacheTs = Date.now();
|
|
1313
|
-
} finally {
|
|
1314
|
-
_mcpRefreshInflight = null;
|
|
1315
|
-
}
|
|
1316
|
-
})();
|
|
1317
|
-
return _mcpRefreshInflight;
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
1323
|
function getMcpServers() {
|
|
1321
1324
|
const now = Date.now();
|
|
1322
|
-
if (
|
|
1323
|
-
|
|
1325
|
+
if (_mcpServersCache && (now - _mcpServersCacheTs) < _MCP_CACHE_TTL) {
|
|
1326
|
+
return _mcpServersCache;
|
|
1327
|
+
}
|
|
1328
|
+
try {
|
|
1329
|
+
const entries = [
|
|
1330
|
+
..._readClaudeUserMcpServers(),
|
|
1331
|
+
..._readCopilotUserMcpServers(),
|
|
1332
|
+
..._readWorkspaceMcpServers(PROJECTS),
|
|
1333
|
+
];
|
|
1334
|
+
_mcpServersCache = _dedupeMcpServers(entries);
|
|
1335
|
+
} catch (e) {
|
|
1336
|
+
console.error('[mcp-discovery] refresh failed:', e.message?.split('\n')?.[0]);
|
|
1337
|
+
if (!_mcpServersCache) _mcpServersCache = [];
|
|
1324
1338
|
}
|
|
1325
|
-
|
|
1339
|
+
_mcpServersCacheTs = now;
|
|
1340
|
+
return _mcpServersCache;
|
|
1326
1341
|
}
|
|
1327
1342
|
|
|
1328
|
-
// Kick off first discovery on startup so the dashboard has data before the
|
|
1329
|
-
// first slow-state rebuild.
|
|
1330
|
-
_refreshMcpServersCache().catch(() => {});
|
|
1331
|
-
|
|
1332
1343
|
function parsePinnedEntries(content) {
|
|
1333
1344
|
if (!content) return [];
|
|
1334
1345
|
const entries = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1925",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|