@tpsdev-ai/flair 0.47.0 → 0.47.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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.47.0",
3
- "commit": "8e83a73aac3803eceb942532fe565340c9ad52e8",
4
- "builtAt": "2026-08-21T17:53:45.214Z",
2
+ "version": "0.47.1",
3
+ "commit": "2a5259e318163710b73490fb6b6abc5f25aac967",
4
+ "builtAt": "2026-08-22T14:43:00.927Z",
5
5
  "builder": "tsc"
6
6
  }
package/dist/cli.js CHANGED
@@ -2561,11 +2561,13 @@ export function upgradeStatusSuffix(name, status) {
2561
2561
  }
2562
2562
  if (status === "optional")
2563
2563
  return " (install via: openclaw plugins install @tpsdev-ai/openclaw-flair)";
2564
- // flair-mcp is refreshed by re-pinning its wiring (`flair doctor --fix` /
2565
- // the post-upgrade pin refresh), never `npm install -g` a global bin does
2566
- // nothing for an `npx -y -p @tpsdev-ai/flair-mcp` invocation (flair#1208).
2564
+ // flair-mcp is refreshed by re-pinning its wiring, never `npm install -g`
2565
+ // a global bin does nothing for an `npx -y -p @tpsdev-ai/flair-mcp`
2566
+ // invocation (flair#1208). The re-pin is `flair upgrade`'s own job (the
2567
+ // #1135/#1167 pin refresh) — never advise `doctor --fix` for it
2568
+ // (flair#1324).
2567
2569
  if (status === "outdated" && name === FLAIR_MCP_PACKAGE) {
2568
- return " (npx-wired — run: flair doctor --fix to re-pin)";
2570
+ return " (npx-wired — flair upgrade refreshes the pin)";
2569
2571
  }
2570
2572
  return "";
2571
2573
  }
@@ -10220,11 +10222,69 @@ program
10220
10222
  console.log("\n✅ Everything is up to date.");
10221
10223
  return;
10222
10224
  }
