feat(hub): persist agent list to disk for restart recovery
Store agent records in ~/.super-multica/agents/agents.json. Hub restores agents on startup and updates the file on create/delete. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
36e9acb35f
commit
c6d0476679
2 changed files with 67 additions and 1 deletions
47
src/hub/agent-store.ts
Normal file
47
src/hub/agent-store.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { DATA_DIR } from "../shared/index.js";
|
||||
|
||||
export interface AgentRecord {
|
||||
id: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
const AGENTS_DIR = join(DATA_DIR, "agents");
|
||||
const AGENTS_FILE = join(AGENTS_DIR, "agents.json");
|
||||
|
||||
function ensureDir(): void {
|
||||
if (!existsSync(AGENTS_DIR)) {
|
||||
mkdirSync(AGENTS_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
export function loadAgentRecords(): AgentRecord[] {
|
||||
if (!existsSync(AGENTS_FILE)) return [];
|
||||
try {
|
||||
const content = readFileSync(AGENTS_FILE, "utf-8");
|
||||
return JSON.parse(content) as AgentRecord[];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function saveAgentRecords(records: AgentRecord[]): void {
|
||||
ensureDir();
|
||||
writeFileSync(AGENTS_FILE, JSON.stringify(records, null, 2), "utf-8");
|
||||
}
|
||||
|
||||
export function addAgentRecord(record: AgentRecord): void {
|
||||
const records = loadAgentRecords();
|
||||
if (records.some((r) => r.id === record.id)) return;
|
||||
records.push(record);
|
||||
saveAgentRecords(records);
|
||||
}
|
||||
|
||||
export function removeAgentRecord(id: string): void {
|
||||
const records = loadAgentRecords();
|
||||
const filtered = records.filter((r) => r.id !== id);
|
||||
if (filtered.length !== records.length) {
|
||||
saveAgentRecords(filtered);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import type { ConnectionState } from "../shared/gateway-sdk/types.js";
|
|||
import { AsyncAgent } from "../agent/async-agent.js";
|
||||
import { getDeviceId } from "./device.js";
|
||||
import { GatewayClient } from "../shared/gateway-sdk/client.js";
|
||||
import { loadAgentRecords, addAgentRecord, removeAgentRecord } from "./agent-store.js";
|
||||
|
||||
export class Hub {
|
||||
private readonly agents = new Map<string, AsyncAgent>();
|
||||
|
|
@ -23,6 +24,18 @@ export class Hub {
|
|||
this.deviceId = getDeviceId();
|
||||
this.client = this.createClient(this.url);
|
||||
this.client.connect();
|
||||
this.restoreAgents();
|
||||
}
|
||||
|
||||
/** Restore agents from persistent storage */
|
||||
private restoreAgents(): void {
|
||||
const records = loadAgentRecords();
|
||||
for (const record of records) {
|
||||
this.createAgent(record.id, { persist: false });
|
||||
}
|
||||
if (records.length > 0) {
|
||||
console.log(`[Hub] Restored ${records.length} agent(s)`);
|
||||
}
|
||||
}
|
||||
|
||||
private createClient(url: string): GatewayClient {
|
||||
|
|
@ -82,7 +95,7 @@ export class Hub {
|
|||
}
|
||||
|
||||
/** Create new Agent, or rebuild with existing ID */
|
||||
createAgent(id?: string): AsyncAgent {
|
||||
createAgent(id?: string, options?: { persist?: boolean }): AsyncAgent {
|
||||
if (id) {
|
||||
const existing = this.agents.get(id);
|
||||
if (existing && !existing.closed) {
|
||||
|
|
@ -93,6 +106,11 @@ export class Hub {
|
|||
const agent = new AsyncAgent({ sessionId: id });
|
||||
this.agents.set(agent.sessionId, agent);
|
||||
|
||||
// Persist to agent store (skip during restore to avoid duplicates)
|
||||
if (options?.persist !== false) {
|
||||
addAgentRecord({ id: agent.sessionId, createdAt: Date.now() });
|
||||
}
|
||||
|
||||
// Internally consume messages produced by agent
|
||||
void this.consumeAgent(agent);
|
||||
|
||||
|
|
@ -130,6 +148,7 @@ export class Hub {
|
|||
agent.close();
|
||||
this.agents.delete(id);
|
||||
this.agentSenders.delete(id);
|
||||
removeAgentRecord(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue