@synity/bitrix-skills 1.3.7 → 1.3.9

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cli.js +93 -15
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.9
4
+
5
+ ### Patch Changes
6
+
7
+ - feat(license): auto-detect UUID tokens (from OTP email flow) — pass as `token` field instead of `key`
8
+
9
+ ## 1.3.8
10
+
11
+ ### Patch Changes
12
+
13
+ - fix(list): global features now show correct [installed] status by checking ~/.claude/skills/<name>/ on disk instead of project manifest
14
+
15
+ fix(install): add --all flag + generic install handler for bx, bx-crm, bx-calendar global skills
16
+
3
17
  ## 1.3.7
4
18
 
5
19
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -944,11 +944,15 @@ import { createRequire } from "module";
944
944
 
945
945
  // src/commands/install.ts
946
946
  init_esm_shims();
947
+ init_fs_safety();
947
948
  import { Command, Option } from "clipanion";
948
949
  import chalk from "chalk";
949
950
  import { existsSync as existsSync4 } from "fs";
951
+ import { access as access4, copyFile as copyFile3, mkdir as mkdir4, readdir as readdir2 } from "fs/promises";
950
952
  import { createInterface } from "readline";
951
- import { join as join4, relative as relative2 } from "path";
953
+ import { dirname as dirname3, join as join4, relative as relative2, resolve as resolve2 } from "path";
954
+ import { homedir as homedir2 } from "os";
955
+ import { fileURLToPath as fileURLToPath5 } from "url";
952
956
 
953
957
  // src/lib/feature-registry.ts
954
958
  init_esm_shims();