10223
- // Nothing to install via npm/openclaw. What is left is advisory: packages
10224
- // not detected (missing) and/or a flair-mcp whose wired pin is behind latest
10225
- // both fixed by re-wiring (`flair doctor --fix`), never by the
10226
- // npm-install + restart transaction below (#1168/#1208). Print the remedies
10227
- // and stop.
10225
+ // ONE pin-refresh implementation, two callers (flair#1324): the post-
10226
+ // install refresh below (#1135/#1167), and the stale-pin-only path when
10227
+ // flair-mcp's wired pin is behind latest but no package needs installing,
10228
+ // `flair upgrade` refreshes the pin itself instead of advising a
10229
+ // `doctor --fix` round-trip. Only refreshes clients that are ALREADY
10230
+ // wired — never wires new ones. Best-effort: failures warn but never fail
10231
+ // the upgrade.
10232
+ async function refreshWiredMcpClientPins(targetPort) {
10233
+ const agentId = resolveAgentIdOrEnv({}) ?? (() => {
10234
+ try {
10235
+ const kd = defaultKeysDir();
10236
+ const keyFiles = readdirSync(kd).filter((f) => f.endsWith(".key"));
10237
+ // Node-scoped federation keys aren't agents (flair#1193) — never
10238
+ // pin-refresh a connector as one.
10239
+ const agentKeyFile = keyFiles.find((f) => !isNodeKeyId(f.replace(/\.key$/, ""), kd));
10240
+ return agentKeyFile ? agentKeyFile.replace(/\.key$/, "") : null;
10241
+ }
10242
+ catch {
10243
+ return null;
10244
+ }
10245
+ })();
10246
+ if (!agentId) {
10247
+ console.log("\n (no agent id known — skip MCP client pin refresh; run `flair init` to refresh manually)");
10248
+ return;
10249
+ }
10250
+ const httpUrl = `http://127.0.0.1:${targetPort}`;
10251
+ const mcpEnv = { FLAIR_AGENT_ID: agentId, FLAIR_URL: httpUrl };
10252
+ const detected = detectClients().filter(c => c.detected);
10253
+ if (detected.length === 0)
10254
+ return;
10255
+ console.log("\n Refreshing MCP client pins...");
10256
+ for (const client of detected) {
10257
+ const configPath = clientConfigPath(client.id);
10258
+ if (!existsSync(configPath))
10259
+ continue;
10260
+ // Only refresh clients that are already wired — don't wire new ones.
10261
+ let hasFlair = false;
10262
+ try {
10263
+ const raw = readFileSync(configPath, "utf-8");
10264
+ if (client.id === "codex") {
10265
+ hasFlair = codexConfigHasFlairSection(raw);
10266
+ }
10267
+ else {
10268
+ const cfg = JSON.parse(raw);
10269
+ hasFlair = !!cfg.mcpServers?.flair;
10270
+ }
10271
+ }
10272
+ catch { /* unreadable/malformed — skip */ }
10273
+ if (!hasFlair)
10274
+ continue;
10275
+ const env = { ...mcpEnv, FLAIR_CLIENT: client.id };
10276
+ const result = client.wire(env);
10277
+ console.log(` ${result.ok ? "✓" : "•"} ${result.message}`);
10278
+ }
10279
+ }
10280
+ // Nothing to install via npm/openclaw. What is left is advisory (packages
10281
+ // not detected) and/or a flair-mcp whose wired pin is behind latest. The
10282
+ // stale pin is `flair upgrade`'s OWN job (flair#1324): refresh it right
10283
+ // here rather than bouncing the user to `flair doctor --fix` — advice
10284
+ // that was both roundabout and, until #1324, routed every upgrading user
10285
+ // through doctor's consent hazard. Under --check, only say what a real
10286
+ // run will do. `npm install -g` remains wrong for flair-mcp either way
10287
+ // (#1168/#1208).
10228
10288
  if (totalUpgrades === 0) {
10229
10289
  if (missing.length > 0) {
10230
10290
  const npmMissing = missing.filter((f) => f.name !== FLAIR_MCP_PACKAGE);
@@ -10239,7 +10299,12 @@ program
10239
10299
  }
10240
10300
  if (flairMcpOutdated) {
10241
10301
  console.log(`\n⬆️ flair-mcp is wired via npx (pinned ${flairMcpOutdated.installed} → latest ${flairMcpOutdated.latest}).`);
10242
- console.log(` Re-pin it: flair doctor --fix`);
10302
+ if (checkOnly) {
10303
+ console.log(" Run: flair upgrade (refreshes the pin)");
10304
+ }
10305
+ else {
10306
+ await refreshWiredMcpClientPins(resolveHttpPort({}));
10307
+ }
10243
10308
  }
10244
10309
  return;
10245
10310
  }
@@ -10460,54 +10525,7 @@ program
10460
10525
  // version. Runs BEFORE the restart so --no-restart and --no-verify paths
10461
10526
  // also get the refresh (flair#1167). Best-effort: failures warn but never
10462
10527
  // fail the upgrade.
10463
- await (async () => {
10464
- const agentId = resolveAgentIdOrEnv({}) ?? (() => {
10465
- try {
10466
- const kd = defaultKeysDir();
10467
- const keyFiles = readdirSync(kd).filter((f) => f.endsWith(".key"));
10468
- // Node-scoped federation keys aren't agents (flair#1193) — never
10469
- // pin-refresh a connector as one.
10470
- const agentKeyFile = keyFiles.find((f) => !isNodeKeyId(f.replace(/\.key$/, ""), kd));
10471
- return agentKeyFile ? agentKeyFile.replace(/\.key$/, "") : null;
10472
- }
10473
- catch {
10474
- return null;
10475
- }
10476
- })();
10477
- if (!agentId) {
10478
- console.log("\n (no agent id known — skip MCP client pin refresh; run `flair init` to refresh manually)");
10479
- return;
10480
- }
10481
- const httpUrl = `http://127.0.0.1:${upgradePort}`;
10482
- const mcpEnv = { FLAIR_AGENT_ID: agentId, FLAIR_URL: httpUrl };
10483
- const detected = detectClients().filter(c => c.detected);
10484
- if (detected.length === 0)
10485
- return;
10486
- console.log("\n Refreshing MCP client pins...");
10487
- for (const client of detected) {
10488
- const configPath = clientConfigPath(client.id);
10489
- if (!existsSync(configPath))
10490
- continue;
10491
- // Only refresh clients that are already wired — don't wire new ones.
10492
- let hasFlair = false;
10493
- try {
10494
- const raw = readFileSync(configPath, "utf-8");
10495
- if (client.id === "codex") {
10496
- hasFlair = codexConfigHasFlairSection(raw);
10497
- }
10498
- else {
10499
- const cfg = JSON.parse(raw);
10500
- hasFlair = !!cfg.mcpServers?.flair;
10501
- }
10502
- }
10503
- catch { /* unreadable/malformed — skip */ }
10504
- if (!hasFlair)
10505
- continue;
10506
- const env = { ...mcpEnv, FLAIR_CLIENT: client.id };
10507
- const result = client.wire(env);
10508
- console.log(` ${result.ok ? "✓" : "•"} ${result.message}`);
10509
- }
10510
- })();
10528
+ await refreshWiredMcpClientPins(upgradePort);
10511
10529
  // ── Restart + verify + rollback (flair#635) ─────────────────────────────
