alegra-data-agent 0.2.3 → 0.2.5
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/api.js +83 -16
- package/dist/auth.js +23 -8
- package/dist/autoupdate.js +216 -0
- package/dist/commands/hook.js +41 -45
- package/dist/commands/install.js +25 -16
- package/dist/commands/login.js +1 -1
- package/dist/commands/status.js +125 -5
- package/dist/commands/uninstall.js +16 -4
- package/dist/commands/update.js +56 -8
- package/dist/installer.js +247 -72
- package/dist/lib.js +58 -28
- package/dist/main.js +4 -0
- package/dist/render.js +3 -0
- package/dist/signature.js +17 -5
- package/dist/targets.js +10 -7
- package/dist/ui.js +2 -2
- package/package.json +12 -9
package/dist/installer.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { copyFileSync, existsSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join, resolve, sep } from "node:path";
|
|
4
|
-
import { fetchManifest, fetchSkillFile } from "./api.js";
|
|
2
|
+
import { copyFileSync, existsSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
4
|
+
import { AuthRequiredError, fetchManifest, fetchSkillFile } from "./api.js";
|
|
5
5
|
import { dim, gray, green, logError, red, yellow } from "./colors.js";
|
|
6
|
-
import { adaptStdioCommand, ensureDir,
|
|
6
|
+
import { adaptStdioCommand, ensureDir, getAntigravityGeminiLegacyMcpConfigPath, getAntigravityGeminiLegacySkillsDir, getAntigravityLegacyMcpConfigPath, getAntigravityLegacySkillsDir, getClaudeSettingsPath, getCursorLegacyRulesDir, getLockFilePath, getOpenCodeLegacyPromptsDir, getPackageVersion, readLockFile, sha256File, writeLockFile, } from "./lib.js";
|
|
7
7
|
import { inlineReferences, renderMcpForTarget } from "./render.js";
|
|
8
8
|
import { TARGETS, detectInstalledTargets } from "./targets.js";
|
|
9
9
|
const SKILL_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
@@ -44,6 +44,17 @@ function mcpConfigHash(mcp) {
|
|
|
44
44
|
});
|
|
45
45
|
return createHash("sha256").update(salient).digest("hex");
|
|
46
46
|
}
|
|
47
|
+
function stableStringify(value) {
|
|
48
|
+
if (value === null || typeof value !== "object")
|
|
49
|
+
return JSON.stringify(value);
|
|
50
|
+
if (Array.isArray(value))
|
|
51
|
+
return `[${value.map(stableStringify).join(",")}]`;
|
|
52
|
+
const o = value;
|
|
53
|
+
return `{${Object.keys(o)
|
|
54
|
+
.sort()
|
|
55
|
+
.map((k) => `${JSON.stringify(k)}:${stableStringify(o[k])}`)
|
|
56
|
+
.join(",")}}`;
|
|
57
|
+
}
|
|
47
58
|
function safeJoinSkillFile(skillRoot, rel) {
|
|
48
59
|
const candidate = resolve(skillRoot, rel);
|
|
49
60
|
const root = resolve(skillRoot);
|
|
@@ -73,33 +84,51 @@ export function verifyInstalledFiles(dir, entry) {
|
|
|
73
84
|
function sha256Buffer(buf) {
|
|
74
85
|
return createHash("sha256").update(buf).digest("hex");
|
|
75
86
|
}
|
|
87
|
+
const DOWNLOAD_CONCURRENCY = 8;
|
|
88
|
+
async function mapWithLimit(items, limit, fn) {
|
|
89
|
+
const out = new Array(items.length);
|
|
90
|
+
let next = 0;
|
|
91
|
+
const worker = async () => {
|
|
92
|
+
for (;;) {
|
|
93
|
+
const i = next++;
|
|
94
|
+
if (i >= items.length)
|
|
95
|
+
return;
|
|
96
|
+
out[i] = await fn(items[i]);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
76
102
|
async function stageSkill(skillName, entry) {
|
|
77
103
|
validateSkillName(skillName);
|
|
78
104
|
for (const rel of entry.files)
|
|
79
105
|
validateRelPath(rel);
|
|
80
|
-
|
|
81
|
-
for (const rel of entry.files) {
|
|
106
|
+
return mapWithLimit(entry.files, DOWNLOAD_CONCURRENCY, async (rel) => {
|
|
82
107
|
const data = await fetchSkillFile(skillName, rel);
|
|
83
108
|
const actualHash = sha256Buffer(data);
|
|
84
109
|
const expectedHash = entry.sha256[rel];
|
|
85
110
|
if (actualHash !== expectedHash) {
|
|
86
111
|
throw new Error(`hash inválido para ${skillName}/${rel}: esperaba ${expectedHash.slice(0, 12)}…, recibido ${actualHash.slice(0, 12)}…`);
|
|
87
112
|
}
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
113
|
+
return { rel, data };
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function skillDirsFor(spec) {
|
|
117
|
+
return [spec.skillsDir, ...(spec.mirrorSkillsDirs ?? [])];
|
|
91
118
|
}
|
|
92
119
|
function writeSkillToTarget(skillName, staged, spec) {
|
|
93
120
|
validateSkillName(skillName);
|
|
94
121
|
if (spec.skillFormat === "folder") {
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
122
|
+
for (const base of skillDirsFor(spec)) {
|
|
123
|
+
const dst = join(base, skillName);
|
|
124
|
+
if (existsSync(dst))
|
|
125
|
+
withFsRetry(() => rmSync(dst, { recursive: true, force: true }));
|
|
126
|
+
ensureDir(dst);
|
|
127
|
+
for (const f of staged) {
|
|
128
|
+
const dstFile = safeJoinSkillFile(dst, f.rel);
|
|
129
|
+
ensureDir(dirname(dstFile));
|
|
130
|
+
withFsRetry(() => writeFileSync(dstFile, f.data));
|
|
131
|
+
}
|
|
103
132
|
}
|
|
104
133
|
return;
|
|
105
134
|
}
|
|
@@ -151,6 +180,22 @@ function parseFrontmatterMinimal(content) {
|
|
|
151
180
|
function sleepSync(ms) {
|
|
152
181
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
153
182
|
}
|
|
183
|
+
function withFsRetry(fn) {
|
|
184
|
+
for (let attempt = 0;; attempt++) {
|
|
185
|
+
try {
|
|
186
|
+
fn();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
const code = err.code;
|
|
191
|
+
if (attempt < 4 && (code === "EPERM" || code === "EBUSY" || code === "EACCES")) {
|
|
192
|
+
sleepSync(150);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
154
199
|
function atomicWriteFile(path, content) {
|
|
155
200
|
ensureDir(dirname(path));
|
|
156
201
|
const tmp = `${path}.${process.pid}.tmp`;
|
|
@@ -174,11 +219,30 @@ function atomicWriteFile(path, content) {
|
|
|
174
219
|
}
|
|
175
220
|
}
|
|
176
221
|
}
|
|
222
|
+
const MAX_BACKUPS = 3;
|
|
223
|
+
const backedUpThisRun = new Set();
|
|
177
224
|
function backupIfExists(path) {
|
|
178
225
|
if (!existsSync(path))
|
|
179
226
|
return;
|
|
180
|
-
|
|
181
|
-
|
|
227
|
+
if (backedUpThisRun.has(path))
|
|
228
|
+
return;
|
|
229
|
+
copyFileSync(path, `${path}.bak-${Date.now()}`);
|
|
230
|
+
backedUpThisRun.add(path);
|
|
231
|
+
pruneBackups(path);
|
|
232
|
+
}
|
|
233
|
+
function pruneBackups(path) {
|
|
234
|
+
try {
|
|
235
|
+
const dir = dirname(path);
|
|
236
|
+
const prefix = `${basename(path)}.bak-`;
|
|
237
|
+
const stale = readdirSync(dir)
|
|
238
|
+
.filter((f) => f.startsWith(prefix))
|
|
239
|
+
.sort()
|
|
240
|
+
.slice(0, -MAX_BACKUPS);
|
|
241
|
+
for (const f of stale)
|
|
242
|
+
rmSync(join(dir, f), { force: true });
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
}
|
|
182
246
|
}
|
|
183
247
|
function readJsonOrEmpty(path) {
|
|
184
248
|
if (!existsSync(path))
|
|
@@ -266,6 +330,19 @@ export function upsertTomlBlock(content, name, block) {
|
|
|
266
330
|
const sep = content.endsWith("\n\n") ? "" : content.endsWith("\n") ? "\n" : "\n\n";
|
|
267
331
|
return `${content}${sep}${fenced}\n`;
|
|
268
332
|
}
|
|
333
|
+
export function extractTomlBlock(content, name) {
|
|
334
|
+
const { begin, end } = tomlMarkers(name);
|
|
335
|
+
const bi = content.indexOf(begin);
|
|
336
|
+
if (bi === -1)
|
|
337
|
+
return null;
|
|
338
|
+
const ei = content.indexOf(end, bi);
|
|
339
|
+
if (ei === -1)
|
|
340
|
+
return null;
|
|
341
|
+
return content
|
|
342
|
+
.slice(bi + begin.length, ei)
|
|
343
|
+
.replace(/^\n/, "")
|
|
344
|
+
.replace(/\n$/, "");
|
|
345
|
+
}
|
|
269
346
|
export function removeTomlBlock(content, name) {
|
|
270
347
|
const { begin, end } = tomlMarkers(name);
|
|
271
348
|
const bi = content.indexOf(begin);
|
|
@@ -283,9 +360,7 @@ export function removeTomlBlock(content, name) {
|
|
|
283
360
|
return content.slice(0, start) + content.slice(stop);
|
|
284
361
|
}
|
|
285
362
|
function registerMcpInTomlBlock(mcp, spec) {
|
|
286
|
-
const existing = existsSync(spec.mcpConfigPath)
|
|
287
|
-
? readFileSync(spec.mcpConfigPath, "utf-8")
|
|
288
|
-
: "";
|
|
363
|
+
const existing = existsSync(spec.mcpConfigPath) ? readFileSync(spec.mcpConfigPath, "utf-8") : "";
|
|
289
364
|
const next = upsertTomlBlock(existing, mcp.name, renderMcpToml(mcp));
|
|
290
365
|
if (next === existing)
|
|
291
366
|
return;
|
|
@@ -303,14 +378,47 @@ function unregisterMcpInTomlBlock(name, spec) {
|
|
|
303
378
|
atomicWriteFile(spec.mcpConfigPath, next);
|
|
304
379
|
return true;
|
|
305
380
|
}
|
|
381
|
+
export function inspectMcpInTarget(mcp, spec) {
|
|
382
|
+
try {
|
|
383
|
+
if (!existsSync(spec.mcpConfigPath))
|
|
384
|
+
return "missing";
|
|
385
|
+
if (spec.mcpMechanism === "toml-block") {
|
|
386
|
+
const block = extractTomlBlock(readFileSync(spec.mcpConfigPath, "utf-8"), mcp.name);
|
|
387
|
+
if (block === null)
|
|
388
|
+
return "missing";
|
|
389
|
+
return block.trim() === renderMcpToml(mcp).trim() ? "ok" : "modified";
|
|
390
|
+
}
|
|
391
|
+
const cfg = JSON.parse(readFileSync(spec.mcpConfigPath, "utf-8"));
|
|
392
|
+
const section = cfg[spec.mcpRootKey];
|
|
393
|
+
if (!section || typeof section !== "object")
|
|
394
|
+
return "missing";
|
|
395
|
+
const current = section[mcp.name];
|
|
396
|
+
if (current === undefined)
|
|
397
|
+
return "missing";
|
|
398
|
+
return stableStringify(current) === stableStringify(renderMcpForTarget(mcp, spec))
|
|
399
|
+
? "ok"
|
|
400
|
+
: "modified";
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
return "missing";
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function mcpMissingInTargets(mcp, targets) {
|
|
407
|
+
return targets.filter((id) => inspectMcpInTarget(mcp, TARGETS[id]) === "missing");
|
|
408
|
+
}
|
|
409
|
+
const cleanedTargets = new Set();
|
|
306
410
|
function resolveTargets(opts) {
|
|
307
411
|
const ids = opts.targets?.length ? opts.targets : detectInstalledTargets();
|
|
308
412
|
if (ids.length === 0) {
|
|
309
413
|
ids.push("claude-code");
|
|
310
414
|
}
|
|
311
415
|
const specs = ids.map((id) => TARGETS[id]);
|
|
312
|
-
for (const spec of specs)
|
|
416
|
+
for (const spec of specs) {
|
|
417
|
+
if (cleanedTargets.has(spec.id))
|
|
418
|
+
continue;
|
|
419
|
+
cleanedTargets.add(spec.id);
|
|
313
420
|
cleanupLegacySkillArtifacts(spec);
|
|
421
|
+
}
|
|
314
422
|
return specs;
|
|
315
423
|
}
|
|
316
424
|
const LEGACY_RENAMED_SKILLS = ["alanube", "alegra-producto", "alegra-ce-universe"];
|
|
@@ -346,10 +454,12 @@ function stripMcpEntries(path, names) {
|
|
|
346
454
|
function cleanupLegacySkillArtifacts(spec) {
|
|
347
455
|
try {
|
|
348
456
|
if (spec.skillFormat === "folder") {
|
|
349
|
-
for (const
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
457
|
+
for (const base of skillDirsFor(spec)) {
|
|
458
|
+
for (const old of LEGACY_RENAMED_SKILLS) {
|
|
459
|
+
const dir = join(base, old);
|
|
460
|
+
if (existsSync(dir))
|
|
461
|
+
rmSync(dir, { recursive: true, force: true });
|
|
462
|
+
}
|
|
353
463
|
}
|
|
354
464
|
}
|
|
355
465
|
const lock = readLockFile();
|
|
@@ -368,12 +478,19 @@ function cleanupLegacySkillArtifacts(spec) {
|
|
|
368
478
|
stripMcpEntries(getClaudeSettingsPath(), ourMcps);
|
|
369
479
|
}
|
|
370
480
|
if (spec.id === "antigravity") {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
481
|
+
const legacySkillDirs = [
|
|
482
|
+
getAntigravityLegacySkillsDir(),
|
|
483
|
+
getAntigravityGeminiLegacySkillsDir(),
|
|
484
|
+
];
|
|
485
|
+
for (const base of legacySkillDirs) {
|
|
486
|
+
for (const name of [...Object.keys(lock.skills), ...LEGACY_RENAMED_SKILLS]) {
|
|
487
|
+
const d = join(base, name);
|
|
488
|
+
if (existsSync(d))
|
|
489
|
+
rmSync(d, { recursive: true, force: true });
|
|
490
|
+
}
|
|
375
491
|
}
|
|
376
492
|
stripMcpEntries(getAntigravityLegacyMcpConfigPath(), ourMcps);
|
|
493
|
+
stripMcpEntries(getAntigravityGeminiLegacyMcpConfigPath(), ourMcps);
|
|
377
494
|
}
|
|
378
495
|
}
|
|
379
496
|
catch {
|
|
@@ -411,35 +528,56 @@ export async function installSkill(name, manifest, opts = {}) {
|
|
|
411
528
|
targets: targetIds,
|
|
412
529
|
};
|
|
413
530
|
}
|
|
531
|
+
let staged;
|
|
414
532
|
try {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
name,
|
|
421
|
-
installedFrom: getPackageVersion(),
|
|
422
|
-
installedAt: new Date().toISOString(),
|
|
423
|
-
bundleHash: entry.bundleHash,
|
|
424
|
-
targets: targetIds,
|
|
425
|
-
};
|
|
426
|
-
writeLockFile(lock);
|
|
533
|
+
staged = await stageSkill(name, entry);
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
if (err instanceof AuthRequiredError)
|
|
537
|
+
throw err;
|
|
427
538
|
return {
|
|
428
539
|
name,
|
|
429
540
|
kind: "skill",
|
|
430
|
-
status:
|
|
541
|
+
status: "failed",
|
|
431
542
|
targets: targetIds,
|
|
543
|
+
error: err instanceof Error ? err.message : String(err),
|
|
432
544
|
};
|
|
433
545
|
}
|
|
434
|
-
|
|
546
|
+
const okTargets = [];
|
|
547
|
+
const failures = [];
|
|
548
|
+
for (const t of targets) {
|
|
549
|
+
try {
|
|
550
|
+
writeSkillToTarget(name, staged, t);
|
|
551
|
+
okTargets.push(t.id);
|
|
552
|
+
}
|
|
553
|
+
catch (err) {
|
|
554
|
+
failures.push(`${t.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (okTargets.length === 0) {
|
|
435
558
|
return {
|
|
436
559
|
name,
|
|
437
560
|
kind: "skill",
|
|
438
561
|
status: "failed",
|
|
439
562
|
targets: targetIds,
|
|
440
|
-
error:
|
|
563
|
+
error: failures.join("; "),
|
|
441
564
|
};
|
|
442
565
|
}
|
|
566
|
+
lock.skills[name] = {
|
|
567
|
+
name,
|
|
568
|
+
installedFrom: getPackageVersion(),
|
|
569
|
+
installedAt: new Date().toISOString(),
|
|
570
|
+
bundleHash: entry.bundleHash,
|
|
571
|
+
targets: okTargets,
|
|
572
|
+
};
|
|
573
|
+
writeLockFile(lock);
|
|
574
|
+
return {
|
|
575
|
+
name,
|
|
576
|
+
kind: "skill",
|
|
577
|
+
status: prev ? "updated" : "installed",
|
|
578
|
+
targets: okTargets,
|
|
579
|
+
error: failures.length > 0 ? `targets con fallo — ${failures.join("; ")}` : undefined,
|
|
580
|
+
};
|
|
443
581
|
}
|
|
444
582
|
export function installMcp(mcp, opts = {}) {
|
|
445
583
|
try {
|
|
@@ -470,7 +608,14 @@ export function installMcp(mcp, opts = {}) {
|
|
|
470
608
|
const targetsMatch = prev?.targets &&
|
|
471
609
|
prev.targets.length === targetIds.length &&
|
|
472
610
|
prev.targets.every((t) => targetIds.includes(t));
|
|
473
|
-
|
|
611
|
+
const configChanged = !prev || prev.configHash !== mcpConfigHash(mcp);
|
|
612
|
+
const writeTargets = opts.force || configChanged
|
|
613
|
+
? wantedTargets
|
|
614
|
+
: wantedTargets.filter((t) => inspectMcpInTarget(mcp, t) === "missing");
|
|
615
|
+
const alreadyThere = wantedTargets
|
|
616
|
+
.filter((t) => !writeTargets.includes(t) && inspectMcpInTarget(mcp, t) !== "missing")
|
|
617
|
+
.map((t) => t.id);
|
|
618
|
+
if (writeTargets.length === 0 && targetsMatch) {
|
|
474
619
|
return { name: mcp.name, kind: "mcp", status: "unchanged", targets: targetIds };
|
|
475
620
|
}
|
|
476
621
|
if (opts.dryRun) {
|
|
@@ -478,37 +623,45 @@ export function installMcp(mcp, opts = {}) {
|
|
|
478
623
|
name: mcp.name,
|
|
479
624
|
kind: "mcp",
|
|
480
625
|
status: prev ? "updated" : "installed",
|
|
481
|
-
targets:
|
|
626
|
+
targets: writeTargets.map((t) => t.id),
|
|
482
627
|
};
|
|
483
628
|
}
|
|
484
|
-
|
|
485
|
-
|
|
629
|
+
const written = [];
|
|
630
|
+
const failures = [];
|
|
631
|
+
for (const t of writeTargets) {
|
|
632
|
+
try {
|
|
486
633
|
registerMcpInTarget(mcp, t);
|
|
634
|
+
written.push(t.id);
|
|
635
|
+
}
|
|
636
|
+
catch (err) {
|
|
637
|
+
failures.push(`${t.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
487
638
|
}
|
|
488
|
-
lock.mcps[mcp.name] = {
|
|
489
|
-
name: mcp.name,
|
|
490
|
-
installedFrom: getPackageVersion(),
|
|
491
|
-
installedAt: new Date().toISOString(),
|
|
492
|
-
configHash: mcpConfigHash(mcp),
|
|
493
|
-
targets: targetIds,
|
|
494
|
-
};
|
|
495
|
-
writeLockFile(lock);
|
|
496
|
-
return {
|
|
497
|
-
name: mcp.name,
|
|
498
|
-
kind: "mcp",
|
|
499
|
-
status: prev ? "updated" : "installed",
|
|
500
|
-
targets: targetIds,
|
|
501
|
-
};
|
|
502
639
|
}
|
|
503
|
-
|
|
640
|
+
const okTargets = [...new Set([...alreadyThere, ...written])];
|
|
641
|
+
if (okTargets.length === 0) {
|
|
504
642
|
return {
|
|
505
643
|
name: mcp.name,
|
|
506
644
|
kind: "mcp",
|
|
507
645
|
status: "failed",
|
|
508
646
|
targets: targetIds,
|
|
509
|
-
error:
|
|
647
|
+
error: failures.join("; "),
|
|
510
648
|
};
|
|
511
649
|
}
|
|
650
|
+
lock.mcps[mcp.name] = {
|
|
651
|
+
name: mcp.name,
|
|
652
|
+
installedFrom: getPackageVersion(),
|
|
653
|
+
installedAt: new Date().toISOString(),
|
|
654
|
+
configHash: mcpConfigHash(mcp),
|
|
655
|
+
targets: okTargets,
|
|
656
|
+
};
|
|
657
|
+
writeLockFile(lock);
|
|
658
|
+
return {
|
|
659
|
+
name: mcp.name,
|
|
660
|
+
kind: "mcp",
|
|
661
|
+
status: prev ? "updated" : "installed",
|
|
662
|
+
targets: okTargets,
|
|
663
|
+
error: failures.length > 0 ? `targets con fallo — ${failures.join("; ")}` : undefined,
|
|
664
|
+
};
|
|
512
665
|
}
|
|
513
666
|
export async function installAll(opts = {}) {
|
|
514
667
|
let manifest;
|
|
@@ -561,9 +714,11 @@ export function uninstallSkill(name, opts = {}) {
|
|
|
561
714
|
for (const tId of targetIds) {
|
|
562
715
|
const spec = TARGETS[tId];
|
|
563
716
|
if (spec.skillFormat === "folder") {
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
717
|
+
for (const base of skillDirsFor(spec)) {
|
|
718
|
+
const dst = join(base, name);
|
|
719
|
+
if (existsSync(dst))
|
|
720
|
+
rmSync(dst, { recursive: true, force: true });
|
|
721
|
+
}
|
|
567
722
|
}
|
|
568
723
|
else {
|
|
569
724
|
const ext = spec.skillFormat === "cursor-mdc" ? ".mdc" : ".md";
|
|
@@ -621,6 +776,15 @@ export function uninstallAll(opts = {}) {
|
|
|
621
776
|
results.push(uninstallSkill(name, opts));
|
|
622
777
|
for (const name of Object.keys(lock.mcps ?? {}))
|
|
623
778
|
results.push(uninstallMcp(name, opts));
|
|
779
|
+
if (!opts.dryRun) {
|
|
780
|
+
for (const spec of Object.values(TARGETS)) {
|
|
781
|
+
try {
|
|
782
|
+
cleanupLegacySkillArtifacts(spec);
|
|
783
|
+
}
|
|
784
|
+
catch {
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
624
788
|
return results;
|
|
625
789
|
}
|
|
626
790
|
export function detectDrift(manifest) {
|
|
@@ -651,16 +815,27 @@ export function detectDrift(manifest) {
|
|
|
651
815
|
toUpdate: [],
|
|
652
816
|
unchanged: [],
|
|
653
817
|
orphaned: [],
|
|
818
|
+
missing: [],
|
|
654
819
|
};
|
|
655
820
|
const manifestMcps = manifest.mcps ?? {};
|
|
656
821
|
for (const name of Object.keys(manifestMcps)) {
|
|
657
822
|
const prevMcp = lock.mcps?.[name];
|
|
658
|
-
if (!prevMcp)
|
|
823
|
+
if (!prevMcp) {
|
|
659
824
|
mcps.toInstall.push(name);
|
|
660
|
-
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (prevMcp.configHash !== mcpConfigHash(manifestMcps[name])) {
|
|
661
828
|
mcps.toUpdate.push(name);
|
|
662
|
-
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
const missing = mcpMissingInTargets(manifestMcps[name], (prevMcp.targets ?? []));
|
|
832
|
+
if (missing.length > 0) {
|
|
833
|
+
mcps.toUpdate.push(name);
|
|
834
|
+
mcps.missing.push(name);
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
663
837
|
mcps.unchanged.push(name);
|
|
838
|
+
}
|
|
664
839
|
}
|
|
665
840
|
for (const name of Object.keys(lock.mcps ?? {})) {
|
|
666
841
|
if (!manifestMcps[name])
|
package/dist/lib.js
CHANGED
|
@@ -5,7 +5,7 @@ import { dirname, join, relative, sep } from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
export const PRODUCT_NAME = "alegra-data-agent";
|
|
7
7
|
export const COMMAND_NAME = "alegra-data-agent";
|
|
8
|
-
export const REPO_SLUG = "
|
|
8
|
+
export const REPO_SLUG = "Alegra-Team/data-agent-selfservice";
|
|
9
9
|
export const REPO_DEFAULT_BRANCH = "main";
|
|
10
10
|
export const GATEWAY_URL = process.env.ALEGRA_DATA_AGENT_GATEWAY_URL ?? "https://alegra-data-agent.vercel.app";
|
|
11
11
|
export const ALL_TARGETS = [
|
|
@@ -19,12 +19,6 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
19
19
|
export function getPackageRoot() {
|
|
20
20
|
return join(__dirname, "..");
|
|
21
21
|
}
|
|
22
|
-
export function getRegistryDir() {
|
|
23
|
-
return join(getPackageRoot(), "skills-registry");
|
|
24
|
-
}
|
|
25
|
-
export function getManifestPath() {
|
|
26
|
-
return join(getRegistryDir(), "index.json");
|
|
27
|
-
}
|
|
28
22
|
export function getClaudeSkillsDir() {
|
|
29
23
|
return join(homedir(), ".claude", "skills");
|
|
30
24
|
}
|
|
@@ -59,10 +53,16 @@ export function getOpenCodeConfigPath() {
|
|
|
59
53
|
return join(base, "opencode", "opencode.json");
|
|
60
54
|
}
|
|
61
55
|
export function getAntigravitySkillsDir() {
|
|
62
|
-
return join(homedir(), ".gemini", "antigravity", "skills");
|
|
56
|
+
return join(homedir(), ".gemini", "antigravity-cli", "skills");
|
|
57
|
+
}
|
|
58
|
+
export function getAntigravityGeminiSkillsDir() {
|
|
59
|
+
return join(homedir(), ".gemini", "skills");
|
|
63
60
|
}
|
|
64
61
|
export function getAntigravityMcpConfigPath() {
|
|
65
|
-
return join(homedir(), ".gemini", "
|
|
62
|
+
return join(homedir(), ".gemini", "config", "mcp_config.json");
|
|
63
|
+
}
|
|
64
|
+
export function getGeminiSettingsPath() {
|
|
65
|
+
return join(homedir(), ".gemini", "settings.json");
|
|
66
66
|
}
|
|
67
67
|
export function getAntigravityLegacySkillsDir() {
|
|
68
68
|
return join(homedir(), ".antigravity", "skills");
|
|
@@ -70,6 +70,28 @@ export function getAntigravityLegacySkillsDir() {
|
|
|
70
70
|
export function getAntigravityLegacyMcpConfigPath() {
|
|
71
71
|
return join(homedir(), ".antigravity", "settings.json");
|
|
72
72
|
}
|
|
73
|
+
export function getAntigravityGeminiLegacySkillsDir() {
|
|
74
|
+
return join(homedir(), ".gemini", "antigravity", "skills");
|
|
75
|
+
}
|
|
76
|
+
export function getAntigravityGeminiLegacyMcpConfigPath() {
|
|
77
|
+
return join(homedir(), ".gemini", "antigravity", "mcp_config.json");
|
|
78
|
+
}
|
|
79
|
+
export function getCursorHooksPath() {
|
|
80
|
+
return join(homedir(), ".cursor", "hooks.json");
|
|
81
|
+
}
|
|
82
|
+
export function getCodexHooksPath() {
|
|
83
|
+
const codexHome = process.env.CODEX_HOME;
|
|
84
|
+
const base = codexHome && codexHome.length > 0 ? codexHome : join(homedir(), ".codex");
|
|
85
|
+
return join(base, "hooks.json");
|
|
86
|
+
}
|
|
87
|
+
export function getOpenCodePluginsDir() {
|
|
88
|
+
const xdgHome = process.env.XDG_CONFIG_HOME;
|
|
89
|
+
const base = xdgHome && xdgHome.length > 0 ? xdgHome : join(homedir(), ".config");
|
|
90
|
+
return join(base, "opencode", "plugins");
|
|
91
|
+
}
|
|
92
|
+
export function getOpenCodePluginPath() {
|
|
93
|
+
return join(getOpenCodePluginsDir(), "alegra-data-agent-autoupdate.js");
|
|
94
|
+
}
|
|
73
95
|
export function getCodexSkillsDir() {
|
|
74
96
|
return join(homedir(), ".agents", "skills");
|
|
75
97
|
}
|
|
@@ -117,6 +139,15 @@ export function getConfigDir() {
|
|
|
117
139
|
export function getAuthFilePath() {
|
|
118
140
|
return join(getConfigDir(), "auth.json");
|
|
119
141
|
}
|
|
142
|
+
export function getLastCheckPath() {
|
|
143
|
+
return join(getConfigDir(), "last-check.json");
|
|
144
|
+
}
|
|
145
|
+
export function getManifestCachePath() {
|
|
146
|
+
return join(getConfigDir(), "manifest-cache.json");
|
|
147
|
+
}
|
|
148
|
+
export function getAutoUpdateLogPath() {
|
|
149
|
+
return join(getConfigDir(), "autoupdate.log");
|
|
150
|
+
}
|
|
120
151
|
let _cachedVersion = null;
|
|
121
152
|
export function getPackageVersion() {
|
|
122
153
|
if (_cachedVersion)
|
|
@@ -130,31 +161,29 @@ export function getPackageVersion() {
|
|
|
130
161
|
}
|
|
131
162
|
return _cachedVersion;
|
|
132
163
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return null;
|
|
137
|
-
try {
|
|
138
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
164
|
+
let _lockCache = null;
|
|
165
|
+
function emptyLock() {
|
|
166
|
+
return { schemaVersion: 1, productName: PRODUCT_NAME, skills: {} };
|
|
143
167
|
}
|
|
144
168
|
export function readLockFile() {
|
|
169
|
+
if (_lockCache)
|
|
170
|
+
return _lockCache;
|
|
145
171
|
const path = getLockFilePath();
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
172
|
+
let lock = emptyLock();
|
|
173
|
+
if (existsSync(path)) {
|
|
174
|
+
try {
|
|
175
|
+
lock = JSON.parse(readFileSync(path, "utf-8"));
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
lock = emptyLock();
|
|
179
|
+
}
|
|
154
180
|
}
|
|
181
|
+
_lockCache = lock;
|
|
182
|
+
return lock;
|
|
155
183
|
}
|
|
156
184
|
export function writeLockFile(lock) {
|
|
157
185
|
const path = getLockFilePath();
|
|
186
|
+
_lockCache = lock;
|
|
158
187
|
mkdirSync(dirname(path), { recursive: true });
|
|
159
188
|
writeFileSync(path, `${JSON.stringify(lock, null, 2)}\n`, "utf-8");
|
|
160
189
|
}
|
|
@@ -254,7 +283,8 @@ export function ensureDir(path) {
|
|
|
254
283
|
mkdirSync(path, { recursive: true });
|
|
255
284
|
}
|
|
256
285
|
export function adaptStdioCommand(command, args, platform = process.platform) {
|
|
257
|
-
|
|
286
|
+
const winShims = new Set(["npx", "npm", "pnpm", "sf", "uvx", "bunx", "yarn", "deno"]);
|
|
287
|
+
if (platform === "win32" && winShims.has(command)) {
|
|
258
288
|
return { command: "cmd", args: ["/c", command, ...args] };
|
|
259
289
|
}
|
|
260
290
|
return { command, args };
|
package/dist/main.js
CHANGED
|
@@ -37,6 +37,7 @@ function parseArgs(argv) {
|
|
|
37
37
|
noHook: false,
|
|
38
38
|
noBounce: false,
|
|
39
39
|
autoUpdate: false,
|
|
40
|
+
background: false,
|
|
40
41
|
};
|
|
41
42
|
const positional = [];
|
|
42
43
|
const only = [];
|
|
@@ -56,6 +57,8 @@ function parseArgs(argv) {
|
|
|
56
57
|
flags.noBounce = true;
|
|
57
58
|
else if (a === "--auto-update")
|
|
58
59
|
flags.autoUpdate = true;
|
|
60
|
+
else if (a === "--background")
|
|
61
|
+
flags.background = true;
|
|
59
62
|
else if (a === "-h" || a === "--help")
|
|
60
63
|
flags.help = true;
|
|
61
64
|
else if (a === "-v" || a === "--version")
|
|
@@ -128,6 +131,7 @@ async function main() {
|
|
|
128
131
|
exitCode = await runUpdate({
|
|
129
132
|
yes: args.flags.yes,
|
|
130
133
|
dryRun: args.flags.dryRun,
|
|
134
|
+
background: args.flags.background,
|
|
131
135
|
targets: args.targets.length > 0 ? args.targets : undefined,
|
|
132
136
|
});
|
|
133
137
|
break;
|
package/dist/render.js
CHANGED
|
@@ -81,6 +81,9 @@ export function renderMcpForTarget(mcp, spec) {
|
|
|
81
81
|
obj.type = "remote";
|
|
82
82
|
obj.enabled = true;
|
|
83
83
|
}
|
|
84
|
+
else if (spec.mcpHttpTypeValue) {
|
|
85
|
+
obj.type = spec.mcpHttpTypeValue;
|
|
86
|
+
}
|
|
84
87
|
if (mcp.auth.type === "gateway") {
|
|
85
88
|
if (!mcp.auth.tokenEnv) {
|
|
86
89
|
throw new Error(`MCP '${mcp.name}' es type=gateway pero no trae tokenEnv`);
|