@@ -996,15 +1000,18 @@ function listFeatures(featuresDir) {
996
1000
  // src/lib/license.ts
997
1001
  init_esm_shims();
998
1002
  var LICENSE_WORKER_URL = "https://license-gate.synity.workers.dev";
1003
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
999
1004
  async function verifyLicense(key) {
1000
1005
  if (!key || key.trim() === "") {
1001
1006
  return { ok: true, tier: 0 };
1002
1007
  }
1008
+ const trimmed = key.trim();
1009
+ const isToken = UUID_RE.test(trimmed);
1003
1010
  try {
1004
1011
  const res = await fetch(`${LICENSE_WORKER_URL}/verify`, {
1005
1012
  method: "POST",
1006
1013
  headers: { "Content-Type": "application/json" },
1007
- body: JSON.stringify({ key: key.trim() }),
1014
+ body: JSON.stringify(isToken ? { token: trimmed } : { key: trimmed }),
1008
1015
  signal: AbortSignal.timeout(8e3)
1009
1016
  });
1010
1017
  if (!res.ok) {
@@ -1046,11 +1053,11 @@ function computeChecksum(filepath) {
1046
1053
 
1047
1054
  // src/commands/install.ts
1048
1055
  function promptKey() {
1049
- return new Promise((resolve2) => {
1056
+ return new Promise((resolve4) => {
1050
1057
  const rl = createInterface({ input: process.stdin, output: process.stdout });
1051
1058
  rl.question(chalk.cyan("License key (Enter to skip \u2014 free tier only): "), (answer) => {
1052
1059
  rl.close();
1053
- resolve2(answer.trim());
1060
+ resolve4(answer.trim());
1054
1061
  });
1055
1062
  });
1056
1063
  }
@@ -1096,6 +1103,63 @@ async function installBxTask(cwd) {
1096
1103
  return { ok: false, message: `bx-task: ${err.message}` };
1097
1104
  }
1098
1105
  }
1106
+ async function getFeatureAssetsDir(featureName) {
1107
+ const here = dirname3(fileURLToPath5(import.meta.url));
1108
+ const candidates = [
1109
+ // dist/cli.js context (inlined): ../src/features/<name>/assets
1110
+ resolve2(here, `../src/features/${featureName}/assets`),
1111
+ // dist/features/<name>/ context: ../../../src/features/<name>/assets
1112
+ resolve2(here, `../../../src/features/${featureName}/assets`),
1113
+ // dev (running from src/)
1114
+ resolve2(here, `../features/${featureName}/assets`)
1115
+ ];
1116
+ for (const c of candidates) {
1117
+ try {
1118
+ await access4(c);
1119
+ return c;
1120
+ } catch {
1121
+ }
1122
+ }
1123
+ return candidates[0];
1124
+ }
1125
+ async function copyDirRecursive(srcDir, destDir, skillBase, installedFiles, onConflict) {
1126
+ await mkdir4(destDir, { recursive: true });
1127
+ const entries = await readdir2(srcDir, { withFileTypes: true });
1128
+ for (const entry of entries) {
1129
+ const src = join4(srcDir, entry.name);
1130
+ const dest = join4(destDir, entry.name);
1131
+ if (entry.isDirectory()) {
1132
+ await copyDirRecursive(src, dest, skillBase, installedFiles, onConflict);
1133
+ continue;
1134
+ }
1135
+ assertContainedIn(dest, skillBase, dest);
1136
+ await assertNotSymlink(dest);
1137
+ try {
1138
+ await access4(dest);
1139
+ if (onConflict === "skip") continue;
1140
+ } catch {
1141
+ }
1142
+ await copyFile3(src, dest);
1143
+ installedFiles.push(dest);
1144
+ }
1145
+ }
1146
+ async function installGlobalSkill(featureName) {
1147
+ try {
1148
+ const skillBase = resolve2(homedir2(), ".claude", "skills");
1149
+ const dest = resolve2(skillBase, featureName);
1150
+ const assetsDir = await getFeatureAssetsDir(featureName);
1151
+ const installedFiles = [];
1152
+ await copyDirRecursive(assetsDir, dest, skillBase, installedFiles, "overwrite");
1153
+ return {
1154
+ ok: true,
1155
+ message: `${featureName}: ${installedFiles.length} files installed`,
1156
+ installPath: dest,
1157
+ installedAbsPaths: installedFiles
1158
+ };
1159
+ } catch (err) {
1160
+ return { ok: false, message: `${featureName}: ${err.message}` };
1161
+ }
1162
+ }
1099
1163
  function upsertFeature(manifest, entry) {
1100
1164
  const idx = manifest.features.findIndex((f) => f.name === entry.name);
1101
1165
  const features = [...manifest.features];
@@ -1122,6 +1186,7 @@ var InstallCommand = class extends Command {
1122
1186
  ]
1123
1187
  });
1124
1188
  key = Option.String("--key", { description: "License key to unlock paid tier features" });
1189
+ all = Option.Boolean("--all", false, { description: "Install all available features" });
1125
1190
  featuresFlag = Option.String("--features", { description: "Comma-separated feature names" });
1126
1191
  featureArgs = Option.Rest({ required: 0 });
1127
1192
  async execute() {
@@ -1207,10 +1272,15 @@ Installing ${chalk.bold(name)}...
1207
1272
  ok = r.ok;
1208
1273
  message = r.message;
1209
1274
  if (r.installPath) {
1210
- const { homedir: homedir2 } = await import("os");
1211
1275
  installPath = join4(homedir2(), ".claude", "skills", "bx-task");
1212
1276
  }
1213
1277
  installedAbsPaths = r.installedAbsPaths ?? [];
1278
+ } else if (featureInfo.target === "global") {
1279
+ const r = await installGlobalSkill(name);
1280
+ ok = r.ok;
1281
+ message = r.message;
1282
+ if (r.installPath) installPath = r.installPath;
1283
+ installedAbsPaths = r.installedAbsPaths ?? [];
1214
1284
  } else {
1215
1285
  this.context.stderr.write(chalk.yellow(` ! No install handler for feature: ${name}
1216
1286
  `));
@@ -1321,6 +1391,15 @@ Uninstalling ${chalk2.bold(name)}...
1321
1391
  init_esm_shims();
1322
1392
  import { Command as Command3 } from "clipanion";
1323
1393
  import chalk3 from "chalk";
1394
+ import { existsSync as existsSync5 } from "fs";
1395
+ import { resolve as resolve3 } from "path";
1396
+ import { homedir as homedir3 } from "os";
1397
+ function isInstalled(f, manifest) {
1398
+ if (f.target === "global") {
1399
+ return existsSync5(resolve3(homedir3(), ".claude", "skills", f.name));
1400
+ }
1401
+ return !!manifest?.features.find((e) => e.name === f.name);
1402
+ }
1324
1403
  var ListCommand = class extends Command3 {
1325
1404
  static paths = [["list"]];
1326
1405
  static usage = Command3.Usage({
@@ -1329,20 +1408,19 @@ var ListCommand = class extends Command3 {
1329
1408
  async execute() {
1330
1409
  const features = listFeatures();
1331
1410
  const manifest = readManifest(process.cwd());
1332
- const installed = new Map(manifest?.features.map((f) => [f.name, f]) ?? []);
1333
1411
  if (features.length === 0) {
1334
1412
  this.context.stdout.write(chalk3.yellow("No features found.\n"));
1335
1413
  return 0;
1336
1414
  }
1337
1415
  this.context.stdout.write("\nAvailable features:\n");
1338
1416
  for (const f of features) {
1339
- const entry = installed.get(f.name);
1340
- const check = entry ? chalk3.green("\u2713") : chalk3.gray("\u25CB");
1417
+ const installed = isInstalled(f, manifest);
1418
+ const check = installed ? chalk3.green("\u2713") : chalk3.gray("\u25CB");
1341
1419
  const name = chalk3.bold(f.name.padEnd(12));
1342
1420
  const ver = chalk3.cyan(f.version.padEnd(14));
1343
1421
  const target = chalk3.gray(f.target.padEnd(8));
1344
1422
  const desc = f.description;
1345
- const status = entry ? chalk3.green("[installed]") : chalk3.gray("[not installed]");
1423
+ const status = installed ? chalk3.green("[installed]") : chalk3.gray("[not installed]");
1346
1424
  this.context.stdout.write(` ${check} ${name} ${ver} ${target} ${desc} ${status}
1347
1425
  `);
1348
1426
  }
@@ -1354,7 +1432,7 @@ var ListCommand = class extends Command3 {
1354
1432
  init_esm_shims();
1355
1433
  import { Command as Command4 } from "clipanion";
1356
1434
  import chalk4 from "chalk";
1357
- import { existsSync as existsSync5 } from "fs";
1435
+ import { existsSync as existsSync6 } from "fs";
1358
1436
  import { join as join5 } from "path";
1359
1437
  var VerifyCommand = class extends Command4 {
1360
1438
  static paths = [["verify"]];
@@ -1378,7 +1456,7 @@ var VerifyCommand = class extends Command4 {
1378
1456
  `);
1379
1457
  this.context.stdout.write(chalk4.gray(` install path: ${feature.installPath}
1380
1458
  `));
1381
- if (!existsSync5(feature.installPath)) {
1459
+ if (!existsSync6(feature.installPath)) {
1382
1460
  this.context.stderr.write(chalk4.red(` \u2717 install path missing: ${feature.installPath}
1383
1461
  `));
1384
1462
  anyMismatch = true;
@@ -1391,7 +1469,7 @@ var VerifyCommand = class extends Command4 {
1391
1469
  }
1392
1470
  for (const [rel, expectedHash] of Object.entries(stored)) {
1393
1471
  const absPath = join5(feature.installPath, rel);
1394
- if (!existsSync5(absPath)) {
1472
+ if (!existsSync6(absPath)) {
1395
1473
  this.context.stderr.write(chalk4.red(` \u2717 missing: ${rel}
1396
1474
  `));
1397
1475
  anyMismatch = true;
@@ -1430,7 +1508,7 @@ var VerifyCommand = class extends Command4 {
1430
1508
  init_esm_shims();
1431
1509
  import { Command as Command5 } from "clipanion";
1432
1510
  import chalk5 from "chalk";
1433
- import { existsSync as existsSync6 } from "fs";
1511
+ import { existsSync as existsSync7 } from "fs";
1434
1512
  import { relative as relative3 } from "path";
1435
1513
  var UpdateCommand = class extends Command5 {
1436
1514
  static paths = [["update"]];
@@ -1478,7 +1556,7 @@ var UpdateCommand = class extends Command5 {
1478
1556
  const { buildDestMap: buildDestMap2 } = await Promise.resolve().then(() => (init_dest_map(), dest_map_exports));
1479
1557
  const assetManifest = await loadManifest2();
1480
1558
  const dests = buildDestMap2(assetManifest, cwd);
1481
- installedAbsPaths = dests.map((d) => d.destAbs).filter((p) => existsSync6(p));
1559
+ installedAbsPaths = dests.map((d) => d.destAbs).filter((p) => existsSync7(p));
1482
1560
  } catch {
1483
1561
  }
1484
1562
  } else if (feature.name === "bx-task") {
@@ -1507,7 +1585,7 @@ var UpdateCommand = class extends Command5 {
1507
1585
  const checksums = {};
1508
1586
  for (const absPath of installedAbsPaths) {
1509
1587
  try {
1510
- if (existsSync6(absPath)) {
1588
+ if (existsSync7(absPath)) {
1511
1589
  const rel = relative3(feature.installPath, absPath);
1512
1590
  checksums[rel] = computeChecksum(absPath);
1513
1591
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synity/bitrix-skills",
3
- "version": "1.3.7",
3
+ "version": "1.3.9",
4
4
  "description": "Multi-feature Bitrix24 tooling CLI for Synity projects",
5
5
  "type": "module",
6
6
  "bin": {