vexp-cli 2.0.13 → 2.0.15

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/dist/autostart.js CHANGED
@@ -251,12 +251,17 @@ function installWindowsStartup() {
251
251
  fs.mkdirSync(path.dirname(p), { recursive: true });
252
252
  const logPath = path.join(homedir(), ".vexp", "autostart.log");
253
253
  fs.mkdirSync(path.dirname(logPath), { recursive: true });
254
- const cmdParts = [launcher.bin, ...launcher.args]
255
- .map((a) => `""${a.replace(/"/g, '\\"')}""`)
256
- .join(" ");
254
+ // Two-context escaping: cmd.exe and VBScript both quote-double, but the
255
+ // contexts nest. Build the cmd string first, then wrap it as a VBScript
256
+ // string literal. /d /s /c forces legacy single-strip quote semantics so
257
+ // the outer `cmd ... "<inner>"` survives intact.
258
+ const quoteCmdArg = (a) => `"${a.replace(/"/g, '""')}"`;
259
+ const vbString = (s) => `"${s.replace(/"/g, '""')}"`;
260
+ const cmdParts = [launcher.bin, ...launcher.args].map(quoteCmdArg).join(" ");
261
+ const command = `%ComSpec% /d /s /c "${cmdParts} >> ${quoteCmdArg(logPath)} 2>&1"`;
257
262
  const script = `' vexp autostart — launches \`vexp serve\` hidden at login.\n` +
258
263
  `Set sh = CreateObject("WScript.Shell")\n` +
259
- `sh.Run "cmd /c " & ${JSON.stringify(cmdParts)} & " >> ""${logPath.replace(/\\/g, "\\\\")}"" 2>&1", 0, False\n`;
264
+ `sh.Run ${vbString(command)}, 0, False\n`;
260
265
  fs.writeFileSync(p, script);
261
266
  return { installed: true, mechanism: "windows-startup", path: p };
262
267
  }
@@ -318,6 +323,21 @@ export function uninstallAutostart() {
318
323
  export function installAutostartIfNeeded() {
319
324
  if (process.env.VEXP_NO_AUTOSTART_INSTALL === "1")
320
325
  return;
326
+ // 2.0.12 shipped a broken Windows VBS template; rewrite any existing
327
+ // file matching that signature regardless of marker state, so upgraded
328
+ // users get fixed without manual autostart uninstall/install.
329
+ if (process.platform === "win32") {
330
+ try {
331
+ const p = windowsStartupPath();
332
+ if (fs.existsSync(p)) {
333
+ const existing = fs.readFileSync(p, "utf-8");
334
+ if (/sh\.Run\s+"cmd \/c "\s*&/.test(existing)) {
335
+ installWindowsStartup();
336
+ }
337
+ }
338
+ }
339
+ catch { /* non-fatal */ }
340
+ }
321
341
  const marker = installMarkerPath();
322
342
  if (fs.existsSync(marker))
323
343
  return;
package/dist/license.js CHANGED
@@ -143,13 +143,21 @@ function clearDeviceBlocked() {
143
143
  * exactly as before. This MUST never throw.
144
144
  */
145
145
  export async function tryOnlineRefresh(longJwt) {
146
- // Rate limit: at most one network call per REFRESH_BACKOFF_MS
147
- const last = readLastCheck();
148
- if (Date.now() - last < REFRESH_BACKOFF_MS)
149
- return;
150
146
  // Global kill switch for users who want to stay fully offline
151
147
  if (process.env.VEXP_OFFLINE === "1")
152
148
  return;
149
+ // Rate limit: at most one network call per REFRESH_BACKOFF_MS,
150
+ // UNLESS the long JWT is within the last 7 days of validity.
151
+ // That window is where expiry is imminent — bypass backoff so any
152
+ // CLI invocation in the danger zone attempts a refresh.
153
+ const last = readLastCheck();
154
+ const longClaims = verifyAndDecode(longJwt);
155
+ const nearExpiryMs = 7 * 24 * 60 * 60 * 1000;
156
+ const nearExpiry = longClaims
157
+ ? longClaims.exp * 1000 - Date.now() < nearExpiryMs
158
+ : false;
159
+ if (!nearExpiry && Date.now() - last < REFRESH_BACKOFF_MS)
160
+ return;
153
161
  const controller = new AbortController();
154
162
  const timer = setTimeout(() => controller.abort(), VALIDATE_TIMEOUT_MS);
155
163
  try {
@@ -309,7 +317,8 @@ export function readLicenseLimits() {
309
317
  if (!longJwt)
310
318
  return FREE_LIMITS;
311
319
  const longClaims = verifyAndDecode(longJwt);
312
- if (!longClaims || longClaims.exp * 1000 < Date.now()) {
320
+ // Signature-invalid long JWT is fatal nothing the server can do.
321
+ if (!longClaims) {
313
322
  return FREE_LIMITS;
314
323
  }
315
324
  // Device cap enforcement: if this machine is over-cap, the server told
@@ -319,7 +328,9 @@ export function readLicenseLimits() {
319
328
  return FREE_LIMITS;
320
329
  }
321
330
  // Fire-and-forget opportunistic refresh. Never awaited — we do not block
322
- // CLI startup on the network.
331
+ // CLI startup on the network. We send the long JWT *even if it's already
332
+ // past `exp`*, because the server (Fix 3) accepts expired JWTs and
333
+ // re-issues freshTokens as long as the Stripe subscription (`pe`) holds.
323
334
  void tryOnlineRefresh(longJwt);
324
335
  // Prefer a still-valid freshToken within the grace window
325
336
  const fresh = loadFreshToken();
@@ -332,5 +343,11 @@ export function readLicenseLimits() {
332
343
  // grace window → server is unreachable for too long. Fall back to the
333
344
  // long JWT (keeps working as today, no downgrade to free).
334
345
  }
346
+ // No usable fresh token. If the long JWT itself has expired, we have
347
+ // nothing left to authenticate with → free tier. (The refresh fired
348
+ // above will populate fresh.jwt asynchronously for the next call.)
349
+ if (longClaims.exp * 1000 < Date.now()) {
350
+ return FREE_LIMITS;
351
+ }
335
352
  return claimsToLimits(longClaims);
336
353
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vexp-cli",
3
- "version": "2.0.13",
3
+ "version": "2.0.15",
4
4
  "description": "Vexp — Context Engine for AI Coding Agents. Pre-indexes your codebase into a dependency graph and delivers ranked context to any MCP-compatible agent. 58% lower cost per task, 90% fewer tool calls (SWE-bench Verified). Works with Claude Code, Cursor, Copilot, Windsurf, Codex, Cline, Aider, and 12+ agents. Local-first. Your code never leaves your machine.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -99,10 +99,10 @@
99
99
  "node": ">=20.0.0"
100
100
  },
101
101
  "optionalDependencies": {
102
- "@vexp/core-linux-x64": "2.0.13",
103
- "@vexp/core-linux-arm64": "2.0.13",
104
- "@vexp/core-darwin-x64": "2.0.13",
105
- "@vexp/core-darwin-arm64": "2.0.13",
106
- "@vexp/core-win32-x64": "2.0.13"
102
+ "@vexp/core-linux-x64": "2.0.15",
103
+ "@vexp/core-linux-arm64": "2.0.15",
104
+ "@vexp/core-darwin-x64": "2.0.15",
105
+ "@vexp/core-darwin-arm64": "2.0.15",
106
+ "@vexp/core-win32-x64": "2.0.15"
107
107
  }
108
108
  }