@lark-people/cli 0.0.0-beta.1 → 0.0.0-beta.3

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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-people/cli",
3
- "version": "0.0.0-beta.1",
3
+ "version": "0.0.0-beta.3",
4
4
  "description": "The official CLI for People open platform",
5
5
  "bin": {
6
6
  "people-cli": "scripts/run.js"
@@ -10,7 +10,16 @@ const CURRENT_PACKAGE_DIR = path.resolve(__dirname, "..");
10
10
  const DEFAULT_PKG = "@lark-people/cli";
11
11
  const DEFAULT_SKILLS_DIR = path.join(__dirname, "..", "skills");
12
12
  const isWindows = process.platform === "win32";
13
- const EXCLUDED_BUNDLED_SKILLS = new Set(["people-skill-maker"]);
13
+ // Skills excluded from installation. `people-skill-maker` is an authoring-only
14
+ // skill; the people-attendance/people-overtime/people-hire skills are gated
15
+ // until their capabilities are ready for release (keep in sync with
16
+ // internal/features/features.go).
17
+ const EXCLUDED_BUNDLED_SKILLS = new Set([
18
+ "people-skill-maker",
19
+ "people-attendance",
20
+ "people-overtime",
21
+ "people-hire",
22
+ ]);
14
23
 
15
24
  function readPackageMeta(packageDir = CURRENT_PACKAGE_DIR) {
16
25
  try {
@@ -32,6 +41,9 @@ const messages = {
32
41
  step1Done: "已全局安装",
33
42
  step1Upgraded: "已升级到 v%s",
34
43
  step1Fail: "全局安装失败。运行以下命令重试: npm install -g %s",
44
+ step1MigrateSpinner: "检测到旧版包 %s 占用 people-cli 命令,正在迁移...",
45
+ step1MigrateDone: "已卸载旧版包 %s",
46
+ step1MigrateFail: "卸载旧版包 %s 失败,请手动运行: npm uninstall -g %s",
35
47
  step2Spinner: "正在安装 Skills...",
36
48
  step2Skip: "官方 Skills 已齐全,跳过",
37
49
  step2Done: "Skills 已安装",
@@ -62,6 +74,9 @@ const messages = {
62
74
  step1Done: "Installed globally",
63
75
  step1Upgraded: "Upgraded to v%s",
64
76
  step1Fail: "Failed to install globally. Run manually: npm install -g %s",
77
+ step1MigrateSpinner: "Legacy package %s owns the people-cli command. Migrating...",
78
+ step1MigrateDone: "Removed legacy package %s",
79
+ step1MigrateFail: "Failed to remove legacy package %s. Run manually: npm uninstall -g %s",
65
80
  step2Spinner: "Installing skills...",
66
81
  step2Skip: "Official skills already present. Skipped",
67
82
  step2Done: "Skills installed",
@@ -212,6 +227,76 @@ function resolveGlobalPackageDir(packageName = PKG) {
212
227
  }
213
228
  }
214
229
 
230
+ // Extract the owning npm package name from a resolved path that points inside a
231
+ // node_modules tree, e.g. ".../node_modules/@people/people-cli/scripts/run.js"
232
+ // -> "@people/people-cli". Returns null when the path is not inside a package.
233
+ function packageNameFromModulePath(resolvedPath) {
234
+ if (!resolvedPath) {
235
+ return null;
236
+ }
237
+ const normalized = String(resolvedPath).replace(/\\/g, "/");
238
+ const marker = "/node_modules/";
239
+ const idx = normalized.lastIndexOf(marker);
240
+ if (idx === -1) {
241
+ return null;
242
+ }
243
+ const segments = normalized.slice(idx + marker.length).split("/").filter(Boolean);
244
+ if (segments.length === 0) {
245
+ return null;
246
+ }
247
+ if (segments[0].startsWith("@")) {
248
+ return segments.length >= 2 ? `${segments[0]}/${segments[1]}` : null;
249
+ }
250
+ return segments[0];
251
+ }
252
+
253
+ // Resolve which npm package currently owns the global `people-cli` bin. On
254
+ // POSIX the bin is a symlink into the owning package; on Windows the .cmd shim
255
+ // is not a symlink, so ownership cannot be resolved and we return null.
256
+ function resolveGlobalBinOwner(binName = BIN_NAME, deps = {}) {
257
+ const runSilentFn = deps.runSilent || runSilent;
258
+ const realpathSync = deps.realpathSync || fs.realpathSync;
259
+ try {
260
+ const prefix = runSilentFn("npm", ["prefix", "-g"], { timeout: 15000 }).toString().trim();
261
+ const binPath = isWindows
262
+ ? path.join(prefix, `${binName}.cmd`)
263
+ : path.join(prefix, "bin", binName);
264
+ const resolved = realpathSync(binPath);
265
+ return packageNameFromModulePath(resolved);
266
+ } catch (_) {
267
+ return null;
268
+ }
269
+ }
270
+
271
+ // Decide whether a globally installed package owns the `people-cli` bin under a
272
+ // DIFFERENT package name than the one we are about to install. This is the root
273
+ // cause of `npm EEXIST` after a package rename: npm refuses to overwrite a bin
274
+ // owned by another package. Returns the conflicting owner name, or null.
275
+ function findConflictingBinOwner(targetPackageName = PKG, deps = {}) {
276
+ const owner = deps.resolveGlobalBinOwner
277
+ ? deps.resolveGlobalBinOwner(BIN_NAME)
278
+ : resolveGlobalBinOwner();
279
+ if (!owner || owner === targetPackageName) {
280
+ return null;
281
+ }
282
+ return owner;
283
+ }
284
+
285
+ // Uninstall a conflicting legacy package that owns the `people-cli` bin so the
286
+ // renamed package can claim the bin without `npm EEXIST`.
287
+ function removeConflictingLegacyPackage(owner, deps = {}) {
288
+ if (!owner) {
289
+ return false;
290
+ }
291
+ const runSilentFn = deps.runSilent || runSilent;
292
+ try {
293
+ runSilentFn("npm", ["uninstall", "-g", owner], { timeout: 120000 });
294
+ return true;
295
+ } catch (_) {
296
+ return false;
297
+ }
298
+ }
299
+
215
300
  function sameResolvedPath(a, b) {
216
301
  if (!a || !b) {
217
302
  return false;
@@ -503,6 +588,32 @@ async function stepInstallGlobally(msg) {
503
588
  return false;
504
589
  }
505
590
 
591
+ // Detect a legacy package that owns the `people-cli` bin under a different
592
+ // name (e.g. after renaming @people/people-cli -> @lark-people/cli). Left in
593
+ // place, npm aborts the install with EEXIST. Remove it first so the renamed
594
+ // package (always installed under currentPkg's name) can claim the bin.
595
+ const conflictingOwner = findConflictingBinOwner(currentPkg);
596
+ if (conflictingOwner) {
597
+ const migrateSpinner = p ? p.spinner() : null;
598
+ if (migrateSpinner) {
599
+ migrateSpinner.start(fmt(msg.step1MigrateSpinner, conflictingOwner));
600
+ } else {
601
+ console.log(fmt(msg.step1MigrateSpinner, conflictingOwner));
602
+ }
603
+ const removed = removeConflictingLegacyPackage(conflictingOwner);
604
+ if (removed) {
605
+ if (migrateSpinner) {
606
+ migrateSpinner.stop(fmt(msg.step1MigrateDone, conflictingOwner));
607
+ } else {
608
+ console.log(fmt(msg.step1MigrateDone, conflictingOwner));
609
+ }
610
+ } else if (migrateSpinner) {
611
+ migrateSpinner.stop(fmt(msg.step1MigrateFail, conflictingOwner, conflictingOwner));
612
+ } else {
613
+ console.error(fmt(msg.step1MigrateFail, conflictingOwner, conflictingOwner));
614
+ }
615
+ }
616
+
506
617
  const spinner = p ? p.spinner() : null;
507
618
  if (spinner) {
508
619
  spinner.start(
@@ -518,12 +629,22 @@ async function stepInstallGlobally(msg) {
518
629
  }
519
630
  return true;
520
631
  } catch (_) {
521
- if (spinner) {
522
- spinner.stop(fmt(msg.step1Fail, PKG));
523
- } else {
524
- console.error(fmt(msg.step1Fail, PKG));
632
+ // Last-resort retry: force npm to overwrite any bin left behind by a legacy
633
+ // package that could not be cleanly removed above.
634
+ try {
635
+ await runSilentAsync("npm", ["install", "-g", "--force", plan.installTarget], { timeout: 120000 });
636
+ if (spinner) {
637
+ spinner.stop(plan.needsUpgrade ? fmt(msg.step1Upgraded, plan.installVersion) : msg.step1Done);
638
+ }
639
+ return true;
640
+ } catch (_) {
641
+ if (spinner) {
642
+ spinner.stop(fmt(msg.step1Fail, PKG));
643
+ } else {
644
+ console.error(fmt(msg.step1Fail, PKG));
645
+ }
646
+ process.exit(1);
525
647
  }
526
- process.exit(1);
527
648
  }
528
649
  }
529
650
 
@@ -672,15 +793,19 @@ module.exports = {
672
793
  buildInstallAuthLoginArgs,
673
794
  DEFAULT_SKILLS_DIR,
674
795
  diffMissingSkills,
796
+ findConflictingBinOwner,
675
797
  formatCommandFailure,
676
798
  buildSkillsCommandArgs,
677
799
  inspectExistingAppConfig,
678
800
  listBundledSkills,
679
801
  listInstalledGlobalSkills,
802
+ packageNameFromModulePath,
680
803
  parseGlobalSkillsJSON,
681
804
  parseGlobalSkillsText,
682
805
  parseLangArg,
683
806
  planGlobalInstall,
807
+ removeConflictingLegacyPackage,
808
+ resolveGlobalBinOwner,
684
809
  resolveSkillsLauncher,
685
810
  stepInstallSkills,
686
811
  main,