yaver-cli 1.99.167 → 1.99.171

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaver-cli",
3
- "version": "1.99.167",
3
+ "version": "1.99.171",
4
4
  "mcpName": "io.github.kivanccakmak/yaver",
5
5
  "description": "Unified npm bootstrap for the Yaver agent, SDK injection, and local-first developer runtime",
6
6
  "bin": {
@@ -36,6 +36,25 @@ async function ensureAgentBinary({ quiet = false } = {}) {
36
36
  const asset = await resolveAsset();
37
37
  const localAgentPath = resolveLocalAgentBinary(asset);
38
38
  if (localAgentPath) return localAgentPath;
39
+ // Defense in depth: if the cache holds a HIGHER version than
40
+ // what GH resolved, prefer it. Two failure modes this catches:
41
+ // 1. Older wrappers with the namespaced-tag-strip bug resolve
42
+ // to a stale legacy `v*` release even when newer `cli/v*`
43
+ // tags exist. Cache snapshot wins until npm catches up.
44
+ // 2. A new release publishes its tag but skips the platform
45
+ // asset (e.g. macOS notarize fails); falling back to a
46
+ // previously-cached newer build is better than running
47
+ // the older one.
48
+ const higherCached = findHigherCachedThan(asset.version);
49
+ if (higherCached) {
50
+ if (!quiet) {
51
+ console.error(
52
+ `[yaver] cache holds a newer agent (${higherCached.version}) than the resolved release ` +
53
+ `(${asset.version}); preferring cached binary.`,
54
+ );
55
+ }
56
+ return higherCached.path;
57
+ }
39
58
  const installDir = path.join(CACHE_ROOT, asset.version, asset.cacheKey);
40
59
  const binaryPath = path.join(installDir, asset.binaryName);
41
60
  if (fs.existsSync(binaryPath)) {
@@ -62,6 +81,33 @@ async function ensureAgentBinary({ quiet = false } = {}) {
62
81
  }
63
82
  }
64
83
 
84
+ /** Walk CACHE_ROOT and return the path + version of any cached
85
+ * binary newer than `version` that matches this platform. Returns
86
+ * null when nothing higher is cached. Used as a "prefer-newer"
87
+ * override on top of the GH-resolved version. */
88
+ function findHigherCachedThan(version) {
89
+ if (!fs.existsSync(CACHE_ROOT)) return null;
90
+ if (!semver.valid(version)) return null;
91
+ const platform = process.platform;
92
+ const arch = process.arch;
93
+ const goArch = arch === 'x64' ? 'amd64' : arch === 'arm64' ? 'arm64' : arch;
94
+ const cacheKey = `${platform}-${goArch}`;
95
+ const binaryName = platform === 'win32' ? 'yaver.exe' : 'yaver';
96
+ const versions = fs.readdirSync(CACHE_ROOT).filter((v) => semver.valid(v));
97
+ versions.sort(semver.rcompare);
98
+ for (const v of versions) {
99
+ if (semver.lte(v, version)) break; // sorted desc; nothing higher remains
100
+ const p = path.join(CACHE_ROOT, v, cacheKey, binaryName);
101
+ if (fs.existsSync(p)) {
102
+ if (process.platform !== 'win32') {
103
+ try { fs.chmodSync(p, 0o755); } catch (_) {}
104
+ }
105
+ return { path: p, version: v };
106
+ }
107
+ }
108
+ return null;
109
+ }
110
+
65
111
  /** Walk CACHE_ROOT and return the path to the newest yaver binary
66
112
  * matching this platform — used as fallback when GH /releases is
67
113
  * rate-limited and no specific version was resolvable. */
@@ -497,13 +543,13 @@ async function fetchRemoteAsset(platform, goArch) {
497
543
  downloadName: asset.name,
498
544
  archiveType: 'exe',
499
545
  url: asset.browser_download_url,
500
- version: release.tag_name.replace(/^v/, ''),
546
+ version: stripCliTagPrefix(release.tag_name),
501
547
  };
502
548
  }
503
549
 
504
550
  if (platform === 'darwin' || platform === 'linux') {
505
551
  const release = await fetchLatestRelease(DEFAULT_REPO);
506
- const version = release.tag_name.replace(/^v/, '');
552
+ const version = stripCliTagPrefix(release.tag_name);
507
553
  const asset = findReleaseAsset(release, [
508
554
  `yaver-v${version}-${platform}-${goArch}.tar.gz`,
509
555
  `yaver-${platform}-${goArch}.tar.gz`,
@@ -525,6 +571,19 @@ async function fetchRemoteAsset(platform, goArch) {
525
571
  throw new Error(`unsupported platform for npm bootstrap: ${platform}`);
526
572
  }
527
573
 
574
+ // Strip the CLI release tag prefix and return the bare semver — or
575
+ // null if this isn't a CLI release at all. Tags now live in per-surface
576
+ // namespaces: `cli/v1.99.167`, `mobile/v1.18.91`, `web/v1.1.131`, etc.
577
+ // Pre-1.99.124 releases were just `v1.99.149`. Only those two shapes
578
+ // belong to the CLI; mobile/web/relay tags must NOT pass through, or
579
+ // `findReleaseAsset` will look for a yaver-linux-arm64 tarball on a
580
+ // mobile release and silently fail. Returning null here lets
581
+ // `semver.valid(stripCliTagPrefix(...))` filter non-CLI tags out.
582
+ function stripCliTagPrefix(tag) {
583
+ const match = String(tag || '').match(/^(?:cli\/)?v(.+)$/);
584
+ return match ? match[1] : null;
585
+ }
586
+
528
587
  async function fetchLatestRelease(repo) {
529
588
  const response = await request(`https://api.github.com/repos/${repo}/releases?per_page=20`, {
530
589
  headers: {
@@ -545,7 +604,7 @@ async function fetchLatestRelease(repo) {
545
604
  }
546
605
 
547
606
  const latest = Array.isArray(releases)
548
- ? releases.find((release) => !release.draft && !release.prerelease && semver.valid(String(release.tag_name || '').replace(/^v/, '')))
607
+ ? releases.find((release) => !release.draft && !release.prerelease && semver.valid(stripCliTagPrefix(String(release.tag_name || ''))))
549
608
  : null;
550
609
 
551
610
  if (!latest || !latest.tag_name) {