alegra-data-agent 0.2.5 → 0.2.6
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/dist/commands/status.js +56 -2
- package/dist/commands/update.js +1 -1
- package/dist/installer.js +51 -12
- package/package.json +1 -1
package/dist/commands/status.js
CHANGED
|
@@ -35,13 +35,20 @@ function reportMcps(manifest, lock) {
|
|
|
35
35
|
const ok = [];
|
|
36
36
|
const missing = [];
|
|
37
37
|
const modified = [];
|
|
38
|
+
const broken = [];
|
|
39
|
+
const stale = [];
|
|
40
|
+
const prevHashes = lock.mcps?.[name]?.renderedHash;
|
|
38
41
|
for (const t of targets) {
|
|
39
42
|
const label = TARGETS[t].label;
|
|
40
|
-
const state = inspectMcpInTarget(entry, TARGETS[t]);
|
|
43
|
+
const state = inspectMcpInTarget(entry, TARGETS[t], prevHashes?.[t]);
|
|
41
44
|
if (state === "ok")
|
|
42
45
|
ok.push(label);
|
|
43
46
|
else if (state === "missing")
|
|
44
47
|
missing.push(label);
|
|
48
|
+
else if (state === "stale")
|
|
49
|
+
stale.push(label);
|
|
50
|
+
else if (state === "broken")
|
|
51
|
+
broken.push(label);
|
|
45
52
|
else
|
|
46
53
|
modified.push(label);
|
|
47
54
|
}
|
|
@@ -50,13 +57,59 @@ function reportMcps(manifest, lock) {
|
|
|
50
57
|
notes.push(gray(`ok en ${ok.join(", ")}`));
|
|
51
58
|
if (modified.length > 0)
|
|
52
59
|
notes.push(dim(`editado a mano en ${modified.join(", ")}`));
|
|
60
|
+
if (broken.length > 0)
|
|
61
|
+
notes.push(yellow(`sin \`type\` — el cliente la descarta en ${broken.join(", ")}`));
|
|
62
|
+
if (stale.length > 0)
|
|
63
|
+
notes.push(dim(`formato viejo en ${stale.join(", ")} — se actualiza`));
|
|
53
64
|
if (missing.length > 0)
|
|
54
65
|
notes.push(yellow(`falta en ${missing.join(", ")}`));
|
|
55
|
-
const icon = missing.length > 0
|
|
66
|
+
const icon = missing.length > 0 || broken.length > 0
|
|
67
|
+
? yellow("✗")
|
|
68
|
+
: stale.length > 0 || modified.length > 0
|
|
69
|
+
? dim("~")
|
|
70
|
+
: green("✓");
|
|
56
71
|
log(` ${icon} ${name} ${gray("·")} ${notes.join(gray(" · "))}`);
|
|
57
72
|
}
|
|
58
73
|
log("");
|
|
59
74
|
}
|
|
75
|
+
function reportAccountConnectorOverlap(manifest) {
|
|
76
|
+
const claudeCfg = TARGETS["claude-code"].mcpConfigPath;
|
|
77
|
+
if (!existsSync(claudeCfg))
|
|
78
|
+
return;
|
|
79
|
+
let connected = [];
|
|
80
|
+
try {
|
|
81
|
+
const cfg = JSON.parse(readFileSync(claudeCfg, "utf-8"));
|
|
82
|
+
if (!Array.isArray(cfg.claudeAiMcpEverConnected))
|
|
83
|
+
return;
|
|
84
|
+
connected = cfg.claudeAiMcpEverConnected.filter((x) => typeof x === "string");
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (connected.length === 0)
|
|
90
|
+
return;
|
|
91
|
+
const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
92
|
+
const overlap = [];
|
|
93
|
+
for (const name of Object.keys(manifest.mcps ?? {})) {
|
|
94
|
+
const ours = norm(name);
|
|
95
|
+
const dup = connected.find((c) => {
|
|
96
|
+
const label = norm(c.replace(/^claude\.ai\s*/i, ""));
|
|
97
|
+
return label.includes(ours) || ours.includes(label);
|
|
98
|
+
});
|
|
99
|
+
if (dup)
|
|
100
|
+
overlap.push(`${name} ↔ "${dup}"`);
|
|
101
|
+
}
|
|
102
|
+
if (overlap.length === 0)
|
|
103
|
+
return;
|
|
104
|
+
logWarn(`Tenés ${overlap.length} connector(s) de tu cuenta que duplican un MCP del agente:`);
|
|
105
|
+
for (const o of overlap)
|
|
106
|
+
log(gray(` ${o}`));
|
|
107
|
+
log(gray(" Son servidores distintos, cada uno con su propia autenticación."));
|
|
108
|
+
log(gray(" Si el oficial no está autenticado, el agente puede responder por el"));
|
|
109
|
+
log(gray(" tuyo — y un connector personalizado apunta a donde alguien escribió."));
|
|
110
|
+
log(gray(` Autenticá los del agente: /mcp → click en el servicio → Authenticate.`));
|
|
111
|
+
log("");
|
|
112
|
+
}
|
|
60
113
|
export function findRogueMetabase(manifest, specs = Object.values(TARGETS)) {
|
|
61
114
|
const official = manifest.mcps?.metabase?.url;
|
|
62
115
|
if (!official)
|
|
@@ -163,6 +216,7 @@ export async function runStatus() {
|
|
|
163
216
|
log("");
|
|
164
217
|
reportMcps(manifest, lock);
|
|
165
218
|
reportRogueMetabase(manifest);
|
|
219
|
+
reportAccountConnectorOverlap(manifest);
|
|
166
220
|
const total = drift.toInstall.length +
|
|
167
221
|
drift.toUpdate.length +
|
|
168
222
|
drift.mcps.toInstall.length +
|
package/dist/commands/update.js
CHANGED
|
@@ -96,7 +96,7 @@ async function runUpdateOnce(opts) {
|
|
|
96
96
|
log(` ${cyan("+")} ${name} ${gray("(MCP nuevo)")}`);
|
|
97
97
|
for (const name of drift.mcps.toUpdate) {
|
|
98
98
|
const motivo = drift.mcps.missing.includes(name)
|
|
99
|
-
? "(MCP ausente
|
|
99
|
+
? "(MCP ausente o descartado por el cliente — se repone)"
|
|
100
100
|
: "(MCP actualizado)";
|
|
101
101
|
log(` ${yellow("↻")} ${name} ${gray(motivo)}`);
|
|
102
102
|
}
|
package/dist/installer.js
CHANGED
|
@@ -378,7 +378,26 @@ function unregisterMcpInTomlBlock(name, spec) {
|
|
|
378
378
|
atomicWriteFile(spec.mcpConfigPath, next);
|
|
379
379
|
return true;
|
|
380
380
|
}
|
|
381
|
-
export function
|
|
381
|
+
export function renderedEntryHash(mcp, spec) {
|
|
382
|
+
const rendered = spec.mcpMechanism === "toml-block"
|
|
383
|
+
? renderMcpToml(mcp).trim()
|
|
384
|
+
: stableStringify(renderMcpForTarget(mcp, spec));
|
|
385
|
+
return createHash("sha256").update(rendered).digest("hex");
|
|
386
|
+
}
|
|
387
|
+
function hashOf(value) {
|
|
388
|
+
return createHash("sha256").update(value).digest("hex");
|
|
389
|
+
}
|
|
390
|
+
function isDiscardedByClient(current, spec) {
|
|
391
|
+
if (!spec.mcpHttpTypeValue)
|
|
392
|
+
return false;
|
|
393
|
+
if (!current || typeof current !== "object")
|
|
394
|
+
return false;
|
|
395
|
+
const entry = current;
|
|
396
|
+
if (typeof entry[spec.mcpUrlKey] !== "string")
|
|
397
|
+
return false;
|
|
398
|
+
return typeof entry.type !== "string";
|
|
399
|
+
}
|
|
400
|
+
export function inspectMcpInTarget(mcp, spec, prevHash) {
|
|
382
401
|
try {
|
|
383
402
|
if (!existsSync(spec.mcpConfigPath))
|
|
384
403
|
return "missing";
|
|
@@ -386,7 +405,10 @@ export function inspectMcpInTarget(mcp, spec) {
|
|
|
386
405
|
const block = extractTomlBlock(readFileSync(spec.mcpConfigPath, "utf-8"), mcp.name);
|
|
387
406
|
if (block === null)
|
|
388
407
|
return "missing";
|
|
389
|
-
|
|
408
|
+
const current = block.trim();
|
|
409
|
+
if (current === renderMcpToml(mcp).trim())
|
|
410
|
+
return "ok";
|
|
411
|
+
return prevHash && hashOf(current) === prevHash ? "stale" : "modified";
|
|
390
412
|
}
|
|
391
413
|
const cfg = JSON.parse(readFileSync(spec.mcpConfigPath, "utf-8"));
|
|
392
414
|
const section = cfg[spec.mcpRootKey];
|
|
@@ -395,16 +417,24 @@ export function inspectMcpInTarget(mcp, spec) {
|
|
|
395
417
|
const current = section[mcp.name];
|
|
396
418
|
if (current === undefined)
|
|
397
419
|
return "missing";
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
420
|
+
const currentStr = stableStringify(current);
|
|
421
|
+
if (currentStr === stableStringify(renderMcpForTarget(mcp, spec)))
|
|
422
|
+
return "ok";
|
|
423
|
+
if (prevHash && hashOf(currentStr) === prevHash)
|
|
424
|
+
return "stale";
|
|
425
|
+
if (isDiscardedByClient(current, spec))
|
|
426
|
+
return "broken";
|
|
427
|
+
return "modified";
|
|
401
428
|
}
|
|
402
429
|
catch {
|
|
403
430
|
return "missing";
|
|
404
431
|
}
|
|
405
432
|
}
|
|
406
|
-
function
|
|
407
|
-
return targets.filter((id) =>
|
|
433
|
+
function mcpNeedsRepairInTargets(mcp, targets, renderedHash) {
|
|
434
|
+
return targets.filter((id) => {
|
|
435
|
+
const state = inspectMcpInTarget(mcp, TARGETS[id], renderedHash?.[id]);
|
|
436
|
+
return state === "missing" || state === "stale" || state === "broken";
|
|
437
|
+
});
|
|
408
438
|
}
|
|
409
439
|
const cleanedTargets = new Set();
|
|
410
440
|
function resolveTargets(opts) {
|
|
@@ -609,11 +639,14 @@ export function installMcp(mcp, opts = {}) {
|
|
|
609
639
|
prev.targets.length === targetIds.length &&
|
|
610
640
|
prev.targets.every((t) => targetIds.includes(t));
|
|
611
641
|
const configChanged = !prev || prev.configHash !== mcpConfigHash(mcp);
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
642
|
+
const prevHashes = readLockFile().mcps?.[mcp.name]?.renderedHash;
|
|
643
|
+
const needsWrite = (t) => {
|
|
644
|
+
const state = inspectMcpInTarget(mcp, t, prevHashes?.[t.id]);
|
|
645
|
+
return state === "missing" || state === "stale" || state === "broken";
|
|
646
|
+
};
|
|
647
|
+
const writeTargets = opts.force || configChanged ? wantedTargets : wantedTargets.filter(needsWrite);
|
|
615
648
|
const alreadyThere = wantedTargets
|
|
616
|
-
.filter((t) => !writeTargets.includes(t) &&
|
|
649
|
+
.filter((t) => !writeTargets.includes(t) && !needsWrite(t))
|
|
617
650
|
.map((t) => t.id);
|
|
618
651
|
if (writeTargets.length === 0 && targetsMatch) {
|
|
619
652
|
return { name: mcp.name, kind: "mcp", status: "unchanged", targets: targetIds };
|
|
@@ -647,12 +680,18 @@ export function installMcp(mcp, opts = {}) {
|
|
|
647
680
|
error: failures.join("; "),
|
|
648
681
|
};
|
|
649
682
|
}
|
|
683
|
+
const renderedHash = { ...(prev?.renderedHash ?? {}) };
|
|
684
|
+
for (const t of writeTargets) {
|
|
685
|
+
if (okTargets.includes(t.id))
|
|
686
|
+
renderedHash[t.id] = renderedEntryHash(mcp, t);
|
|
687
|
+
}
|
|
650
688
|
lock.mcps[mcp.name] = {
|
|
651
689
|
name: mcp.name,
|
|
652
690
|
installedFrom: getPackageVersion(),
|
|
653
691
|
installedAt: new Date().toISOString(),
|
|
654
692
|
configHash: mcpConfigHash(mcp),
|
|
655
693
|
targets: okTargets,
|
|
694
|
+
renderedHash,
|
|
656
695
|
};
|
|
657
696
|
writeLockFile(lock);
|
|
658
697
|
return {
|
|
@@ -828,7 +867,7 @@ export function detectDrift(manifest) {
|
|
|
828
867
|
mcps.toUpdate.push(name);
|
|
829
868
|
continue;
|
|
830
869
|
}
|
|
831
|
-
const missing =
|
|
870
|
+
const missing = mcpNeedsRepairInTargets(manifestMcps[name], (prevMcp.targets ?? []), prevMcp.renderedHash);
|
|
832
871
|
if (missing.length > 0) {
|
|
833
872
|
mcps.toUpdate.push(name);
|
|
834
873
|
mcps.missing.push(name);
|