linersh 1.1.2 → 1.1.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.
Files changed (2) hide show
  1. package/bin/liner.js +47 -7
  2. package/package.json +6 -6
package/bin/liner.js CHANGED
@@ -11,14 +11,22 @@
11
11
  import { fileURLToPath } from "node:url";
12
12
  import { createRequire } from "node:module";
13
13
  import { spawn, spawnSync } from "node:child_process";
14
- import { existsSync, readFileSync, rmSync } from "node:fs";
14
+ import { existsSync, readFileSync, realpathSync, rmSync, statSync } from "node:fs";
15
15
  import { homedir } from "node:os";
16
- import { dirname, join } from "node:path";
16
+ import { basename, dirname, join } from "node:path";
17
17
 
18
18
  const here = dirname(fileURLToPath(import.meta.url));
19
19
  const packageRoot = join(here, "..");
20
20
  const argv = process.argv.slice(2);
21
21
 
22
+ // How many Liner launchers are already running above this one. The PATH
23
+ // fallback below must never start another launcher, and this depth limit
24
+ // stops a loop even where symlink checks can't see one (Windows .cmd shims).
25
+ const LAUNCHER_DEPTH_ENV = "LINER_LAUNCHER_DEPTH";
26
+ const launcherDepth = Number.parseInt(process.env[LAUNCHER_DEPTH_ENV] ?? "0", 10) || 0;
27
+ const MAX_PATH_FALLBACK_DEPTH = 1;
28
+ process.env[LAUNCHER_DEPTH_ENV] = String(launcherDepth + 1);
29
+
22
30
  const COMMAND_GROUPS = [
23
31
  {
24
32
  title: "Common workflow commands:",
@@ -373,17 +381,49 @@ function findRepoVenvBinary() {
373
381
  return null;
374
382
  }
375
383
 
384
+ // A separately installed Liner CLI on PATH, never a Liner launcher: npm puts
385
+ // this script on PATH through a symlink, so running the first hit would start
386
+ // this launcher again, which would do the same, without end.
376
387
  function findPathBinary() {
377
- const probe = spawnSync(process.platform === "win32" ? "where" : "which", ["liner"], {
378
- encoding: "utf8",
379
- });
388
+ if (launcherDepth > MAX_PATH_FALLBACK_DEPTH) return null;
389
+ // `where` lists every match; `which` needs -a, or it stops at the first one,
390
+ // which is usually this launcher.
391
+ const probe = spawnSync(
392
+ process.platform === "win32" ? "where" : "which",
393
+ process.platform === "win32" ? ["liner"] : ["-a", "liner"],
394
+ { encoding: "utf8" },
395
+ );
380
396
  if (probe.status !== 0) return null;
381
397
  const hits = probe.stdout
382
398
  .split(/\r?\n/)
383
399
  .map((s) => s.trim())
384
400
  .filter(Boolean);
385
- const thisShim = fileURLToPath(import.meta.url);
386
- return hits.find((hit) => hit !== thisShim) ?? null;
401
+ return hits.find((hit) => !isLinerLauncher(hit)) ?? null;
402
+ }
403
+
404
+ function isLinerLauncher(candidate) {
405
+ let real;
406
+ try {
407
+ real = realpathSync(candidate);
408
+ } catch {
409
+ return true;
410
+ }
411
+ if (real === realpathSync(fileURLToPath(import.meta.url))) return true;
412
+ // Another linersh install's launcher.
413
+ if (basename(real) === "liner.js" && basename(dirname(real)) === "bin") return true;
414
+ // npm's wrapper scripts (Windows .cmd/.ps1, and extensionless shell
415
+ // wrappers where symlinks aren't used) name the launcher they run, in any
416
+ // install layout. Real CLI binaries are not small text files that do.
417
+ return wrapperRunsLinerLauncher(real);
418
+ }
419
+
420
+ function wrapperRunsLinerLauncher(path) {
421
+ try {
422
+ if (statSync(path).size > 64 * 1024) return false;
423
+ return /linersh[\\/]+bin[\\/]+liner\.js/.test(readFileSync(path, "utf8"));
424
+ } catch {
425
+ return false;
426
+ }
387
427
  }
388
428
 
389
429
  function exeName() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linersh",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Liner — npm shim, Go TUI launcher, and headless methodology runner.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -48,11 +48,11 @@
48
48
  "yaml": "^2.6.1"
49
49
  },
50
50
  "optionalDependencies": {
51
- "linersh-darwin-arm64": "1.1.2",
52
- "linersh-darwin-x64": "1.1.2",
53
- "linersh-linux-arm64": "1.1.2",
54
- "linersh-linux-x64": "1.1.2",
55
- "linersh-win32-x64": "1.1.2"
51
+ "linersh-darwin-arm64": "1.1.3",
52
+ "linersh-darwin-x64": "1.1.3",
53
+ "linersh-linux-arm64": "1.1.3",
54
+ "linersh-linux-x64": "1.1.3",
55
+ "linersh-win32-x64": "1.1.3"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/node": "^22.10.0",