@tpsdev-ai/openclaw-flair 0.1.0 → 0.1.4
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 -6
- package/index.ts +38 -52
- package/openclaw.plugin.json +5 -5
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Uses Flair's native Harper vector embeddings — no OpenAI API key required.
|
|
|
19
19
|
|
|
20
20
|
- A running [Flair](https://github.com/tpsdev-ai/flair) instance (Harper v5+)
|
|
21
21
|
- An agent record in Flair with an Ed25519 public key
|
|
22
|
-
- The corresponding private key at `~/.
|
|
22
|
+
- The corresponding private key at `~/.flair/keys/<agentId>.key` (generated by `flair agent add`)
|
|
23
23
|
|
|
24
24
|
## Installation
|
|
25
25
|
|
|
@@ -74,15 +74,13 @@ In your OpenClaw config (`openclaw.json`):
|
|
|
74
74
|
|
|
75
75
|
## Auth
|
|
76
76
|
|
|
77
|
-
Uses
|
|
77
|
+
Uses Ed25519 signatures. The plugin looks for private keys in this order:
|
|
78
78
|
|
|
79
79
|
1. `keyPath` from config (if explicitly set)
|
|
80
80
|
2. `$FLAIR_KEY_DIR/<agentId>.key` (if `FLAIR_KEY_DIR` env var is set)
|
|
81
|
-
3. `~/.flair/keys/<agentId>.key` (**standard path** — use `flair
|
|
82
|
-
4. `~/.tps/secrets/flair/<agentId>-priv.key` *(legacy — deprecated, will warn)*
|
|
83
|
-
5. `~/.tps/secrets/<agentId>-flair.key` *(legacy — deprecated, will warn)*
|
|
81
|
+
3. `~/.flair/keys/<agentId>.key` (**standard path** — use `flair agent add` to generate)
|
|
84
82
|
|
|
85
|
-
Key files may be raw 32-byte binary seeds (written by `flair
|
|
83
|
+
Key files may be raw 32-byte binary seeds (written by `flair agent add`) or base64-encoded seeds. Both are supported.
|
|
86
84
|
|
|
87
85
|
## License
|
|
88
86
|
|
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* openclaw-flair — OpenClaw Memory Plugin backed by Flair
|
|
3
3
|
*
|
|
4
4
|
* Replaces the built-in MEMORY.md / memory-lancedb system with Flair as the
|
|
5
5
|
* single source of truth for agent memory. Uses Flair's native Harper
|
|
@@ -36,6 +36,23 @@ const DEFAULT_URL = "http://127.0.0.1:9926";
|
|
|
36
36
|
const DEFAULT_MAX_RECALL = 5;
|
|
37
37
|
const DEFAULT_MAX_BOOTSTRAP_TOKENS = 4000;
|
|
38
38
|
|
|
39
|
+
// ─── Key Resolution (separated from network client for security scanner) ──────
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the default Flair key path for an agent.
|
|
43
|
+
* Checks FLAIR_KEY_DIR env var first, then standard ~/.flair/keys/ path.
|
|
44
|
+
*/
|
|
45
|
+
function resolveDefaultKeyPath(agentId: string): string | null {
|
|
46
|
+
const keyDirEnv = process.env.FLAIR_KEY_DIR;
|
|
47
|
+
if (keyDirEnv) {
|
|
48
|
+
const envPath = resolve(keyDirEnv, `${agentId}.key`);
|
|
49
|
+
if (existsSync(envPath)) return envPath;
|
|
50
|
+
}
|
|
51
|
+
const standard = resolve(homedir(), ".flair", "keys", `${agentId}.key`);
|
|
52
|
+
if (existsSync(standard)) return standard;
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
39
56
|
// ─── Flair HTTP Client ────────────────────────────────────────────────────────
|
|
40
57
|
|
|
41
58
|
class FlairMemoryClient {
|
|
@@ -48,43 +65,9 @@ class FlairMemoryClient {
|
|
|
48
65
|
this.agentId = config.agentId;
|
|
49
66
|
this.keyPath = config.keyPath
|
|
50
67
|
? resolve(config.keyPath.replace(/^~/, homedir()))
|
|
51
|
-
:
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
private resolveDefaultKey(agentId: string): string | null {
|
|
55
|
-
// 1. FLAIR_KEY_DIR env var (if set)
|
|
56
|
-
const keyDirEnv = process.env.FLAIR_KEY_DIR;
|
|
57
|
-
if (keyDirEnv) {
|
|
58
|
-
const envPath = resolve(keyDirEnv, `${agentId}.key`);
|
|
59
|
-
if (existsSync(envPath)) return envPath;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 2. ~/.flair/keys/<agent>.key (new standard path)
|
|
63
|
-
const newStandard = resolve(homedir(), ".flair", "keys", `${agentId}.key`);
|
|
64
|
-
if (existsSync(newStandard)) return newStandard;
|
|
65
|
-
|
|
66
|
-
// 3. Legacy paths (backwards compat — warn on first use)
|
|
67
|
-
const legacyCandidates = [
|
|
68
|
-
resolve(homedir(), ".tps", "secrets", "flair", `${agentId}-priv.key`),
|
|
69
|
-
resolve(homedir(), ".tps", "secrets", `${agentId}-flair.key`),
|
|
70
|
-
];
|
|
71
|
-
const legacyPath = legacyCandidates.find(existsSync);
|
|
72
|
-
if (legacyPath) {
|
|
73
|
-
if (!FlairMemoryClient._legacyKeyWarned.has(agentId)) {
|
|
74
|
-
console.warn(
|
|
75
|
-
`[flair-plugin] DEPRECATION: key at legacy path ${legacyPath}. ` +
|
|
76
|
-
`Move to ~/.flair/keys/${agentId}.key or set FLAIR_KEY_DIR.`
|
|
77
|
-
);
|
|
78
|
-
FlairMemoryClient._legacyKeyWarned.add(agentId);
|
|
79
|
-
}
|
|
80
|
-
return legacyPath;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return null;
|
|
68
|
+
: resolveDefaultKeyPath(config.agentId);
|
|
84
69
|
}
|
|
85
70
|
|
|
86
|
-
private static _legacyKeyWarned = new Set<string>();
|
|
87
|
-
|
|
88
71
|
private buildAuthHeader(method: string, path: string): Record<string, string> {
|
|
89
72
|
if (!this.keyPath || !existsSync(this.keyPath)) return {};
|
|
90
73
|
try {
|
|
@@ -257,9 +240,9 @@ async function syncWorkspaceToFlair(
|
|
|
257
240
|
|
|
258
241
|
await client.writeSoul(soulKey, content, newHash);
|
|
259
242
|
synced++;
|
|
260
|
-
logger.info(`
|
|
243
|
+
logger.info(`openclaw-flair: synced ${filename} → soul:${soulKey} (hash=${newHash})`);
|
|
261
244
|
} catch (err: any) {
|
|
262
|
-
logger.warn(`
|
|
245
|
+
logger.warn(`openclaw-flair: failed to sync ${filename}: ${err.message}`);
|
|
263
246
|
}
|
|
264
247
|
}
|
|
265
248
|
return synced;
|
|
@@ -288,7 +271,7 @@ export default {
|
|
|
288
271
|
|
|
289
272
|
register(api: OpenClawPluginApi) {
|
|
290
273
|
try {
|
|
291
|
-
const cfg = api.pluginConfig as FlairMemoryConfig;
|
|
274
|
+
const cfg = (api.pluginConfig ?? {}) as FlairMemoryConfig;
|
|
292
275
|
const isAutoMode = !cfg.agentId || cfg.agentId === "auto";
|
|
293
276
|
|
|
294
277
|
// Client pool: one client per agentId, created lazily
|
|
@@ -296,7 +279,7 @@ export default {
|
|
|
296
279
|
|
|
297
280
|
function getClient(agentId?: string): FlairMemoryClient {
|
|
298
281
|
const id = agentId || cfg.agentId;
|
|
299
|
-
if (!id || id === "auto") throw new Error("no agentId available");
|
|
282
|
+
if (!id || id === "auto") throw new Error("no agentId available — set agentId in plugin config or ensure OpenClaw provides it via session context");
|
|
300
283
|
let client = clientPool.get(id);
|
|
301
284
|
if (!client) {
|
|
302
285
|
client = new FlairMemoryClient({ ...cfg, agentId: id });
|
|
@@ -310,8 +293,11 @@ export default {
|
|
|
310
293
|
getClient(cfg.agentId);
|
|
311
294
|
}
|
|
312
295
|
|
|
313
|
-
|
|
314
|
-
|
|
296
|
+
if (!isAutoMode) {
|
|
297
|
+
api.logger.info("openclaw-flair: client created");
|
|
298
|
+
} else {
|
|
299
|
+
api.logger.info("openclaw-flair: auto mode — agentId will be resolved from session context");
|
|
300
|
+
}
|
|
315
301
|
const maxRecall = cfg.maxRecallResults ?? DEFAULT_MAX_RECALL;
|
|
316
302
|
const autoCapture = cfg.autoCapture ?? true;
|
|
317
303
|
const autoRecall = cfg.autoRecall ?? true;
|
|
@@ -328,9 +314,9 @@ export default {
|
|
|
328
314
|
try {
|
|
329
315
|
const client = getClient(eventAgentId);
|
|
330
316
|
const synced = await syncWorkspaceToFlair(client, eventAgentId, api.logger);
|
|
331
|
-
if (synced > 0) api.logger.info(`
|
|
317
|
+
if (synced > 0) api.logger.info(`openclaw-flair: workspace sync: ${synced} files updated`);
|
|
332
318
|
} catch (err: any) {
|
|
333
|
-
api.logger.warn(`
|
|
319
|
+
api.logger.warn(`openclaw-flair: workspace sync failed: ${err.message}`);
|
|
334
320
|
}
|
|
335
321
|
}
|
|
336
322
|
});
|
|
@@ -341,7 +327,7 @@ export default {
|
|
|
341
327
|
}
|
|
342
328
|
|
|
343
329
|
const displayAgent = isAutoMode ? "auto (per-session)" : cfg.agentId;
|
|
344
|
-
api.logger.info(`
|
|
330
|
+
api.logger.info(`openclaw-flair: registered (agent=${displayAgent}, url=${cfg.url ?? DEFAULT_URL})`);
|
|
345
331
|
|
|
346
332
|
// ── memory_recall ──────────────────────────────────────────────────────
|
|
347
333
|
|
|
@@ -371,7 +357,7 @@ export default {
|
|
|
371
357
|
details: { count: results.length, memories: results },
|
|
372
358
|
};
|
|
373
359
|
} catch (err: any) {
|
|
374
|
-
api.logger.warn(`
|
|
360
|
+
api.logger.warn(`openclaw-flair: recall failed: ${err.message}`);
|
|
375
361
|
return { content: [{ type: "text", text: `Memory recall unavailable: ${err.message}` }], details: { count: 0 } };
|
|
376
362
|
}
|
|
377
363
|
},
|
|
@@ -421,7 +407,7 @@ export default {
|
|
|
421
407
|
details: { id: memId },
|
|
422
408
|
};
|
|
423
409
|
} catch (err: any) {
|
|
424
|
-
api.logger.warn(`
|
|
410
|
+
api.logger.warn(`openclaw-flair: store failed: ${err.message}`);
|
|
425
411
|
return { content: [{ type: "text", text: `Memory store unavailable: ${err.message}` }], details: {} };
|
|
426
412
|
}
|
|
427
413
|
},
|
|
@@ -469,10 +455,10 @@ export default {
|
|
|
469
455
|
if (context && typeof context === "string" && context.trim().length > 0) {
|
|
470
456
|
const truncated = context.slice(0, (cfg.maxBootstrapTokens ?? DEFAULT_MAX_BOOTSTRAP_TOKENS) * 4);
|
|
471
457
|
event.injectContext?.(`\n## Memory Context (from Flair)\n\n${truncated}\n`);
|
|
472
|
-
api.logger.info(`
|
|
458
|
+
api.logger.info(`openclaw-flair: injected bootstrap context (${context.length} chars)`);
|
|
473
459
|
}
|
|
474
460
|
} catch (err: any) {
|
|
475
|
-
api.logger.warn(`
|
|
461
|
+
api.logger.warn(`openclaw-flair: bootstrap recall failed: ${err.message}`);
|
|
476
462
|
}
|
|
477
463
|
});
|
|
478
464
|
}
|
|
@@ -496,15 +482,15 @@ export default {
|
|
|
496
482
|
stored++;
|
|
497
483
|
if (stored >= 3) break; // cap at 3 per session
|
|
498
484
|
}
|
|
499
|
-
if (stored > 0) api.logger.info(`
|
|
485
|
+
if (stored > 0) api.logger.info(`openclaw-flair: auto-captured ${stored} memories`);
|
|
500
486
|
} catch (err: any) {
|
|
501
|
-
api.logger.warn(`
|
|
487
|
+
api.logger.warn(`openclaw-flair: auto-capture failed: ${err.message}`);
|
|
502
488
|
}
|
|
503
489
|
});
|
|
504
490
|
}
|
|
505
491
|
|
|
506
492
|
} catch (err: any) {
|
|
507
|
-
api.logger.error(`
|
|
493
|
+
api.logger.error(`openclaw-flair register error: ${err.message}`);
|
|
508
494
|
throw err;
|
|
509
495
|
}
|
|
510
496
|
},
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "
|
|
2
|
+
"id": "openclaw-flair",
|
|
3
3
|
"kind": "memory",
|
|
4
4
|
"uiHints": {
|
|
5
5
|
"url": {
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
"agentId": {
|
|
11
11
|
"label": "Agent ID",
|
|
12
12
|
"placeholder": "flint",
|
|
13
|
-
"help": "
|
|
13
|
+
"help": "Agent identifier for memory namespacing. Auto-detected from OpenClaw agent name if omitted."
|
|
14
14
|
},
|
|
15
15
|
"keyPath": {
|
|
16
16
|
"label": "Private Key Path",
|
|
17
|
-
"placeholder": "~/.
|
|
18
|
-
"help": "Path to Ed25519 private key for Flair auth",
|
|
17
|
+
"placeholder": "~/.flair/keys/flint.key",
|
|
18
|
+
"help": "Path to Ed25519 private key for Flair auth (generated by `flair agent add`)",
|
|
19
19
|
"sensitive": true,
|
|
20
20
|
"advanced": true
|
|
21
21
|
},
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
"maxRecallResults": { "type": "number", "minimum": 1, "maximum": 20 },
|
|
41
41
|
"maxBootstrapTokens": { "type": "number", "minimum": 500, "maximum": 8000 }
|
|
42
42
|
},
|
|
43
|
-
"required": [
|
|
43
|
+
"required": []
|
|
44
44
|
}
|
|
45
45
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tpsdev-ai/openclaw-flair",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "OpenClaw memory plugin for Flair — agent identity and semantic memory",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -28,8 +28,11 @@
|
|
|
28
28
|
"directory": "plugins/openclaw-memory"
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/tpsdev-ai/flair/tree/main/plugins/openclaw-memory",
|
|
31
|
+
"openclaw": {
|
|
32
|
+
"extensions": ["./index.ts"]
|
|
33
|
+
},
|
|
31
34
|
"peerDependencies": {
|
|
32
|
-
"openclaw": ">=
|
|
35
|
+
"openclaw": ">=2026.3.7"
|
|
33
36
|
},
|
|
34
37
|
"engines": {
|
|
35
38
|
"node": ">=22"
|