Add automatic memory integration so EJClaw host directly calls Memento MCP for recall at session start and reflect on compact, removing reliance on agent voluntary tool use. Stage 1: New sessions get room memory briefing injected into system prompt (CLAUDE.md / AGENTS.md) via host-side MCP client. Stage 2: PreCompact hook automatically calls reflect + remember to persist session summaries as room-memory fragments. Also restores missing source files (token-rotation, codex-token-rotation, claude-usage exports) to fix full build from source. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
import fs from 'fs';
|
|
import os from 'os';
|
|
import path from 'path';
|
|
|
|
const HOST_CLAUDE_DIR = path.join(os.homedir(), '.claude');
|
|
const CLAUDE_ACCOUNTS_DIR = path.join(os.homedir(), '.claude-accounts');
|
|
|
|
let activeIndex = 0;
|
|
const rateLimitedAccounts = new Set<number>();
|
|
|
|
function listClaudeAccounts(): string[] {
|
|
if (!fs.existsSync(CLAUDE_ACCOUNTS_DIR)) return [];
|
|
|
|
return fs
|
|
.readdirSync(CLAUDE_ACCOUNTS_DIR, { withFileTypes: true })
|
|
.filter((entry) => entry.isDirectory() && /^\d+$/.test(entry.name))
|
|
.sort((a, b) => Number(a.name) - Number(b.name))
|
|
.map((entry) => path.join(CLAUDE_ACCOUNTS_DIR, entry.name));
|
|
}
|
|
|
|
function copyIfExists(src: string, dst: string): void {
|
|
if (!fs.existsSync(src)) return;
|
|
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
fs.copyFileSync(src, dst);
|
|
}
|
|
|
|
function syncHostClaudeAccount(accountDir: string): void {
|
|
copyIfExists(
|
|
path.join(accountDir, '.credentials.json'),
|
|
path.join(HOST_CLAUDE_DIR, '.credentials.json'),
|
|
);
|
|
copyIfExists(path.join(accountDir, 'settings.json'), path.join(HOST_CLAUDE_DIR, 'settings.json'));
|
|
}
|
|
|
|
export function getTokenCount(): number {
|
|
const accounts = listClaudeAccounts();
|
|
return accounts.length > 0 ? accounts.length : 1;
|
|
}
|
|
|
|
export function rotateToken(_reason?: string): boolean {
|
|
const accounts = listClaudeAccounts();
|
|
if (accounts.length <= 1) return false;
|
|
|
|
rateLimitedAccounts.add(activeIndex);
|
|
|
|
const candidates = accounts.map((_, index) => index);
|
|
const nextIndex =
|
|
candidates.find(
|
|
(index) => index !== activeIndex && !rateLimitedAccounts.has(index),
|
|
) ??
|
|
candidates.find((index) => index !== activeIndex);
|
|
|
|
if (nextIndex === undefined) return false;
|
|
|
|
syncHostClaudeAccount(accounts[nextIndex]);
|
|
activeIndex = nextIndex;
|
|
return true;
|
|
}
|
|
|
|
export function markTokenHealthy(): void {
|
|
rateLimitedAccounts.delete(activeIndex);
|
|
}
|