@tpsdev-ai/flair 0.40.0 → 0.41.0

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
@@ -774,6 +774,14 @@ export function buildDirectSpawnEnv(opts) {
774
774
  HTTP_PORT: String(opts.httpPort),
775
775
  OPERATIONSAPI_NETWORK_PORT: opsNetworkPortValue(opts.opsBindHost, opts.opsPort),
776
776
  LOCAL_STUDIO: "false",
777
+ // flair#905 / lrf5: Harper's forceDowngradePrompt reads CONFIRM_DOWNGRADE
778
+ // from the environment (via the `prompt` npm package's assignCmdEnvVariables
779
+ // override). Under launchd/systemd stdin is not a TTY, so the prompt gets
780
+ // EOF and Harper exits 0 without starting — leaving the instance DOWN with
781
+ // no error. Setting this to "yes" makes the prompt non-interactive: Harper
782
+ // proceeds without blocking, which is the correct default for a managed
783
+ // restart where the operator already chose to proceed.
784
+ CONFIRM_DOWNGRADE: "yes",
777
785
  };
778
786
  if (opts.adminPass)
779
787
  env.HDB_ADMIN_PASSWORD = opts.adminPass;
@@ -9688,6 +9696,50 @@ program
9688
9696
  console.error(` Recover by hand: npm install -g @tpsdev-ai/flair@${toVersion} && flair start`);
9689
9697
  process.exit(1);
9690
9698
  }
9699
+ // flair#1053: when the engine (Harper) version changed, the pre-upgrade
9700
+ // snapshot is the ONLY way back — the old Harper cannot read data written
9701
+ // by the new one (e.g. 5.2 LZ4-compressed storage is unreadable by 5.1).
9702
+ // Restore it before restarting, or refuse loudly when none exists.
9703
+ if (engineVersionChanging) {
9704
+ if (snapshotPath) {
9705
+ console.log(`\nEngine version changed — restoring pre-upgrade snapshot before rollback...`);
9706
+ console.log(` snapshot: ${snapshotPath}`);
9707
+ console.log(` target: ${upgradeDataDir}`);
9708
+ try {
9709
+ await validateSnapshotArchive({ file: snapshotPath, targetDir: upgradeDataDir });
9710
+ rmSync(upgradeDataDir, { recursive: true, force: true });
9711
+ mkdirSync(upgradeDataDir, { recursive: true, mode: 0o700 });
9712
+ await extractSnapshotSafely({ file: snapshotPath, targetDir: upgradeDataDir });
9713
+ console.log(` ✅ snapshot restored`);
9714
+ }
9715
+ catch (err) {
9716
+ console.error(`❌ snapshot restore failed: ${err.message}`);
9717
+ console.error(` @tpsdev-ai/flair@${toVersion} is installed but the data directory could not be restored.`);
9718
+ console.error(` The snapshot itself is intact at ${snapshotPath} — restore it by hand:`);
9719
+ console.error(` flair snapshot restore "${snapshotPath}"`);
9720
+ console.error(` Then: flair start`);
9721
+ process.exit(1);
9722
+ }
9723
+ }
9724
+ else {
9725
+ // No snapshot exists — the old Harper WILL NOT BOOT against the new
9726
+ // data. Refuse loudly rather than attempting a guaranteed failure.
9727
+ console.error(`\n❌ Cannot roll back: the Harper engine version changed (${currentEngineVersion ?? "?"} → ${targetEngineVersion ?? "?"}) and no pre-upgrade snapshot exists.`);
9728
+ console.error(` The old Harper cannot read data written by the new engine — restarting without a snapshot restore would fail.`);
9729
+ console.error(` @tpsdev-ai/flair@${toVersion} is installed but NOT running.`);
9730
+ if (snapshotDecision === "nudge") {
9731
+ console.error(` A snapshot was skipped because --no-engine-snapshot was passed.`);
9732
+ console.error(` Recovery options:`);
9733
+ console.error(` 1. Re-upgrade to the version that wrote this data: npm install -g @tpsdev-ai/flair@${expectedFlairVersion ?? "latest"} && flair start`);
9734
+ console.error(` 2. Restore from a ` + "`flair backup`" + ` JSON export on a fresh data directory.`);
9735
+ }
9736
+ else {
9737
+ console.error(` No snapshot was taken (data directory may not have existed, or the snapshot step was skipped).`);
9738
+ console.error(` Recovery: re-upgrade to the version that wrote this data, or restore from a ` + "`flair backup`" + ` JSON export.`);
9739
+ }
9740
+ process.exit(1);
9741
+ }
9742
+ }
9691
9743
  // Same post-swap rule as the upgrade restart above: the rolled-back
9692
9744
  // version's own CLI is the thing that knows how to start it.
9693
9745
  const rolledBackCli = resolveInstalledFlairCli(flairPackageDir(), toVersion);
@@ -10321,8 +10373,18 @@ async function stopFlairProcess(port, dataDir) {
10321
10373
  }
10322
10374
  catch { }
10323
10375
  }
10324
- // Wait briefly for shutdown
10325
- await new Promise((r) => setTimeout(r, 2000));
10376
+ // flair#905 / lrf5: wait for every signalled process to actually exit.
10377
+ // A blind 2-second sleep is not a guarantee — Harper may be flushing
10378
+ // RocksDB WAL/MANIFEST, and the next start will fail with a locked data
10379
+ // directory if the old process hasn't released it yet. The launchd path
10380
+ // above already does this via waitForProcessExit; the port-based path
10381
+ // must match that guarantee.
10382
+ for (const target of targets) {
10383
+ try {
10384
+ await waitForProcessExit(target, STARTUP_TIMEOUT_MS);
10385
+ }
10386
+ catch { /* best-effort — the next start will surface the real problem */ }
10387
+ }
10326
10388
  }
10327
10389
  /**
10328
10390
  * Start the local Flair (Harper) process — launchd `start` on darwin when a
package/docs/upgrade.md CHANGED
@@ -357,7 +357,7 @@ if you want to see it scripted end-to-end.
357
357
  upgraded via `flair upgrade`. Real snapshots for production datasets run hundreds of
358
358
  megabytes — plan disk capacity accordingly. Cleanup of old engine snapshots is manual;
359
359
  `flair snapshot list` shows available snapshots, and you can delete entries you no longer
360
- need.
360
+ need (e.g. `rm ~/.flair/upgrade-snapshots/flair-data-<old>.tar.gz`).
361
361
 
362
362
  ## Rollback
363
363
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpsdev-ai/flair",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
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",
@@ -64,7 +64,7 @@
64
64
  "@harperfast/oauth": "2.4.0",
65
65
  "@types/js-yaml": "4.0.9",
66
66
  "commander": "14.0.3",
67
- "harper": "5.1.22",
67
+ "harper": "5.2.0",
68
68
  "harper-fabric-embeddings": "^0.5.0",
69
69
  "jose": "6.2.2",
70
70
  "js-yaml": "^4.3.1",