@localskills/cli 0.7.0 → 0.9.0
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 +427 -38
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -161,7 +161,8 @@ var require_src = __commonJS({
|
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
// src/index.ts
|
|
164
|
-
import {
|
|
164
|
+
import { createRequire } from "module";
|
|
165
|
+
import { Command as Command10 } from "commander";
|
|
165
166
|
|
|
166
167
|
// src/commands/auth.ts
|
|
167
168
|
import { Command } from "commander";
|
|
@@ -521,6 +522,23 @@ var x = class {
|
|
|
521
522
|
}
|
|
522
523
|
}
|
|
523
524
|
};
|
|
525
|
+
var kt = class extends x {
|
|
526
|
+
get cursor() {
|
|
527
|
+
return this.value ? 0 : 1;
|
|
528
|
+
}
|
|
529
|
+
get _value() {
|
|
530
|
+
return this.cursor === 0;
|
|
531
|
+
}
|
|
532
|
+
constructor(e2) {
|
|
533
|
+
super(e2, false), this.value = !!e2.initialValue, this.on("userInput", () => {
|
|
534
|
+
this.value = this._value;
|
|
535
|
+
}), this.on("confirm", (s) => {
|
|
536
|
+
this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
|
|
537
|
+
}), this.on("cursor", () => {
|
|
538
|
+
this.value = !this.value;
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
};
|
|
524
542
|
var Lt = class extends x {
|
|
525
543
|
options;
|
|
526
544
|
cursor = 0;
|
|
@@ -876,6 +894,33 @@ var X2 = (t) => {
|
|
|
876
894
|
for (const A of f) for (const w of A) B2.push(w);
|
|
877
895
|
return h && B2.push(g), B2;
|
|
878
896
|
};
|
|
897
|
+
var Re = (t) => {
|
|
898
|
+
const r = t.active ?? "Yes", s = t.inactive ?? "No";
|
|
899
|
+
return new kt({ active: r, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
|
|
900
|
+
const i = t.withGuide ?? _.withGuide, a = `${i ? `${import_picocolors2.default.gray(d)}
|
|
901
|
+
` : ""}${W2(this.state)} ${t.message}
|
|
902
|
+
`, o = this.value ? r : s;
|
|
903
|
+
switch (this.state) {
|
|
904
|
+
case "submit": {
|
|
905
|
+
const u = i ? `${import_picocolors2.default.gray(d)} ` : "";
|
|
906
|
+
return `${a}${u}${import_picocolors2.default.dim(o)}`;
|
|
907
|
+
}
|
|
908
|
+
case "cancel": {
|
|
909
|
+
const u = i ? `${import_picocolors2.default.gray(d)} ` : "";
|
|
910
|
+
return `${a}${u}${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(o))}${i ? `
|
|
911
|
+
${import_picocolors2.default.gray(d)}` : ""}`;
|
|
912
|
+
}
|
|
913
|
+
default: {
|
|
914
|
+
const u = i ? `${import_picocolors2.default.cyan(d)} ` : "", l = i ? import_picocolors2.default.cyan(x2) : "";
|
|
915
|
+
return `${a}${u}${this.value ? `${import_picocolors2.default.green(Q2)} ${r}` : `${import_picocolors2.default.dim(H2)} ${import_picocolors2.default.dim(r)}`}${t.vertical ? i ? `
|
|
916
|
+
${import_picocolors2.default.cyan(d)} ` : `
|
|
917
|
+
` : ` ${import_picocolors2.default.dim("/")} `}${this.value ? `${import_picocolors2.default.dim(H2)} ${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.green(Q2)} ${s}`}
|
|
918
|
+
${l}
|
|
919
|
+
`;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
} }).prompt();
|
|
923
|
+
};
|
|
879
924
|
var R2 = { message: (t = [], { symbol: r = import_picocolors2.default.gray(d), secondarySymbol: s = import_picocolors2.default.gray(d), output: i = process.stdout, spacing: a = 1, withGuide: o } = {}) => {
|
|
880
925
|
const u = [], l = o ?? _.withGuide, n = l ? s : "", c = l ? `${r} ` : "", g = l ? `${s} ` : "";
|
|
881
926
|
for (let p = 0; p < a; p++) u.push(n);
|
|
@@ -1101,14 +1146,13 @@ ${l}
|
|
|
1101
1146
|
} }).prompt();
|
|
1102
1147
|
|
|
1103
1148
|
// src/lib/config.ts
|
|
1104
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from "fs";
|
|
1149
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, copyFileSync } from "fs";
|
|
1105
1150
|
import { join } from "path";
|
|
1106
1151
|
import { homedir } from "os";
|
|
1107
1152
|
var CONFIG_DIR = join(homedir(), ".localskills");
|
|
1108
1153
|
var CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
1109
|
-
var
|
|
1110
|
-
|
|
1111
|
-
api_url: "https://localskills.sh",
|
|
1154
|
+
var DEFAULT_PROFILE_NAME = "default";
|
|
1155
|
+
var DEFAULT_PROFILE = {
|
|
1112
1156
|
token: null,
|
|
1113
1157
|
installed_skills: {},
|
|
1114
1158
|
defaults: {
|
|
@@ -1117,21 +1161,109 @@ var DEFAULT_CONFIG = {
|
|
|
1117
1161
|
},
|
|
1118
1162
|
anonymous_key: null
|
|
1119
1163
|
};
|
|
1120
|
-
|
|
1164
|
+
var DEFAULT_API_URL = "https://localskills.sh";
|
|
1165
|
+
var ProfileNotFoundError = class extends Error {
|
|
1166
|
+
constructor(profileName, availableProfiles, source) {
|
|
1167
|
+
const sourceLabel = source === "flag" ? "" : source === "env" ? " (from LOCALSKILLS_PROFILE)" : "";
|
|
1168
|
+
super(`Profile "${profileName}"${sourceLabel} does not exist. Available profiles: ${availableProfiles.join(", ")}`);
|
|
1169
|
+
this.profileName = profileName;
|
|
1170
|
+
this.availableProfiles = availableProfiles;
|
|
1171
|
+
this.source = source;
|
|
1172
|
+
this.name = "ProfileNotFoundError";
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
var _profileOverride;
|
|
1176
|
+
function setProfileOverride(name) {
|
|
1177
|
+
_profileOverride = name;
|
|
1178
|
+
}
|
|
1179
|
+
function resolveProfileName(config) {
|
|
1180
|
+
if (_profileOverride) {
|
|
1181
|
+
if (!config.profiles[_profileOverride]) {
|
|
1182
|
+
throw new ProfileNotFoundError(
|
|
1183
|
+
_profileOverride,
|
|
1184
|
+
Object.keys(config.profiles),
|
|
1185
|
+
"flag"
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
return _profileOverride;
|
|
1189
|
+
}
|
|
1190
|
+
const envProfile = process.env.LOCALSKILLS_PROFILE;
|
|
1191
|
+
if (envProfile) {
|
|
1192
|
+
if (!config.profiles[envProfile]) {
|
|
1193
|
+
throw new ProfileNotFoundError(
|
|
1194
|
+
envProfile,
|
|
1195
|
+
Object.keys(config.profiles),
|
|
1196
|
+
"env"
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
return envProfile;
|
|
1200
|
+
}
|
|
1201
|
+
return config.active_profile;
|
|
1202
|
+
}
|
|
1203
|
+
function getActiveProfileName() {
|
|
1204
|
+
const full = loadFullConfig();
|
|
1205
|
+
return resolveProfileName(full);
|
|
1206
|
+
}
|
|
1207
|
+
function validateV3(config) {
|
|
1208
|
+
if (!config.profiles || typeof config.profiles !== "object") {
|
|
1209
|
+
config.profiles = {
|
|
1210
|
+
[DEFAULT_PROFILE_NAME]: { ...DEFAULT_PROFILE, installed_skills: {} }
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
if (!config.profiles[DEFAULT_PROFILE_NAME]) {
|
|
1214
|
+
config.profiles[DEFAULT_PROFILE_NAME] = { ...DEFAULT_PROFILE, installed_skills: {} };
|
|
1215
|
+
}
|
|
1216
|
+
if (!config.active_profile || !config.profiles[config.active_profile]) {
|
|
1217
|
+
config.active_profile = DEFAULT_PROFILE_NAME;
|
|
1218
|
+
}
|
|
1219
|
+
return config;
|
|
1220
|
+
}
|
|
1221
|
+
function loadFullConfig() {
|
|
1121
1222
|
if (!existsSync(CONFIG_PATH)) {
|
|
1122
|
-
return {
|
|
1223
|
+
return {
|
|
1224
|
+
config_version: 3,
|
|
1225
|
+
api_url: DEFAULT_API_URL,
|
|
1226
|
+
active_profile: DEFAULT_PROFILE_NAME,
|
|
1227
|
+
profiles: {
|
|
1228
|
+
[DEFAULT_PROFILE_NAME]: { ...DEFAULT_PROFILE, installed_skills: {} }
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1123
1231
|
}
|
|
1124
1232
|
try {
|
|
1125
1233
|
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
1234
|
+
if (raw.config_version === 3) {
|
|
1235
|
+
return validateV3(raw);
|
|
1236
|
+
}
|
|
1237
|
+
let v2;
|
|
1126
1238
|
if (!raw.config_version || raw.config_version < 2) {
|
|
1127
|
-
|
|
1239
|
+
v2 = migrateV1toV2(raw);
|
|
1240
|
+
} else {
|
|
1241
|
+
v2 = raw;
|
|
1242
|
+
}
|
|
1243
|
+
const v3 = migrateV2toV3(v2);
|
|
1244
|
+
saveFullConfig(v3);
|
|
1245
|
+
return v3;
|
|
1246
|
+
} catch (err) {
|
|
1247
|
+
if (err instanceof SyntaxError) {
|
|
1248
|
+
console.error(`Warning: Config file is corrupt (${CONFIG_PATH}). Backing up and starting fresh.`);
|
|
1249
|
+
try {
|
|
1250
|
+
copyFileSync(CONFIG_PATH, CONFIG_PATH + ".bak");
|
|
1251
|
+
console.error(` Backup saved to ${CONFIG_PATH}.bak`);
|
|
1252
|
+
} catch {
|
|
1253
|
+
}
|
|
1254
|
+
return {
|
|
1255
|
+
config_version: 3,
|
|
1256
|
+
api_url: DEFAULT_API_URL,
|
|
1257
|
+
active_profile: DEFAULT_PROFILE_NAME,
|
|
1258
|
+
profiles: {
|
|
1259
|
+
[DEFAULT_PROFILE_NAME]: { ...DEFAULT_PROFILE, installed_skills: {} }
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1128
1262
|
}
|
|
1129
|
-
|
|
1130
|
-
} catch {
|
|
1131
|
-
return { ...DEFAULT_CONFIG, installed_skills: {} };
|
|
1263
|
+
throw err;
|
|
1132
1264
|
}
|
|
1133
1265
|
}
|
|
1134
|
-
function
|
|
1266
|
+
function saveFullConfig(config) {
|
|
1135
1267
|
mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
1136
1268
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", {
|
|
1137
1269
|
mode: 384
|
|
@@ -1142,10 +1274,35 @@ function saveConfig(config) {
|
|
|
1142
1274
|
} catch {
|
|
1143
1275
|
}
|
|
1144
1276
|
}
|
|
1277
|
+
function loadConfig() {
|
|
1278
|
+
const full = loadFullConfig();
|
|
1279
|
+
const profileName = resolveProfileName(full);
|
|
1280
|
+
const profile = full.profiles[profileName] ?? { ...DEFAULT_PROFILE, installed_skills: {} };
|
|
1281
|
+
return {
|
|
1282
|
+
config_version: 2,
|
|
1283
|
+
api_url: full.api_url,
|
|
1284
|
+
token: profile.token,
|
|
1285
|
+
installed_skills: profile.installed_skills,
|
|
1286
|
+
defaults: profile.defaults,
|
|
1287
|
+
anonymous_key: profile.anonymous_key ?? null
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
function saveConfig(config) {
|
|
1291
|
+
const full = loadFullConfig();
|
|
1292
|
+
const profileName = resolveProfileName(full);
|
|
1293
|
+
full.api_url = config.api_url;
|
|
1294
|
+
full.profiles[profileName] = {
|
|
1295
|
+
token: config.token,
|
|
1296
|
+
installed_skills: config.installed_skills,
|
|
1297
|
+
defaults: config.defaults,
|
|
1298
|
+
anonymous_key: config.anonymous_key ?? null
|
|
1299
|
+
};
|
|
1300
|
+
saveFullConfig(full);
|
|
1301
|
+
}
|
|
1145
1302
|
function migrateV1toV2(v1) {
|
|
1146
1303
|
const v2 = {
|
|
1147
1304
|
config_version: 2,
|
|
1148
|
-
api_url: v1.api_url ||
|
|
1305
|
+
api_url: v1.api_url || DEFAULT_API_URL,
|
|
1149
1306
|
token: v1.token,
|
|
1150
1307
|
installed_skills: {},
|
|
1151
1308
|
defaults: {
|
|
@@ -1174,9 +1331,23 @@ function migrateV1toV2(v1) {
|
|
|
1174
1331
|
]
|
|
1175
1332
|
};
|
|
1176
1333
|
}
|
|
1177
|
-
saveConfig(v2);
|
|
1178
1334
|
return v2;
|
|
1179
1335
|
}
|
|
1336
|
+
function migrateV2toV3(v2) {
|
|
1337
|
+
return {
|
|
1338
|
+
config_version: 3,
|
|
1339
|
+
api_url: v2.api_url || DEFAULT_API_URL,
|
|
1340
|
+
active_profile: DEFAULT_PROFILE_NAME,
|
|
1341
|
+
profiles: {
|
|
1342
|
+
[DEFAULT_PROFILE_NAME]: {
|
|
1343
|
+
token: v2.token,
|
|
1344
|
+
installed_skills: v2.installed_skills,
|
|
1345
|
+
defaults: v2.defaults,
|
|
1346
|
+
anonymous_key: v2.anonymous_key ?? null
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1180
1351
|
function getToken() {
|
|
1181
1352
|
return loadConfig().token;
|
|
1182
1353
|
}
|
|
@@ -1198,6 +1369,11 @@ function setAnonymousKey(key) {
|
|
|
1198
1369
|
config.anonymous_key = key;
|
|
1199
1370
|
saveConfig(config);
|
|
1200
1371
|
}
|
|
1372
|
+
function setLastUpdateCheck(ts) {
|
|
1373
|
+
const config = loadFullConfig();
|
|
1374
|
+
config.last_update_check = ts;
|
|
1375
|
+
saveFullConfig(config);
|
|
1376
|
+
}
|
|
1201
1377
|
|
|
1202
1378
|
// src/lib/api-client.ts
|
|
1203
1379
|
var ApiClient = class {
|
|
@@ -1423,6 +1599,22 @@ function isValidSemVer(v) {
|
|
|
1423
1599
|
function isValidSemVerRange(range) {
|
|
1424
1600
|
return RANGE_RE.test(range);
|
|
1425
1601
|
}
|
|
1602
|
+
function compareSemVer(a, b) {
|
|
1603
|
+
if (a.major !== b.major)
|
|
1604
|
+
return a.major > b.major ? 1 : -1;
|
|
1605
|
+
if (a.minor !== b.minor)
|
|
1606
|
+
return a.minor > b.minor ? 1 : -1;
|
|
1607
|
+
if (a.patch !== b.patch)
|
|
1608
|
+
return a.patch > b.patch ? 1 : -1;
|
|
1609
|
+
return 0;
|
|
1610
|
+
}
|
|
1611
|
+
function isGreaterThan(next, prev) {
|
|
1612
|
+
const a = parseSemVer(next);
|
|
1613
|
+
const b = parseSemVer(prev);
|
|
1614
|
+
if (!a || !b)
|
|
1615
|
+
return false;
|
|
1616
|
+
return compareSemVer(a, b) === 1;
|
|
1617
|
+
}
|
|
1426
1618
|
|
|
1427
1619
|
// ../../packages/shared/dist/utils/index.js
|
|
1428
1620
|
function titleFromSlug(slug) {
|
|
@@ -1447,8 +1639,8 @@ function buildVersionQuery(range) {
|
|
|
1447
1639
|
console.error(`Invalid version specifier: ${range}`);
|
|
1448
1640
|
process.exit(1);
|
|
1449
1641
|
}
|
|
1450
|
-
function formatVersionLabel(semver,
|
|
1451
|
-
return semver ? `v${semver}` : `v${
|
|
1642
|
+
function formatVersionLabel(semver, version2) {
|
|
1643
|
+
return semver ? `v${semver}` : `v${version2}`;
|
|
1452
1644
|
}
|
|
1453
1645
|
function cancelGuard(value) {
|
|
1454
1646
|
if (Ct(value)) {
|
|
@@ -2150,13 +2342,13 @@ function getCacheDir(slug) {
|
|
|
2150
2342
|
}
|
|
2151
2343
|
return dir;
|
|
2152
2344
|
}
|
|
2153
|
-
function store(slug, content, skill,
|
|
2345
|
+
function store(slug, content, skill, version2) {
|
|
2154
2346
|
const dir = getCacheDir(slug);
|
|
2155
2347
|
mkdirSync5(dir, { recursive: true });
|
|
2156
2348
|
writeFileSync5(join12(dir, "raw.md"), content);
|
|
2157
2349
|
const meta = {
|
|
2158
2350
|
hash: skill.contentHash,
|
|
2159
|
-
version,
|
|
2351
|
+
version: version2,
|
|
2160
2352
|
semver: skill.currentSemver ?? null,
|
|
2161
2353
|
name: skill.name,
|
|
2162
2354
|
description: skill.description,
|
|
@@ -2202,14 +2394,14 @@ function purge(slug) {
|
|
|
2202
2394
|
rmSync3(dir, { recursive: true, force: true });
|
|
2203
2395
|
}
|
|
2204
2396
|
}
|
|
2205
|
-
function storePackage(slug, zipBuffer, manifest, skill,
|
|
2397
|
+
function storePackage(slug, zipBuffer, manifest, skill, version2) {
|
|
2206
2398
|
const dir = getCacheDir(slug);
|
|
2207
2399
|
mkdirSync5(dir, { recursive: true });
|
|
2208
2400
|
writeFileSync5(join12(dir, "package.zip"), zipBuffer);
|
|
2209
2401
|
writeFileSync5(join12(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
2210
2402
|
const meta = {
|
|
2211
2403
|
hash: skill.contentHash,
|
|
2212
|
-
version,
|
|
2404
|
+
version: version2,
|
|
2213
2405
|
semver: skill.currentSemver ?? null,
|
|
2214
2406
|
name: skill.name,
|
|
2215
2407
|
description: skill.description,
|
|
@@ -2358,13 +2550,13 @@ function parsePlatforms(raw) {
|
|
|
2358
2550
|
}
|
|
2359
2551
|
return platforms;
|
|
2360
2552
|
}
|
|
2361
|
-
function buildSkillRecord(cacheKey, skill,
|
|
2553
|
+
function buildSkillRecord(cacheKey, skill, version2, resolvedSemver, requestedRange, existingInstallations, newInstallations) {
|
|
2362
2554
|
return {
|
|
2363
2555
|
slug: cacheKey,
|
|
2364
2556
|
name: skill.name,
|
|
2365
2557
|
type: skill.type ?? "skill",
|
|
2366
2558
|
hash: skill.contentHash,
|
|
2367
|
-
version,
|
|
2559
|
+
version: version2,
|
|
2368
2560
|
semver: resolvedSemver ?? null,
|
|
2369
2561
|
semverRange: requestedRange ?? null,
|
|
2370
2562
|
cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -2449,13 +2641,13 @@ var installCommand = new Command2("install").description("Install a skill locall
|
|
|
2449
2641
|
const format = resData.format ?? "text";
|
|
2450
2642
|
const cacheKey = resData.skill.publicId || slug;
|
|
2451
2643
|
if (format === "package") {
|
|
2452
|
-
const { skill: skill2, downloadUrl, manifest, version:
|
|
2453
|
-
spinner.stop(`Fetched ${skill2.name} ${formatVersionLabel(resolvedSemver2,
|
|
2644
|
+
const { skill: skill2, downloadUrl, manifest, version: version3, semver: resolvedSemver2 } = resData;
|
|
2645
|
+
spinner.stop(`Fetched ${skill2.name} ${formatVersionLabel(resolvedSemver2, version3)} (package, ${manifest.files.length} files)`);
|
|
2454
2646
|
const dlSpinner = bt2();
|
|
2455
2647
|
dlSpinner.start("Downloading package...");
|
|
2456
2648
|
const zipBuffer = await client.fetchBinary(downloadUrl);
|
|
2457
2649
|
dlSpinner.stop(`Downloaded ${(zipBuffer.length / 1024).toFixed(1)} KB`);
|
|
2458
|
-
storePackage(cacheKey, zipBuffer, manifest, skill2,
|
|
2650
|
+
storePackage(cacheKey, zipBuffer, manifest, skill2, version3);
|
|
2459
2651
|
const installations2 = [];
|
|
2460
2652
|
const results2 = [];
|
|
2461
2653
|
for (const platformId of platforms) {
|
|
@@ -2486,7 +2678,7 @@ var installCommand = new Command2("install").description("Install a skill locall
|
|
|
2486
2678
|
config.installed_skills[cacheKey] = buildSkillRecord(
|
|
2487
2679
|
cacheKey,
|
|
2488
2680
|
skill2,
|
|
2489
|
-
|
|
2681
|
+
version3,
|
|
2490
2682
|
resolvedSemver2,
|
|
2491
2683
|
requestedRange,
|
|
2492
2684
|
config.installed_skills[cacheKey]?.installations,
|
|
@@ -2499,9 +2691,9 @@ var installCommand = new Command2("install").description("Install a skill locall
|
|
|
2499
2691
|
Le(`Done! Installed to ${installations2.length} target(s).`);
|
|
2500
2692
|
return;
|
|
2501
2693
|
}
|
|
2502
|
-
const { skill, content, version, semver: resolvedSemver } = resData;
|
|
2503
|
-
spinner.stop(`Fetched ${skill.name} ${formatVersionLabel(resolvedSemver,
|
|
2504
|
-
store(cacheKey, content, skill,
|
|
2694
|
+
const { skill, content, version: version2, semver: resolvedSemver } = resData;
|
|
2695
|
+
spinner.stop(`Fetched ${skill.name} ${formatVersionLabel(resolvedSemver, version2)}`);
|
|
2696
|
+
store(cacheKey, content, skill, version2);
|
|
2505
2697
|
const contentType = skill.type ?? "skill";
|
|
2506
2698
|
const installations = [];
|
|
2507
2699
|
const results = [];
|
|
@@ -2543,7 +2735,7 @@ var installCommand = new Command2("install").description("Install a skill locall
|
|
|
2543
2735
|
config.installed_skills[cacheKey] = buildSkillRecord(
|
|
2544
2736
|
cacheKey,
|
|
2545
2737
|
skill,
|
|
2546
|
-
|
|
2738
|
+
version2,
|
|
2547
2739
|
resolvedSemver,
|
|
2548
2740
|
requestedRange,
|
|
2549
2741
|
config.installed_skills[cacheKey]?.installations,
|
|
@@ -2693,7 +2885,7 @@ var pullCommand = new Command5("pull").description("Pull latest versions of all
|
|
|
2693
2885
|
}
|
|
2694
2886
|
const resData = res.data;
|
|
2695
2887
|
const format = resData.format ?? "text";
|
|
2696
|
-
const { skill, version } = resData;
|
|
2888
|
+
const { skill, version: version2 } = resData;
|
|
2697
2889
|
if (skill.contentHash === installed.hash) {
|
|
2698
2890
|
spinner.stop(`${slug} \u2014 up to date`);
|
|
2699
2891
|
skipped++;
|
|
@@ -2702,7 +2894,7 @@ var pullCommand = new Command5("pull").description("Pull latest versions of all
|
|
|
2702
2894
|
if (format === "package") {
|
|
2703
2895
|
const { downloadUrl, manifest } = resData;
|
|
2704
2896
|
const zipBuffer = await client.fetchBinary(downloadUrl);
|
|
2705
|
-
storePackage(slug, zipBuffer, manifest, skill,
|
|
2897
|
+
storePackage(slug, zipBuffer, manifest, skill, version2);
|
|
2706
2898
|
for (const installation of installed.installations) {
|
|
2707
2899
|
const adapter = getAdapter(installation.platform);
|
|
2708
2900
|
const targetPath = adapter.resolvePath(slug, installation.scope, installation.projectDir, skill.type ?? "skill");
|
|
@@ -2711,7 +2903,7 @@ var pullCommand = new Command5("pull").description("Pull latest versions of all
|
|
|
2711
2903
|
}
|
|
2712
2904
|
} else {
|
|
2713
2905
|
const { content } = resData;
|
|
2714
|
-
store(slug, content, skill,
|
|
2906
|
+
store(slug, content, skill, version2);
|
|
2715
2907
|
for (const installation of installed.installations) {
|
|
2716
2908
|
if (installation.method === "symlink") {
|
|
2717
2909
|
getPlatformFile(slug, installation.platform, skill);
|
|
@@ -2741,11 +2933,11 @@ ${transformed}`
|
|
|
2741
2933
|
}
|
|
2742
2934
|
}
|
|
2743
2935
|
installed.hash = skill.contentHash;
|
|
2744
|
-
installed.version =
|
|
2936
|
+
installed.version = version2;
|
|
2745
2937
|
installed.semver = resData.semver ?? null;
|
|
2746
2938
|
installed.cachedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2747
2939
|
updated++;
|
|
2748
|
-
spinner.stop(`${slug} \u2014 updated to ${formatVersionLabel(res.data.semver,
|
|
2940
|
+
spinner.stop(`${slug} \u2014 updated to ${formatVersionLabel(res.data.semver, version2)}`);
|
|
2749
2941
|
}
|
|
2750
2942
|
saveConfig(config);
|
|
2751
2943
|
Le(`Pull complete. ${updated} updated, ${skipped} up to date.`);
|
|
@@ -3283,9 +3475,198 @@ async function uploadAnonymousSkill(client, params) {
|
|
|
3283
3475
|
R2.info(`Install: localskills install ${res.data.publicId}`);
|
|
3284
3476
|
}
|
|
3285
3477
|
|
|
3478
|
+
// src/commands/profile.ts
|
|
3479
|
+
import { Command as Command9 } from "commander";
|
|
3480
|
+
var PROFILE_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
3481
|
+
var MAX_PROFILE_NAME_LENGTH = 32;
|
|
3482
|
+
var profileCommand = new Command9("profile").description("Manage CLI profiles for multiple accounts");
|
|
3483
|
+
profileCommand.command("list").description("List all profiles").action(() => {
|
|
3484
|
+
const config = loadFullConfig();
|
|
3485
|
+
const persisted = config.active_profile;
|
|
3486
|
+
let resolved;
|
|
3487
|
+
try {
|
|
3488
|
+
resolved = getActiveProfileName();
|
|
3489
|
+
} catch (err) {
|
|
3490
|
+
if (err instanceof ProfileNotFoundError) {
|
|
3491
|
+
console.error(`Warning: ${err.message}
|
|
3492
|
+
`);
|
|
3493
|
+
resolved = persisted;
|
|
3494
|
+
} else {
|
|
3495
|
+
throw err;
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
const names = Object.keys(config.profiles);
|
|
3499
|
+
console.log("Profiles:\n");
|
|
3500
|
+
for (const name of names) {
|
|
3501
|
+
const profile = config.profiles[name];
|
|
3502
|
+
let marker = "";
|
|
3503
|
+
if (name === resolved && name === persisted) {
|
|
3504
|
+
marker = " (active)";
|
|
3505
|
+
} else if (name === resolved) {
|
|
3506
|
+
marker = " (active via override)";
|
|
3507
|
+
} else if (name === persisted) {
|
|
3508
|
+
marker = " (default)";
|
|
3509
|
+
}
|
|
3510
|
+
const auth = profile.token ? "authenticated" : "not authenticated";
|
|
3511
|
+
const skillCount = Object.keys(profile.installed_skills).length;
|
|
3512
|
+
console.log(` ${name}${marker} \u2014 ${auth}, ${skillCount} skill${skillCount !== 1 ? "s" : ""}`);
|
|
3513
|
+
}
|
|
3514
|
+
});
|
|
3515
|
+
profileCommand.command("create <name>").description("Create a new profile").action((name) => {
|
|
3516
|
+
if (!PROFILE_NAME_RE.test(name)) {
|
|
3517
|
+
console.error(
|
|
3518
|
+
`Error: Invalid profile name "${name}". Profile names must use lowercase letters, numbers, and hyphens, and must start with a letter or number.`
|
|
3519
|
+
);
|
|
3520
|
+
process.exit(1);
|
|
3521
|
+
}
|
|
3522
|
+
if (name.length > MAX_PROFILE_NAME_LENGTH) {
|
|
3523
|
+
console.error(`Error: Profile name must be ${MAX_PROFILE_NAME_LENGTH} characters or fewer.`);
|
|
3524
|
+
process.exit(1);
|
|
3525
|
+
}
|
|
3526
|
+
const config = loadFullConfig();
|
|
3527
|
+
if (config.profiles[name]) {
|
|
3528
|
+
console.error(`Error: Profile "${name}" already exists.`);
|
|
3529
|
+
process.exit(1);
|
|
3530
|
+
}
|
|
3531
|
+
config.profiles[name] = {
|
|
3532
|
+
token: null,
|
|
3533
|
+
installed_skills: {},
|
|
3534
|
+
defaults: {
|
|
3535
|
+
scope: "project",
|
|
3536
|
+
method: "symlink"
|
|
3537
|
+
},
|
|
3538
|
+
anonymous_key: null
|
|
3539
|
+
};
|
|
3540
|
+
saveFullConfig(config);
|
|
3541
|
+
console.log(`Profile "${name}" created.`);
|
|
3542
|
+
});
|
|
3543
|
+
profileCommand.command("switch <name>").description("Switch the active profile").action((name) => {
|
|
3544
|
+
const config = loadFullConfig();
|
|
3545
|
+
if (!config.profiles[name]) {
|
|
3546
|
+
console.error(`Error: Profile "${name}" does not exist.`);
|
|
3547
|
+
console.error(`Available profiles: ${Object.keys(config.profiles).join(", ")}`);
|
|
3548
|
+
process.exit(1);
|
|
3549
|
+
}
|
|
3550
|
+
config.active_profile = name;
|
|
3551
|
+
saveFullConfig(config);
|
|
3552
|
+
console.log(`Switched to profile "${name}".`);
|
|
3553
|
+
});
|
|
3554
|
+
profileCommand.command("delete <name>").description("Delete a profile").option("-f, --force", "Skip confirmation prompt").action(async (name, opts) => {
|
|
3555
|
+
if (name === DEFAULT_PROFILE_NAME) {
|
|
3556
|
+
console.error(`Error: Cannot delete the "${DEFAULT_PROFILE_NAME}" profile.`);
|
|
3557
|
+
process.exit(1);
|
|
3558
|
+
}
|
|
3559
|
+
const config = loadFullConfig();
|
|
3560
|
+
if (!config.profiles[name]) {
|
|
3561
|
+
console.error(`Error: Profile "${name}" does not exist.`);
|
|
3562
|
+
process.exit(1);
|
|
3563
|
+
}
|
|
3564
|
+
if (name === config.active_profile) {
|
|
3565
|
+
console.error(`Error: Cannot delete the active profile "${name}". Run \`localskills profile switch <other>\` first.`);
|
|
3566
|
+
process.exit(1);
|
|
3567
|
+
}
|
|
3568
|
+
if (!opts.force) {
|
|
3569
|
+
const profile = config.profiles[name];
|
|
3570
|
+
const skillCount = Object.keys(profile.installed_skills).length;
|
|
3571
|
+
const details = [
|
|
3572
|
+
profile.token ? "authenticated session" : null,
|
|
3573
|
+
skillCount > 0 ? `${skillCount} installed skill${skillCount !== 1 ? "s" : ""}` : null
|
|
3574
|
+
].filter(Boolean);
|
|
3575
|
+
const detailStr = details.length > 0 ? ` (has ${details.join(", ")})` : "";
|
|
3576
|
+
const confirmed = await Re({
|
|
3577
|
+
message: `Delete profile "${name}"${detailStr}? This cannot be undone.`
|
|
3578
|
+
});
|
|
3579
|
+
if (Ct(confirmed) || !confirmed) {
|
|
3580
|
+
console.log("Cancelled.");
|
|
3581
|
+
return;
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
delete config.profiles[name];
|
|
3585
|
+
saveFullConfig(config);
|
|
3586
|
+
console.log(`Profile "${name}" deleted.`);
|
|
3587
|
+
});
|
|
3588
|
+
|
|
3589
|
+
// src/lib/update-check.ts
|
|
3590
|
+
var REGISTRY_URL = "https://registry.npmjs.org/@localskills/cli/latest";
|
|
3591
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3592
|
+
var FETCH_TIMEOUT_MS = 500;
|
|
3593
|
+
var pendingCheck = null;
|
|
3594
|
+
var pendingAbort = null;
|
|
3595
|
+
var currentVer = null;
|
|
3596
|
+
function shouldCheck() {
|
|
3597
|
+
if (process.env.LOCALSKILLS_NO_UPDATE_CHECK === "1") return false;
|
|
3598
|
+
try {
|
|
3599
|
+
const config = loadFullConfig();
|
|
3600
|
+
if (config.update_check_opted_out) return false;
|
|
3601
|
+
const last = config.last_update_check;
|
|
3602
|
+
if (last) {
|
|
3603
|
+
const elapsed = Date.now() - new Date(last).getTime();
|
|
3604
|
+
if (elapsed < CHECK_INTERVAL_MS) return false;
|
|
3605
|
+
}
|
|
3606
|
+
} catch {
|
|
3607
|
+
return false;
|
|
3608
|
+
}
|
|
3609
|
+
return true;
|
|
3610
|
+
}
|
|
3611
|
+
function startUpdateCheck(currentVersion) {
|
|
3612
|
+
try {
|
|
3613
|
+
if (!shouldCheck()) return;
|
|
3614
|
+
currentVer = currentVersion;
|
|
3615
|
+
try {
|
|
3616
|
+
setLastUpdateCheck((/* @__PURE__ */ new Date()).toISOString());
|
|
3617
|
+
} catch {
|
|
3618
|
+
}
|
|
3619
|
+
pendingAbort = new AbortController();
|
|
3620
|
+
pendingCheck = fetch(REGISTRY_URL, { signal: pendingAbort.signal }).then((res) => {
|
|
3621
|
+
if (!res.ok) return null;
|
|
3622
|
+
return res.json();
|
|
3623
|
+
}).then((data) => {
|
|
3624
|
+
if (data && typeof data.version === "string" && isGreaterThan(data.version, currentVersion)) {
|
|
3625
|
+
return data.version;
|
|
3626
|
+
}
|
|
3627
|
+
return null;
|
|
3628
|
+
}).catch(() => null);
|
|
3629
|
+
} catch {
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
async function printUpdateNotice() {
|
|
3633
|
+
try {
|
|
3634
|
+
if (!pendingCheck) return;
|
|
3635
|
+
const timeout = new Promise((resolve7) => {
|
|
3636
|
+
setTimeout(() => resolve7(null), FETCH_TIMEOUT_MS).unref();
|
|
3637
|
+
});
|
|
3638
|
+
const remoteVersion = await Promise.race([pendingCheck, timeout]);
|
|
3639
|
+
pendingAbort?.abort();
|
|
3640
|
+
pendingAbort = null;
|
|
3641
|
+
pendingCheck = null;
|
|
3642
|
+
if (remoteVersion) {
|
|
3643
|
+
const from = currentVer ?? "unknown";
|
|
3644
|
+
process.stderr.write(
|
|
3645
|
+
`
|
|
3646
|
+
Update available: ${from} \u2192 ${remoteVersion}
|
|
3647
|
+
Run: npm update -g @localskills/cli
|
|
3648
|
+
`
|
|
3649
|
+
);
|
|
3650
|
+
}
|
|
3651
|
+
} catch {
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3286
3655
|
// src/index.ts
|
|
3287
|
-
var
|
|
3288
|
-
|
|
3656
|
+
var require2 = createRequire(import.meta.url);
|
|
3657
|
+
var { version } = require2("../package.json");
|
|
3658
|
+
var program = new Command10();
|
|
3659
|
+
program.name("localskills").description("Install and manage agent skills from localskills.sh").version(version).option("--profile <name>", "Use a specific profile").option("--no-update-check", "Skip update check");
|
|
3660
|
+
program.hook("preAction", (thisCommand) => {
|
|
3661
|
+
const opts = thisCommand.opts();
|
|
3662
|
+
setProfileOverride(opts.profile);
|
|
3663
|
+
if (opts.updateCheck !== false) {
|
|
3664
|
+
startUpdateCheck(version);
|
|
3665
|
+
}
|
|
3666
|
+
});
|
|
3667
|
+
program.hook("postAction", async () => {
|
|
3668
|
+
await printUpdateNotice();
|
|
3669
|
+
});
|
|
3289
3670
|
program.addCommand(loginCommand);
|
|
3290
3671
|
program.addCommand(logoutCommand);
|
|
3291
3672
|
program.addCommand(whoamiCommand);
|
|
@@ -3296,4 +3677,12 @@ program.addCommand(pullCommand);
|
|
|
3296
3677
|
program.addCommand(publishCommand);
|
|
3297
3678
|
program.addCommand(pushCommand);
|
|
3298
3679
|
program.addCommand(shareCommand);
|
|
3299
|
-
program.
|
|
3680
|
+
program.addCommand(profileCommand);
|
|
3681
|
+
program.parseAsync().catch((err) => {
|
|
3682
|
+
if (err instanceof ProfileNotFoundError) {
|
|
3683
|
+
console.error(`Error: ${err.message}`);
|
|
3684
|
+
} else {
|
|
3685
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
3686
|
+
}
|
|
3687
|
+
process.exit(1);
|
|
3688
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localskills/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "CLI for localskills.sh — install agent skills locally",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"tsup": "^8",
|
|
20
20
|
"typescript": "^5.7",
|
|
21
21
|
"vitest": "^3",
|
|
22
|
-
"@localskills/
|
|
23
|
-
"@localskills/
|
|
22
|
+
"@localskills/shared": "0.0.0",
|
|
23
|
+
"@localskills/tsconfig": "0.0.0"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsup",
|