@cannbot-ai/install-helper 1.2.1-beta.0 → 1.2.1-beta.2
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/index.js +112 -40
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -2913,7 +2913,7 @@ function createRepositoryManager(customPath) {
|
|
|
2913
2913
|
|
|
2914
2914
|
// src/core/cannbot-delegate.ts
|
|
2915
2915
|
import { existsSync as existsSync17, readFileSync as readFileSync12, writeFileSync as writeFileSync5, copyFileSync as copyFileSync3, unlinkSync as unlinkSync6, readdirSync as readdirSync8 } from "fs";
|
|
2916
|
-
import { join as join16, resolve as
|
|
2916
|
+
import { join as join16, resolve as resolve3 } from "path";
|
|
2917
2917
|
import { execa as execa6 } from "execa";
|
|
2918
2918
|
|
|
2919
2919
|
// src/core/cannbot-installer.ts
|
|
@@ -3154,7 +3154,7 @@ async function ensureCannbotDiscovery() {
|
|
|
3154
3154
|
|
|
3155
3155
|
// src/core/cannbot-registry.ts
|
|
3156
3156
|
import { existsSync as existsSync13, readFileSync as readFileSync9, readdirSync as readdirSync6, rmSync as rmSync3, rmdirSync, unlinkSync as unlinkSync4, lstatSync as lstatSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
3157
|
-
import { join as join12, resolve, sep as sep2, dirname as dirname6 } from "path";
|
|
3157
|
+
import { join as join12, resolve as resolve2, sep as sep2, dirname as dirname6 } from "path";
|
|
3158
3158
|
|
|
3159
3159
|
// src/core/record.ts
|
|
3160
3160
|
init_paths();
|
|
@@ -3163,38 +3163,88 @@ init_fs_helpers();
|
|
|
3163
3163
|
init_logger();
|
|
3164
3164
|
init_i18n();
|
|
3165
3165
|
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync8, unlinkSync as unlinkSync3, readdirSync as readdirSync5 } from "fs";
|
|
3166
|
-
import { join as join11 } from "path";
|
|
3166
|
+
import { join as join11, resolve } from "path";
|
|
3167
|
+
var LEGACY_LOCATION_KEY = "__legacy__";
|
|
3167
3168
|
function getInstallsDir() {
|
|
3168
3169
|
return join11(getCannbotConfigDir(), "installs");
|
|
3169
3170
|
}
|
|
3170
3171
|
function getRecordPath(pluginId) {
|
|
3171
3172
|
return join11(getInstallsDir(), `${pluginId}.json`);
|
|
3172
3173
|
}
|
|
3173
|
-
function
|
|
3174
|
+
function locationKey(loc) {
|
|
3175
|
+
return `${loc.tool}:${loc.level}:${resolve(loc.installPath)}`;
|
|
3176
|
+
}
|
|
3177
|
+
function readStore(pluginId) {
|
|
3174
3178
|
const recordPath = getRecordPath(pluginId);
|
|
3175
3179
|
if (!existsSync12(recordPath)) {
|
|
3176
3180
|
return null;
|
|
3177
3181
|
}
|
|
3182
|
+
let parsed;
|
|
3178
3183
|
try {
|
|
3179
|
-
|
|
3180
|
-
return JSON.parse(content);
|
|
3184
|
+
parsed = JSON.parse(readFileSync8(recordPath, "utf-8"));
|
|
3181
3185
|
} catch {
|
|
3182
3186
|
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
3183
3187
|
return null;
|
|
3184
3188
|
}
|
|
3189
|
+
const store = parsed;
|
|
3190
|
+
if (store && typeof store === "object" && "locations" in store && store.locations) {
|
|
3191
|
+
return { version: 2, locations: store.locations };
|
|
3192
|
+
}
|
|
3193
|
+
const legacy = parsed;
|
|
3194
|
+
if (legacy && typeof legacy === "object" && typeof legacy.pluginId === "string") {
|
|
3195
|
+
const key = legacy.tool && legacy.level && legacy.installPath ? locationKey(legacy) : LEGACY_LOCATION_KEY;
|
|
3196
|
+
return { version: 2, locations: { [key]: legacy } };
|
|
3197
|
+
}
|
|
3198
|
+
logger.warn(t("record_corrupted").replace("{file}", recordPath));
|
|
3199
|
+
return null;
|
|
3185
3200
|
}
|
|
3186
|
-
function
|
|
3201
|
+
function writeStore(pluginId, store) {
|
|
3187
3202
|
const installsDir = getInstallsDir();
|
|
3188
3203
|
if (!existsSync12(installsDir)) {
|
|
3189
3204
|
mkdirSync5(installsDir, { recursive: true });
|
|
3190
3205
|
}
|
|
3191
|
-
|
|
3192
|
-
atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
3206
|
+
atomicWriteFileSync(getRecordPath(pluginId), JSON.stringify(store, null, 2));
|
|
3193
3207
|
}
|
|
3194
|
-
function
|
|
3208
|
+
function readRecords(pluginId) {
|
|
3209
|
+
const store = readStore(pluginId);
|
|
3210
|
+
return store ? Object.values(store.locations) : [];
|
|
3211
|
+
}
|
|
3212
|
+
var byLatest = (a, b) => (b.installTime || "").localeCompare(a.installTime || "");
|
|
3213
|
+
function readRecord(pluginId) {
|
|
3214
|
+
const records = readRecords(pluginId);
|
|
3215
|
+
if (records.length === 0) {
|
|
3216
|
+
return null;
|
|
3217
|
+
}
|
|
3218
|
+
const cwd = resolve(process.cwd());
|
|
3219
|
+
const localMatch = records.filter((r) => r.level === "project" && resolve(r.installPath) === cwd).sort(byLatest);
|
|
3220
|
+
if (localMatch.length > 0) return localMatch[0];
|
|
3221
|
+
const globalMatch = records.filter((r) => r.level === "global").sort(byLatest);
|
|
3222
|
+
if (globalMatch.length > 0) return globalMatch[0];
|
|
3223
|
+
return [...records].sort(byLatest)[0];
|
|
3224
|
+
}
|
|
3225
|
+
function writeRecord(record) {
|
|
3226
|
+
const store = readStore(record.pluginId) ?? { version: 2, locations: {} };
|
|
3227
|
+
store.locations[locationKey(record)] = record;
|
|
3228
|
+
writeStore(record.pluginId, store);
|
|
3229
|
+
}
|
|
3230
|
+
function deleteRecord(pluginId, where) {
|
|
3195
3231
|
const recordPath = getRecordPath(pluginId);
|
|
3196
|
-
if (existsSync12(recordPath)) {
|
|
3232
|
+
if (!existsSync12(recordPath)) {
|
|
3233
|
+
return;
|
|
3234
|
+
}
|
|
3235
|
+
if (!where) {
|
|
3236
|
+
unlinkSync3(recordPath);
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
3239
|
+
const store = readStore(pluginId);
|
|
3240
|
+
if (!store) {
|
|
3241
|
+
return;
|
|
3242
|
+
}
|
|
3243
|
+
delete store.locations[locationKey(where)];
|
|
3244
|
+
if (Object.keys(store.locations).length === 0) {
|
|
3197
3245
|
unlinkSync3(recordPath);
|
|
3246
|
+
} else {
|
|
3247
|
+
writeStore(pluginId, store);
|
|
3198
3248
|
}
|
|
3199
3249
|
}
|
|
3200
3250
|
function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames, configRootConfigLink) {
|
|
@@ -3445,7 +3495,7 @@ function toInstalledEntry(target, registryPath, record) {
|
|
|
3445
3495
|
};
|
|
3446
3496
|
}
|
|
3447
3497
|
function scanCannbotInstalled(target) {
|
|
3448
|
-
const root =
|
|
3498
|
+
const root = resolve2(target || process.cwd());
|
|
3449
3499
|
const installed = [];
|
|
3450
3500
|
for (const tool of CANNBOT_SUPPORTED_TOOLS) {
|
|
3451
3501
|
const result = readCannbotRegistry(root, tool);
|
|
@@ -3471,7 +3521,7 @@ function countMarkerBlocks(content, pluginName) {
|
|
|
3471
3521
|
return content.split(start).length - 1;
|
|
3472
3522
|
}
|
|
3473
3523
|
function removeIfSafe(pathToRemove, allowedBase) {
|
|
3474
|
-
const resolved =
|
|
3524
|
+
const resolved = resolve2(pathToRemove);
|
|
3475
3525
|
if (resolved !== allowedBase && !resolved.startsWith(allowedBase + sep2)) {
|
|
3476
3526
|
return "skipped";
|
|
3477
3527
|
}
|
|
@@ -3488,8 +3538,8 @@ function removeIfSafe(pathToRemove, allowedBase) {
|
|
|
3488
3538
|
}
|
|
3489
3539
|
}
|
|
3490
3540
|
function removeEmptyDirUpward(dirPath, stopAt) {
|
|
3491
|
-
let current =
|
|
3492
|
-
const stop =
|
|
3541
|
+
let current = resolve2(dirPath);
|
|
3542
|
+
const stop = resolve2(stopAt);
|
|
3493
3543
|
let removedAny = false;
|
|
3494
3544
|
while (current.startsWith(stop + sep2)) {
|
|
3495
3545
|
try {
|
|
@@ -3505,7 +3555,7 @@ function removeEmptyDirUpward(dirPath, stopAt) {
|
|
|
3505
3555
|
return removedAny;
|
|
3506
3556
|
}
|
|
3507
3557
|
function cleanHookSettings(hookSettingsPath, target, pluginId) {
|
|
3508
|
-
const resolved =
|
|
3558
|
+
const resolved = resolve2(target, hookSettingsPath);
|
|
3509
3559
|
if (!existsSync13(resolved)) return;
|
|
3510
3560
|
let settings;
|
|
3511
3561
|
try {
|
|
@@ -3618,7 +3668,7 @@ function uninstallCannbotPlugin(pluginId, target) {
|
|
|
3618
3668
|
removedDirs: 0,
|
|
3619
3669
|
errors: []
|
|
3620
3670
|
};
|
|
3621
|
-
const root =
|
|
3671
|
+
const root = resolve2(target || process.cwd());
|
|
3622
3672
|
let read = null;
|
|
3623
3673
|
let record;
|
|
3624
3674
|
for (const tool of CANNBOT_SUPPORTED_TOOLS) {
|
|
@@ -3652,7 +3702,7 @@ function uninstallCannbotPlugin(pluginId, target) {
|
|
|
3652
3702
|
}
|
|
3653
3703
|
if (record.instructions) {
|
|
3654
3704
|
const instructionsPath = join12(root, record.instructions);
|
|
3655
|
-
const resolved =
|
|
3705
|
+
const resolved = resolve2(instructionsPath);
|
|
3656
3706
|
if (resolved.startsWith(allowedBase + sep2) && existsSync13(resolved)) {
|
|
3657
3707
|
try {
|
|
3658
3708
|
const content = readFileSync9(resolved, "utf-8");
|
|
@@ -3736,16 +3786,21 @@ function uninstallCannbotPlugin(pluginId, target) {
|
|
|
3736
3786
|
}
|
|
3737
3787
|
function findOrphanedDelegatedRecords(target) {
|
|
3738
3788
|
const orphans = [];
|
|
3739
|
-
const installed = new Set(scanCannbotInstalled(target).map((e) => e.id));
|
|
3740
3789
|
const installsDir = join12(getCannbotConfigDir(), "installs");
|
|
3741
3790
|
if (!existsSync13(installsDir)) return orphans;
|
|
3742
3791
|
for (const file of readdirSync6(installsDir)) {
|
|
3743
3792
|
if (!file.endsWith(".json") || file === "skills.json") continue;
|
|
3744
3793
|
const pluginId = file.replace(/\.json$/, "");
|
|
3745
3794
|
try {
|
|
3746
|
-
const
|
|
3747
|
-
|
|
3748
|
-
if (
|
|
3795
|
+
const records = readRecords(pluginId);
|
|
3796
|
+
const delegated = records.filter((r) => r?.kind === "delegated");
|
|
3797
|
+
if (delegated.length === 0) continue;
|
|
3798
|
+
const isOrphan = delegated.some((rec) => {
|
|
3799
|
+
const recTarget = rec.installPath || target;
|
|
3800
|
+
const installedThere = new Set(scanCannbotInstalled(recTarget).map((e) => e.id));
|
|
3801
|
+
return !installedThere.has(pluginId);
|
|
3802
|
+
});
|
|
3803
|
+
if (isOrphan) {
|
|
3749
3804
|
orphans.push(pluginId);
|
|
3750
3805
|
}
|
|
3751
3806
|
} catch {
|
|
@@ -3886,10 +3941,7 @@ function readAllRecords() {
|
|
|
3886
3941
|
const records = [];
|
|
3887
3942
|
const plugins = getAllPlugins();
|
|
3888
3943
|
for (const plugin of plugins) {
|
|
3889
|
-
|
|
3890
|
-
if (record) {
|
|
3891
|
-
records.push(record);
|
|
3892
|
-
}
|
|
3944
|
+
records.push(...readRecords(plugin.id));
|
|
3893
3945
|
}
|
|
3894
3946
|
return records;
|
|
3895
3947
|
}
|
|
@@ -4598,7 +4650,7 @@ function installerSupportsBundledFlag() {
|
|
|
4598
4650
|
}
|
|
4599
4651
|
async function delegateInstall(options) {
|
|
4600
4652
|
const { pluginId, tool, level } = options;
|
|
4601
|
-
const target =
|
|
4653
|
+
const target = resolve3(options.target || process.cwd());
|
|
4602
4654
|
const displayName = options.displayName || pluginId;
|
|
4603
4655
|
const result = {
|
|
4604
4656
|
success: false,
|
|
@@ -5331,7 +5383,9 @@ function printEnhancedSummary(results, tool, configRoot) {
|
|
|
5331
5383
|
""
|
|
5332
5384
|
];
|
|
5333
5385
|
if (successResults.length === 1) {
|
|
5334
|
-
const plugin = findPlugin(successResults[0].pluginId
|
|
5386
|
+
const plugin = findPlugin(successResults[0].pluginId, {
|
|
5387
|
+
source: successResults[0].source
|
|
5388
|
+
});
|
|
5335
5389
|
const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
|
|
5336
5390
|
lines.push(
|
|
5337
5391
|
` ${chalk6.dim(t("install_enhanced_docs"))}: ${chalk6.dim(docsPath)}`
|
|
@@ -5499,7 +5553,8 @@ async function pluginInstallFlow() {
|
|
|
5499
5553
|
displayName: selected[index]?.plugin?.displayName || result.pluginId,
|
|
5500
5554
|
success: result.success,
|
|
5501
5555
|
skillsCount: result.skillsCount,
|
|
5502
|
-
agentsCount: result.agentsCount
|
|
5556
|
+
agentsCount: result.agentsCount,
|
|
5557
|
+
source: selected[index]?.source
|
|
5503
5558
|
}));
|
|
5504
5559
|
printInstallSummary(summary);
|
|
5505
5560
|
const configRoot = getConfigRoot(answers.tool, answers.level);
|
|
@@ -6099,6 +6154,9 @@ async function installCommand(names, options) {
|
|
|
6099
6154
|
logger.info(`${t("install_to")}: ${chalk10.cyan(getSkillsRoot(tool2, level2))}`);
|
|
6100
6155
|
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
|
|
6101
6156
|
logger.blank();
|
|
6157
|
+
if (failCount > 0) {
|
|
6158
|
+
process.exitCode = 1;
|
|
6159
|
+
}
|
|
6102
6160
|
return;
|
|
6103
6161
|
}
|
|
6104
6162
|
}
|
|
@@ -6135,6 +6193,7 @@ async function installCommand(names, options) {
|
|
|
6135
6193
|
logger.step(` ${p.id}${badge} (${p.aliases.join(", ")})`);
|
|
6136
6194
|
}
|
|
6137
6195
|
logger.info(`${t("available_skills")}: ${chalk10.cyan("install-helper install --list")}`);
|
|
6196
|
+
process.exitCode = 1;
|
|
6138
6197
|
return;
|
|
6139
6198
|
}
|
|
6140
6199
|
let tool;
|
|
@@ -6188,7 +6247,6 @@ async function installCommand(names, options) {
|
|
|
6188
6247
|
spinner.fail(t("repo_clone_failed").replace("{error}", error instanceof Error ? error.message : t("error_unknown")).replace("{url}", "").replace("{dir}", ""));
|
|
6189
6248
|
return;
|
|
6190
6249
|
}
|
|
6191
|
-
const allPlugins = getAllPlugins();
|
|
6192
6250
|
const results = await installPlugins(
|
|
6193
6251
|
pluginsToInstall,
|
|
6194
6252
|
tool,
|
|
@@ -6199,17 +6257,24 @@ async function installCommand(names, options) {
|
|
|
6199
6257
|
source
|
|
6200
6258
|
);
|
|
6201
6259
|
const summary = results.map((result) => {
|
|
6202
|
-
const plugin =
|
|
6260
|
+
const plugin = findPlugin(result.pluginId, { source });
|
|
6203
6261
|
return {
|
|
6204
6262
|
pluginId: result.pluginId,
|
|
6205
6263
|
displayName: plugin?.displayName || result.pluginId,
|
|
6206
6264
|
success: result.success,
|
|
6207
6265
|
skillsCount: result.skillsCount,
|
|
6208
|
-
agentsCount: result.agentsCount
|
|
6266
|
+
agentsCount: result.agentsCount,
|
|
6267
|
+
// the command-line source is the user intent — legacy entries
|
|
6268
|
+
// carry no `source` field, so plugin?.source alone would lose the
|
|
6269
|
+
// "skills" scope and the docs hint would resolve cannbot-first
|
|
6270
|
+
source: source ?? plugin?.source
|
|
6209
6271
|
};
|
|
6210
6272
|
});
|
|
6211
6273
|
printInstallSummary(summary);
|
|
6212
6274
|
printEnhancedSummary(summary, tool, configRoot);
|
|
6275
|
+
if (results.some((r) => !r.success)) {
|
|
6276
|
+
process.exitCode = 1;
|
|
6277
|
+
}
|
|
6213
6278
|
}
|
|
6214
6279
|
}
|
|
6215
6280
|
if (skills.length > 0) {
|
|
@@ -6246,6 +6311,9 @@ async function installCommand(names, options) {
|
|
|
6246
6311
|
logger.info(`${t("install_to")}: ${chalk10.cyan(getSkillsRoot(tool, level))}`);
|
|
6247
6312
|
logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
|
|
6248
6313
|
logger.blank();
|
|
6314
|
+
if (failCount > 0) {
|
|
6315
|
+
process.exitCode = 1;
|
|
6316
|
+
}
|
|
6249
6317
|
}
|
|
6250
6318
|
}
|
|
6251
6319
|
async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes, source) {
|
|
@@ -6286,7 +6354,7 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
|
|
|
6286
6354
|
// src/commands/uninstall.ts
|
|
6287
6355
|
init_registry();
|
|
6288
6356
|
import { existsSync as existsSync21, unlinkSync as unlinkSync8, rmdirSync as rmdirSync2, readdirSync as readdirSync11, readFileSync as readFileSync15 } from "fs";
|
|
6289
|
-
import { resolve as
|
|
6357
|
+
import { resolve as resolve4, basename as basename4, sep as sep3, join as join20 } from "path";
|
|
6290
6358
|
import chalk11 from "chalk";
|
|
6291
6359
|
import { select as select5, checkbox as checkbox2, Separator as Separator4, confirm as confirm3 } from "@inquirer/prompts";
|
|
6292
6360
|
init_config();
|
|
@@ -6686,13 +6754,13 @@ async function uninstallPluginById(pluginId, batchMode) {
|
|
|
6686
6754
|
return;
|
|
6687
6755
|
}
|
|
6688
6756
|
logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
|
|
6689
|
-
const configRoot =
|
|
6690
|
-
const allowedBases = [
|
|
6757
|
+
const configRoot = record.configRoot;
|
|
6758
|
+
const allowedBases = [resolve4(configRoot), resolve4(record.installPath)];
|
|
6691
6759
|
if (record.tool === "codex") {
|
|
6692
|
-
allowedBases.push(
|
|
6760
|
+
allowedBases.push(resolve4(getSkillsRoot(record.tool, record.level, record.installPath)));
|
|
6693
6761
|
}
|
|
6694
6762
|
const isSafePath = (p) => {
|
|
6695
|
-
const resolved =
|
|
6763
|
+
const resolved = resolve4(p);
|
|
6696
6764
|
return allowedBases.some((base) => resolved === base || resolved.startsWith(base + sep3));
|
|
6697
6765
|
};
|
|
6698
6766
|
let removedFiles = 0;
|
|
@@ -6720,7 +6788,7 @@ async function uninstallPluginById(pluginId, batchMode) {
|
|
|
6720
6788
|
const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
|
|
6721
6789
|
for (const dirPath of sortedDirs) {
|
|
6722
6790
|
try {
|
|
6723
|
-
if (
|
|
6791
|
+
if (resolve4(dirPath) === resolve4(configRoot)) {
|
|
6724
6792
|
continue;
|
|
6725
6793
|
}
|
|
6726
6794
|
if (existsSync21(dirPath)) {
|
|
@@ -6737,7 +6805,7 @@ async function uninstallPluginById(pluginId, batchMode) {
|
|
|
6737
6805
|
}
|
|
6738
6806
|
if (record.tool === "codex") {
|
|
6739
6807
|
try {
|
|
6740
|
-
const agentsRoot =
|
|
6808
|
+
const agentsRoot = resolve4(getSkillsRoot(record.tool, record.level, record.installPath), "..");
|
|
6741
6809
|
if (existsSync21(agentsRoot) && readdirSync11(agentsRoot).length === 0) {
|
|
6742
6810
|
rmdirSync2(agentsRoot);
|
|
6743
6811
|
logger.step(` ${t("uninstall_clean_empty_dir")}: ${basename4(agentsRoot)}/`);
|
|
@@ -6746,7 +6814,11 @@ async function uninstallPluginById(pluginId, batchMode) {
|
|
|
6746
6814
|
} catch {
|
|
6747
6815
|
}
|
|
6748
6816
|
}
|
|
6749
|
-
deleteRecord(plugin.id
|
|
6817
|
+
deleteRecord(plugin.id, {
|
|
6818
|
+
tool: record.tool,
|
|
6819
|
+
level: record.level,
|
|
6820
|
+
installPath: record.installPath
|
|
6821
|
+
});
|
|
6750
6822
|
removeInstalledPlugin(plugin.id);
|
|
6751
6823
|
const backups = findBackups(configRoot);
|
|
6752
6824
|
const otherBackups = backups.filter((b) => b.pluginId !== pluginId);
|