@tpsdev-ai/flair 0.51.2 → 0.53.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.
Files changed (74) hide show
  1. package/README.md +10 -5
  2. package/dist/build-info.json +3 -3
  3. package/dist/cli.js +1037 -566
  4. package/dist/doctor-client.js +35 -0
  5. package/dist/hook-install.js +74 -0
  6. package/dist/install/global-bin-path.js +14 -0
  7. package/dist/lib/auth-resolve.js +15 -0
  8. package/dist/lib/doctor-run.js +28 -15
  9. package/dist/lib/launchd-repair.js +198 -0
  10. package/dist/lib/stabilize-mqtt-network.js +123 -0
  11. package/dist/lib/upgrade-exec-path.js +257 -0
  12. package/dist/lib/upgrade-plain-tree.js +558 -0
  13. package/dist/rem/promote-policy.js +204 -0
  14. package/dist/rem/restore.js +55 -15
  15. package/dist/rem/runner.js +203 -20
  16. package/dist/resources/AdminMemory.js +2 -1
  17. package/dist/resources/AgentSeed.js +26 -10
  18. package/dist/resources/Asset.js +203 -0
  19. package/dist/resources/AutoPromoteCandidates.js +2 -4
  20. package/dist/resources/Credential.js +14 -0
  21. package/dist/resources/Federation.js +80 -0
  22. package/dist/resources/Integration.js +12 -0
  23. package/dist/resources/Memory.js +158 -60
  24. package/dist/resources/MemoryBootstrap.js +63 -20
  25. package/dist/resources/MemoryCandidate.js +12 -0
  26. package/dist/resources/MemoryConsolidate.js +2 -1
  27. package/dist/resources/MemoryDedupStats.js +17 -2
  28. package/dist/resources/MemoryFeed.js +30 -0
  29. package/dist/resources/MemoryGrant.js +14 -0
  30. package/dist/resources/MemoryReflect.js +75 -17
  31. package/dist/resources/Message.js +190 -0
  32. package/dist/resources/OrgEvent.js +12 -0
  33. package/dist/resources/PromoteMemoryCandidate.js +76 -0
  34. package/dist/resources/RecordUsage.js +1 -1
  35. package/dist/resources/Relationship.js +12 -0
  36. package/dist/resources/SemanticSearch.js +45 -13
  37. package/dist/resources/Soul.js +54 -18
  38. package/dist/resources/WorkspaceState.js +12 -0
  39. package/dist/resources/auth-middleware.js +17 -44
  40. package/dist/resources/authority-field-guard.js +37 -0
  41. package/dist/resources/bm25-index-service.js +1 -1
  42. package/dist/resources/bm25-index.js +50 -11
  43. package/dist/resources/embedding-space-guard.js +238 -0
  44. package/dist/resources/embeddings-provider.js +32 -5
  45. package/dist/resources/federation-classify.js +23 -1
  46. package/dist/resources/health.js +11 -2
  47. package/dist/resources/hit-tracking.js +244 -0
  48. package/dist/resources/mcp-tools.js +272 -7
  49. package/dist/resources/memory-reflect-lib.js +111 -0
  50. package/dist/resources/migrations/embedding-stamp.js +22 -4
  51. package/dist/resources/owner-field-guard.js +62 -0
  52. package/dist/resources/promotion-stamp.js +29 -0
  53. package/dist/resources/record-owner-guard.js +71 -5
  54. package/dist/resources/record-types.js +30 -7
  55. package/dist/resources/relay-lib.js +205 -0
  56. package/dist/resources/relay-ops.js +294 -0
  57. package/dist/resources/skill-write.js +120 -0
  58. package/dist/resources/soul-adk-guard.js +68 -0
  59. package/dist/resources/soul-write-policy.js +63 -0
  60. package/dist/resources/table-helpers.js +2 -0
  61. package/dist/resources/usage-recording.js +3 -3
  62. package/dist/src/rem/promote-policy.js +204 -0
  63. package/docs/api-reference.md +374 -0
  64. package/docs/auth.md +52 -0
  65. package/docs/federation.md +4 -0
  66. package/docs/integrations.md +6 -6
  67. package/docs/mcp-clients.md +16 -1
  68. package/docs/releasing.md +11 -8
  69. package/docs/rem.md +20 -2
  70. package/docs/upgrade.md +47 -2
  71. package/package.json +6 -5
  72. package/schemas/memory.graphql +51 -2
  73. package/schemas/message.graphql +74 -0
  74. package/templates/launchd/start-flair-with-admin-pass.sh +73 -0
@@ -0,0 +1,257 @@
1
+ /**
2
+ * upgrade-exec-path.ts — flair#1109 (b)
3
+ *
4
+ * `flair upgrade` reports and upgrades the npm-global `@tpsdev-ai/flair`
5
+ * package. A host can also be serving from a different exec path — a plain
6
+ * extracted tree (npm pack + tar under systemd), a checkout, a second prefix.
7
+ * When those paths differ, the npm-global listing is not "the" install: it
8
+ * may be a stale relic, and upgrading it will not touch the tree serving
9
+ * traffic.
10
+ *
11
+ * This module is detection and wording only. It does not add an in-place
12
+ * tarball-swap upgrade lane (that is #1109 (a), kept elsewhere).
13
+ *
14
+ * Everything that classifies or formats is pure and dependency-injected
15
+ * except the default /proc and lsof readers used when the CLI asks about a
16
+ * live pid.
17
+ */
18
+ import { readFileSync, realpathSync, statSync } from "node:fs";
19
+ import { dirname, resolve } from "node:path";
20
+ import { execFileSync } from "node:child_process";
21
+ import { npmGlobalFlairPackageDir } from "../install/global-bin-path.js";
22
+ /** The CLI / server package whose install path we are comparing. */
23
+ export const FLAIR_PACKAGE = "@tpsdev-ai/flair";
24
+ const PROCESS_PROBE_TIMEOUT_MS = 2000;
25
+ /**
26
+ * Walk up from `startPath` looking for `@tpsdev-ai/flair`'s own package.json.
27
+ *
28
+ * Named search, not a hop count: a harper bin lives several levels under
29
+ * the package root, a CLI script lives at `dist/cli.js`, and a serving
30
+ * cwd may already BE the package root. Checking `name` means an intermediate
31
+ * `package.json` (harper, a workspace) cannot be mistaken for ours.
32
+ */
33
+ export function findFlairPackageDir(startPath) {
34
+ let dir;
35
+ try {
36
+ const st = statSync(startPath);
37
+ dir = st.isDirectory() ? startPath : dirname(startPath);
38
+ }
39
+ catch {
40
+ dir = startPath;
41
+ }
42
+ for (let i = 0; i < 8; i++) {
43
+ const loc = readFlairPackageAt(dir);
44
+ if (loc)
45
+ return loc;
46
+ const parent = dirname(dir);
47
+ if (parent === dir)
48
+ break;
49
+ dir = parent;
50
+ }
51
+ return null;
52
+ }
53
+ /** Read `@tpsdev-ai/flair` at exactly `dir`, or null if it is not that package. */
54
+ export function readFlairPackageAt(dir) {
55
+ try {
56
+ const pkg = JSON.parse(readFileSync(resolve(dir, "package.json"), "utf-8"));
57
+ if (pkg?.name !== FLAIR_PACKAGE)
58
+ return null;
59
+ return {
60
+ dir: canonicalPath(dir),
61
+ version: typeof pkg.version === "string" && pkg.version ? pkg.version : null,
62
+ };
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ /** Canonical path for equality: realpath when it exists, else lexical resolve. */
69
+ export function canonicalPath(p) {
70
+ const resolved = resolve(p);
71
+ try {
72
+ return realpathSync(resolved);
73
+ }
74
+ catch {
75
+ return resolved;
76
+ }
77
+ }
78
+ export function sameInstallPath(a, b) {
79
+ return canonicalPath(a) === canonicalPath(b);
80
+ }
81
+ /**
82
+ * Path-shaped tokens from a process command line (null- or space-separated).
83
+ * Flags and bare words (`run`, `.`) are dropped — they are not exec paths.
84
+ */
85
+ export function extractPathHints(cmdline) {
86
+ return cmdline
87
+ .split(/\0|\s+/)
88
+ .map((t) => t.trim())
89
+ .filter((t) => t.length > 0)
90
+ .filter((t) => {
91
+ if (t.startsWith("-"))
92
+ return false;
93
+ if (t.startsWith("/") || t.startsWith("\\"))
94
+ return true;
95
+ if (/^[A-Za-z]:[\\/]/.test(t))
96
+ return true;
97
+ if (t.includes("node_modules") || t.includes("/") || t.includes("\\"))
98
+ return true;
99
+ return false;
100
+ });
101
+ }
102
+ export function defaultReadProcessCwd(pid) {
103
+ if (!Number.isInteger(pid) || pid <= 0)
104
+ return null;
105
+ try {
106
+ return realpathSync(`/proc/${pid}/cwd`);
107
+ }
108
+ catch {
109
+ try {
110
+ const out = execFileSync("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
111
+ encoding: "utf-8",
112
+ timeout: PROCESS_PROBE_TIMEOUT_MS,
113
+ stdio: ["ignore", "pipe", "ignore"],
114
+ });
115
+ const m = String(out).match(/^n(.+)$/m);
116
+ return m ? m[1] : null;
117
+ }
118
+ catch {
119
+ return null;
120
+ }
121
+ }
122
+ }
123
+ export function defaultReadProcessCmdline(pid) {
124
+ if (!Number.isInteger(pid) || pid <= 0)
125
+ return null;
126
+ try {
127
+ return readFileSync(`/proc/${pid}/cmdline`, "utf-8");
128
+ }
129
+ catch {
130
+ try {
131
+ const out = execFileSync("ps", ["-p", String(pid), "-o", "command="], {
132
+ encoding: "utf-8",
133
+ timeout: PROCESS_PROBE_TIMEOUT_MS,
134
+ stdio: ["ignore", "pipe", "ignore"],
135
+ });
136
+ const trimmed = String(out).trim();
137
+ return trimmed === "" ? null : trimmed;
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * Locate the `@tpsdev-ai/flair` tree a live pid is executing from.
146
+ *
147
+ * Prefers cwd (Harper is spawned with `cwd: <package dir>`), then path
148
+ * tokens on the command line (the harper bin lives under that tree).
149
+ * Returns null when neither hint resolves to our package — never guesses.
150
+ */
151
+ export function resolveServingFlairPackage(pid, hooks = {}) {
152
+ const readCwd = hooks.readCwd ?? defaultReadProcessCwd;
153
+ const readCmdline = hooks.readCmdline ?? defaultReadProcessCmdline;
154
+ const hints = [];
155
+ try {
156
+ const cwd = readCwd(pid);
157
+ if (cwd)
158
+ hints.push(cwd);
159
+ }
160
+ catch { /* injected readers must not fail the check */ }
161
+ try {
162
+ const cmdline = readCmdline(pid);
163
+ if (cmdline)
164
+ hints.push(...extractPathHints(cmdline));
165
+ }
166
+ catch { /* same */ }
167
+ for (const hint of hints) {
168
+ const found = findFlairPackageDir(hint);
169
+ if (found)
170
+ return found;
171
+ }
172
+ return null;
173
+ }
174
+ export function resolveNpmGlobalFlairPackage(prefix, platform = process.platform) {
175
+ if (!prefix || prefix.trim() === "")
176
+ return null;
177
+ return readFlairPackageAt(npmGlobalFlairPackageDir(prefix, platform));
178
+ }
179
+ /**
180
+ * `prefixKnown` is true only when `npm prefix -g` actually returned a
181
+ * prefix. A failed/absent probe is not "the global package is missing" —
182
+ * that is `unknown` (no warning). A known prefix with no `@tpsdev-ai/flair`
183
+ * under it is a real mismatch.
184
+ */
185
+ export function classifyExecPathVsNpmGlobal(input) {
186
+ const running = input.serving ?? input.cli;
187
+ if (!running)
188
+ return { kind: "unknown" };
189
+ if (!input.prefixKnown)
190
+ return { kind: "unknown" };
191
+ const source = input.serving ? "serving-instance" : "this-cli";
192
+ if (input.global && sameInstallPath(running.dir, input.global.dir)) {
193
+ return { kind: "match", runningPath: running.dir, globalPath: input.global.dir, source };
194
+ }
195
+ return {
196
+ kind: "mismatch",
197
+ runningPath: running.dir,
198
+ runningVersion: running.version,
199
+ globalPath: input.global?.dir ?? null,
200
+ globalVersion: input.global?.version ?? null,
201
+ source,
202
+ };
203
+ }
204
+ function versionLabel(version) {
205
+ return version ? ` (${version})` : "";
206
+ }
207
+ /**
208
+ * Operator-facing warning. Names both paths (and versions when readable)
209
+ * and says what `flair upgrade` will and will not touch. Does not propose
210
+ * an in-place tarball swap — that lane is out of scope for (b).
211
+ */
212
+ export function formatExecPathMismatchWarning(check) {
213
+ if (check.kind !== "mismatch")
214
+ return null;
215
+ const subject = check.source === "serving-instance"
216
+ ? "The running instance's exec path is not the npm-global install."
217
+ : "This CLI's exec path is not the npm-global install.";
218
+ const runningLabel = check.source === "serving-instance" ? "Running" : "This CLI";
219
+ const lines = [
220
+ `⚠️ ${subject}`,
221
+ ` ${runningLabel}: ${check.runningPath}${versionLabel(check.runningVersion)}`,
222
+ ];
223
+ if (check.globalPath) {
224
+ lines.push(` npm-global: ${check.globalPath}${versionLabel(check.globalVersion)}`);
225
+ }
226
+ else {
227
+ lines.push(" npm-global: not installed (no @tpsdev-ai/flair under the npm global prefix)");
228
+ }
229
+ lines.push(" `flair upgrade` only upgrades the npm-global packages. The tree serving traffic is unchanged.");
230
+ return lines.join("\n");
231
+ }
232
+ /**
233
+ * The one function `flair upgrade` calls. Best-effort: a missing pid, a
234
+ * missing prefix, or an unreadable /proc entry degrades to "unknown"
235
+ * (no warning) rather than failing the command.
236
+ */
237
+ export function collectUpgradeExecPathWarning(input) {
238
+ try {
239
+ const prefixKnown = typeof input.npmGlobalPrefix === "string" && input.npmGlobalPrefix.trim() !== "";
240
+ const serving = input.servingPid != null
241
+ ? resolveServingFlairPackage(input.servingPid, input.hooks)
242
+ : null;
243
+ const cli = findFlairPackageDir(input.cliPackageDir);
244
+ const global = prefixKnown
245
+ ? resolveNpmGlobalFlairPackage(input.npmGlobalPrefix, input.platform ?? process.platform)
246
+ : null;
247
+ return formatExecPathMismatchWarning(classifyExecPathVsNpmGlobal({
248
+ serving,
249
+ cli,
250
+ global,
251
+ prefixKnown,
252
+ }));
253
+ }
254
+ catch {
255
+ return null;
256
+ }
257
+ }