@tpsdev-ai/flair 0.53.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.
- package/README.md +4 -1
- package/dist/build-info.json +3 -3
- package/dist/cli.js +1791 -15648
- package/dist/commands/agent.js +453 -0
- package/dist/commands/attention.js +121 -0
- package/dist/commands/backup.js +115 -0
- package/dist/commands/bootstrap.js +91 -0
- package/dist/commands/bridge.js +608 -0
- package/dist/commands/deploy.js +180 -0
- package/dist/commands/doctor.js +1654 -0
- package/dist/commands/export.js +110 -0
- package/dist/commands/federation.js +1575 -0
- package/dist/commands/fleet.js +73 -0
- package/dist/commands/grant.js +109 -0
- package/dist/commands/hook.js +193 -0
- package/dist/commands/idp.js +193 -0
- package/dist/commands/import.js +134 -0
- package/dist/commands/init.js +1203 -0
- package/dist/commands/inspect.js +45 -0
- package/dist/commands/keys.js +187 -0
- package/dist/commands/mcp.js +707 -0
- package/dist/commands/memory.js +501 -0
- package/dist/commands/migrate-harness-memory.js +270 -0
- package/dist/commands/orgevent.js +138 -0
- package/dist/commands/presence.js +76 -0
- package/dist/commands/principal.js +338 -0
- package/dist/commands/quality.js +1164 -0
- package/dist/commands/reembed.js +296 -0
- package/dist/commands/relationship.js +76 -0
- package/dist/commands/rem.js +1048 -0
- package/dist/commands/restore.js +130 -0
- package/dist/commands/search.js +244 -0
- package/dist/commands/service.js +315 -0
- package/dist/commands/session.js +184 -0
- package/dist/commands/soul.js +155 -0
- package/dist/commands/status.js +914 -0
- package/dist/commands/test.js +93 -0
- package/dist/commands/uninstall.js +143 -0
- package/dist/commands/upgrade.js +1592 -0
- package/dist/commands/workspace.js +114 -0
- package/dist/deploy.js +24 -0
- package/dist/fabric-npm-install.js +87 -0
- package/dist/federation-verify.js +498 -0
- package/dist/fleet-verify.js +144 -21
- package/dist/install/clients.js +167 -0
- package/dist/lib/auth-resolve.js +76 -1
- package/dist/lib/daemon-liveness.js +131 -2
- package/dist/lib/doctor-config-path.js +61 -0
- package/dist/lib/doctor-federation-driver.js +189 -0
- package/dist/lib/doctor-run.js +40 -0
- package/dist/lib/entity-vocab-cli.js +3 -3
- package/dist/lib/federation-pair-identity.js +47 -0
- package/dist/lib/launchd-repair.js +5 -4
- package/dist/lib/ops-api-bind.js +115 -0
- package/dist/lib/owned-pins.js +219 -0
- package/dist/lib/uninstall-purge.js +218 -0
- package/dist/rem/restore.js +8 -10
- package/dist/resources/AgentReadPosition.js +74 -0
- package/dist/resources/Federation.js +8 -2
- package/dist/resources/Memory.js +4 -3
- package/dist/resources/MemoryBootstrap.js +41 -25
- package/dist/resources/MemoryCandidate.js +5 -6
- package/dist/resources/OrgEventCatchup.js +126 -47
- package/dist/resources/agent-read-position-lib.js +83 -0
- package/dist/resources/agent-read-position.js +120 -0
- package/dist/resources/embeddings-boot.js +32 -0
- package/dist/resources/federation-peer-liveness.js +73 -0
- package/dist/resources/health.js +68 -19
- package/dist/resources/mcp-tools.js +43 -279
- package/dist/resources/memory-visibility.js +3 -3
- package/dist/resources/migration-boot.js +59 -18
- package/dist/resources/migrations/embedding-stamp.js +20 -1
- package/dist/resources/migrations/recheck.js +43 -0
- package/dist/resources/migrations/runner.js +6 -1
- package/dist/resources/migrations/stamp-outstanding.js +171 -0
- package/dist/resources/migrations/visibility-backfill.js +2 -2
- package/dist/resources/org-event-catchup-lib.js +47 -0
- package/dist/resources/record-owner-guard.js +1 -0
- package/dist/stamp-migration-verify.js +163 -0
- package/dist/stamp-outstanding.js +144 -0
- package/docs/api-reference.md +4 -2
- package/docs/deploying-on-fabric.md +11 -10
- package/docs/deployment.md +3 -1
- package/docs/federation.md +19 -0
- package/docs/hosted-on-fabric.md +3 -3
- package/docs/quickstart.md +2 -1
- package/docs/releasing.md +15 -7
- package/docs/spoke-bringup.md +10 -5
- package/docs/standalone-local.md +3 -1
- package/docs/upgrade.md +25 -6
- package/node_modules/@tpsdev-ai/flair-tool-descriptors/LICENSE +19 -0
- package/node_modules/@tpsdev-ai/flair-tool-descriptors/README.md +22 -0
- package/node_modules/@tpsdev-ai/flair-tool-descriptors/dist/index.d.ts +70 -0
- package/node_modules/@tpsdev-ai/flair-tool-descriptors/dist/index.js +665 -0
- package/node_modules/@tpsdev-ai/flair-tool-descriptors/package.json +46 -0
- package/package.json +9 -4
- package/schemas/agent.graphql +15 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { resolveKeyPath, buildEd25519Auth } from "../lib/auth-resolve.js";
|
|
2
|
+
let cli;
|
|
3
|
+
/** Bind shared CLI helpers. cli.ts calls this immediately before register(program). */
|
|
4
|
+
export function bindCli(fns) {
|
|
5
|
+
cli = fns;
|
|
6
|
+
}
|
|
7
|
+
function resolveBaseUrl(opts) {
|
|
8
|
+
return cli.resolveBaseUrl(opts);
|
|
9
|
+
}
|
|
10
|
+
function resolveSigningAgentId(opts, command) {
|
|
11
|
+
return cli.resolveSigningAgentId(opts, command);
|
|
12
|
+
}
|
|
13
|
+
function parseEntitiesOptionOrExit(csv) {
|
|
14
|
+
return cli.parseEntitiesOptionOrExit(csv);
|
|
15
|
+
}
|
|
16
|
+
// ─── flair workspace ─────────────────────────────────────────────────────────
|
|
17
|
+
//
|
|
18
|
+
// Coordination write surface (Kris #510). `workspace set` writes the
|
|
19
|
+
// agent's OWN WorkspaceState via a signed PUT /WorkspaceState/{id}. Identity
|
|
20
|
+
// is asserted by including agentId in the body — the server never trusts it
|
|
21
|
+
// blindly, it 403s any mismatch against the Ed25519 signature's agentId
|
|
22
|
+
// (WorkspaceState.put(), resources/WorkspaceState.ts), so this is a
|
|
23
|
+
// self-declaration the server verifies 1:1, not attribution-from-body.
|
|
24
|
+
//
|
|
25
|
+
// (flair#679, measured against a real spawned Harper): table-backed resources
|
|
26
|
+
// only accept writes via PUT /<Table>/<id> — a bare POST /WorkspaceState 405s
|
|
27
|
+
// ("does not have a post method implemented to handle HTTP method POST"),
|
|
28
|
+
// same restriction documented in resources/Memory.ts and already fixed for
|
|
29
|
+
// `soul set` (#498). WorkspaceState.ts DOES define a post() method, but
|
|
30
|
+
// Harper's REST layer never routes a real HTTP POST to it — post() is only
|
|
31
|
+
// reachable via in-process resource instantiation, never the wire. put(),
|
|
32
|
+
// unlike post(), does NOT default createdAt/timestamp/agentId — the CLI
|
|
33
|
+
// supplies them all explicitly below.
|
|
34
|
+
export const MAX_WORKSPACE_FIELD_LENGTH = 2000;
|
|
35
|
+
/** Register the `flair workspace` command group (flair#1635). */
|
|
36
|
+
export function register(program) {
|
|
37
|
+
const ENTITIES_OPTION_DESCRIPTION = cli.ENTITIES_OPTION_DESCRIPTION;
|
|
38
|
+
const workspace = program.command("workspace").description("Manage agent workspace state (The Office Space)");
|
|
39
|
+
workspace
|
|
40
|
+
.command("set")
|
|
41
|
+
.description("Set your agent's current workspace state (PUT /WorkspaceState/{id})")
|
|
42
|
+
.requiredOption("--ref <ref>", "Workspace ref (branch, worktree, or task ref)")
|
|
43
|
+
.option("--label <text>", "Human-readable label for this workspace")
|
|
44
|
+
.option("--provider <name>", "Provider/runtime (e.g. claude-code, openclaw)", "cli")
|
|
45
|
+
.option("--task <id>", "Task/issue id this workspace is attached to")
|
|
46
|
+
.option("--phase <phase>", "Current phase (e.g. design, implement, review)")
|
|
47
|
+
.option("--summary <text>", "Short summary of current workspace state")
|
|
48
|
+
.option("--entities <csv>", ENTITIES_OPTION_DESCRIPTION)
|
|
49
|
+
.option("--agent <id>", "Agent ID (env: FLAIR_AGENT_ID)")
|
|
50
|
+
.option("--port <port>", "Harper HTTP port")
|
|
51
|
+
.option("--target <url>", "Remote Flair URL (env: FLAIR_TARGET)")
|
|
52
|
+
.action(async (opts) => {
|
|
53
|
+
const { agentId } = resolveSigningAgentId(opts, "workspace set");
|
|
54
|
+
if (!agentId) {
|
|
55
|
+
console.error("Error: agent ID required. Pass --agent <id> or set FLAIR_AGENT_ID environment variable.");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
// Validate field lengths (free text → cap to bound the write).
|
|
59
|
+
for (const [name, val] of [["ref", opts.ref], ["label", opts.label], ["summary", opts.summary]]) {
|
|
60
|
+
if (val && String(val).length > MAX_WORKSPACE_FIELD_LENGTH) {
|
|
61
|
+
console.error(`Error: --${name} exceeds ${MAX_WORKSPACE_FIELD_LENGTH} character limit (got ${String(val).length}).`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// flair#1288: validate --entities before any key/network work; exits 1
|
|
66
|
+
// with the canonical format-and-type-set message on any malformed value.
|
|
67
|
+
const entities = opts.entities ? parseEntitiesOptionOrExit(String(opts.entities)) : undefined;
|
|
68
|
+
const keyPath = resolveKeyPath(agentId);
|
|
69
|
+
if (!keyPath) {
|
|
70
|
+
console.error(`Error: private key not found for agent '${agentId}'. Check ~/.flair/keys/ or set FLAIR_KEY_DIR.`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const baseUrl = resolveBaseUrl(opts).replace(/\/$/, "");
|
|
74
|
+
// Deterministic id (agentId:ref) — re-running `workspace set` for the same
|
|
75
|
+
// ref overwrites the same record, which is intentional (one row per
|
|
76
|
+
// agent+ref, not an append log).
|
|
77
|
+
const id = `${agentId}:${opts.ref}`;
|
|
78
|
+
const auth = buildEd25519Auth(agentId, "PUT", `/WorkspaceState/${id}`, keyPath);
|
|
79
|
+
// agentId IS included in the body now — WorkspaceState.put() (unlike
|
|
80
|
+
// post()) does not auto-attribute from the signature, it 403s any
|
|
81
|
+
// mismatch. This is a self-declaration the server verifies against the
|
|
82
|
+
// signature, not a forgeable claim.
|
|
83
|
+
const now = new Date().toISOString();
|
|
84
|
+
const body = {
|
|
85
|
+
id,
|
|
86
|
+
agentId,
|
|
87
|
+
ref: opts.ref,
|
|
88
|
+
provider: opts.provider ?? "cli",
|
|
89
|
+
timestamp: now,
|
|
90
|
+
createdAt: now,
|
|
91
|
+
};
|
|
92
|
+
if (opts.label)
|
|
93
|
+
body.label = opts.label;
|
|
94
|
+
if (opts.task)
|
|
95
|
+
body.taskId = opts.task;
|
|
96
|
+
if (opts.phase)
|
|
97
|
+
body.phase = opts.phase;
|
|
98
|
+
if (opts.summary)
|
|
99
|
+
body.summary = opts.summary;
|
|
100
|
+
if (entities && entities.length > 0)
|
|
101
|
+
body.entities = entities;
|
|
102
|
+
const res = await fetch(`${baseUrl}/WorkspaceState/${id}`, {
|
|
103
|
+
method: "PUT",
|
|
104
|
+
headers: { "Content-Type": "application/json", Authorization: auth },
|
|
105
|
+
body: JSON.stringify(body),
|
|
106
|
+
});
|
|
107
|
+
if (!res.ok) {
|
|
108
|
+
const text = await res.text().catch(() => "");
|
|
109
|
+
console.error(`Error: PUT /WorkspaceState/${id} failed (${res.status}): ${text}`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
console.log(`✓ Workspace state updated for '${agentId}': ref=${opts.ref}${opts.phase ? `, phase=${opts.phase}` : ""}`);
|
|
113
|
+
});
|
|
114
|
+
}
|
package/dist/deploy.js
CHANGED
|
@@ -6,6 +6,8 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { COMPONENT_ENV_FILENAME, PUBLIC_URL_KEY, isLoopbackUrl, planComponentEnv, publicUrlRemedy, } from "./component-env.js";
|
|
8
8
|
import { awaitOriginQuiescent, awaitReplicationConvergence, defaultConvergenceDeps, parseReplicationFailure, } from "./replication-convergence.js";
|
|
9
|
+
import { DEFAULT_STAMP_VERIFY_TIMEOUT_MS, verifyStampMigrationConverged } from "./stamp-migration-verify.js";
|
|
10
|
+
import { FABRIC_NPM_INSTALL_COMMAND } from "./fabric-npm-install.js";
|
|
9
11
|
// Files that must be PRESENT for a deploy root to be usable at all — a
|
|
10
12
|
// preflight sanity check, not the payload definition.
|
|
11
13
|
//
|
|
@@ -218,6 +220,12 @@ export function buildHarperDeployArgs(opts, url, project) {
|
|
|
218
220
|
`replicated=${opts.replicated !== false}`,
|
|
219
221
|
`deployment_timeout=${deploymentTimeoutMs}`,
|
|
220
222
|
`install_timeout=${installTimeoutMs}`,
|
|
223
|
+
// flair#886: Harper's default `npm install` writes every tarball into
|
|
224
|
+
// the node's `~/.npm/_cacache` and never evicts it. This command runs
|
|
225
|
+
// the same install against a disposable cache and deletes it after
|
|
226
|
+
// (see src/fabric-npm-install.ts). One argv element so Harper's
|
|
227
|
+
// `key=value` parser keeps the spaces inside the value.
|
|
228
|
+
`install_command=${FABRIC_NPM_INSTALL_COMMAND}`,
|
|
221
229
|
];
|
|
222
230
|
// --ignore-replication-errors escape hatch. Only appended when
|
|
223
231
|
// set — omitted entirely otherwise, so this is a no-op for every existing
|
|
@@ -929,6 +937,22 @@ export async function deploy(opts) {
|
|
|
929
937
|
timeoutMs: opts.verifyTimeoutMs ?? DEFAULT_VERIFY_TIMEOUT_MS,
|
|
930
938
|
onProgress: opts.onProgress,
|
|
931
939
|
});
|
|
940
|
+
// flair#1073: route verify proves the component is serving. It does not
|
|
941
|
+
// prove embedding-stamp re-embedded pre-flip rows. `upgrade --target`
|
|
942
|
+
// was reporting success while a Fabric corpus stayed split for days.
|
|
943
|
+
// Same --no-verify escape hatch; needs Basic admin (token-only skips).
|
|
944
|
+
if (opts.fabricUser && opts.fabricPassword) {
|
|
945
|
+
await verifyStampMigrationConverged({
|
|
946
|
+
baseUrl: url,
|
|
947
|
+
fabricUser: opts.fabricUser,
|
|
948
|
+
fabricPassword: opts.fabricPassword,
|
|
949
|
+
timeoutMs: opts.verifyTimeoutMs ?? DEFAULT_STAMP_VERIFY_TIMEOUT_MS,
|
|
950
|
+
onProgress: opts.onProgress,
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
else {
|
|
954
|
+
opts.onProgress?.("skipping embedding-stamp convergence check — needs --fabric-user/--fabric-password (token-only auth cannot read /HealthDetail)");
|
|
955
|
+
}
|
|
932
956
|
// Only meaningful for a target that is not loopback: a local Harper SHOULD
|
|
933
957
|
// advertise loopback, and asserting otherwise there would be wrong.
|
|
934
958
|
if (publicUrl) {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fabric-npm-install.ts — node-side `npm install` for Fabric deploys (flair#886).
|
|
3
|
+
*
|
|
4
|
+
* Harper's default component install writes every fetched tarball into the
|
|
5
|
+
* node's `~/.npm/_cacache` and never evicts it. A month of `flair deploy` /
|
|
6
|
+
* `flair upgrade --target` therefore leaves a growing cache on the hub —
|
|
7
|
+
* hundreds of MB per install once `node-llama-cpp` prebuilds are in the
|
|
8
|
+
* tree — while `get_components` and `system_information` never show it.
|
|
9
|
+
*
|
|
10
|
+
* This file is the `install_command` those two CLI paths pass to
|
|
11
|
+
* `harper deploy`. It runs `npm install` against a disposable cache
|
|
12
|
+
* directory and deletes that directory afterwards, so cache lifetime
|
|
13
|
+
* matches the deploy rather than the node.
|
|
14
|
+
*
|
|
15
|
+
* Harper splits `install_command` on spaces and spawns without a shell
|
|
16
|
+
* (`Application.installApplication`), so the command must be a single
|
|
17
|
+
* executable plus a path — no `sh -c`, no `&& rm`. The flags after
|
|
18
|
+
* `install` match Harper's own default when `install.command` is unset
|
|
19
|
+
* (`--force --ignore-scripts`); a custom command replaces that default
|
|
20
|
+
* entirely, and dropping either flag would change what the node runs.
|
|
21
|
+
*/
|
|
22
|
+
import { spawnSync } from "node:child_process";
|
|
23
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
24
|
+
import { tmpdir } from "node:os";
|
|
25
|
+
import { join, resolve } from "node:path";
|
|
26
|
+
import { pathToFileURL } from "node:url";
|
|
27
|
+
/** Path Harper runs, relative to the extracted component directory. */
|
|
28
|
+
export const FABRIC_NPM_INSTALL_ENTRY = "dist/fabric-npm-install.js";
|
|
29
|
+
/**
|
|
30
|
+
* Passed as `install_command=…` to `harper deploy`. Harper's
|
|
31
|
+
* `buildRequest` JSON-parses values that look like JSON; this string
|
|
32
|
+
* does not, so it stays a string. `split(" ")` must yield exactly
|
|
33
|
+
* `["node", FABRIC_NPM_INSTALL_ENTRY]`.
|
|
34
|
+
*/
|
|
35
|
+
export const FABRIC_NPM_INSTALL_COMMAND = `node ${FABRIC_NPM_INSTALL_ENTRY}`;
|
|
36
|
+
/** Harper's default argv when `install.command` is unset, plus `--cache`. */
|
|
37
|
+
export const FABRIC_NPM_INSTALL_ARGS_BEFORE_CACHE = [
|
|
38
|
+
"install",
|
|
39
|
+
"--force",
|
|
40
|
+
"--ignore-scripts",
|
|
41
|
+
];
|
|
42
|
+
/**
|
|
43
|
+
* Run `npm install` with `--cache` pointed at a fresh temp directory,
|
|
44
|
+
* then remove that directory on success or failure.
|
|
45
|
+
*
|
|
46
|
+
* Returns npm's exit status. Throws only when the spawn itself fails
|
|
47
|
+
* to start (ENOENT on `npm`, etc.) — the same split `fabric-upgrade`'s
|
|
48
|
+
* local staging install uses.
|
|
49
|
+
*/
|
|
50
|
+
export function installWithEphemeralNpmCache(deps = {}) {
|
|
51
|
+
const spawn = deps.spawn ??
|
|
52
|
+
((command, args, opts) => spawnSync(command, args, opts));
|
|
53
|
+
const mkdtemp = deps.mkdtemp ?? ((prefix) => mkdtempSync(prefix));
|
|
54
|
+
const rm = deps.rm ?? ((path) => rmSync(path, { recursive: true, force: true }));
|
|
55
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
56
|
+
const tmp = deps.tmpdir ?? tmpdir();
|
|
57
|
+
const cacheDir = mkdtemp(join(tmp, "flair-npm-"));
|
|
58
|
+
try {
|
|
59
|
+
const result = spawn("npm", [...FABRIC_NPM_INSTALL_ARGS_BEFORE_CACHE, "--cache", cacheDir], { cwd, stdio: "inherit" });
|
|
60
|
+
if (result.error)
|
|
61
|
+
throw result.error;
|
|
62
|
+
return result.status ?? 1;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
rm(cacheDir);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function invokedAsMain() {
|
|
69
|
+
const entry = process.argv[1];
|
|
70
|
+
if (!entry)
|
|
71
|
+
return false;
|
|
72
|
+
try {
|
|
73
|
+
return import.meta.url === pathToFileURL(resolve(entry)).href;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (invokedAsMain()) {
|
|
80
|
+
try {
|
|
81
|
+
process.exit(installWithEphemeralNpmCache());
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
console.error(err instanceof Error ? err.message : err);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
}
|