@tpsdev-ai/flair 0.52.0 → 0.54.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.
Files changed (99) hide show
  1. package/README.md +4 -1
  2. package/dist/build-info.json +3 -3
  3. package/dist/cli.js +1914 -15328
  4. package/dist/commands/agent.js +453 -0
  5. package/dist/commands/attention.js +121 -0
  6. package/dist/commands/backup.js +115 -0
  7. package/dist/commands/bootstrap.js +91 -0
  8. package/dist/commands/bridge.js +608 -0
  9. package/dist/commands/deploy.js +180 -0
  10. package/dist/commands/doctor.js +1654 -0
  11. package/dist/commands/export.js +110 -0
  12. package/dist/commands/federation.js +1575 -0
  13. package/dist/commands/fleet.js +73 -0
  14. package/dist/commands/grant.js +109 -0
  15. package/dist/commands/hook.js +193 -0
  16. package/dist/commands/idp.js +193 -0
  17. package/dist/commands/import.js +134 -0
  18. package/dist/commands/init.js +1203 -0
  19. package/dist/commands/inspect.js +45 -0
  20. package/dist/commands/keys.js +187 -0
  21. package/dist/commands/mcp.js +707 -0
  22. package/dist/commands/memory.js +501 -0
  23. package/dist/commands/migrate-harness-memory.js +270 -0
  24. package/dist/commands/orgevent.js +138 -0
  25. package/dist/commands/presence.js +76 -0
  26. package/dist/commands/principal.js +338 -0
  27. package/dist/commands/quality.js +1164 -0
  28. package/dist/commands/reembed.js +296 -0
  29. package/dist/commands/relationship.js +76 -0
  30. package/dist/commands/rem.js +1048 -0
  31. package/dist/commands/restore.js +130 -0
  32. package/dist/commands/search.js +244 -0
  33. package/dist/commands/service.js +315 -0
  34. package/dist/commands/session.js +184 -0
  35. package/dist/commands/soul.js +155 -0
  36. package/dist/commands/status.js +914 -0
  37. package/dist/commands/test.js +93 -0
  38. package/dist/commands/uninstall.js +143 -0
  39. package/dist/commands/upgrade.js +1592 -0
  40. package/dist/commands/workspace.js +114 -0
  41. package/dist/deploy.js +24 -0
  42. package/dist/fabric-npm-install.js +87 -0
  43. package/dist/federation-verify.js +498 -0
  44. package/dist/fleet-verify.js +144 -21
  45. package/dist/install/clients.js +167 -0
  46. package/dist/lib/auth-resolve.js +76 -1
  47. package/dist/lib/daemon-liveness.js +131 -2
  48. package/dist/lib/doctor-config-path.js +61 -0
  49. package/dist/lib/doctor-federation-driver.js +189 -0
  50. package/dist/lib/doctor-run.js +40 -0
  51. package/dist/lib/entity-vocab-cli.js +3 -3
  52. package/dist/lib/federation-pair-identity.js +47 -0
  53. package/dist/lib/launchd-repair.js +199 -0
  54. package/dist/lib/ops-api-bind.js +115 -0
  55. package/dist/lib/owned-pins.js +219 -0
  56. package/dist/lib/stabilize-mqtt-network.js +123 -0
  57. package/dist/lib/uninstall-purge.js +218 -0
  58. package/dist/rem/restore.js +8 -10
  59. package/dist/resources/AgentReadPosition.js +74 -0
  60. package/dist/resources/Federation.js +8 -2
  61. package/dist/resources/Memory.js +4 -3
  62. package/dist/resources/MemoryBootstrap.js +41 -25
  63. package/dist/resources/MemoryCandidate.js +5 -6
  64. package/dist/resources/OrgEventCatchup.js +126 -47
  65. package/dist/resources/agent-read-position-lib.js +83 -0
  66. package/dist/resources/agent-read-position.js +120 -0
  67. package/dist/resources/embeddings-boot.js +32 -0
  68. package/dist/resources/federation-peer-liveness.js +73 -0
  69. package/dist/resources/health.js +68 -19
  70. package/dist/resources/mcp-tools.js +43 -279
  71. package/dist/resources/memory-visibility.js +3 -3
  72. package/dist/resources/migration-boot.js +59 -18
  73. package/dist/resources/migrations/embedding-stamp.js +20 -1
  74. package/dist/resources/migrations/recheck.js +43 -0
  75. package/dist/resources/migrations/runner.js +6 -1
  76. package/dist/resources/migrations/stamp-outstanding.js +171 -0
  77. package/dist/resources/migrations/visibility-backfill.js +2 -2
  78. package/dist/resources/org-event-catchup-lib.js +47 -0
  79. package/dist/resources/record-owner-guard.js +1 -0
  80. package/dist/stamp-migration-verify.js +163 -0
  81. package/dist/stamp-outstanding.js +144 -0
  82. package/docs/api-reference.md +4 -2
  83. package/docs/deploying-on-fabric.md +11 -10
  84. package/docs/deployment.md +3 -1
  85. package/docs/federation.md +19 -0
  86. package/docs/hosted-on-fabric.md +3 -3
  87. package/docs/quickstart.md +2 -1
  88. package/docs/releasing.md +15 -7
  89. package/docs/spoke-bringup.md +10 -5
  90. package/docs/standalone-local.md +3 -1
  91. package/docs/upgrade.md +25 -6
  92. package/node_modules/@tpsdev-ai/flair-tool-descriptors/LICENSE +19 -0
  93. package/node_modules/@tpsdev-ai/flair-tool-descriptors/README.md +22 -0
  94. package/node_modules/@tpsdev-ai/flair-tool-descriptors/dist/index.d.ts +70 -0
  95. package/node_modules/@tpsdev-ai/flair-tool-descriptors/dist/index.js +665 -0
  96. package/node_modules/@tpsdev-ai/flair-tool-descriptors/package.json +46 -0
  97. package/package.json +9 -4
  98. package/schemas/agent.graphql +15 -0
  99. package/templates/launchd/start-flair-with-admin-pass.sh +73 -0
