Files
nexus/scripts/sync-agents-sanitized.mjs
T
developer a55951f315
CI - Build & Test / Backend (.NET) (push) Successful in 31s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 17s
CI - Build & Test / Security Check (push) Successful in 3s
CI - Build & Test / Deploy Nexus (push) Failing after 19s
P4: Agent-Identitäten ohne Secrets — sanitized config feed
- Replace Python-based sanitizer in deploy script with lightweight jq/alpine
- Add sync-agents-sanitized.mjs for on-demand and watch-mode sync
- Add AgentConfigPath to appsettings.json (explicit default)
- Extend /health/live endpoint to report agent count from sanitized config
- Document architecture in docs/agent-identity-architecture.md
- No openclaw.json secrets ever reach Nexus API containers

Verification:
- curl /api/v1/agents → 9 agents, zero secrets in response
- agents-sanitized.json contains only 'agents' key, no gateway/auth
- All C# code paths read from agents-sanitized.json (AgentConfigPath)
- Bridge controller resolves agent IDs via AgentService.GetAllowedAgentIdsAsync()
2026-07-13 09:12:19 +02:00

100 lines
2.7 KiB
JavaScript

#!/usr/bin/env node
/**
* sync-agents-sanitized.mjs
*
* Keeps agents-sanitized.json in sync with openclaw.json.
* Strips all secrets (gateway, channels, auth, tools, plugins, etc.)
* and only writes the "agents" key.
*
* Modes:
* --once Run once and exit
* --watch Watch openclaw.json and re-generate on changes (default)
*
* Usage:
* node sync-agents-sanitized.mjs --once
* node sync-agents-sanitized.mjs --watch
*
* Paths (defaults, override with OPENCLAW_CONFIG and SANITIZED_OUTPUT env vars):
* Source: /home/node/.openclaw/openclaw.json
* Output: /home/node/.openclaw/agents-sanitized.json
*/
import { watch } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';
const SRC = process.env.OPENCLAW_CONFIG || '/home/node/.openclaw/openclaw.json';
const OUT = process.env.SANITIZED_OUTPUT || '/home/node/.openclaw/agents-sanitized.json';
let running = true;
let debounceTimer = null;
const DEBOUNCE_MS = 500;
function log(msg) {
const ts = new Date().toISOString();
process.stderr.write(`[agents-sanitized ${ts}] ${msg}\n`);
}
async function generate() {
try {
const raw = await readFile(SRC, 'utf-8');
const data = JSON.parse(raw);
const agents = data?.agents;
if (!agents) {
log(`ERROR: "agents" key not found in ${SRC}`);
return false;
}
const sanitized = { agents };
const json = JSON.stringify(sanitized, null, 2) + '\n';
await writeFile(OUT, json, 'utf-8');
const agentCount = agents?.list?.length ?? 0;
log(`Generated ${OUT} with ${agentCount} agents (${json.length} bytes)`);
return true;
} catch (err) {
log(`ERROR: ${err.message}`);
return false;
}
}
async function main() {
const mode = process.argv.includes('--once') ? 'once' : 'watch';
log(`Starting in ${mode} mode`);
log(` Source: ${SRC}`);
log(` Output: ${OUT}`);
// Initial generation
const ok = await generate();
if (mode === 'once') {
process.exit(ok ? 0 : 1);
}
// Watch mode
log('Watching for changes...');
watch(SRC, (eventType) => {
if (eventType !== 'change') return;
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
log(`Detected change in ${SRC}`);
await generate();
}, DEBOUNCE_MS);
});
// Keep process alive
process.on('SIGINT', () => { running = false; process.exit(0); });
process.on('SIGTERM', () => { running = false; process.exit(0); });
// Periodic check every 5 minutes as fallback
setInterval(async () => {
const now = Date.now();
log('Periodic re-sync check');
await generate();
}, 5 * 60 * 1000);
}
main().catch((err) => {
log(`FATAL: ${err.message}`);
process.exit(1);
});