feat(kb): per-agent hf-token via secret-mgr (openclaw mirror)

Phase 4c openclaw side. dynamic-kb-* tools now resolve a per-agent
hf-token via the existing secret-mgr binary (same path ClawSkills
`tc-ctrl get-token` workflow uses), instead of relying solely on the
plugin-level apiKey config.

  plugin/index.ts kbDeps.tokenFor: spawns secret-mgr with
    AGENT_ID env set, parses stdout, 5s timeout. Returns null on any
    failure so the caller cleanly falls back to the plugin-level
    KBClient (which uses Config.APIKey via Bearer).

  plugin/index.ts kbDeps.makeClient: builds a fresh KBClient bound
    to the resolved per-agent token. Plugin-level Client stays as
    the fallback when no agent token is set.

Cross-runtime parity: Plexum side uses HostAPI.GetAgentSecret RPC,
openclaw side shells out to secret-mgr. Same KBDeps interface; same
behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-06-06 00:32:52 +01:00
parent 2553d102f3
commit cb331d3340

View File

@@ -940,9 +940,10 @@ function register(api: PluginAPI): void {
// ---- dynamic-kb-* family (DESIGN-DYNAMIC-BLOCK.md §3.3 / §4.4)
// Cross-runtime mirror of HarborForge.PlexumPlugin/internal/tools/kb.go +
// /internal/kbblock + /internal/kbclient. v1 auth = plugin-level apiKey
// via Bearer (per-agent hf-token resolution is a TODO matching the
// Plexum side).
// /internal/kbblock + /internal/kbclient. v1 auth: per-agent hf-token
// resolved via secret-mgr (matches the rest of the Hangman-Lab
// openclaw plugins). Falls back to plugin-level apiKey when no
// per-agent token is configured.
const kbCfg = resolveConfig();
const kbBackendUrl =
typeof kbCfg?.backendUrl === 'string' && kbCfg.backendUrl
@@ -951,8 +952,24 @@ function register(api: PluginAPI): void {
const kbApiKey = typeof kbCfg?.apiKey === 'string' ? (kbCfg.apiKey as string) : '';
const kbDeps: KBDeps = {
client: kbApiKey ? new KBClient(kbBackendUrl, kbApiKey) : null,
tokenFor: null,
makeClient: null,
tokenFor: async (agentId: string): Promise<string | null> => {
// Per-agent hf-token via secret-mgr (decision #20 mirror).
// Spawn the existing secret-mgr binary with AGENT_ID set; same
// path ClawSkills `tc-ctrl get-token` workflow shells to.
try {
const { spawnSync } = await import('node:child_process');
const res = spawnSync('secret-mgr', ['get-secret', '--key', 'hf-token'], {
env: { ...process.env, AGENT_ID: agentId },
encoding: 'utf8',
timeout: 5000,
});
if (res.status === 0 && res.stdout.trim()) return res.stdout.trim();
return null;
} catch {
return null;
}
},
makeClient: (token: string) => new KBClient(kbBackendUrl, token),
turnFor: () => 0,
};