@@ -0,0 +1,115 @@
1
+ import { resolveAdminUser } from "../lib/auth-resolve.js";
2
+ import * as render from "../render.js";
3
+ import { mkdirSync, renameSync, writeFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ let cli;
7
+ /** Bind the cli-locals this module depends on. */
8
+ export function bindCli(fns) {
9
+ cli = fns;
10
+ }
11
+ function addSharedCredentialOptions(...args) {
12
+ return cli.addSharedCredentialOptions(...args);
13
+ }
14
+ function applyAdminPassFile(...args) {
15
+ return cli.applyAdminPassFile(...args);
16
+ }
17
+ function resolveHttpPort(...args) {
18
+ return cli.resolveHttpPort(...args);
19
+ }
20
+ export function register(program) {
21
+ // ─── flair backup ────────────────────────────────────────────────────────────
22
+ addSharedCredentialOptions(program
23
+ .command("backup")
24
+ .description("Export agents, memories, and souls to a JSON archive")
25
+ .option("--output <path>", "Output file path (default: ~/.flair/backups/flair-backup-<timestamp>.json)")
26
+ .option("--agents <ids>", "Comma-separated agent IDs to include (default: all)")
27
+ .option("--port <port>", "Harper HTTP port")
28
+ .option("--url <url>", "Flair base URL (overrides --port)")).action(async (opts) => {
29
+ const baseUrl = opts.url ?? `http://127.0.0.1:${resolveHttpPort(opts)}`;
30
+ applyAdminPassFile(opts);
31
+ // Env is a second-class fallback after the explicit flags (same order
32
+ // backup used before the shared helper). FLAIR_ADMIN_PASS is still
33
+ // accepted so existing scripts keep working.
34
+ const adminPass = opts.adminPass ?? process.env.FLAIR_ADMIN_PASS ?? "";
35
+ const adminUser = resolveAdminUser(opts.adminUser);
36
+ if (!adminPass) {
37
+ console.error("Error: --admin-pass, --admin-pass-file, or FLAIR_ADMIN_PASS required for backup");
38
+ process.exit(1);
39
+ }
40
+ // flair#968: `flair backup > file.json` captures the progress report, not
41
+ // the archive (which goes to --output, defaulting to ~/.flair/backups/...).
42
+ // The result was exit 0 and a plausible-looking file of a few hundred bytes —
43
+ // a false success immediately before a destructive upgrade.
44
+ //
45
+ // When stdout is not a TTY, route progress output to stderr. The archive
46
+ // still goes to --output / the default path. This makes `flair backup >
47
+ // file.json` produce an EMPTY file — unmistakably not a valid archive —
48
+ // while leaving default-path callers (schedulers, cron) completely
49
+ // unaffected.
50
+ const log = process.stdout.isTTY
51
+ ? console.log.bind(console)
52
+ : console.error.bind(console);
53
+ const auth = `Basic ${Buffer.from(`${adminUser}:${adminPass}`).toString("base64")}`;
54
+ async function adminGet(path) {
55
+ const res = await fetch(`${baseUrl}${path}`, {
56
+ headers: { Authorization: auth },
57
+ signal: AbortSignal.timeout(10_000),
58
+ });
59
+ if (!res.ok) {
60
+ const text = await res.text().catch(() => "");
61
+ throw new Error(`GET ${path} failed (${res.status}): ${text}`);
62
+ }
63
+ return res.json();
64
+ }
65
+ log("Fetching agents...");
66
+ const allAgents = await adminGet("/Agent/");
67
+ const filterIds = opts.agents ? opts.agents.split(",").map((s) => s.trim()) : null;
68
+ const agents = filterIds ? allAgents.filter((a) => filterIds.includes(a.id)) : allAgents;
69
+ log(`Fetching memories for ${agents.length} agent(s)...`);
70
+ const memories = [];
71
+ for (const agent of agents) {
72
+ try {
73
+ const agentMemories = await adminGet(`/Memory/?agentId=${encodeURIComponent(agent.id)}`);
74
+ if (Array.isArray(agentMemories))
75
+ memories.push(...agentMemories);
76
+ }
77
+ catch (err) {
78
+ console.warn(` Warning: could not fetch memories for ${agent.id}: ${err.message}`);
79
+ }
80
+ }
81
+ log("Fetching souls...");
82
+ const souls = [];
83
+ for (const agent of agents) {
84
+ try {
85
+ const agentSouls = await adminGet(`/Soul/?agentId=${encodeURIComponent(agent.id)}`);
86
+ if (Array.isArray(agentSouls))
87
+ souls.push(...agentSouls);
88
+ }
89
+ catch (err) {
90
+ console.warn(` Warning: could not fetch souls for ${agent.id}: ${err.message}`);
91
+ }
92
+ }
93
+ const backup = {
94
+ version: 1,
95
+ createdAt: new Date().toISOString(),
96
+ source: baseUrl,
97
+ agents,
98
+ memories,
99
+ souls,
100
+ };
101
+ // Determine output path
102
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
103
+ const defaultOutput = join(homedir(), ".flair", "backups", `flair-backup-${timestamp}.json`);
104
+ const outputPath = opts.output ?? defaultOutput;
105
+ mkdirSync(join(outputPath, ".."), { recursive: true });
106
+ const tmp = outputPath + ".tmp";
107
+ writeFileSync(tmp, JSON.stringify(backup, null, 2) + "\n", "utf-8");
108
+ renameSync(tmp, outputPath);
109
+ log(`\n${render.icons.ok} ${render.wrap(render.c.green, "Backup complete")}`);
110
+ log(render.kv("Agents", render.wrap(render.c.bold, String(agents.length))));
111
+ log(render.kv("Memories", render.wrap(render.c.bold, String(memories.length))));
112
+ log(render.kv("Souls", render.wrap(render.c.bold, String(souls.length))));
113
+ log(render.kv("Output", render.wrap(render.c.dim, outputPath)));
114
+ });
115
+ }
@@ -0,0 +1,91 @@
1
+ import { authedRequest } from "../lib/auth-resolve.js";
2
+ import * as render from "../render.js";
3
+ let cli;
4
+ /** Bind the cli-locals this module depends on. */
5
+ export function bindCli(fns) {
6
+ cli = fns;
7
+ }
8
+ function resolveBaseUrl(...args) {
9
+ return cli.resolveBaseUrl(...args);
10
+ }
11
+ function resolveSigningAgentId(...args) {
12
+ return cli.resolveSigningAgentId(...args);
13
+ }
14
+ export function register(program) {
15
+ // ─── flair bootstrap ─────────────────────────────────────────────────────────
16
+ //
17
+ // `flair bootstrap` prints agent context (soul, memories) and a structured budget
18
+ // footer summarizing token usage and memory inclusion/truncation. The footer is
19
+ // parseable for downstream agents to react to budget pressure.
20
+ //
21
+ // Budget footer format (printed to stderr):
22
+ // [budget: <used>/<max> tokens, <included> included, <truncated> truncated]
23
+ //
24
+ // Fields:
25
+ // - tokens: estimated tokens used / max budget
26
+ // - included: number of memories/soul entries included in context
27
+ // - truncated: number of memories excluded due to token budget
28
+ //
29
+ // When truncated &gt; 0, the agent should consider asking for more context or reducing scope.
30
+ program
31
+ .command("bootstrap")
32
+ .description("Cold-start context: get soul + recent memories as formatted text")
33
+ .option("--agent <id>", "Agent ID (or set FLAIR_AGENT_ID env)")
34
+ .option("--max-tokens <n>", "Maximum tokens in output", "4000")
35
+ .option("--port <port>", "Harper HTTP port")
36
+ .option("--url <url>", "Flair base URL (overrides --port)")
37
+ .option("--target <url>", "Remote Flair URL (env: FLAIR_TARGET; alias for --url)")
38
+ .option("--key <path>", "Ed25519 private key path")
39
+ .option("--json", "Emit JSON {context, tokenEstimate, memoriesIncluded, ...} (also: pipe + FLAIR_OUTPUT=json)")
40
+ .action(async (opts) => {
41
+ const { agentId, source } = resolveSigningAgentId(opts, "bootstrap");
42
+ if (!agentId) {
43
+ console.error(`${render.icons.error} --agent <id> required (or set FLAIR_AGENT_ID)`);
44
+ process.exit(2);
45
+ }
46
+ const baseUrl = resolveBaseUrl(opts);
47
+ const mode = render.resolveOutputMode(opts);
48
+ try {
49
+ // flair#747: routed through the shared resolver — --key is an
50
+ // explicit tier-1 override (as before), now additionally backstopped
51
+ // by env admin-pass / ~/.flair/admin-pass / the Ed25519 floor if
52
+ // neither --key nor the agent's own resolveKeyPath(agentId) lookup
53
+ // finds a usable key. Previously this only ever tried Ed25519 (no
54
+ // admin fallback at all) and sent NO Authorization header when no key
55
+ // was found, relying on Harper's local passthrough.
56
+ const result = (await authedRequest("POST", "/BootstrapMemories", { agentId, maxTokens: parseInt(opts.maxTokens, 10) }, { baseUrl, agentId, agentIdSource: source, explicitKeyPath: opts.key }));
57
+ if (mode === "json") {
58
+ // Agent-first: emit the full server response, augmented with the cap
59
+ // that was requested. Includes context, sections, tokenEstimate, etc.
60
+ console.log(render.asJSON({ ...result, maxTokens: parseInt(opts.maxTokens, 10) }));
61
+ return;
62
+ }
63
+ // Human mode: print context to stdout, budget footer to stderr (parseable).
64
+ if (result.context) {
65
+ console.log(result.context);
66
+ }
67
+ else {
68
+ console.error(`${render.icons.error} No context available.`);
69
+ process.exit(1);
70
+ }
71
+ // flair#1199 — the budget footer reflects the PROSE the human injects
72
+ // (stdout = result.context), not the full serialized payload. tokenEstimate
73
+ // now measures the whole response (structured containers + prose), which
74
+ // the CLI's structured fields the human doesn't read would inflate.
75
+ const tokensUsed = typeof result.context === "string" && result.context.length > 0
76
+ ? Math.ceil(result.context.length / 4)
77
+ : (result.tokenEstimate ?? 0);
78
+ const maxTokens = parseInt(opts.maxTokens, 10);
79
+ const included = result.memoriesIncluded ?? 0;
80
+ const truncated = result.memoriesTruncated ?? 0;
81
+ const tokenPct = maxTokens > 0 ? (tokensUsed / maxTokens) * 100 : 0;
82
+ const tokenIcon = tokenPct >= 90 ? render.icons.warn : tokenPct >= 70 ? render.icons.info : render.icons.ok;
83
+ const truncIcon = truncated > 0 ? render.icons.warn : render.icons.ok;
84
+ console.error(`${tokenIcon} budget ${tokensUsed}/${maxTokens} tokens (${tokenPct.toFixed(0)}%) ${render.icons.bullet} ${render.icons.ok} ${included} included ${render.icons.bullet} ${truncIcon} ${truncated} truncated`);
85
+ }
86
+ catch (err) {
87
+ console.error(`${render.icons.error} Bootstrap failed: ${err.message}`);
88
+ process.exit(1);
89
+ }
90
+ });
91
+ }