Files
EJClaw/src/token-rotation.ts
Eyejoker 3467e245d5 feat: auto memory pipeline — host-driven Memento recall/reflect
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>
2026-03-24 03:48:36 +09:00

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);
}