yaver-cli 1.99.360 → 1.99.368

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.360",
3
+ "version": "1.99.368",
4
4
  "mcpName": "io.github.yaver-io/yaver",
5
5
  "description": "Unified npm bootstrap for the Yaver agent, SDK injection, and local-first developer runtime",
6
6
  "bin": {
@@ -59,7 +59,15 @@ async function ensureAgentBinary({ quiet = false } = {}) {
59
59
  const binaryPath = path.join(installDir, asset.binaryName);
60
60
  if (fs.existsSync(binaryPath)) {
61
61
  if (process.platform !== 'win32') fs.chmodSync(binaryPath, 0o755);
62
- return binaryPath;
62
+ if (cachedBinaryRuns(binaryPath)) return binaryPath;
63
+ // Present but unusable: re-download rather than hand back a brick. The
64
+ // old code returned it on existence alone, so one interrupted download
65
+ // wedged that version forever — every later run found the file, returned
66
+ // it, and failed identically with nothing to point at.
67
+ if (!quiet) {
68
+ console.error(`[yaver] cached agent ${asset.version} is present but will not run — re-downloading`);
69
+ }
70
+ try { fs.rmSync(installDir, { recursive: true, force: true }); } catch (_) {}
63
71
  }
64
72
  return await downloadAndCacheAgent(asset, { quiet });
65
73
  } catch (err) {
@@ -81,6 +89,44 @@ async function ensureAgentBinary({ quiet = false } = {}) {
81
89
  }
82
90
  }
83
91
 
92
+ /** A cached agent binary is only usable if it actually RUNS.
93
+ *
94
+ * `fs.existsSync` answers "is there a file here", which is a proxy for the
95
+ * thing we need: "can this machine exec it and get a version back". The two
96
+ * disagree in exactly the cases that matter — an interrupted download leaves a
97
+ * truncated file, a killed extract leaves a 0-byte one, an unsigned or
98
+ * quarantined binary on macOS exists and refuses to launch. Every one of those
99
+ * passes existsSync and then dies at spawn with a bare exit code and nothing
100
+ * said (observed 2026-07-25: a launchd agent flapping on status 78 because its
101
+ * version dir held no binary — the operator sees "the daemon won't start" and
102
+ * no reason anywhere).
103
+ *
104
+ * So probe the operation: run `--version` with a short timeout. Cheap (single
105
+ * exec, no network) and it turns a silent brick into a fall back onto the last
106
+ * binary that demonstrably works. */
107
+ function cachedBinaryRuns(binaryPath) {
108
+ try {
109
+ const st = fs.statSync(binaryPath);
110
+ // A real agent is tens of MB; anything tiny is a truncated download, and
111
+ // exec'ing it would just be a slower way to learn that.
112
+ if (!st.isFile() || st.size < 1024 * 1024) return false;
113
+ } catch (_) {
114
+ return false;
115
+ }
116
+ try {
117
+ const res = spawnSync(binaryPath, ['--version'], {
118
+ timeout: 10_000,
119
+ encoding: 'utf8',
120
+ stdio: ['ignore', 'pipe', 'pipe'],
121
+ });
122
+ if (res.error) return false;
123
+ if (res.status !== 0) return false;
124
+ return /\byaver\b/i.test(String(res.stdout || '') + String(res.stderr || ''));
125
+ } catch (_) {
126
+ return false;
127
+ }
128
+ }
129
+
84
130
  /** Walk CACHE_ROOT and return the path + version of any cached
85
131
  * binary newer than `version` that matches this platform. Returns
86
132
  * null when nothing higher is cached. Used as a "prefer-newer"
@@ -102,6 +148,13 @@ function findHigherCachedThan(version) {
102
148
  if (process.platform !== 'win32') {
103
149
  try { fs.chmodSync(p, 0o755); } catch (_) {}
104
150
  }
151
+ // Verify before preferring it: a broken NEWER cache entry must not
152
+ // shadow a working older one. Skipping this is how "prefer newest"
153
+ // becomes "prefer whatever downloaded halfway".
154
+ if (!cachedBinaryRuns(p)) {
155
+ console.error(`[yaver] cached agent ${v} will not run (incomplete download?) — skipping it`);
156
+ continue;
157
+ }
105
158
  return { path: p, version: v };
106
159
  }
107
160
  }
@@ -126,6 +179,13 @@ function findMostRecentCachedAgent() {
126
179
  if (process.platform !== 'win32') {
127
180
  try { fs.chmodSync(p, 0o755); } catch (_) {}
128
181
  }
182
+ // This is the offline/rate-limited fallback — the LAST thing standing
183
+ // between the user and "yaver does nothing". Returning a file that
184
+ // cannot exec here produces a bare failure with no network to blame.
185
+ if (!cachedBinaryRuns(p)) {
186
+ console.error(`[yaver] cached agent ${v} will not run (incomplete download?) — trying an older one`);
187
+ continue;
188
+ }
129
189
  return p;
130
190
  }
131
191
  }