@tpsdev-ai/flair 0.44.0 → 0.44.1

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
@@ -20,7 +20,7 @@ import { probeInstance } from "./probe.js";
20
20
  import { sweepFleet, renderFleetSweepTable, FLEET_EXIT_OK, } from "./fleet-verify.js";
21
21
  import { markStale, sortOldestVersionFirst } from "./fleet-presence.js";
22
22
  import { detectClients, renderWiringSummary, wireClaudeCode, wireCodex, wireGemini, wireCursor, clientConfigPath, codexConfigHasFlairSection } from "./install/clients.js";
23
- import { flairCliVersion, mcpServerSpec, unpinnedSpecWarning } from "./lib/mcp-spec.js";
23
+ import { flairCliVersion, clearFlairCliVersionCache, mcpServerSpec, unpinnedSpecWarning } from "./lib/mcp-spec.js";
24
24
  import { resolveAgentKeyPath, loadEd25519PrivateKeyFromFile, signClientAssertion, buildTokenRequestForm, getMcpAccessToken, McpTokenRequestError, defaultMcpClientId, defaultMcpTokenEndpoint, defaultMcpResource, defaultMcpIssuer, MAX_ASSERTION_LIFETIME_SECONDS, } from "./mcp-client-assertion.js";
25
25
  import { enableMcp, disableMcp, mcpStatus, checkLocalOriginRefusal, selfVerifyMcpMetadata, } from "./lib/mcp-enable.js";
26
26
  import { readClientMcpBlock, checkClaudeMdBootstrap, inspectSessionStartHook, upgradeSessionStartHookCommand, fixClaudeMdBootstrap, fixSessionStartHook, applyOrReportClaudeMdBootstrap, applyOrReportSessionStartHook, resolveWireFlairUrl, planAgentIterations, inferSoleAgentId, fixCommandAgentHint, describeAgentGateFinding, embeddingsSkipRemedy, classifyKeyFile, resolveCollisionSafeName, pruneDateStamp, PRUNED_DIR_NAME, } from "./doctor-client.js";
@@ -2318,6 +2318,25 @@ export function shouldPrintUpgradeLine(status, showAll) {
2318
2318
  return false;
2319
2319
  return true;
2320
2320
  }
2321
+ /**
2322
+ * Returns the human-readable suffix for a package status line in
2323
+ * `flair upgrade` / `flair upgrade --check` output.
2324
+ *
2325
+ * flair-mcp is zero-install via npx — its suffix must never suggest a
2326
+ * global install (flair#1168).
2327
+ */
2328
+ export function upgradeStatusSuffix(name, status) {
2329
+ if (status === "current")
2330
+ return " (current)";
2331
+ if (status === "missing") {
2332
+ return name === "@tpsdev-ai/flair-mcp"
2333
+ ? " (zero-install via npx — run: flair doctor --fix)"
2334
+ : " (run: npm install -g)";
2335
+ }
2336
+ if (status === "optional")
2337
+ return " (install via: openclaw plugins install @tpsdev-ai/openclaw-flair)";
2338
+ return "";
2339
+ }
2321
2340
  /**
2322
2341
  * Pure flag resolution for `flair upgrade`'s restart/verify defaults
2323
2342
  * (flair#635 decision: restart is now the default; `--no-restart` opts
@@ -9451,10 +9470,7 @@ program
9451
9470
  : status === "optional" ? "○"
9452
9471
  : "❔";
9453
9472
  const installedLabel = installed ?? (status === "optional" ? "not installed (openclaw not detected)" : "not detected");
9454
- const suffix = status === "current" ? " (current)"
9455
- : status === "missing" ? " (run: npm install -g)"
9456
- : status === "optional" ? " (install via: openclaw plugins install @tpsdev-ai/openclaw-flair)"
9457
- : "";
9473
+ const suffix = upgradeStatusSuffix(name, status);
9458
9474
  console.log(` ${icon} ${name}: ${installedLabel} → ${latest}${suffix}`);
9459
9475
  }
9460
9476
  catch { /* skip unavailable packages */ }
@@ -9480,8 +9496,15 @@ program
9480
9496
  return;
9481
9497
  }
9482
9498
  if (missing.length > 0 && outdated.length === 0) {
9499
+ const npmMissing = missing.filter((f) => f.name !== "@tpsdev-ai/flair-mcp");
9500
+ const mcpMissing = missing.filter((f) => f.name === "@tpsdev-ai/flair-mcp");
9483
9501
  console.log(`\n❔ ${missing.length} package${missing.length > 1 ? "s" : ""} not detected — all detected packages are up to date.`);
9484
- console.log(` Install missing: npm install -g ${missing.map((f) => f.name).join(" ")}`);
9502
+ if (npmMissing.length > 0) {
9503
+ console.log(` Install missing: npm install -g ${npmMissing.map((f) => f.name).join(" ")}`);
9504
+ }
9505
+ if (mcpMissing.length > 0) {
9506
+ console.log(` flair-mcp is zero-install via npx — run: flair doctor --fix to re-wire the hook`);
9507
+ }
9485
9508
  return;
9486
9509
  }
9487
9510
  if (checkOnly) {
@@ -9690,6 +9713,61 @@ program
9690
9713
  console.error(` ❌ ${pkg} upgrade failed: ${err.message}`);
9691
9714
  }
9692
9715
  }
9716
+ // flair#1167: `npm install -g` replaced package.json in-place, so the
9717
+ // module-load-cached CLI version is stale. Clear it so mcpServerSpec()
9718
+ // resolves the NEW version for the pin refresh below.
9719
+ clearFlairCliVersionCache();
9720
+ // ── Refresh wired MCP client configs (flair#1135, flair#1167) ──────────
9721
+ // After a successful package install, the flair-mcp package on disk is
9722
+ // newer than the pinned version in wired client configs. Re-run wiring for
9723
+ // already-wired clients so the pin stays in lockstep with the installed
9724
+ // version. Runs BEFORE the restart so --no-restart and --no-verify paths
9725
+ // also get the refresh (flair#1167). Best-effort: failures warn but never
9726
+ // fail the upgrade.
9727
+ await (async () => {
9728
+ const agentId = resolveAgentIdOrEnv({}) ?? (() => {
9729
+ try {
9730
+ const keyFiles = readdirSync(defaultKeysDir()).filter((f) => f.endsWith(".key"));
9731
+ return keyFiles.length > 0 ? keyFiles[0].replace(/\.key$/, "") : null;
9732
+ }
9733
+ catch {
9734
+ return null;
9735
+ }
9736
+ })();
9737
+ if (!agentId) {
9738
+ console.log("\n (no agent id known — skip MCP client pin refresh; run `flair init` to refresh manually)");
9739
+ return;
9740
+ }
9741
+ const httpUrl = `http://127.0.0.1:${upgradePort}`;
9742
+ const mcpEnv = { FLAIR_AGENT_ID: agentId, FLAIR_URL: httpUrl };
9743
+ const detected = detectClients().filter(c => c.detected);
9744
+ if (detected.length === 0)
9745
+ return;
9746
+ console.log("\n Refreshing MCP client pins...");
9747
+ for (const client of detected) {
9748
+ const configPath = clientConfigPath(client.id);
9749
+ if (!existsSync(configPath))
9750
+ continue;
9751
+ // Only refresh clients that are already wired — don't wire new ones.
9752
+ let hasFlair = false;
9753
+ try {
9754
+ const raw = readFileSync(configPath, "utf-8");
9755
+ if (client.id === "codex") {
9756
+ hasFlair = codexConfigHasFlairSection(raw);
9757
+ }
9758
+ else {
9759
+ const cfg = JSON.parse(raw);
9760
+ hasFlair = !!cfg.mcpServers?.flair;
9761
+ }
9762
+ }
9763
+ catch { /* unreadable/malformed — skip */ }
9764
+ if (!hasFlair)
9765
+ continue;
9766
+ const env = { ...mcpEnv, FLAIR_CLIENT: client.id };
9767
+ const result = client.wire(env);
9768
+ console.log(` ${result.ok ? "✓" : "•"} ${result.message}`);
9769
+ }
9770
+ })();
9693
9771
  // ── Restart + verify + rollback (flair#635) ─────────────────────────────
9694
9772
  // Decision (2026-07-08): restart is now the default post-upgrade step —
9695
9773
  // installing new code without restarting leaves the OLD process serving
@@ -9908,55 +9986,6 @@ program
9908
9986
  authedGet: (path) => verifyAuthedGet(baseUrl, path, defaultKeysDir()),
9909
9987
  });
9910
9988
  const verdict = decideAfterVerify(verify, previousFlairVersion);
9911
- // ── Refresh wired MCP client configs (flair#1135) ──────────────────────
9912
- // After a successful upgrade, the flair-mcp package on disk is newer than
9913
- // the pinned version in wired client configs. Re-run wiring for
9914
- // already-wired clients so the pin stays in lockstep with the installed
9915
- // version. Best-effort: failures warn but never fail the upgrade.
9916
- const refreshWiredClients = async () => {
9917
- const agentId = resolveAgentIdOrEnv({}) ?? (() => {
9918
- try {
9919
- const keyFiles = readdirSync(defaultKeysDir()).filter((f) => f.endsWith(".key"));
9920
- return keyFiles.length > 0 ? keyFiles[0].replace(/\.key$/, "") : null;
9921
- }
9922
- catch {
9923
- return null;
9924
- }
9925
- })();
9926
- if (!agentId) {
9927
- console.log("\n (no agent id known — skip MCP client pin refresh; run `flair init` to refresh manually)");
9928
- return;
9929
- }
9930
- const httpUrl = `http://127.0.0.1:${upgradePort}`;
9931
- const mcpEnv = { FLAIR_AGENT_ID: agentId, FLAIR_URL: httpUrl };
9932
- const detected = detectClients().filter(c => c.detected);
9933
- if (detected.length === 0)
9934
- return;
9935
- console.log("\n Refreshing MCP client pins...");
9936
- for (const client of detected) {
9937
- const configPath = clientConfigPath(client.id);
9938
- if (!existsSync(configPath))
9939
- continue;
9940
- // Only refresh clients that are already wired — don't wire new ones.
9941
- let hasFlair = false;
9942
- try {
9943
- const raw = readFileSync(configPath, "utf-8");
9944
- if (client.id === "codex") {
9945
- hasFlair = codexConfigHasFlairSection(raw);
9946
- }
9947
- else {
9948
- const cfg = JSON.parse(raw);
9949
- hasFlair = !!cfg.mcpServers?.flair;
9950
- }
9951
- }
9952
- catch { /* unreadable/malformed — skip */ }
9953
- if (!hasFlair)
9954
- continue;
9955
- const env = { ...mcpEnv, FLAIR_CLIENT: client.id };
9956
- const result = client.wire(env);
9957
- console.log(` ${result.ok ? "✓" : "•"} ${result.message}`);
9958
- }
9959
- };
9960
9989
  if (verdict.kind === "ok") {
9961
9990
  // flair#1022: the verified facts are unchanged and still stated — the
9962
9991
  // upgrade did land. What changes is the MARKER and the claim around it.
@@ -9972,7 +10001,6 @@ program
9972
10001
  else
9973
10002
  console.log(line);
9974
10003
  }
