mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 08:02:31 +00:00
The three usage scanners and their stores, plus the renderer usage-overview model, each carried a file-level `eslint-disable max-lines` and had grown to 338-769 counted lines against a 300-line budget. AGENTS.md calls for splitting rather than suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all seven suppressions and prunes their entries (341 -> 334). Each file is cut along the seams it already had -- and that several of the suppression comments named out loud: filesystem discovery / record parsing / attribution / aggregation for the scanners, and pricing policy / scope filters / rollups / session rows / automation attribution for the stores. Pure move, no behavior change. Code is relocated verbatim; the only edits are import plumbing and, where a private class method became a free function, the mechanical `this.state` -> `state` parameter threading. Every converted call site passes `this.state` at call time and the automation path takes a live `getState: () => this.state` getter, so no state is snapshotted. No barrel exports: each new module owns real logic and importers point at the owner. Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green (remaining failures are pre-existing load flakes in untouched files, each green when re-run serially), no import cycles among the 64 affected modules, and a statement-level diff of every split confirms the moves are verbatim.
70 lines
2.4 KiB
TypeScript
70 lines
2.4 KiB
TypeScript
import type {
|
|
CodexUsageRange,
|
|
CodexUsageScope,
|
|
CodexUsageSessionRow
|
|
} from '../../shared/codex-usage-types'
|
|
import type { CodexUsagePersistedState } from './types'
|
|
import { getFilteredSessions, getScopedSessionPrimaryModel } from './codex-usage-scope-filters'
|
|
|
|
export function buildRecentSessions(
|
|
state: CodexUsagePersistedState,
|
|
scope: CodexUsageScope,
|
|
range: CodexUsageRange,
|
|
limit = 12
|
|
): CodexUsageSessionRow[] {
|
|
return getFilteredSessions(state, scope, range)
|
|
.slice(0, limit)
|
|
.map((session) => {
|
|
const matchingLocations = session.locationBreakdown.filter((entry) =>
|
|
scope === 'all' ? true : entry.worktreeId !== null
|
|
)
|
|
const scopedLocations =
|
|
matchingLocations.length > 0 ? matchingLocations : session.locationBreakdown
|
|
const totals = scopedLocations.reduce(
|
|
(acc, entry) => {
|
|
acc.events += entry.eventCount
|
|
acc.inputTokens += entry.inputTokens
|
|
acc.cachedInputTokens += entry.cachedInputTokens
|
|
acc.outputTokens += entry.outputTokens
|
|
acc.reasoningOutputTokens += entry.reasoningOutputTokens
|
|
acc.totalTokens += entry.totalTokens
|
|
acc.hasInferredPricing ||= entry.hasInferredPricing
|
|
return acc
|
|
},
|
|
{
|
|
events: 0,
|
|
inputTokens: 0,
|
|
cachedInputTokens: 0,
|
|
outputTokens: 0,
|
|
reasoningOutputTokens: 0,
|
|
totalTokens: 0,
|
|
hasInferredPricing: false
|
|
}
|
|
)
|
|
const durationMinutes = Math.max(
|
|
0,
|
|
Math.round(
|
|
(new Date(session.lastTimestamp).getTime() - new Date(session.firstTimestamp).getTime()) /
|
|
60_000
|
|
)
|
|
)
|
|
return {
|
|
sessionId: session.sessionId,
|
|
lastActiveAt: session.lastTimestamp,
|
|
durationMinutes,
|
|
projectLabel:
|
|
scopedLocations.length > 1
|
|
? 'Multiple locations'
|
|
: (scopedLocations[0]?.projectLabel ?? session.primaryProjectLabel),
|
|
model: getScopedSessionPrimaryModel(session, scope),
|
|
events: totals.events,
|
|
inputTokens: totals.inputTokens,
|
|
cachedInputTokens: totals.cachedInputTokens,
|
|
outputTokens: totals.outputTokens,
|
|
reasoningOutputTokens: totals.reasoningOutputTokens,
|
|
totalTokens: totals.totalTokens,
|
|
hasInferredPricing: session.hasInferredPricing || totals.hasInferredPricing
|
|
}
|
|
})
|
|
}
|