Files
orca/src/main/codex-usage/codex-usage-event-attribution.ts
T
Neil 97b71c2285 refactor(usage): split AI-usage scanners and stores under the max-lines budget (#14668)
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.
2026-08-15 18:33:33 -07:00

114 lines
3.6 KiB
TypeScript

import { win32, posix } from 'node:path'
import { areWorktreePathsEqual } from '../ipc/worktree-logic'
import {
looksLikeWindowsPath,
normalizeComparablePath,
normalizeFsPath
} from '../usage/usage-path-comparison'
import type { UsageScanWorktreeRef } from '../usage/usage-provider-contract'
import type { CodexUsageAttributedEvent, CodexUsageParsedEvent } from './types'
export type CodexUsageWorktreeRef = UsageScanWorktreeRef
function getDefaultProjectLabel(cwd: string | null): string {
if (!cwd) {
return 'Unknown location'
}
const parts = cwd.replace(/\\/g, '/').split('/').filter(Boolean)
if (parts.length >= 2) {
return parts.slice(-2).join('/')
}
return parts.at(-1) ?? cwd
}
function localDayFromTimestamp(timestamp: string): string | null {
const parsed = new Date(timestamp)
if (Number.isNaN(parsed.getTime())) {
return null
}
const year = parsed.getFullYear()
const month = String(parsed.getMonth() + 1).padStart(2, '0')
const day = String(parsed.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
function isContainingPath(candidatePath: string, targetPath: string): boolean {
const useWin32 = looksLikeWindowsPath(candidatePath) || looksLikeWindowsPath(targetPath)
const relativePath = useWin32
? win32.relative(candidatePath, targetPath)
: posix.relative(candidatePath, targetPath)
if (!relativePath) {
return true
}
// Why: on Windows, `path.relative('C:\\repo', 'D:\\other')` returns an
// absolute `D:\\other` path instead of a `..`-prefixed relative. Treating
// that as "contained" would attribute off-drive Codex usage to the wrong
// Orca worktree.
const isAbsoluteRelative = useWin32
? win32.isAbsolute(relativePath)
: posix.isAbsolute(relativePath)
const parentPrefix = useWin32 ? `..${win32.sep}` : `..${posix.sep}`
// Why: `..name` is a valid child path; only `..` and `../...` escape.
return (
!isAbsoluteRelative &&
relativePath !== '..' &&
!relativePath.startsWith(parentPrefix) &&
relativePath !== '.'
)
}
function findContainingWorktree(
cwd: string,
worktrees: (CodexUsageWorktreeRef & { canonicalPath: string })[]
): CodexUsageWorktreeRef | null {
const normalizedCwd = normalizeFsPath(cwd)
for (const worktree of worktrees) {
if (areWorktreePathsEqual(worktree.canonicalPath, normalizedCwd)) {
return worktree
}
if (isContainingPath(worktree.canonicalPath, normalizedCwd)) {
return worktree
}
}
return null
}
export async function attributeCodexUsageEvent(
event: CodexUsageParsedEvent,
worktrees: (CodexUsageWorktreeRef & { canonicalPath: string })[]
): Promise<CodexUsageAttributedEvent | null> {
const day = localDayFromTimestamp(event.timestamp)
if (!day) {
return null
}
let repoId: string | null = null
let worktreeId: string | null = null
let projectKey = 'unscoped'
let projectLabel = getDefaultProjectLabel(event.cwd)
if (event.cwd) {
const worktree = findContainingWorktree(event.cwd, worktrees)
if (worktree) {
repoId = worktree.repoId
worktreeId = worktree.worktreeId
projectKey = `worktree:${worktree.worktreeId}`
projectLabel = worktree.displayName
} else {
// Why: all-local mode should still collapse repeated off-Orca sessions by
// location, but those keys must normalize slash/case differences so the
// same folder does not fragment into multiple "projects" across platforms.
projectKey = `cwd:${normalizeComparablePath(event.cwd)}`
}
}
return {
...event,
day,
projectKey,
projectLabel,
repoId,
worktreeId
}
}