MCPcopy Index your code
hub / github.com/BlockRunAI/ClawRouter / FileSpendControlStorage

Class FileSpendControlStorage

src/spend-control.ts:69–131  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

67}
68
69export class FileSpendControlStorage implements SpendControlStorage {
70 private readonly spendingFile: string;
71
72 constructor() {
73 this.spendingFile = path.join(WALLET_DIR, "spending.json");
74 }
75
76 load(): { limits: SpendLimits; history: SpendRecord[] } | null {
77 try {
78 if (fs.existsSync(this.spendingFile)) {
79 const data = JSON.parse(readTextFileSync(this.spendingFile));
80 const rawLimits = data.limits ?? {};
81 const rawHistory = data.history ?? [];
82
83 const limits: SpendLimits = {};
84 for (const key of ["perRequest", "hourly", "daily", "session"] as const) {
85 const val = rawLimits[key];
86 if (typeof val === "number" && val > 0 && Number.isFinite(val)) {
87 limits[key] = val;
88 }
89 }
90
91 const history: SpendRecord[] = [];
92 if (Array.isArray(rawHistory)) {
93 for (const r of rawHistory) {
94 if (
95 typeof r?.timestamp === "number" &&
96 typeof r?.amount === "number" &&
97 Number.isFinite(r.timestamp) &&
98 Number.isFinite(r.amount) &&
99 r.amount >= 0
100 ) {
101 history.push({
102 timestamp: r.timestamp,
103 amount: r.amount,
104 model: typeof r.model === "string" ? r.model : undefined,
105 action: typeof r.action === "string" ? r.action : undefined,
106 });
107 }
108 }
109 }
110
111 return { limits, history };
112 }
113 } catch (err) {
114 console.error(`[ClawRouter] Failed to load spending data, starting fresh: ${err}`);
115 }
116 return null;
117 }
118
119 save(data: { limits: SpendLimits; history: SpendRecord[] }): void {
120 try {
121 if (!fs.existsSync(WALLET_DIR)) {
122 fs.mkdirSync(WALLET_DIR, { recursive: true, mode: 0o700 });
123 }
124 fs.writeFileSync(this.spendingFile, JSON.stringify(data, null, 2), {
125 mode: 0o600,
126 });

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected