@meistrari/tela-cli 1.11.0 → 1.12.1
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/skills.js +9 -3
- package/dist/index.js +287 -102
- package/package.json +1 -1
package/dist/commands/skills.js
CHANGED
|
@@ -177,15 +177,21 @@ async function installSkills(args, options, io) {
|
|
|
177
177
|
ignoreScripts: flags.ignoreScripts,
|
|
178
178
|
...(flags.symlink ? {} : { targets: [] }),
|
|
179
179
|
});
|
|
180
|
+
// A failed install script rolls back only its own skill, so report the rest and still fail.
|
|
181
|
+
const installedSlugs = new Set(installed.map(entry => entry.slug));
|
|
182
|
+
const failed = flags.rest.filter(slug => !installedSlugs.has(slug));
|
|
180
183
|
if (options.json) {
|
|
181
|
-
(0, output_1.writeJson)(io, { installed });
|
|
182
|
-
return 0;
|
|
184
|
+
(0, output_1.writeJson)(io, failed.length > 0 ? { installed, failed } : { installed });
|
|
185
|
+
return failed.length > 0 ? 1 : 0;
|
|
183
186
|
}
|
|
184
187
|
for (const entry of installed)
|
|
185
188
|
(0, output_1.writeLine)(io, `Installed ${entry.slug}@${entry.sha.slice(0, 7)} to ${entry.dir}`);
|
|
186
|
-
return 0;
|
|
189
|
+
return failed.length > 0 ? 1 : 0;
|
|
187
190
|
}
|
|
188
191
|
function writeSkillManagerDiagnostic(io, diagnostic) {
|
|
192
|
+
// A failed uninstall script throws, and the command prints that error.
|
|
193
|
+
if (diagnostic.type === 'lifecycle-failed' && diagnostic.phase === 'uninstall')
|
|
194
|
+
return;
|
|
189
195
|
(0, output_1.writeError)(io, diagnostic.message);
|
|
190
196
|
}
|
|
191
197
|
async function upgradeSkills(args, options, io) {
|
package/dist/index.js
CHANGED
|
@@ -77236,9 +77236,7 @@ var require_skill_filesystem = __commonJS({
|
|
|
77236
77236
|
var node_os_1 = require("node:os");
|
|
77237
77237
|
var node_path_1 = __importDefault(require("node:path"));
|
|
77238
77238
|
var node_process_1 = __importDefault(require("node:process"));
|
|
77239
|
-
var node_util_1 = require("node:util");
|
|
77240
77239
|
var archive_1 = require_archive();
|
|
77241
|
-
var run = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
77242
77240
|
function resolveDefaultSkillManagerPaths(home = (0, node_os_1.homedir)()) {
|
|
77243
77241
|
return {
|
|
77244
77242
|
installRoot: node_path_1.default.join(home, ".agents", "skills"),
|
|
@@ -77425,14 +77423,72 @@ There is now nothing at ${target}. The previous installation is intact at ${back
|
|
|
77425
77423
|
return { removed: false, reason: `Skipped ${linkPath}: ${describeError(error48)}` };
|
|
77426
77424
|
}
|
|
77427
77425
|
}
|
|
77428
|
-
|
|
77426
|
+
var SCRIPT_KILL_GRACE_MS = 3e3;
|
|
77427
|
+
var SCRIPT_GROUP_POLL_MS = 50;
|
|
77428
|
+
var MAX_SCRIPT_OUTPUT_LENGTH = 64 * 1024;
|
|
77429
|
+
async function runSkillScript(script, cwd, onOutput, signal) {
|
|
77430
|
+
if (signal?.aborted)
|
|
77431
|
+
return { ok: false, cancelled: true, output: "" };
|
|
77432
|
+
return await new Promise((resolve) => {
|
|
77433
|
+
const child = (0, node_child_process_1.spawn)("sh", ["-c", script], { cwd, detached: node_process_1.default.platform !== "win32" });
|
|
77434
|
+
let output = "";
|
|
77435
|
+
let settled = false;
|
|
77436
|
+
let cancelled = false;
|
|
77437
|
+
const append2 = (chunk) => {
|
|
77438
|
+
if (settled)
|
|
77439
|
+
return;
|
|
77440
|
+
const text2 = chunk.toString();
|
|
77441
|
+
output = `${output}${text2}`.slice(-MAX_SCRIPT_OUTPUT_LENGTH);
|
|
77442
|
+
onOutput?.(text2);
|
|
77443
|
+
};
|
|
77444
|
+
const finish = (ok) => {
|
|
77445
|
+
if (settled)
|
|
77446
|
+
return;
|
|
77447
|
+
settled = true;
|
|
77448
|
+
signal?.removeEventListener("abort", cancel2);
|
|
77449
|
+
resolve({ ok: ok && !cancelled, cancelled, output: output.trimEnd() });
|
|
77450
|
+
};
|
|
77451
|
+
function cancel2() {
|
|
77452
|
+
cancelled = true;
|
|
77453
|
+
killProcessTree(child, "SIGTERM");
|
|
77454
|
+
setTimeout(() => killProcessTree(child, "SIGKILL"), SCRIPT_KILL_GRACE_MS).unref();
|
|
77455
|
+
}
|
|
77456
|
+
signal?.addEventListener("abort", cancel2, { once: true });
|
|
77457
|
+
child.stdout.on("data", append2);
|
|
77458
|
+
child.stderr.on("data", append2);
|
|
77459
|
+
child.on("error", (error48) => {
|
|
77460
|
+
append2(`${describeError(error48)}
|
|
77461
|
+
`);
|
|
77462
|
+
finish(false);
|
|
77463
|
+
});
|
|
77464
|
+
child.on("exit", () => {
|
|
77465
|
+
if (cancelled)
|
|
77466
|
+
void waitForProcessGroupExit(child).then(() => finish(false));
|
|
77467
|
+
});
|
|
77468
|
+
child.on("close", (code) => finish(code === 0));
|
|
77469
|
+
});
|
|
77470
|
+
}
|
|
77471
|
+
async function waitForProcessGroupExit(child) {
|
|
77472
|
+
if (child.pid === void 0 || node_process_1.default.platform === "win32")
|
|
77473
|
+
return;
|
|
77474
|
+
while (true) {
|
|
77475
|
+
try {
|
|
77476
|
+
node_process_1.default.kill(-child.pid, 0);
|
|
77477
|
+
} catch {
|
|
77478
|
+
return;
|
|
77479
|
+
}
|
|
77480
|
+
await new Promise((resolve) => setTimeout(resolve, SCRIPT_GROUP_POLL_MS));
|
|
77481
|
+
}
|
|
77482
|
+
}
|
|
77483
|
+
function killProcessTree(child, signal) {
|
|
77484
|
+
if (child.pid === void 0)
|
|
77485
|
+
return;
|
|
77429
77486
|
try {
|
|
77430
|
-
|
|
77431
|
-
|
|
77432
|
-
|
|
77433
|
-
|
|
77434
|
-
|
|
77435
|
-
return { ok: false, output };
|
|
77487
|
+
if (node_process_1.default.platform === "win32")
|
|
77488
|
+
(0, node_child_process_1.spawn)("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
77489
|
+
else
|
|
77490
|
+
node_process_1.default.kill(-child.pid, signal);
|
|
77491
|
+
} catch {
|
|
77436
77492
|
}
|
|
77437
77493
|
}
|
|
77438
77494
|
function describeError(error48) {
|
|
@@ -77489,8 +77545,8 @@ var require_skills = __commonJS({
|
|
|
77489
77545
|
...path2 ? { queries: { path: path2 } } : {}
|
|
77490
77546
|
}));
|
|
77491
77547
|
}
|
|
77492
|
-
async function downloadSkillArchive(archiveUrl) {
|
|
77493
|
-
const response = await fetch(archiveUrl);
|
|
77548
|
+
async function downloadSkillArchive(archiveUrl, signal) {
|
|
77549
|
+
const response = await fetch(archiveUrl, { signal });
|
|
77494
77550
|
if (!response.ok) {
|
|
77495
77551
|
(0, http_1.assertHttpOk)(response, await response.text().catch(() => ""), "Failed to download skill archive");
|
|
77496
77552
|
}
|
|
@@ -77507,7 +77563,7 @@ var require_skill_manager = __commonJS({
|
|
|
77507
77563
|
return mod && mod.__esModule ? mod : { "default": mod };
|
|
77508
77564
|
};
|
|
77509
77565
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
77510
|
-
exports2.SkillInstallDirectoryConflictError = exports2.SkillLifecycleError = exports2.resolveDefaultSkillManagerPaths = void 0;
|
|
77566
|
+
exports2.SkillInstallDirectoryConflictError = exports2.SkillInstallCancelledError = exports2.SkillLifecycleError = exports2.resolveDefaultSkillManagerPaths = void 0;
|
|
77511
77567
|
exports2.createSkillManager = createSkillManager2;
|
|
77512
77568
|
var promises_1 = require("node:fs/promises");
|
|
77513
77569
|
var node_path_1 = __importDefault(require("node:path"));
|
|
@@ -77529,10 +77585,22 @@ var require_skill_manager = __commonJS({
|
|
|
77529
77585
|
this.phase = phase;
|
|
77530
77586
|
this.name = "SkillLifecycleError";
|
|
77531
77587
|
this.slug = entry.slug;
|
|
77532
|
-
this.retainedState =
|
|
77588
|
+
this.retainedState = "unchanged";
|
|
77533
77589
|
}
|
|
77534
77590
|
};
|
|
77535
77591
|
exports2.SkillLifecycleError = SkillLifecycleError;
|
|
77592
|
+
var SkillInstallCancelledError = class extends Error {
|
|
77593
|
+
slug;
|
|
77594
|
+
installed;
|
|
77595
|
+
code = "skill-install-cancelled";
|
|
77596
|
+
constructor(slug, installed) {
|
|
77597
|
+
super(`Installation was cancelled before ${slug} finished.`);
|
|
77598
|
+
this.slug = slug;
|
|
77599
|
+
this.installed = installed;
|
|
77600
|
+
this.name = "SkillInstallCancelledError";
|
|
77601
|
+
}
|
|
77602
|
+
};
|
|
77603
|
+
exports2.SkillInstallCancelledError = SkillInstallCancelledError;
|
|
77536
77604
|
var SkillInstallDirectoryConflictError = class extends Error {
|
|
77537
77605
|
code = "skill-install-directory-conflict";
|
|
77538
77606
|
path;
|
|
@@ -77567,17 +77635,31 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77567
77635
|
const diagnostics = [];
|
|
77568
77636
|
const installed = [];
|
|
77569
77637
|
const emit = createDiagnosticEmitter(options2, diagnostics);
|
|
77638
|
+
const signal = installOptions.signal;
|
|
77570
77639
|
for (const slug of slugs) {
|
|
77571
|
-
|
|
77572
|
-
|
|
77573
|
-
|
|
77574
|
-
|
|
77575
|
-
|
|
77576
|
-
|
|
77577
|
-
|
|
77578
|
-
|
|
77579
|
-
|
|
77580
|
-
|
|
77640
|
+
if (signal?.aborted)
|
|
77641
|
+
throw new SkillInstallCancelledError(slug, installed);
|
|
77642
|
+
let entry;
|
|
77643
|
+
try {
|
|
77644
|
+
entry = await installOne({
|
|
77645
|
+
client,
|
|
77646
|
+
diagnostics: emit,
|
|
77647
|
+
force: installOptions.force ?? false,
|
|
77648
|
+
ignoreScripts: installOptions.ignoreScripts ?? false,
|
|
77649
|
+
inventoryFile,
|
|
77650
|
+
paths,
|
|
77651
|
+
progress: options2.onInstallProgress,
|
|
77652
|
+
signal,
|
|
77653
|
+
slug,
|
|
77654
|
+
targets
|
|
77655
|
+
});
|
|
77656
|
+
} catch (error48) {
|
|
77657
|
+
if (signal?.aborted)
|
|
77658
|
+
throw new SkillInstallCancelledError(slug, installed);
|
|
77659
|
+
if (error48 instanceof SkillLifecycleError && error48.phase === "install")
|
|
77660
|
+
continue;
|
|
77661
|
+
throw error48;
|
|
77662
|
+
}
|
|
77581
77663
|
if (!entry)
|
|
77582
77664
|
throw new Error(`Skill install for ${slug} did not produce an inventory entry.`);
|
|
77583
77665
|
installed.push(entry);
|
|
@@ -77598,54 +77680,69 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77598
77680
|
return { upgraded, unchanged, replaced, diagnostics };
|
|
77599
77681
|
const targets = normalizeTargets(upgradeOptions.targets ?? paths.targets, paths.installRoot);
|
|
77600
77682
|
const client = await resolveClient();
|
|
77601
|
-
|
|
77602
|
-
|
|
77603
|
-
|
|
77604
|
-
|
|
77605
|
-
|
|
77606
|
-
|
|
77683
|
+
const signal = upgradeOptions.signal;
|
|
77684
|
+
let currentSlug = "";
|
|
77685
|
+
try {
|
|
77686
|
+
for (const current of selected) {
|
|
77687
|
+
currentSlug = current.slug;
|
|
77688
|
+
signal?.throwIfAborted();
|
|
77689
|
+
const { ingredient, snapshot } = await (0, skills_1.fetchSkillFile)(client, current.slug);
|
|
77690
|
+
const replacementSlug = await resolveReplacementSlug(client, current.slug, ingredient);
|
|
77691
|
+
if (replacementSlug) {
|
|
77692
|
+
const installedReplacement = (await (0, tela_local_config_1.listInstalledSkills)(inventoryFile)).find((entry2) => entry2.slug === replacementSlug);
|
|
77693
|
+
const replacement = await installOne({
|
|
77694
|
+
client,
|
|
77695
|
+
diagnostics: emit,
|
|
77696
|
+
...installedReplacement ? { expectedCurrent: installedReplacement, operation: "upgrade" } : {},
|
|
77697
|
+
force: upgradeOptions.force ?? false,
|
|
77698
|
+
ignoreScripts: upgradeOptions.ignoreScripts ?? false,
|
|
77699
|
+
inventoryFile,
|
|
77700
|
+
paths,
|
|
77701
|
+
progress: options2.onInstallProgress,
|
|
77702
|
+
signal,
|
|
77703
|
+
slug: replacementSlug,
|
|
77704
|
+
targets
|
|
77705
|
+
});
|
|
77706
|
+
if (!replacement)
|
|
77707
|
+
continue;
|
|
77708
|
+
signal?.throwIfAborted();
|
|
77709
|
+
if (await uninstallOne({
|
|
77710
|
+
diagnostics: emit,
|
|
77711
|
+
entry: current,
|
|
77712
|
+
ignoreScripts: upgradeOptions.ignoreScripts ?? false,
|
|
77713
|
+
inventoryFile,
|
|
77714
|
+
operation: "upgrade",
|
|
77715
|
+
paths
|
|
77716
|
+
})) {
|
|
77717
|
+
replaced.push({ from: current.slug, to: replacement });
|
|
77718
|
+
}
|
|
77719
|
+
continue;
|
|
77720
|
+
}
|
|
77721
|
+
if (snapshot.sha === current.sha) {
|
|
77722
|
+
unchanged.push(current.slug);
|
|
77723
|
+
continue;
|
|
77724
|
+
}
|
|
77725
|
+
const entry = await installOne({
|
|
77607
77726
|
client,
|
|
77608
77727
|
diagnostics: emit,
|
|
77609
|
-
|
|
77728
|
+
expectedCurrent: current,
|
|
77610
77729
|
force: upgradeOptions.force ?? false,
|
|
77611
77730
|
ignoreScripts: upgradeOptions.ignoreScripts ?? false,
|
|
77612
77731
|
inventoryFile,
|
|
77732
|
+
operation: "upgrade",
|
|
77613
77733
|
paths,
|
|
77614
|
-
|
|
77734
|
+
progress: options2.onInstallProgress,
|
|
77735
|
+
signal,
|
|
77736
|
+
slug: current.slug,
|
|
77615
77737
|
targets
|
|
77616
77738
|
});
|
|
77617
|
-
if (
|
|
77618
|
-
|
|
77619
|
-
if (await uninstallOne({
|
|
77620
|
-
diagnostics: emit,
|
|
77621
|
-
entry: current,
|
|
77622
|
-
ignoreScripts: upgradeOptions.ignoreScripts ?? false,
|
|
77623
|
-
inventoryFile,
|
|
77624
|
-
operation: "upgrade",
|
|
77625
|
-
paths
|
|
77626
|
-
})) {
|
|
77627
|
-
replaced.push({ from: current.slug, to: replacement });
|
|
77628
|
-
}
|
|
77629
|
-
continue;
|
|
77630
|
-
}
|
|
77631
|
-
if (snapshot.sha === current.sha) {
|
|
77632
|
-
unchanged.push(current.slug);
|
|
77633
|
-
continue;
|
|
77739
|
+
if (entry)
|
|
77740
|
+
upgraded.push(entry);
|
|
77634
77741
|
}
|
|
77635
|
-
|
|
77636
|
-
|
|
77637
|
-
|
|
77638
|
-
|
|
77639
|
-
force: upgradeOptions.force ?? false,
|
|
77640
|
-
ignoreScripts: upgradeOptions.ignoreScripts ?? false,
|
|
77641
|
-
inventoryFile,
|
|
77642
|
-
operation: "upgrade",
|
|
77643
|
-
paths,
|
|
77644
|
-
slug: current.slug,
|
|
77645
|
-
targets
|
|
77646
|
-
});
|
|
77647
|
-
if (entry)
|
|
77648
|
-
upgraded.push(entry);
|
|
77742
|
+
} catch (error48) {
|
|
77743
|
+
if (signal?.aborted)
|
|
77744
|
+
throw new SkillInstallCancelledError(currentSlug, upgraded);
|
|
77745
|
+
throw error48;
|
|
77649
77746
|
}
|
|
77650
77747
|
return { upgraded, unchanged, replaced, diagnostics };
|
|
77651
77748
|
},
|
|
@@ -77739,30 +77836,68 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77739
77836
|
};
|
|
77740
77837
|
}
|
|
77741
77838
|
async function installOne(options2) {
|
|
77839
|
+
options2.progress?.({
|
|
77840
|
+
type: "phase",
|
|
77841
|
+
slug: options2.slug,
|
|
77842
|
+
phase: "fetching-metadata",
|
|
77843
|
+
message: "Fetching skill metadata"
|
|
77844
|
+
});
|
|
77742
77845
|
const { ingredient, snapshot } = await (0, skills_1.fetchSkillFile)(options2.client, options2.slug);
|
|
77743
|
-
|
|
77846
|
+
options2.progress?.({
|
|
77847
|
+
type: "phase",
|
|
77848
|
+
slug: options2.slug,
|
|
77849
|
+
phase: "downloading",
|
|
77850
|
+
message: "Downloading skill files"
|
|
77851
|
+
});
|
|
77852
|
+
const archive = await (0, skills_1.downloadSkillArchive)(snapshot.archiveUrl, options2.signal);
|
|
77853
|
+
options2.signal?.throwIfAborted();
|
|
77854
|
+
options2.progress?.({
|
|
77855
|
+
type: "phase",
|
|
77856
|
+
slug: options2.slug,
|
|
77857
|
+
phase: "extracting",
|
|
77858
|
+
message: "Verifying and extracting files"
|
|
77859
|
+
});
|
|
77744
77860
|
const files = (0, archive_1.verifySnapshotFiles)((0, archive_1.parseTarGz)(archive), snapshot.manifest);
|
|
77745
77861
|
const target = (0, skill_filesystem_1.resolveInstallDir)(options2.paths, options2.slug);
|
|
77746
|
-
const
|
|
77862
|
+
const reportConcurrentChange = () => options2.diagnostics({
|
|
77863
|
+
type: "concurrent-change",
|
|
77864
|
+
slug: options2.slug,
|
|
77865
|
+
operation: options2.operation ?? "install",
|
|
77866
|
+
message: `Skipped ${options2.slug}: it was changed while this ${options2.operation ?? "install"} ran`
|
|
77867
|
+
});
|
|
77868
|
+
const before = (await (0, tela_local_config_1.listInstalledSkills)(options2.inventoryFile)).find((entry2) => entry2.slug === options2.slug) ?? null;
|
|
77869
|
+
if (options2.expectedCurrent && !isSameRegistration(before, options2.expectedCurrent)) {
|
|
77870
|
+
reportConcurrentChange();
|
|
77871
|
+
return null;
|
|
77872
|
+
}
|
|
77873
|
+
if ((0, skill_filesystem_1.isUnownedInstallDir)(target, before?.dir ?? null) && !options2.force)
|
|
77874
|
+
throw new SkillInstallDirectoryConflictError(options2.slug, target);
|
|
77875
|
+
const staged = await (0, skill_filesystem_1.stageSkillFiles)(target, files);
|
|
77747
77876
|
let concurrentChange = false;
|
|
77748
77877
|
let entry = null;
|
|
77749
77878
|
try {
|
|
77879
|
+
const lifecycleEntry = {
|
|
77880
|
+
slug: options2.slug,
|
|
77881
|
+
dir: target,
|
|
77882
|
+
sha: snapshot.sha,
|
|
77883
|
+
bundle: ingredient.bundle,
|
|
77884
|
+
symlinks: [],
|
|
77885
|
+
scripts: ingredient.scripts,
|
|
77886
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
77887
|
+
};
|
|
77888
|
+
await runLifecycle(lifecycleEntry, "install", options2.ignoreScripts, options2.diagnostics, options2.progress, options2.signal);
|
|
77889
|
+
options2.signal?.throwIfAborted();
|
|
77890
|
+
options2.progress?.({
|
|
77891
|
+
type: "phase",
|
|
77892
|
+
slug: options2.slug,
|
|
77893
|
+
phase: "finalizing",
|
|
77894
|
+
message: "Linking and finalizing installation"
|
|
77895
|
+
});
|
|
77750
77896
|
entry = await (0, tela_local_config_1.updateInstalledSkill)(options2.slug, async (registered) => {
|
|
77751
|
-
if (
|
|
77897
|
+
if (!isSameRegistration(registered, before)) {
|
|
77752
77898
|
concurrentChange = true;
|
|
77753
|
-
options2.diagnostics({
|
|
77754
|
-
type: "concurrent-change",
|
|
77755
|
-
slug: options2.slug,
|
|
77756
|
-
operation: options2.operation ?? "install",
|
|
77757
|
-
message: `Skipped ${options2.slug}: it was changed while this upgrade ran`
|
|
77758
|
-
});
|
|
77759
77899
|
return registered;
|
|
77760
77900
|
}
|
|
77761
|
-
if ((0, skill_filesystem_1.isUnownedInstallDir)(target, registered?.dir ?? null) && !options2.force) {
|
|
77762
|
-
throw new SkillInstallDirectoryConflictError(options2.slug, target);
|
|
77763
|
-
}
|
|
77764
|
-
const staged = await (0, skill_filesystem_1.stageSkillFiles)(target, files);
|
|
77765
|
-
stagedInstalls.push(staged);
|
|
77766
77901
|
const desiredLinkPaths = new Set(options2.targets.map((linkTarget) => node_path_1.default.join(linkTarget.root, options2.slug)));
|
|
77767
77902
|
for (const linkPath of registered?.symlinks ?? []) {
|
|
77768
77903
|
if (desiredLinkPaths.has(linkPath))
|
|
@@ -77791,25 +77926,27 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77791
77926
|
}
|
|
77792
77927
|
}
|
|
77793
77928
|
return {
|
|
77794
|
-
|
|
77795
|
-
|
|
77796
|
-
sha: snapshot.sha,
|
|
77797
|
-
bundle: ingredient.bundle,
|
|
77798
|
-
symlinks,
|
|
77799
|
-
scripts: ingredient.scripts,
|
|
77800
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
77929
|
+
...lifecycleEntry,
|
|
77930
|
+
symlinks
|
|
77801
77931
|
};
|
|
77802
77932
|
}, options2.inventoryFile);
|
|
77803
77933
|
} catch (error48) {
|
|
77804
|
-
|
|
77934
|
+
options2.progress?.({
|
|
77935
|
+
type: "phase",
|
|
77936
|
+
slug: options2.slug,
|
|
77937
|
+
phase: "rolling-back",
|
|
77938
|
+
message: "Rolling back failed installation"
|
|
77939
|
+
});
|
|
77940
|
+
await rollbackStagedInstall(staged, error48);
|
|
77805
77941
|
}
|
|
77806
|
-
if (concurrentChange)
|
|
77942
|
+
if (concurrentChange) {
|
|
77943
|
+
await staged.rollback();
|
|
77944
|
+
reportConcurrentChange();
|
|
77807
77945
|
return null;
|
|
77808
|
-
|
|
77809
|
-
|
|
77946
|
+
}
|
|
77947
|
+
await staged.commit();
|
|
77810
77948
|
if (!entry)
|
|
77811
77949
|
throw new Error(`Skill install for ${options2.slug} did not produce an inventory entry.`);
|
|
77812
|
-
await runLifecycle(entry, "install", options2.ignoreScripts, options2.diagnostics);
|
|
77813
77950
|
return entry;
|
|
77814
77951
|
}
|
|
77815
77952
|
function selectUpgradeTargets(installed, slugs) {
|
|
@@ -77828,6 +77965,11 @@ Run \`tela skills install ${slug} --force\` to replace it, or remove it by hand.
|
|
|
77828
77965
|
throw new Error(`${missing.join(", ")} not installed by tela. Nothing to remove.`);
|
|
77829
77966
|
return slugs.map((slug) => bySlug.get(slug));
|
|
77830
77967
|
}
|
|
77968
|
+
function isSameRegistration(left, right) {
|
|
77969
|
+
if (left === null || right === null)
|
|
77970
|
+
return left === right;
|
|
77971
|
+
return isSameInstall(left, right);
|
|
77972
|
+
}
|
|
77831
77973
|
function isSameInstall(left, right) {
|
|
77832
77974
|
return left.installedAt === right.installedAt && left.sha === right.sha && left.dir === right.dir;
|
|
77833
77975
|
}
|
|
@@ -77848,7 +77990,7 @@ Cleanup failed: ${describeError(cleanupError)}`);
|
|
|
77848
77990
|
}
|
|
77849
77991
|
throw error48;
|
|
77850
77992
|
}
|
|
77851
|
-
async function runLifecycle(entry, phase, ignoreScripts, emit) {
|
|
77993
|
+
async function runLifecycle(entry, phase, ignoreScripts, emit, progress2, signal) {
|
|
77852
77994
|
const script = entry.scripts?.[phase];
|
|
77853
77995
|
if (!script)
|
|
77854
77996
|
return;
|
|
@@ -77869,7 +78011,17 @@ Cleanup failed: ${describeError(cleanupError)}`);
|
|
|
77869
78011
|
script,
|
|
77870
78012
|
message: `Running ${phase} script for ${entry.slug}: ${script}`
|
|
77871
78013
|
});
|
|
77872
|
-
|
|
78014
|
+
if (phase === "install") {
|
|
78015
|
+
progress2?.({
|
|
78016
|
+
type: "phase",
|
|
78017
|
+
slug: entry.slug,
|
|
78018
|
+
phase: "running-install-script",
|
|
78019
|
+
message: "Running install script"
|
|
78020
|
+
});
|
|
78021
|
+
}
|
|
78022
|
+
const outcome = await (0, skill_filesystem_1.runSkillScript)(script, entry.dir, phase === "install" ? (output) => progress2?.({ type: "script-output", slug: entry.slug, output }) : void 0, signal);
|
|
78023
|
+
if (outcome.cancelled)
|
|
78024
|
+
signal?.throwIfAborted();
|
|
77873
78025
|
if (outcome.output) {
|
|
77874
78026
|
emit({
|
|
77875
78027
|
type: "lifecycle-output",
|
|
@@ -77880,7 +78032,14 @@ Cleanup failed: ${describeError(cleanupError)}`);
|
|
|
77880
78032
|
});
|
|
77881
78033
|
}
|
|
77882
78034
|
if (!outcome.ok) {
|
|
77883
|
-
|
|
78035
|
+
const error48 = new SkillLifecycleError(entry, phase);
|
|
78036
|
+
emit({
|
|
78037
|
+
type: "lifecycle-failed",
|
|
78038
|
+
slug: entry.slug,
|
|
78039
|
+
phase,
|
|
78040
|
+
message: error48.message
|
|
78041
|
+
});
|
|
78042
|
+
throw error48;
|
|
77884
78043
|
}
|
|
77885
78044
|
}
|
|
77886
78045
|
function lifecycleFailureMessage(entry, phase) {
|
|
@@ -77889,8 +78048,8 @@ Cleanup failed: ${describeError(cleanupError)}`);
|
|
|
77889
78048
|
return `${opener}
|
|
77890
78049
|
${entry.slug} was left in place. Re-run with --ignore-scripts to remove it anyway.`;
|
|
77891
78050
|
return `${opener}
|
|
77892
|
-
${entry.slug} was installed
|
|
77893
|
-
|
|
78051
|
+
${entry.slug} was not installed because its files and inventory entry were rolled back.
|
|
78052
|
+
Retry without the script with \`tela skills install ${entry.slug} --ignore-scripts\`.`;
|
|
77894
78053
|
}
|
|
77895
78054
|
function normalizePaths(paths) {
|
|
77896
78055
|
const installRoot = normalizeAbsolutePath(paths.installRoot, "installRoot");
|
|
@@ -386211,7 +386370,11 @@ var require_setup = __commonJS({
|
|
|
386211
386370
|
exports2.findAgentSetupOrganization = findAgentSetupOrganization;
|
|
386212
386371
|
exports2.validatePreparedAgentSetupInput = validatePreparedAgentSetupInput;
|
|
386213
386372
|
exports2.saveAgentContextSetup = saveAgentContextSetup;
|
|
386373
|
+
exports2.provisionForgejoUserWithSshKey = provisionForgejoUserWithSshKey;
|
|
386374
|
+
exports2.createUniqueSshKeyTitle = createUniqueSshKeyTitle;
|
|
386214
386375
|
exports2.completeAgentSetup = completeAgentSetup;
|
|
386376
|
+
var node_buffer_1 = require("node:buffer");
|
|
386377
|
+
var node_crypto_1 = require("node:crypto");
|
|
386215
386378
|
var node_fs_1 = require("node:fs");
|
|
386216
386379
|
var node_os_1 = require("node:os");
|
|
386217
386380
|
var node_path_1 = require("node:path");
|
|
@@ -386596,6 +386759,28 @@ var require_setup = __commonJS({
|
|
|
386596
386759
|
}
|
|
386597
386760
|
return savedSetup;
|
|
386598
386761
|
}
|
|
386762
|
+
var SSH_KEY_TITLE_CONFLICT = /ssh key title already belongs to a different key/i;
|
|
386763
|
+
async function provisionForgejoUserWithSshKey(organizationName, sshKey, config2, provisionFn, fetchFn) {
|
|
386764
|
+
try {
|
|
386765
|
+
return {
|
|
386766
|
+
response: await provisionFn({ organizationName, sshKey }, config2, fetchFn),
|
|
386767
|
+
title: sshKey.title
|
|
386768
|
+
};
|
|
386769
|
+
} catch (error48) {
|
|
386770
|
+
if (!(error48 instanceof agent_api_1.AgentApiError) || !SSH_KEY_TITLE_CONFLICT.test(error48.message))
|
|
386771
|
+
throw error48;
|
|
386772
|
+
}
|
|
386773
|
+
const title = createUniqueSshKeyTitle(sshKey);
|
|
386774
|
+
return {
|
|
386775
|
+
response: await provisionFn({ organizationName, sshKey: { ...sshKey, title } }, config2, fetchFn),
|
|
386776
|
+
title
|
|
386777
|
+
};
|
|
386778
|
+
}
|
|
386779
|
+
function createUniqueSshKeyTitle(sshKey, host = (0, node_os_1.hostname)()) {
|
|
386780
|
+
const blob = sshKey.key.split(/\s+/)[1] ?? sshKey.key;
|
|
386781
|
+
const fingerprint = (0, node_crypto_1.createHash)("sha256").update(node_buffer_1.Buffer.from(blob, "base64")).digest("base64").replace(/=+$/, "").slice(0, 12);
|
|
386782
|
+
return `${sshKey.title} (${host}, SHA256:${fingerprint})`;
|
|
386783
|
+
}
|
|
386599
386784
|
async function completeAgentSetup(input, options2 = {}) {
|
|
386600
386785
|
const credentials = (0, credentials_1.createAgentCredentialAdapter)(options2.env);
|
|
386601
386786
|
const config2 = await (0, config_1.resolveAgentConfig)({
|
|
@@ -386611,15 +386796,11 @@ var require_setup = __commonJS({
|
|
|
386611
386796
|
const sshKey = loadSshPublicKey(input.sshPublicKeyPath);
|
|
386612
386797
|
const gitSshKey = createGitSshKey(sshKey, input.sshPrivateKeyPath);
|
|
386613
386798
|
const cloneRoot = input.cloneRoot ? resolveCloneRootPath(input.cloneRoot) : config2.cloneRoot;
|
|
386614
|
-
const
|
|
386615
|
-
const response = await provisionFn({
|
|
386616
|
-
organizationName: organization.name,
|
|
386617
|
-
sshKey
|
|
386618
|
-
}, config2, options2.fetchFn);
|
|
386799
|
+
const { response, title } = await provisionForgejoUserWithSshKey(organization.name, sshKey, config2, options2.provisionFn ?? agent_api_1.provisionForgejoUser, options2.fetchFn);
|
|
386619
386800
|
const setup = await saveAgentContextSetup({
|
|
386620
386801
|
config: config2,
|
|
386621
386802
|
organization,
|
|
386622
|
-
gitSshKey,
|
|
386803
|
+
gitSshKey: { ...gitSshKey, title },
|
|
386623
386804
|
cloneRoot
|
|
386624
386805
|
});
|
|
386625
386806
|
return {
|
|
@@ -389332,15 +389513,19 @@ async function installSkills(args, options2, io) {
|
|
|
389332
389513
|
ignoreScripts: flags.ignoreScripts,
|
|
389333
389514
|
...flags.symlink ? {} : { targets: [] }
|
|
389334
389515
|
});
|
|
389516
|
+
const installedSlugs = new Set(installed.map((entry) => entry.slug));
|
|
389517
|
+
const failed = flags.rest.filter((slug) => !installedSlugs.has(slug));
|
|
389335
389518
|
if (options2.json) {
|
|
389336
|
-
writeJson(io, { installed });
|
|
389337
|
-
return 0;
|
|
389519
|
+
writeJson(io, failed.length > 0 ? { installed, failed } : { installed });
|
|
389520
|
+
return failed.length > 0 ? 1 : 0;
|
|
389338
389521
|
}
|
|
389339
389522
|
for (const entry of installed)
|
|
389340
389523
|
writeLine(io, `Installed ${entry.slug}@${entry.sha.slice(0, 7)} to ${entry.dir}`);
|
|
389341
|
-
return 0;
|
|
389524
|
+
return failed.length > 0 ? 1 : 0;
|
|
389342
389525
|
}
|
|
389343
389526
|
function writeSkillManagerDiagnostic(io, diagnostic) {
|
|
389527
|
+
if (diagnostic.type === "lifecycle-failed" && diagnostic.phase === "uninstall")
|
|
389528
|
+
return;
|
|
389344
389529
|
writeError(io, diagnostic.message);
|
|
389345
389530
|
}
|
|
389346
389531
|
async function upgradeSkills(args, options2, io) {
|