9975
- await refreshWiredClients();
9976
10004
  return;
9977
10005
  }
9978
10006
  // flair#741 follow-through: a healthy instance the verifier just couldn't
@@ -9999,7 +10027,6 @@ program
9999
10027
  console.error(line);
10000
10028
  }
10001
10029
  }
10002
- await refreshWiredClients();
10003
10030
  return;
10004
10031
  }
10005
10032
  console.error(`❌ post-restart verification failed: ${verdict.reason}`);
@@ -12130,7 +12157,7 @@ program
12130
12157
  console.log(` ${render.icons.warn} SessionStart hook: wired in ${render.wrap(render.c.dim, hook.path)}, but its command did not run just now`);
12131
12158
  console.log(` ${render.wrap(render.c.dim, hook.detail ?? "")}`);
12132
12159
  console.log(` ${render.wrap(render.c.dim, "The hook command could not be executed. Check that npx can resolve")}`);
12133
- console.log(` ${render.wrap(render.c.dim, "@tpsdev-ai/flair-mcp — a cold cache, missing global install, or network")}`);
12160
+ console.log(` ${render.wrap(render.c.dim, "@tpsdev-ai/flair-mcp — a cold npx cache or network issue")}`);
12134
12161
  console.log(` ${render.wrap(render.c.dim, "issue can prevent the adapter from running on its first invocation.")}`);
12135
12162
  console.log(` ${render.wrap(render.c.dim, "Fix:")} flair doctor --fix ${render.wrap(render.c.dim, "(rewrites the hook to the current silent-failure form)")}`);
12136
12163
  }
@@ -55,8 +55,14 @@ export function resolveFlairCliVersion(startDir) {
55
55
  let cachedVersion;
56
56
  /**
57
57
  * This CLI's own version — the single source for `--version`, the CLI↔server
58
- * handshake, `upgrade --check`, and the MCP pin. Cached: the answer cannot
59
- * change within a process, and several commands ask repeatedly.
58
+ * handshake, `upgrade --check`, and the MCP pin. Cached: the answer normally
59
+ * cannot change within a process, and several commands ask repeatedly.
60
+ *
61
+ * The one exception is `flair upgrade`: `npm install -g` replaces package.json
62
+ * in-place while the process is still running, so the cached version becomes
63
+ * stale. Callers that run after an in-process upgrade must call
64
+ * {@link clearFlairCliVersionCache} first so the next read resolves the new
65
+ * version from disk (flair#1167).
60
66
  */
61
67
  export function flairCliVersion() {
62
68
  if (cachedVersion === undefined) {
@@ -64,6 +70,14 @@ export function flairCliVersion() {
64
70
  }
65
71
  return cachedVersion;
66
72
  }
73
+ /**
74
+ * Clear the cached CLI version so the next call to {@link flairCliVersion}
75
+ * re-resolves from the package.json on disk. Needed after an in-process
76
+ * `npm install -g` replaces the package (flair#1167).
77
+ */
78
+ export function clearFlairCliVersionCache() {
79
+ cachedVersion = undefined;
80
+ }
67
81
  /** True when `version` is usable as a pin. */
68
82
  export function isResolvedVersion(version) {
69
83
  return !!version && version !== UNKNOWN_VERSION;
package/docs/upgrade.md CHANGED
@@ -177,7 +177,8 @@ If you'd rather upgrade by hand instead of `flair upgrade`:
177
177
 
178
178
  ```bash
179
179
  npm install -g @tpsdev-ai/flair@latest
180
- npm install -g @tpsdev-ai/flair-mcp@latest # if installed
180
+ # flair-mcp is zero-install via npx — no global install needed.
181
+ # flair doctor --fix rewires the hook to the current npx form.
181
182
  flair restart
182
183
  ```
183
184
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpsdev-ai/flair",
3
- "version": "0.44.0",
3
+ "version": "0.44.1",
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",