farming-code 2.2.19 → 2.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/backend/server.js CHANGED
@@ -744,8 +744,12 @@ app.get(routePath(BASE_PATH, '/api/claude/settings'), (req, res) => {
744
744
  app.get(routePath(BASE_PATH, '/api/usage'), async (req, res) => {
745
745
  try {
746
746
  const fresh = req.query.fresh === '1';
747
+ const live = req.query.live === '1';
747
748
  if (fresh) usageMonitor.invalidateDailyCache();
748
- const usage = await usageSummaryCache.get('summary', fresh ? { force: true } : {});
749
+ const usage = await usageSummaryCache.get(
750
+ 'summary',
751
+ fresh ? { force: true } : live ? { maxAgeMs: 15_000 } : {},
752
+ );
749
753
  res.json({ usage });
750
754
  } catch (error) {
751
755
  res.status(500).json({ error: error.message || 'Failed to read usage information' });
@@ -23,6 +23,7 @@ class TerminalScreenWorkerPool {
23
23
  this.waiters = [];
24
24
  this.readyWaiters = [];
25
25
  this.pendingStarts = 0;
26
+ this.startTasks = new Set();
26
27
  this.consecutiveStartFailures = 0;
27
28
  this.retryDelayMs = Number.isFinite(options.retryDelayMs)
28
29
  ? Math.max(0, Number(options.retryDelayMs))
@@ -91,13 +92,12 @@ class TerminalScreenWorkerPool {
91
92
  startWorker() {
92
93
  this.pendingStarts += 1;
93
94
  let failed = false;
94
- this.createReadyWorker()
95
+ const startTask = this.createReadyWorker()
95
96
  .then((worker) => {
96
97
  this.pendingStarts -= 1;
97
98
  this.consecutiveStartFailures = 0;
98
99
  if (this.disposed) {
99
- worker.dispose().catch(() => {});
100
- return;
100
+ return worker.dispose().catch(() => {});
101
101
  }
102
102
  this.deliverWorker(worker);
103
103
  })
@@ -122,6 +122,11 @@ class TerminalScreenWorkerPool {
122
122
  this.ensureCapacity();
123
123
  }
124
124
  });
125
+ this.startTasks.add(startTask);
126
+ startTask.then(
127
+ () => this.startTasks.delete(startTask),
128
+ () => this.startTasks.delete(startTask),
129
+ );
125
130
  }
126
131
 
127
132
  scheduleCapacityRetry() {
@@ -197,8 +202,8 @@ class TerminalScreenWorkerPool {
197
202
  }
198
203
 
199
204
  async dispose() {
200
- if (this.disposed) {
201
- return;
205
+ if (this.disposePromise) {
206
+ return this.disposePromise;
202
207
  }
203
208
 
204
209
  this.disposed = true;
@@ -210,7 +215,11 @@ class TerminalScreenWorkerPool {
210
215
  this.waiters.splice(0).forEach(({ reject }) => reject(error));
211
216
  this.readyWaiters.splice(0).forEach(({ reject }) => reject(error));
212
217
  const workers = this.idle.splice(0);
213
- await Promise.allSettled(workers.map(worker => worker.dispose()));
218
+ this.disposePromise = Promise.allSettled([
219
+ ...workers.map(worker => worker.dispose()),
220
+ ...this.startTasks,
221
+ ]).then(() => undefined);
222
+ return this.disposePromise;
214
223
  }
215
224
  }
216
225
 
@@ -10,6 +10,8 @@ const execFileAsync = promisify(execFile);
10
10
  const USAGE_WINDOW_MS = 5 * 60 * 1000;
11
11
  const USAGE_TIMELINE_WINDOW_MS = 24 * 60 * 60 * 1000;
12
12
  const USAGE_TIMELINE_BUCKET_COUNT = 24;
13
+ const USAGE_LIVE_TIMELINE_WINDOW_MS = 60 * 60 * 1000;
14
+ const USAGE_LIVE_TIMELINE_BUCKET_COUNT = 60;
13
15
  const USAGE_DAILY_DAYS = 52 * 7;
14
16
  const USAGE_DAILY_CACHE_MS = 5 * 60 * 1000;
15
17
  const USAGE_LIVE_DAY_CACHE_MS = 5 * 1000;
@@ -1020,6 +1022,12 @@ class UsageMonitor {
1020
1022
  windowMs: historyWindowMs,
1021
1023
  alignToBucket: true,
1022
1024
  });
1025
+ const liveTimeline = buildUsageTimeline(history.providerEvents, {
1026
+ now,
1027
+ windowMs: USAGE_LIVE_TIMELINE_WINDOW_MS,
1028
+ bucketCount: USAGE_LIVE_TIMELINE_BUCKET_COUNT,
1029
+ alignToBucket: true,
1030
+ });
1023
1031
  const openCodeCoverage = history.coverage.find(entry => entry.provider === 'opencode');
1024
1032
  const qoderCoverage = history.coverage.find(entry => entry.provider === 'qoder');
1025
1033
 
@@ -1027,6 +1035,7 @@ class UsageMonitor {
1027
1035
  sampledAt: now,
1028
1036
  windowMs,
1029
1037
  timeline,
1038
+ liveTimeline,
1030
1039
  daily: history.daily,
1031
1040
  providers: [
1032
1041
  {
@@ -1099,6 +1108,8 @@ module.exports = {
1099
1108
  USAGE_WINDOW_MS,
1100
1109
  USAGE_TIMELINE_WINDOW_MS,
1101
1110
  USAGE_TIMELINE_BUCKET_COUNT,
1111
+ USAGE_LIVE_TIMELINE_WINDOW_MS,
1112
+ USAGE_LIVE_TIMELINE_BUCKET_COUNT,
1102
1113
  USAGE_DAILY_DAYS,
1103
1114
  USAGE_DAILY_CACHE_MS,
1104
1115
  USAGE_LIVE_DAY_CACHE_MS,