mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +00:00
* perf(usage): resolve each cwd's worktree once per scan Codex and OpenCode attribution ran the worktree containment search for every parsed event, so a cold scan cost events x worktrees. On 745 MB of real rollouts (~20k events) that is 1.2s with 0 worktrees, 5.0s with 100, 12.8s with 300 and 39.8s with 1000; a full corpus with hundreds of remembered worktrees is where the STA-7724 reparse burned minutes of main-thread CPU. A scan holds only a few hundred distinct cwds, so both scanners now build one memoized resolver per scan and thread it through parsing instead of passing the worktree list to every event. * refactor(usage): make the worktree resolver own canonicalization `createUsageWorktreeResolver` now takes raw worktree refs and canonicalizes them itself, so each scanner has one entry point and neither keeps a private `buildWorktreesWithCanonicalPaths` or `canonicalizePath`. The resolver unit test counts comparisons through the same `areWorktreePathsEqual` mock the scanner-level test uses instead of a property getter.
65 lines
1.8 KiB
TypeScript
65 lines
1.8 KiB
TypeScript
import { normalizeComparablePath } from '../usage/usage-path-comparison'
|
|
import type { UsageScanWorktreeRef } from '../usage/usage-provider-contract'
|
|
import type { UsageWorktreeResolver } from '../usage/usage-worktree-resolver'
|
|
import type { OpenCodeUsageAttributedEvent, OpenCodeUsageParsedEvent } from './types'
|
|
|
|
export type OpenCodeUsageWorktreeRef = 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}`
|
|
}
|
|
|
|
export async function attributeOpenCodeUsageEvent(
|
|
event: OpenCodeUsageParsedEvent,
|
|
resolveWorktree: UsageWorktreeResolver
|
|
): Promise<OpenCodeUsageAttributedEvent | 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 = resolveWorktree(event.cwd)
|
|
if (worktree) {
|
|
repoId = worktree.repoId
|
|
worktreeId = worktree.worktreeId
|
|
projectKey = `worktree:${worktree.worktreeId}`
|
|
projectLabel = worktree.displayName
|
|
} else {
|
|
projectKey = `cwd:${normalizeComparablePath(event.cwd)}`
|
|
}
|
|
}
|
|
|
|
return {
|
|
...event,
|
|
day,
|
|
projectKey,
|
|
projectLabel,
|
|
repoId,
|
|
worktreeId
|
|
}
|
|
}
|