import { getActiveTimeCounter as getActiveTimeCounterImpl } from '../bootstrap/state.js'type ActivityManagerOptions = {getNow?: () => numbergetActiveTimeCounter?: typeof getActiveTimeCounterImpl}/*** ActivityManager handles generic activity tracking for both user and CLI operations.* It automatically deduplicates overlapping activities and provides separate metrics* for user vs CLI active time.*/export class ActivityManager {private activeOperations = new Set<string>()private lastUserActivityTime: number = 0 // Start with 0 to indicate no activity yetprivate lastCLIRecordedTime: numberprivate isCLIActive: boolean = falseprivate readonly USER_ACTIVITY_TIMEOUT_MS = 5000 // 5 secondsprivate readonly getNow: () => numberprivate readonly getActiveTimeCounter: typeof getActiveTimeCounterImplprivate static instance: ActivityManager | null = nullconstructor(options?: ActivityManagerOptions) {this.getNow = options?.getNow ?? (() => Date.now())this.getActiveTimeCounter =options?.getActiveTimeCounter ?? getActiveTimeCounterImplthis.lastCLIRecordedTime = this.getNow()}static getInstance(): ActivityManager {if (!ActivityManager.instance) {ActivityManager.instance = new ActivityManager()}return ActivityManager.instance}/*** Reset the singleton instance (for testing purposes)*/static resetInstance(): void {ActivityManager.instance = null}/*** Create a new instance with custom options (for testing purposes)*/static createInstance(options?: ActivityManagerOptions): ActivityManager {ActivityManager.instance = new ActivityManager(options)return ActivityManager.instance}/*** Called when user interacts with the CLI (typing, commands, etc.)*/recordUserActivity(): void {// Don't record user time if CLI is active (CLI takes precedence)if (!this.isCLIActive && this.lastUserActivityTime !== 0) {const now = this.getNow()const timeSinceLastActivity = (now - this.lastUserActivityTime) / 1000if (timeSinceLastActivity > 0) {const activeTimeCounter = this.getActiveTimeCounter()if (activeTimeCounter) {const timeoutSeconds = this.USER_ACTIVITY_TIMEOUT_MS / 1000// Only record time if within the timeout windowif (timeSinceLastActivity < timeoutSeconds) {activeTimeCounter.add(timeSinceLastActivity, { type: 'user' })}}}}// Update the last user activity timestampthis.lastUserActivityTime = this.getNow()}/*** Starts tracking CLI activity (tool execution, AI response, etc.)*/startCLIActivity(operationId: string): void {// If operation already exists, it likely means the previous one didn't clean up// properly (e.g., component crashed/unmounted without calling end). Force cleanup// to avoid overestimating time - better to underestimate than overestimate.if (this.activeOperations.has(operationId)) {this.endCLIActivity(operationId)}const wasEmpty = this.activeOperations.size === 0this.activeOperations.add(operationId)if (wasEmpty) {this.isCLIActive = truethis.lastCLIRecordedTime = this.getNow()}}/*** Stops tracking CLI activity*/endCLIActivity(operationId: string): void {this.activeOperations.delete(operationId)if (this.activeOperations.size === 0) {// Last operation ended - CLI becoming inactive// Record the CLI time before switching to inactiveconst now = this.getNow()const timeSinceLastRecord = (now - this.lastCLIRecordedTime) / 1000if (timeSinceLastRecord > 0) {const activeTimeCounter = this.getActiveTimeCounter()if (activeTimeCounter) {activeTimeCounter.add(timeSinceLastRecord, { type: 'cli' })}}this.lastCLIRecordedTime = nowthis.isCLIActive = false}}/*** Convenience method to track an async operation automatically (mainly for testing/debugging)*/async trackOperation<T>(operationId: string,fn: () => Promise<T>,): Promise<T> {this.startCLIActivity(operationId)try {return await fn()} finally {this.endCLIActivity(operationId)}}/*** Gets current activity states (mainly for testing/debugging)*/getActivityStates(): {isUserActive: booleanisCLIActive: booleanactiveOperationCount: number} {const now = this.getNow()const timeSinceUserActivity = (now - this.lastUserActivityTime) / 1000const isUserActive =timeSinceUserActivity < this.USER_ACTIVITY_TIMEOUT_MS / 1000return {isUserActive,isCLIActive: this.isCLIActive,activeOperationCount: this.activeOperations.size,}}}// Export singleton instanceexport const activityManager = ActivityManager.getInstance()
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。