10512
10530
  // Decision (2026-07-08): restart is now the default post-upgrade step —
10513
10531
  // installing new code without restarting leaves the OLD process serving
@@ -13094,35 +13112,20 @@ program
13094
13112
  // the SessionStart check above: installed / absent / stale-form).
13095
13113
  // Continuity is OPT-IN — installing the PostToolUse+Stop pair IS the
13096
13114
  // opt-in — so "absent" renders as informational "not enabled": NEVER
13097
- // a pass (an unrun check must not look green) and never counted as an
13098
- // issue. A partial/stale install IS an issue and is --fix-able;
13099
- // --fix also offers first-time enablement (the y/N prompt is the
13100
- // consent; non-TTY --fix is itself the consent signal, matching every
13101
- // other doctor fix).
13115
+ // a pass (an unrun check must not look green), never counted as an
13116
+ // issue, and NEVER wired by --fix (flair#1324: doctor's fixable set
13117
+ // is broken state; initiating an opt-in the user hasn't made is not a
13118
+ // fix — a y/N prompt auto-answers yes in every non-TTY run, so it was
13119
+ // no consent gate at all; enablement is `flair hook install
13120
+ // --continuity` only). A partial or stale pair IS evidence of a prior
13121
+ // opt-in, so repairing it to the complete current form remains a
13122
+ // legitimate --fix.
13102
13123
  const continuity = checkContinuityCaptureHooks(homedir());
13103
13124
  if (continuity.state === "installed") {
13104
13125
  console.log(` ${render.icons.ok} Continuity capture hooks: PostToolUse + Stop wired in ${render.wrap(render.c.dim, continuity.path)}`);
13105
13126
  }
13106
13127
  else if (continuity.state === "absent") {
13107
13128
  console.log(` ${render.icons.info} Continuity capture hooks: not enabled ${render.wrap(render.c.dim, "(opt-in — auto-journal working state into the ephemeral memory tier; enable: flair hook install --continuity)")}`);
13108
- if (autoFix) {
13109
- if (dryRun) {
13110
- console.log(` ${render.wrap(render.c.dim, "Would wire the continuity capture hooks (PostToolUse + Stop) in")} ${continuity.path}`);
13111
- }
13112
- else {
13113
- const proceed = await confirmFix(` Enable continuity capture (PostToolUse + Stop hooks in ${continuity.path})? [y/N] `);
13114
- if (!proceed) {
13115
- console.log(` Skipped.`);
13116
- }
13117
- else {
13118
- const fixAgentId = claudeCodeAgentId || opts.agent || process.env.FLAIR_AGENT_ID;
13119
- const fixRes = fixContinuityCaptureHooks(homedir(), fixAgentId);
13120
- console.log(` ${fixRes.ok ? render.icons.ok : render.icons.warn} ${fixRes.message}`);
13121
- if (fixRes.ok && fixRes.changed)
13122
- fixed++;
13123
- }
13124
- }
13125
- }
13126
13129
  }
13127
13130
  else {
13128
13131
  const continuityDetail = continuity.state === "partial"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpsdev-ai/flair",
3
- "version": "0.47.0",
3
+ "version": "0.47.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",