sync-openclaw-agent-feed.mjs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import fs from "node:fs/promises";
  2. import path from "node:path";
  3. const allowedStatuses = new Set(["working", "idle", "warning", "offline"]);
  4. const allowedAvatarKinds = new Set([
  5. "female-analyst",
  6. "male-ops",
  7. "female-researcher",
  8. "male-dispatcher",
  9. "female-observer",
  10. "male-maintainer"
  11. ]);
  12. function usage() {
  13. console.error("Usage: node scripts/sync-openclaw-agent-feed.mjs <source-json> [target-json]");
  14. process.exit(1);
  15. }
  16. function assertString(value, field, agentId) {
  17. if (typeof value !== "string" || value.trim() === "") {
  18. throw new Error(`Invalid ${field} for agent ${agentId}`);
  19. }
  20. }
  21. function assertNumber(value, field, agentId) {
  22. if (typeof value !== "number" || Number.isNaN(value)) {
  23. throw new Error(`Invalid ${field} for agent ${agentId}`);
  24. }
  25. }
  26. function normalizeAgent(agent) {
  27. const agentId = typeof agent.id === "string" ? agent.id : "unknown";
  28. assertString(agent.id, "id", agentId);
  29. assertString(agent.name, "name", agentId);
  30. assertString(agent.role, "role", agentId);
  31. assertString(agent.host, "host", agentId);
  32. assertString(agent.owner, "owner", agentId);
  33. assertString(agent.currentTask, "currentTask", agentId);
  34. assertString(agent.taskId, "taskId", agentId);
  35. assertString(agent.taskStage, "taskStage", agentId);
  36. assertString(agent.uptime, "uptime", agentId);
  37. assertString(agent.lastHeartbeat, "lastHeartbeat", agentId);
  38. assertString(agent.updatedAt, "updatedAt", agentId);
  39. assertString(agent.lastOutput, "lastOutput", agentId);
  40. assertNumber(agent.queueDepth, "queueDepth", agentId);
  41. assertNumber(agent.todayCompleted, "todayCompleted", agentId);
  42. if (!allowedStatuses.has(agent.status)) {
  43. throw new Error(`Invalid status for agent ${agentId}`);
  44. }
  45. if (agent.avatarKind && !allowedAvatarKinds.has(agent.avatarKind)) {
  46. throw new Error(`Invalid avatarKind for agent ${agentId}`);
  47. }
  48. return {
  49. id: agent.id,
  50. name: agent.name,
  51. role: agent.role,
  52. avatarKind: agent.avatarKind ?? "male-dispatcher",
  53. status: agent.status,
  54. statusLabel: typeof agent.statusLabel === "string" && agent.statusLabel ? agent.statusLabel : undefined,
  55. host: agent.host,
  56. owner: agent.owner,
  57. currentTask: agent.currentTask,
  58. taskId: agent.taskId,
  59. taskStage: agent.taskStage,
  60. queueDepth: agent.queueDepth,
  61. todayCompleted: agent.todayCompleted,
  62. uptime: agent.uptime,
  63. lastHeartbeat: agent.lastHeartbeat,
  64. updatedAt: agent.updatedAt,
  65. lastOutput: agent.lastOutput,
  66. lastError: typeof agent.lastError === "string" ? agent.lastError : undefined,
  67. tags: Array.isArray(agent.tags) ? agent.tags.filter((tag) => typeof tag === "string") : []
  68. };
  69. }
  70. async function main() {
  71. const [, , sourceArg, targetArg] = process.argv;
  72. if (!sourceArg) {
  73. usage();
  74. }
  75. const sourcePath = path.resolve(sourceArg);
  76. const targetPath = path.resolve(targetArg ?? path.join(process.cwd(), "storage", "agents", "openclaw-agents.json"));
  77. const raw = await fs.readFile(sourcePath, "utf8");
  78. const parsed = JSON.parse(raw);
  79. if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.agents)) {
  80. throw new Error("Feed JSON must contain an agents array");
  81. }
  82. const normalized = {
  83. fetchedAt: typeof parsed.fetchedAt === "string" ? parsed.fetchedAt : new Date().toISOString(),
  84. agents: parsed.agents.map(normalizeAgent)
  85. };
  86. await fs.mkdir(path.dirname(targetPath), { recursive: true });
  87. const tempPath = `${targetPath}.tmp`;
  88. await fs.writeFile(tempPath, JSON.stringify(normalized, null, 2), "utf8");
  89. await fs.rename(tempPath, targetPath);
  90. console.log(`OpenClaw agent feed synced to ${targetPath}`);
  91. console.log(`Agents: ${normalized.agents.length}`);
  92. }
  93. main().catch((error) => {
  94. console.error(error instanceof Error ? error.message : String(error));
  95. process.exit(1);
  96. });