@tpsdev-ai/flair 0.25.1 → 0.25.2

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/cli.js CHANGED
@@ -12,8 +12,8 @@ import { create as tarCreate, extract as tarExtract, list as tarList } from "tar
12
12
  import { keystore } from "./keystore.js";
13
13
  import { deploy as deployToFabric, validateOptions as validateDeployOptions, buildTargetUrl as buildDeployUrl } from "./deploy.js";
14
14
  import { fabricUpgrade } from "./fabric-upgrade.js";
15
- import { checkVersion, formatVersionNudge } from "./version-check.js";
16
- import { checkServerHandshake, formatHandshakeNudge } from "./version-handshake.js";
15
+ import { checkVersion, formatVersionNudge, primeVersionCheckCache, FLAIR_PKG_NAME } from "./version-check.js";
16
+ import { checkServerHandshake, formatHandshakeNudge, invalidateHandshakeCache } from "./version-handshake.js";
17
17
  import { probeInstance } from "./probe.js";
18
18
  import { sweepFleet, renderFleetSweepTable, FLEET_EXIT_OK, } from "./fleet-verify.js";
19
19
  import { markStale, sortOldestVersionFirst } from "./fleet-presence.js";
@@ -8053,6 +8053,12 @@ program
8053
8053
  continue;
8054
8054
  const data = await res.json();
8055
8055
  const latest = data.version ?? "unknown";
8056
+ if (name === FLAIR_PKG_NAME && latest !== "unknown") {
8057
+ try {
8058
+ primeVersionCheckCache(latest);
8059
+ }
8060
+ catch { /* best-effort */ }
8061
+ }
8056
8062
  const installed = probe();
8057
8063
  let status;
8058
8064
  if (installed === null) {
@@ -8722,6 +8728,15 @@ async function startFlairProcess(port) {
8722
8728
  async function restartFlair(port) {
8723
8729
  await stopFlairProcess(port);
8724
8730
  await startFlairProcess(port);
8731
+ // Bust the version-handshake cache so the next preAction nudge re-fetches
8732
+ // the LIVE version instead of the pre-restart cached one (the false
8733
+ // "server is running <old>" users hit for up to 60s post-upgrade+restart).
8734
+ // Same (rootPath, serverUrl) key the preAction hook computes (~line 2189)
8735
+ // — must match exactly, or this busts the wrong cache file.
8736
+ try {
8737
+ invalidateHandshakeCache(process.env.ROOTPATH ?? defaultDataDir(), `http://127.0.0.1:${port}`);
8738
+ }
8739
+ catch { /* best-effort — never fail a restart over cache cleanup */ }
8725
8740
  }
8726
8741
  program
8727
8742
  .command("restart")
@@ -116,6 +116,19 @@ export async function checkVersion(installed, injected = {}) {
116
116
  }
117
117
  return { installed, latest: null, source: "unavailable" };
118
118
  }
119
+ /**
120
+ * Primes the version-check cache with an already-known `latest` — e.g. right
121
+ * after `flair upgrade` fetches the true latest fresh via its own direct
122
+ * registry call, so the NEXT `checkVersion` (from `flair status`/`doctor`)
123
+ * reflects it immediately instead of serving a stale cached value for up to
124
+ * `DEFAULT_TTL_MS`. Reuses `writeCacheFile`, which is already best-effort/
125
+ * never-throws — a failed cache write must never surface as a command error.
126
+ */
127
+ export function primeVersionCheckCache(latest, injected = {}) {
128
+ const cachePath = injected.cachePath ?? DEFAULT_CACHE_PATH;
129
+ const now = injected.now ?? (() => Date.now());
130
+ writeCacheFile(cachePath, { latest, checkedAt: now() });
131
+ }
119
132
  const NO_GAP = { severity: "none", majorBehind: false, releasesBehind: 0 };
120
133
  /**
121
134
  * Classify how far `installed` is behind `latest` using major.minor.patch
@@ -14,7 +14,7 @@
14
14
  * ROOTPATH, or a --target-switched remote), and a mismatch cached for one
15
15
  * must never bleed into a nudge about a different one.
16
16
  */
17
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
18
18
  import { createHash } from "node:crypto";
19
19
  import { dirname, join } from "node:path";
20
20
  import { homedir } from "node:os";
@@ -110,6 +110,25 @@ export async function checkServerHandshake(cliVersion, rootPath, serverUrl, inje
110
110
  }
111
111
  return { cliVersion, runningVersion: null, mismatch: false, source: "unavailable" };
112
112
  }
113
+ /**
114
+ * Deletes the cached running-version entry for a given (rootPath, serverUrl)
115
+ * pair — e.g. after `flair restart`/`flair upgrade` bounces the server, so
116
+ * the NEXT `checkServerHandshake` call re-fetches the live version instead
117
+ * of returning the pre-restart one for up to `DEFAULT_HANDSHAKE_TTL_MS`.
118
+ * Best-effort, like the rest of this module: a failed cache delete (missing
119
+ * file, permissions, read-only $HOME) must never surface as an error.
120
+ */
121
+ export function invalidateHandshakeCache(rootPath, serverUrl, injected = {}) {
122
+ const cacheDir = injected.cacheDir ?? DEFAULT_HANDSHAKE_CACHE_DIR;
123
+ try {
124
+ const path = cacheFilePath(cacheDir, rootPath, serverUrl);
125
+ if (existsSync(path))
126
+ rmSync(path);
127
+ }
128
+ catch {
129
+ // Best-effort — a failed cache delete must never surface as an error.
130
+ }
131
+ }
113
132
  /**
114
133
  * The one-line stderr nudge, or null when there's nothing worth printing
115
134
  * (no mismatch, or nothing to compare against). Exact wording per the spec:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpsdev-ai/flair",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "packageManager": "bun@1.3.10",
5
5
  "description": "Identity, memory, and soul for AI agents. Cryptographic identity (Ed25519), semantic memory with local embeddings, and persistent personality — all in a single process.",
6
6
  "type": "module",