@hackersbaby/plugin 0.5.4 → 0.5.5

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/config.ts","../../../../shared/dist/types.js","../../../../shared/src/scoring.ts","../../../../shared/src/anticheat.ts","../../../../shared/src/index.ts","../../../src/api-client.ts","../../../src/queue.ts","../../../src/collector.ts","../../../src/hooks/shared/utils.ts","../../../src/hooks/shared/prompt-submit.ts","../../../src/hooks/cursor/prompt-submit.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport os from 'os';\n\nexport const CONFIG_DIR = path.join(os.homedir(), '.hackersbaby');\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\nexport const QUEUE_FILE = path.join(CONFIG_DIR, 'queue.jsonl');\n\nexport type Source = 'claude' | 'cursor';\n\nexport interface PluginConfig {\n token: string;\n refresh_token: string;\n user_id: string;\n server: string;\n targets?: Source[];\n preferences: {\n dashboard_port: number;\n batch_interval_ms: number;\n };\n}\n\nexport function sessionStateFile(source: Source): string {\n return path.join(CONFIG_DIR, `active-session-${source}.json`);\n}\n\nexport function ensureConfigDir(): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\nexport function loadConfig(): PluginConfig | null {\n try {\n if (!fs.existsSync(CONFIG_FILE)) return null;\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\n return JSON.parse(raw) as PluginConfig;\n } catch {\n return null;\n }\n}\n\nexport function saveConfig(config: PluginConfig): void {\n ensureConfigDir();\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');\n}\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n//# sourceMappingURL=types.js.map","import type { ActionType } from './types';\n\nexport const SESSION_BASE_POINT_CAP = 20000;\nexport const TOOL_CALLS_PER_HOUR_CAP = 4000;\nexport const MIN_SESSION_TOKENS = 2000;\n\nexport const BASE_POINTS: Record<ActionType, { perUnit: number; unitSize: number }> = {\n token_input: { perUnit: 1, unitSize: 1000 },\n token_output: { perUnit: 2, unitSize: 1000 },\n tool_call: { perUnit: 5, unitSize: 1 },\n file_created: { perUnit: 15, unitSize: 1 },\n file_edited: { perUnit: 10, unitSize: 1 },\n commit: { perUnit: 50, unitSize: 1 },\n session_completed: { perUnit: 100, unitSize: 1 },\n deployment: { perUnit: 200, unitSize: 1 },\n prompt_submitted: { perUnit: 15, unitSize: 1 },\n subagent_spawned: { perUnit: 40, unitSize: 1 },\n context_compacted: { perUnit: 30, unitSize: 1 },\n stop_response: { perUnit: 10, unitSize: 1 },\n};\n\nexport const DEPLOY_PATTERNS = [\n /^vercel\\s+(--prod|deploy)/, /^netlify\\s+deploy/, /^fly\\s+deploy/, /^railway\\s+up/,\n /^git\\s+push\\s+\\S+\\s+(main|master|production)/,\n /^(npm|yarn|pnpm)\\s+run\\s+deploy/, /^(yarn|pnpm)\\s+deploy/,\n];\n\nexport function getBasePoints(actionType: ActionType, value: number): number {\n const config = BASE_POINTS[actionType];\n return Math.floor(value / config.unitSize) * config.perUnit;\n}\n\ninterface MultiplierInput { streak_days: number; commit_quality: boolean; language_count: number; }\n\nexport function calculateMultiplier(input: MultiplierInput): number {\n let streak = 1.0;\n if (input.streak_days >= 30) streak = 3.0;\n else if (input.streak_days >= 7) streak = 2.0;\n else if (input.streak_days >= 3) streak = 1.5;\n const quality = input.commit_quality ? 1.5 : 1.0;\n const diversity = input.language_count >= 3 ? 1.2 : 1.0;\n return streak * quality * diversity;\n}\n\nexport function calculateSessionScore(events: Array<{ action_type: ActionType; value: number }>, multiplier: number): number {\n let totalBase = 0;\n for (const e of events) totalBase += getBasePoints(e.action_type, e.value);\n return Math.floor(Math.min(totalBase, SESSION_BASE_POINT_CAP) * multiplier);\n}\n\nexport function isDeployCommand(command: string): boolean {\n return DEPLOY_PATTERNS.some((p) => p.test(command.trim()));\n}\n","/**\n * Anti-cheat cryptographic primitives.\n * Shared between plugin (client) and server.\n *\n * - Event chain: each event hash includes the previous, creating a tamper-evident chain\n * - Batch HMAC: the entire batch is signed with a server-issued session secret\n */\n\nimport { createHmac, createHash } from 'crypto';\n\n/** Hash a single event, chaining it to the previous hash */\nexport function hashEvent(\n event: { action_type: string; value: number; timestamp: string },\n prevHash: string,\n): string {\n const payload = `${prevHash}|${event.action_type}|${event.value}|${event.timestamp}`;\n return createHash('sha256').update(payload).digest('hex').slice(0, 16);\n}\n\n/** Compute the chain hash for an array of events */\nexport function computeChainHash(\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n initialHash: string = '0000000000000000',\n): string {\n let hash = initialHash;\n for (const event of events) {\n hash = hashEvent(event, hash);\n }\n return hash;\n}\n\n/** Sign a batch with HMAC-SHA256 using the session secret */\nexport function signBatch(\n sessionId: string,\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n chainHash: string,\n secret: string,\n): string {\n // Canonical form: sessionId + chainHash + event count + sum of values\n const valueSum = events.reduce((s, e) => s + e.value, 0);\n const message = `${sessionId}|${chainHash}|${events.length}|${valueSum}`;\n return createHmac('sha256', secret).update(message).digest('hex');\n}\n\n/** Verify a batch HMAC signature */\nexport function verifyBatchSignature(\n sessionId: string,\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n chainHash: string,\n signature: string,\n secret: string,\n): boolean {\n const expected = signBatch(sessionId, events, chainHash, secret);\n // Constant-time comparison\n if (expected.length !== signature.length) return false;\n let mismatch = 0;\n for (let i = 0; i < expected.length; i++) {\n mismatch |= expected.charCodeAt(i) ^ signature.charCodeAt(i);\n }\n return mismatch === 0;\n}\n","export * from './types';\nexport * from './scoring';\nexport * from './anticheat';\n","import { loadConfig, saveConfig } from './config';\nimport { computeChainHash, signBatch } from '@hackersbaby/shared';\nimport type { EventBatch } from '@hackersbaby/shared';\n\nexport class APIClient {\n private config = loadConfig();\n private sessionSecret: string | null = null;\n private chainHash: string = '0000000000000000';\n private onChainUpdate: ((chainHash: string, secret: string) => void) | null = null;\n\n /** Set callback to persist chain state between hook processes */\n setChainUpdateCallback(cb: (chainHash: string, secret: string) => void) {\n this.onChainUpdate = cb;\n }\n\n /** Restore crypto state from a previous session */\n restoreCryptoState(secret: string, chainHash: string) {\n this.sessionSecret = secret;\n this.chainHash = chainHash;\n }\n\n private get headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config?.token || ''}`,\n };\n }\n\n /** Get current crypto state for persistence */\n getCryptoState(): { secret: string; chainHash: string } | null {\n if (!this.sessionSecret) return null;\n return { secret: this.sessionSecret, chainHash: this.chainHash };\n }\n\n /** Register session with server and get cryptographic secret */\n async startSession(sessionId: string): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/sessions/start`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ session_id: sessionId }),\n });\n if (res.ok) {\n const data = await res.json() as { session_secret: string; initial_chain_hash: string };\n this.sessionSecret = data.session_secret;\n this.chainHash = data.initial_chain_hash;\n return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n\n async sendBatch(batch: EventBatch): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n\n // Sign the batch if we have a session secret\n if (this.sessionSecret) {\n const prevChainHash = this.chainHash;\n const newChainHash = computeChainHash(batch.events, prevChainHash);\n const signature = signBatch(batch.session_id, batch.events, newChainHash, this.sessionSecret);\n\n batch.signature = signature;\n batch.chain_hash = newChainHash;\n batch.prev_chain_hash = prevChainHash;\n\n const res = await fetch(`${server}/api/scores/batch`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(batch),\n });\n\n if (res.ok) {\n // Only advance chain hash after server confirms acceptance\n this.chainHash = newChainHash;\n if (this.onChainUpdate) {\n this.onChainUpdate(this.chainHash, this.sessionSecret);\n }\n }\n\n return res.ok;\n }\n\n const res = await fetch(`${server}/api/scores/batch`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(batch),\n });\n return res.ok;\n } catch {\n return false;\n }\n }\n\n async refreshTokenIfNeeded(): Promise<void> {\n if (!this.config) return;\n try {\n const parts = this.config.token.split('.');\n if (parts.length !== 3) return;\n const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));\n const expMs = payload.exp * 1000;\n const oneDayMs = 24 * 60 * 60 * 1000;\n if (Date.now() + oneDayMs < expMs) return; // more than 1 day remaining, no refresh needed\n\n const res = await fetch(`${this.config.server}/api/auth/cli-token/refresh`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ refresh_token: this.config.refresh_token }),\n });\n if (res.ok) {\n const data = (await res.json()) as { token?: string; refresh_token?: string };\n if (data.token) {\n this.config.token = data.token;\n if (data.refresh_token) this.config.refresh_token = data.refresh_token;\n saveConfig(this.config);\n }\n }\n } catch {\n // silently ignore refresh errors\n }\n }\n\n async getStatus(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me`, { headers: this.headers });\n if (!res.ok) throw new Error(`getStatus failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getRank(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me/rank`, { headers: this.headers });\n if (!res.ok) throw new Error(`getRank failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getLeaderboard(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/leaderboard/global?limit=10`, { headers: this.headers });\n if (!res.ok) throw new Error(`getLeaderboard failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getPublicStats(): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/stats/public`);\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n\n async getNotifications(): Promise<{ notifications: Array<{ type: string; message: string }> } | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me/notifications`, { headers: this.headers });\n if (!res.ok) return null;\n return res.json() as Promise<{ notifications: Array<{ type: string; message: string }> }>;\n } catch {\n return null;\n }\n }\n\n async claimReferral(code: string): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/referral/claim`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ referral_code: code }),\n });\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n\n async getReferralStats(): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/referral/stats`, { headers: this.headers });\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n}\n","import fs from 'fs';\nimport { QUEUE_FILE, ensureConfigDir } from './config';\nimport type { RawEvent } from '@hackersbaby/shared';\n\nconst MAX_QUEUE_SIZE = 10_000;\nconst MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours\n\nexport function enqueueEvents(events: RawEvent[]): void {\n ensureConfigDir();\n const lines = events.map((e) => JSON.stringify(e)).join('\\n');\n fs.appendFileSync(QUEUE_FILE, lines + '\\n', 'utf-8');\n\n // Trim to MAX_QUEUE_SIZE\n try {\n const content = fs.readFileSync(QUEUE_FILE, 'utf-8');\n const allLines = content.split('\\n').filter((l) => l.trim() !== '');\n if (allLines.length > MAX_QUEUE_SIZE) {\n const trimmed = allLines.slice(allLines.length - MAX_QUEUE_SIZE);\n fs.writeFileSync(QUEUE_FILE, trimmed.join('\\n') + '\\n', 'utf-8');\n }\n } catch {\n // ignore read/write errors on trim\n }\n}\n\nexport function drainQueue(): RawEvent[] {\n try {\n if (!fs.existsSync(QUEUE_FILE)) return [];\n const content = fs.readFileSync(QUEUE_FILE, 'utf-8');\n fs.writeFileSync(QUEUE_FILE, '', 'utf-8');\n\n const now = Date.now();\n const events: RawEvent[] = [];\n for (const line of content.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const event = JSON.parse(line) as RawEvent;\n const age = now - new Date(event.timestamp).getTime();\n if (age <= MAX_AGE_MS) {\n events.push(event);\n }\n } catch {\n // skip malformed lines\n }\n }\n return events;\n } catch {\n return [];\n }\n}\n","import { v4 as uuidv4 } from 'uuid';\nimport { APIClient } from './api-client';\nimport { enqueueEvents, drainQueue } from './queue';\nimport { loadConfig } from './config';\nimport type { Source } from './config';\nimport type { RawEvent, ActionType } from '@hackersbaby/shared';\n\ntype RawEventInput = Omit<RawEvent, 'timestamp'>;\n\nexport class EventCollector {\n readonly sessionId: string;\n private buffer: RawEvent[] = [];\n private flushInterval: ReturnType<typeof setInterval> | null = null;\n readonly client: APIClient;\n private source?: Source;\n\n constructor(sessionId?: string, source?: Source) {\n this.sessionId = sessionId ?? uuidv4();\n this.client = new APIClient();\n this.source = source;\n }\n\n /** Restore crypto state from persisted session data */\n restoreCrypto(secret: string, chainHash: string) {\n this.client.restoreCryptoState(secret, chainHash);\n }\n\n /** Set callback to persist crypto state after each batch */\n setCryptoUpdateCallback(cb: (chainHash: string, secret: string) => void) {\n this.client.setChainUpdateCallback(cb);\n }\n\n addEvent(event: RawEventInput): void {\n const metadata = this.source\n ? { ...event.metadata, source: this.source }\n : event.metadata;\n this.buffer.push({\n ...event,\n metadata,\n timestamp: new Date().toISOString(),\n });\n }\n\n async start(): Promise<void> {\n await this.client.refreshTokenIfNeeded();\n\n // Register session with server for cryptographic signing\n await this.client.startSession(this.sessionId);\n\n // Clear stale queue — events from previous sessions have old timestamps\n // that the server will reject, and retrying them risks chain desync.\n drainQueue();\n\n const config = loadConfig();\n const intervalMs = config?.preferences?.batch_interval_ms ?? 10_000;\n this.flushInterval = setInterval(() => {\n this.flush().catch(() => {});\n }, intervalMs);\n }\n\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n const events = this.buffer.splice(0);\n const batch = { session_id: this.sessionId, events };\n const ok = await this.client.sendBatch(batch);\n if (!ok) {\n enqueueEvents(events);\n }\n }\n\n async stop(): Promise<void> {\n if (this.flushInterval !== null) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n this.addEvent({ action_type: 'session_completed' as ActionType, value: 1, metadata: {} });\n await this.flush();\n }\n}\n","import fs from 'fs';\nimport path from 'path';\nimport type { Source } from '../../config';\nimport { sessionStateFile, CONFIG_DIR } from '../../config';\n\nexport interface SessionState {\n sessionId: string;\n pid: number;\n source: Source;\n sessionSecret?: string;\n chainHash?: string;\n}\n\nexport function loadSessionState(source: Source): SessionState | undefined {\n try {\n const file = sessionStateFile(source);\n if (!fs.existsSync(file)) return undefined;\n return JSON.parse(fs.readFileSync(file, 'utf-8')) as SessionState;\n } catch {\n return undefined;\n }\n}\n\nexport function loadSessionId(source: Source): string | undefined {\n return loadSessionState(source)?.sessionId;\n}\n\nexport function updateSessionState(source: Source, updates: Partial<SessionState>): void {\n const state = loadSessionState(source);\n if (!state) return;\n const file = sessionStateFile(source);\n fs.writeFileSync(file, JSON.stringify({ ...state, ...updates }));\n}\n\nexport function saveSessionState(state: SessionState): void {\n const file = sessionStateFile(state.source);\n fs.writeFileSync(file, JSON.stringify(state));\n}\n\n/** Create an EventCollector with crypto state restored from the session file */\nexport function createCollector(sessionId: string | undefined, source: Source): import('../../collector').EventCollector {\n const { EventCollector } = require('../../collector');\n const collector = new EventCollector(sessionId, source);\n\n // Restore crypto state from session file\n const state = loadSessionState(source);\n if (state?.sessionSecret && state?.chainHash && state.sessionId === sessionId) {\n collector.restoreCrypto(state.sessionSecret, state.chainHash);\n\n // Update session file after each signed batch\n collector.setCryptoUpdateCallback((chainHash: string, secret: string) => {\n updateSessionState(source, { chainHash, sessionSecret: secret });\n });\n }\n\n return collector;\n}\n\nexport function removeSessionState(source: Source): void {\n const file = sessionStateFile(source);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n}\n\n// --- File-based lock to serialize batch sending across concurrent hook processes ---\n\nconst LOCK_STALE_MS = 5_000; // 5 seconds — force-remove stale locks\nconst LOCK_POLL_MS = 20; // polling interval\n\nfunction lockFilePath(source: Source): string {\n return path.join(CONFIG_DIR, `lock-${source}`);\n}\n\nfunction tryAcquireLock(lockPath: string): boolean {\n try {\n const fd = fs.openSync(lockPath, 'wx');\n fs.writeSync(fd, `${process.pid}\\n${Date.now()}`);\n fs.closeSync(fd);\n return true;\n } catch (e: any) {\n if (e.code === 'EEXIST') return false;\n throw e;\n }\n}\n\nfunction clearStaleLock(lockPath: string): boolean {\n try {\n const content = fs.readFileSync(lockPath, 'utf-8');\n const lockTime = parseInt(content.split('\\n')[1] || '0', 10);\n if (Date.now() - lockTime > LOCK_STALE_MS) {\n try { fs.unlinkSync(lockPath); } catch {}\n return true; // was stale, removed\n }\n } catch { return true; /* vanished — retry */ }\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function acquireLock(source: Source): Promise<void> {\n const lockPath = lockFilePath(source);\n const deadline = Date.now() + LOCK_STALE_MS;\n\n while (Date.now() < deadline) {\n if (tryAcquireLock(lockPath)) return;\n clearStaleLock(lockPath);\n await sleep(LOCK_POLL_MS + Math.random() * 30);\n }\n\n // Timeout — force-remove and take lock\n try { fs.unlinkSync(lockPath); } catch {}\n if (!tryAcquireLock(lockPath)) {\n // Another process grabbed it — proceed without lock (best-effort)\n }\n}\n\nfunction releaseLock(source: Source): void {\n try { fs.unlinkSync(lockFilePath(source)); } catch {}\n}\n\n/**\n * Execute a callback while holding a file lock for the given source.\n * Serializes all hook processes for the same source (claude/cursor)\n * to prevent concurrent batch sends from causing chain hash desync.\n */\nexport async function withSessionLock<T>(source: Source, fn: () => Promise<T>): Promise<T> {\n await acquireLock(source);\n try {\n return await fn();\n } finally {\n releaseLock(source);\n }\n}\n\nexport function readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let input = '';\n process.stdin.on('data', (chunk) => { input += chunk; });\n process.stdin.on('end', () => resolve(input));\n });\n}\n\n/**\n * Detect source at runtime. Cursor sets CURSOR_VERSION env var\n * and includes cursor_version in the hook payload.\n * Since Cursor reads ~/.claude/settings.json directly, both tools\n * fire the same hooks — so we detect rather than relying on entry points.\n */\nexport function detectSource(payload?: Record<string, unknown>): Source {\n if (process.env.CURSOR_VERSION) return 'cursor';\n if (payload?.cursor_version) return 'cursor';\n return 'claude';\n}\n\n/**\n * Normalize hook payload across tools.\n * Currently an identity function — Cursor's payload format matches Claude Code's.\n * If formats diverge, add field mappings here (e.g., raw.toolName → tool_name).\n */\nexport function normalizePayload(source: Source, raw: Record<string, unknown>): Record<string, unknown> {\n if (source === 'cursor') {\n return { ...raw };\n }\n return raw;\n}\n","import type { Source } from '../../config';\nimport { loadSessionId, readStdin, normalizePayload, detectSource, createCollector, withSessionLock } from './utils';\n\nexport async function handlePromptSubmit(source: Source): Promise<void> {\n const input = await readStdin();\n try {\n const raw = JSON.parse(input);\n const detected = detectSource(raw);\n const hookData = normalizePayload(detected, raw);\n const sessionId = hookData.session_id || loadSessionId(detected);\n\n // PRIVACY: We only track that a prompt was submitted and its character length.\n // The actual prompt text is NEVER sent to the server.\n const promptLength = typeof hookData.prompt === 'string' ? hookData.prompt.length : 0;\n\n await withSessionLock(detected, async () => {\n const collector = createCollector(sessionId, detected);\n\n collector.addEvent({\n action_type: 'prompt_submitted',\n value: 1,\n metadata: {\n char_length: promptLength,\n has_images: Array.isArray(hookData.images) && hookData.images.length > 0,\n },\n });\n\n await collector.flush();\n });\n } catch {}\n}\n","import { handlePromptSubmit } from '../shared/prompt-submit';\nhandlePromptSubmit('cursor');\n"],"mappings":"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","names":["path","fs","os","exports","exports","exports","computeChainHash","signBatch","exports","res","fs","import_fs","uuidv4","import_fs","import_path","fs","EventCollector","path","fs"]}
1
+ {"version":3,"sources":["../../../src/config.ts","../../../../shared/dist/types.js","../../../../shared/src/scoring.ts","../../../../shared/src/anticheat.ts","../../../../shared/src/index.ts","../../../src/api-client.ts","../../../src/queue.ts","../../../src/collector.ts","../../../src/hooks/shared/utils.ts","../../../src/hooks/shared/prompt-submit.ts","../../../src/hooks/cursor/prompt-submit.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport os from 'os';\n\nexport const CONFIG_DIR = path.join(os.homedir(), '.hackersbaby');\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\nexport const QUEUE_FILE = path.join(CONFIG_DIR, 'queue.jsonl');\n\nexport type Source = 'claude' | 'cursor';\n\nexport interface PluginConfig {\n token: string;\n refresh_token: string;\n user_id: string;\n server: string;\n targets?: Source[];\n preferences: {\n dashboard_port: number;\n batch_interval_ms: number;\n };\n}\n\nexport const SESSIONS_DIR = path.join(CONFIG_DIR, 'sessions');\n\nexport function sessionStateFile(sessionId: string): string {\n return path.join(SESSIONS_DIR, `${sessionId}.json`);\n}\n\n/** @deprecated — only used for migrating old per-source state files */\nexport function legacySessionStateFile(source: Source): string {\n return path.join(CONFIG_DIR, `active-session-${source}.json`);\n}\n\nexport function ensureConfigDir(): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\nexport function loadConfig(): PluginConfig | null {\n try {\n if (!fs.existsSync(CONFIG_FILE)) return null;\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\n return JSON.parse(raw) as PluginConfig;\n } catch {\n return null;\n }\n}\n\nexport function saveConfig(config: PluginConfig): void {\n ensureConfigDir();\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');\n}\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n//# sourceMappingURL=types.js.map","import type { ActionType } from './types';\n\nexport const SESSION_BASE_POINT_CAP = 20000;\nexport const TOOL_CALLS_PER_HOUR_CAP = 4000;\nexport const MIN_SESSION_TOKENS = 2000;\n\nexport const BASE_POINTS: Record<ActionType, { perUnit: number; unitSize: number }> = {\n token_input: { perUnit: 1, unitSize: 1000 },\n token_output: { perUnit: 2, unitSize: 1000 },\n tool_call: { perUnit: 5, unitSize: 1 },\n file_created: { perUnit: 15, unitSize: 1 },\n file_edited: { perUnit: 10, unitSize: 1 },\n commit: { perUnit: 50, unitSize: 1 },\n session_completed: { perUnit: 100, unitSize: 1 },\n deployment: { perUnit: 200, unitSize: 1 },\n prompt_submitted: { perUnit: 15, unitSize: 1 },\n subagent_spawned: { perUnit: 40, unitSize: 1 },\n context_compacted: { perUnit: 30, unitSize: 1 },\n stop_response: { perUnit: 10, unitSize: 1 },\n};\n\nexport const DEPLOY_PATTERNS = [\n /^vercel\\s+(--prod|deploy)/, /^netlify\\s+deploy/, /^fly\\s+deploy/, /^railway\\s+up/,\n /^git\\s+push\\s+\\S+\\s+(main|master|production)/,\n /^(npm|yarn|pnpm)\\s+run\\s+deploy/, /^(yarn|pnpm)\\s+deploy/,\n];\n\nexport function getBasePoints(actionType: ActionType, value: number): number {\n const config = BASE_POINTS[actionType];\n return Math.floor(value / config.unitSize) * config.perUnit;\n}\n\ninterface MultiplierInput { streak_days: number; commit_quality: boolean; language_count: number; }\n\nexport function calculateMultiplier(input: MultiplierInput): number {\n let streak = 1.0;\n if (input.streak_days >= 30) streak = 3.0;\n else if (input.streak_days >= 7) streak = 2.0;\n else if (input.streak_days >= 3) streak = 1.5;\n const quality = input.commit_quality ? 1.5 : 1.0;\n const diversity = input.language_count >= 3 ? 1.2 : 1.0;\n return streak * quality * diversity;\n}\n\nexport function calculateSessionScore(events: Array<{ action_type: ActionType; value: number }>, multiplier: number): number {\n let totalBase = 0;\n for (const e of events) totalBase += getBasePoints(e.action_type, e.value);\n return Math.floor(Math.min(totalBase, SESSION_BASE_POINT_CAP) * multiplier);\n}\n\nexport function isDeployCommand(command: string): boolean {\n return DEPLOY_PATTERNS.some((p) => p.test(command.trim()));\n}\n","/**\n * Anti-cheat cryptographic primitives.\n * Shared between plugin (client) and server.\n *\n * - Event chain: each event hash includes the previous, creating a tamper-evident chain\n * - Batch HMAC: the entire batch is signed with a server-issued session secret\n */\n\nimport { createHmac, createHash } from 'crypto';\n\n/** Hash a single event, chaining it to the previous hash */\nexport function hashEvent(\n event: { action_type: string; value: number; timestamp: string },\n prevHash: string,\n): string {\n const payload = `${prevHash}|${event.action_type}|${event.value}|${event.timestamp}`;\n return createHash('sha256').update(payload).digest('hex').slice(0, 16);\n}\n\n/** Compute the chain hash for an array of events */\nexport function computeChainHash(\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n initialHash: string = '0000000000000000',\n): string {\n let hash = initialHash;\n for (const event of events) {\n hash = hashEvent(event, hash);\n }\n return hash;\n}\n\n/** Sign a batch with HMAC-SHA256 using the session secret */\nexport function signBatch(\n sessionId: string,\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n chainHash: string,\n secret: string,\n): string {\n // Canonical form: sessionId + chainHash + event count + sum of values\n const valueSum = events.reduce((s, e) => s + e.value, 0);\n const message = `${sessionId}|${chainHash}|${events.length}|${valueSum}`;\n return createHmac('sha256', secret).update(message).digest('hex');\n}\n\n/** Verify a batch HMAC signature */\nexport function verifyBatchSignature(\n sessionId: string,\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n chainHash: string,\n signature: string,\n secret: string,\n): boolean {\n const expected = signBatch(sessionId, events, chainHash, secret);\n // Constant-time comparison\n if (expected.length !== signature.length) return false;\n let mismatch = 0;\n for (let i = 0; i < expected.length; i++) {\n mismatch |= expected.charCodeAt(i) ^ signature.charCodeAt(i);\n }\n return mismatch === 0;\n}\n","export * from './types';\nexport * from './scoring';\nexport * from './anticheat';\n","import { loadConfig, saveConfig } from './config';\nimport { computeChainHash, signBatch } from '@hackersbaby/shared';\nimport type { EventBatch } from '@hackersbaby/shared';\n\nexport class APIClient {\n private config = loadConfig();\n private sessionSecret: string | null = null;\n private chainHash: string = '0000000000000000';\n private onChainUpdate: ((chainHash: string, secret: string) => void) | null = null;\n\n /** Set callback to persist chain state between hook processes */\n setChainUpdateCallback(cb: (chainHash: string, secret: string) => void) {\n this.onChainUpdate = cb;\n }\n\n /** Restore crypto state from a previous session */\n restoreCryptoState(secret: string, chainHash: string) {\n this.sessionSecret = secret;\n this.chainHash = chainHash;\n }\n\n private get headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config?.token || ''}`,\n };\n }\n\n /** Get current crypto state for persistence */\n getCryptoState(): { secret: string; chainHash: string } | null {\n if (!this.sessionSecret) return null;\n return { secret: this.sessionSecret, chainHash: this.chainHash };\n }\n\n /** Register session with server and get cryptographic secret */\n async startSession(sessionId: string): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/sessions/start`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ session_id: sessionId }),\n });\n if (res.ok) {\n const data = await res.json() as { session_secret: string; initial_chain_hash: string };\n this.sessionSecret = data.session_secret;\n this.chainHash = data.initial_chain_hash;\n return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n\n async sendBatch(batch: EventBatch): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n\n // Sign the batch if we have a session secret\n if (this.sessionSecret) {\n const prevChainHash = this.chainHash;\n const newChainHash = computeChainHash(batch.events, prevChainHash);\n const signature = signBatch(batch.session_id, batch.events, newChainHash, this.sessionSecret);\n\n batch.signature = signature;\n batch.chain_hash = newChainHash;\n batch.prev_chain_hash = prevChainHash;\n\n const res = await fetch(`${server}/api/scores/batch`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(batch),\n });\n\n if (res.ok) {\n // Only advance chain hash after server confirms acceptance\n this.chainHash = newChainHash;\n if (this.onChainUpdate) {\n this.onChainUpdate(this.chainHash, this.sessionSecret);\n }\n }\n\n return res.ok;\n }\n\n const res = await fetch(`${server}/api/scores/batch`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(batch),\n });\n return res.ok;\n } catch {\n return false;\n }\n }\n\n async refreshTokenIfNeeded(): Promise<void> {\n if (!this.config) return;\n try {\n const parts = this.config.token.split('.');\n if (parts.length !== 3) return;\n const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));\n const expMs = payload.exp * 1000;\n const oneDayMs = 24 * 60 * 60 * 1000;\n if (Date.now() + oneDayMs < expMs) return; // more than 1 day remaining, no refresh needed\n\n const res = await fetch(`${this.config.server}/api/auth/cli-token/refresh`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ refresh_token: this.config.refresh_token }),\n });\n if (res.ok) {\n const data = (await res.json()) as { token?: string; refresh_token?: string };\n if (data.token) {\n this.config.token = data.token;\n if (data.refresh_token) this.config.refresh_token = data.refresh_token;\n saveConfig(this.config);\n }\n }\n } catch {\n // silently ignore refresh errors\n }\n }\n\n async getStatus(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me`, { headers: this.headers });\n if (!res.ok) throw new Error(`getStatus failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getRank(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me/rank`, { headers: this.headers });\n if (!res.ok) throw new Error(`getRank failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getLeaderboard(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/leaderboard/global?limit=10`, { headers: this.headers });\n if (!res.ok) throw new Error(`getLeaderboard failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getPublicStats(): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/stats/public`);\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n\n async getNotifications(): Promise<{ notifications: Array<{ type: string; message: string }> } | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me/notifications`, { headers: this.headers });\n if (!res.ok) return null;\n return res.json() as Promise<{ notifications: Array<{ type: string; message: string }> }>;\n } catch {\n return null;\n }\n }\n\n async claimReferral(code: string): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/referral/claim`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ referral_code: code }),\n });\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n\n async getReferralStats(): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/referral/stats`, { headers: this.headers });\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n}\n","import fs from 'fs';\nimport { QUEUE_FILE, ensureConfigDir } from './config';\nimport type { RawEvent } from '@hackersbaby/shared';\n\nconst MAX_QUEUE_SIZE = 10_000;\nconst MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours\n\nexport function enqueueEvents(events: RawEvent[]): void {\n ensureConfigDir();\n const lines = events.map((e) => JSON.stringify(e)).join('\\n');\n fs.appendFileSync(QUEUE_FILE, lines + '\\n', 'utf-8');\n\n // Trim to MAX_QUEUE_SIZE\n try {\n const content = fs.readFileSync(QUEUE_FILE, 'utf-8');\n const allLines = content.split('\\n').filter((l) => l.trim() !== '');\n if (allLines.length > MAX_QUEUE_SIZE) {\n const trimmed = allLines.slice(allLines.length - MAX_QUEUE_SIZE);\n fs.writeFileSync(QUEUE_FILE, trimmed.join('\\n') + '\\n', 'utf-8');\n }\n } catch {\n // ignore read/write errors on trim\n }\n}\n\nexport function drainQueue(): RawEvent[] {\n try {\n if (!fs.existsSync(QUEUE_FILE)) return [];\n const content = fs.readFileSync(QUEUE_FILE, 'utf-8');\n fs.writeFileSync(QUEUE_FILE, '', 'utf-8');\n\n const now = Date.now();\n const events: RawEvent[] = [];\n for (const line of content.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const event = JSON.parse(line) as RawEvent;\n const age = now - new Date(event.timestamp).getTime();\n if (age <= MAX_AGE_MS) {\n events.push(event);\n }\n } catch {\n // skip malformed lines\n }\n }\n return events;\n } catch {\n return [];\n }\n}\n","import { v4 as uuidv4 } from 'uuid';\nimport { APIClient } from './api-client';\nimport { enqueueEvents, drainQueue } from './queue';\nimport { loadConfig } from './config';\nimport type { Source } from './config';\nimport type { RawEvent, ActionType } from '@hackersbaby/shared';\n\ntype RawEventInput = Omit<RawEvent, 'timestamp'>;\n\nexport class EventCollector {\n readonly sessionId: string;\n private buffer: RawEvent[] = [];\n private flushInterval: ReturnType<typeof setInterval> | null = null;\n readonly client: APIClient;\n private source?: Source;\n\n constructor(sessionId?: string, source?: Source) {\n this.sessionId = sessionId ?? uuidv4();\n this.client = new APIClient();\n this.source = source;\n }\n\n /** Restore crypto state from persisted session data */\n restoreCrypto(secret: string, chainHash: string) {\n this.client.restoreCryptoState(secret, chainHash);\n }\n\n /** Set callback to persist crypto state after each batch */\n setCryptoUpdateCallback(cb: (chainHash: string, secret: string) => void) {\n this.client.setChainUpdateCallback(cb);\n }\n\n addEvent(event: RawEventInput): void {\n const metadata = this.source\n ? { ...event.metadata, source: this.source }\n : event.metadata;\n this.buffer.push({\n ...event,\n metadata,\n timestamp: new Date().toISOString(),\n });\n }\n\n async start(): Promise<void> {\n await this.client.refreshTokenIfNeeded();\n\n // Register session with server for cryptographic signing\n await this.client.startSession(this.sessionId);\n\n // Clear stale queue — events from previous sessions have old timestamps\n // that the server will reject, and retrying them risks chain desync.\n drainQueue();\n\n const config = loadConfig();\n const intervalMs = config?.preferences?.batch_interval_ms ?? 10_000;\n this.flushInterval = setInterval(() => {\n this.flush().catch(() => {});\n }, intervalMs);\n }\n\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n const events = this.buffer.splice(0);\n const batch = { session_id: this.sessionId, events };\n const ok = await this.client.sendBatch(batch);\n if (!ok) {\n enqueueEvents(events);\n }\n }\n\n async stop(): Promise<void> {\n if (this.flushInterval !== null) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n this.addEvent({ action_type: 'session_completed' as ActionType, value: 1, metadata: {} });\n await this.flush();\n }\n}\n","import fs from 'fs';\nimport path from 'path';\nimport type { Source } from '../../config';\nimport { sessionStateFile, SESSIONS_DIR, CONFIG_DIR, legacySessionStateFile } from '../../config';\n\nexport interface SessionState {\n sessionId: string;\n pid: number;\n source: Source;\n sessionSecret?: string;\n chainHash?: string;\n}\n\nfunction ensureSessionsDir(): void {\n if (!fs.existsSync(SESSIONS_DIR)) {\n fs.mkdirSync(SESSIONS_DIR, { recursive: true });\n }\n}\n\nexport function loadSessionState(sessionId: string): SessionState | undefined {\n try {\n const file = sessionStateFile(sessionId);\n if (!fs.existsSync(file)) return undefined;\n return JSON.parse(fs.readFileSync(file, 'utf-8')) as SessionState;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Fallback: find any session file for a given source.\n * Used when session_id is missing from hook payload (legacy/edge case).\n */\nexport function findSessionBySource(source: Source): SessionState | undefined {\n try {\n if (!fs.existsSync(SESSIONS_DIR)) return undefined;\n const files = fs.readdirSync(SESSIONS_DIR).filter(f => f.endsWith('.json'));\n for (const f of files) {\n try {\n const state = JSON.parse(fs.readFileSync(path.join(SESSIONS_DIR, f), 'utf-8')) as SessionState;\n if (state.source === source) return state;\n } catch {}\n }\n } catch {}\n // Also check legacy file as last resort\n try {\n const legacyFile = legacySessionStateFile(source);\n if (fs.existsSync(legacyFile)) {\n return JSON.parse(fs.readFileSync(legacyFile, 'utf-8')) as SessionState;\n }\n } catch {}\n return undefined;\n}\n\nexport function loadSessionId(source: Source): string | undefined {\n return findSessionBySource(source)?.sessionId;\n}\n\nexport function updateSessionState(sessionId: string, updates: Partial<SessionState>): void {\n const state = loadSessionState(sessionId);\n if (!state) return;\n const file = sessionStateFile(sessionId);\n fs.writeFileSync(file, JSON.stringify({ ...state, ...updates }));\n}\n\nexport function saveSessionState(state: SessionState): void {\n ensureSessionsDir();\n const file = sessionStateFile(state.sessionId);\n fs.writeFileSync(file, JSON.stringify(state));\n}\n\n/** Create an EventCollector with crypto state restored from the session file */\nexport function createCollector(sessionId: string | undefined, source: Source): import('../../collector').EventCollector {\n const { EventCollector } = require('../../collector');\n const collector = new EventCollector(sessionId, source);\n\n // Restore crypto state from session file (keyed by sessionId)\n const resolvedId = collector.sessionId;\n const state = loadSessionState(resolvedId);\n if (state?.sessionSecret && state?.chainHash && state.sessionId === resolvedId) {\n collector.restoreCrypto(state.sessionSecret, state.chainHash);\n\n // Update session file after each signed batch\n collector.setCryptoUpdateCallback((chainHash: string, secret: string) => {\n updateSessionState(resolvedId, { chainHash, sessionSecret: secret });\n });\n }\n\n return collector;\n}\n\nexport function removeSessionState(sessionId: string): void {\n try {\n const file = sessionStateFile(sessionId);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n } catch {}\n}\n\n/**\n * Clean up stale session files older than maxAge (default 24h).\n * Called during session-start to prevent buildup.\n */\nexport function cleanStaleSessionFiles(maxAgeMs: number = 7 * 24 * 60 * 60 * 1000): void {\n try {\n if (!fs.existsSync(SESSIONS_DIR)) return;\n const now = Date.now();\n for (const f of fs.readdirSync(SESSIONS_DIR)) {\n if (!f.endsWith('.json')) continue;\n const filePath = path.join(SESSIONS_DIR, f);\n try {\n const stat = fs.statSync(filePath);\n if (now - stat.mtimeMs > maxAgeMs) {\n fs.unlinkSync(filePath);\n }\n } catch {}\n }\n } catch {}\n}\n\n// --- File-based lock to serialize batch sending across concurrent hook processes ---\n\nconst LOCK_STALE_MS = 5_000; // 5 seconds — force-remove stale locks\nconst LOCK_POLL_MS = 20; // polling interval\n\nfunction lockFilePath(sessionId: string): string {\n ensureSessionsDir();\n return path.join(SESSIONS_DIR, `lock-${sessionId}`);\n}\n\nfunction tryAcquireLock(lockPath: string): boolean {\n try {\n const fd = fs.openSync(lockPath, 'wx');\n fs.writeSync(fd, `${process.pid}\\n${Date.now()}`);\n fs.closeSync(fd);\n return true;\n } catch (e: any) {\n if (e.code === 'EEXIST') return false;\n throw e;\n }\n}\n\nfunction clearStaleLock(lockPath: string): boolean {\n try {\n const content = fs.readFileSync(lockPath, 'utf-8');\n const lockTime = parseInt(content.split('\\n')[1] || '0', 10);\n if (Date.now() - lockTime > LOCK_STALE_MS) {\n try { fs.unlinkSync(lockPath); } catch {}\n return true; // was stale, removed\n }\n } catch { return true; /* vanished — retry */ }\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function acquireLock(sessionId: string): Promise<void> {\n const lockPath = lockFilePath(sessionId);\n const deadline = Date.now() + LOCK_STALE_MS;\n\n while (Date.now() < deadline) {\n if (tryAcquireLock(lockPath)) return;\n clearStaleLock(lockPath);\n await sleep(LOCK_POLL_MS + Math.random() * 30);\n }\n\n // Timeout — force-remove and take lock\n try { fs.unlinkSync(lockPath); } catch {}\n if (!tryAcquireLock(lockPath)) {\n // Another process grabbed it — proceed without lock (best-effort)\n }\n}\n\nfunction releaseLock(sessionId: string): void {\n try { fs.unlinkSync(lockFilePath(sessionId)); } catch {}\n}\n\n/**\n * Execute a callback while holding a file lock for the given session.\n * Serializes all hook processes for the same session\n * to prevent concurrent batch sends from causing chain hash desync.\n */\nexport async function withSessionLock<T>(sessionId: string, fn: () => Promise<T>): Promise<T> {\n await acquireLock(sessionId);\n try {\n return await fn();\n } finally {\n releaseLock(sessionId);\n }\n}\n\nexport function readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let input = '';\n process.stdin.on('data', (chunk) => { input += chunk; });\n process.stdin.on('end', () => resolve(input));\n });\n}\n\n/**\n * Detect source at runtime. Cursor sets CURSOR_VERSION env var\n * and includes cursor_version in the hook payload.\n * Since Cursor reads ~/.claude/settings.json directly, both tools\n * fire the same hooks — so we detect rather than relying on entry points.\n */\nexport function detectSource(payload?: Record<string, unknown>): Source {\n if (process.env.CURSOR_VERSION) return 'cursor';\n if (payload?.cursor_version) return 'cursor';\n return 'claude';\n}\n\n/**\n * Normalize hook payload across tools.\n * Currently an identity function — Cursor's payload format matches Claude Code's.\n * If formats diverge, add field mappings here (e.g., raw.toolName → tool_name).\n */\nexport function normalizePayload(source: Source, raw: Record<string, unknown>): Record<string, unknown> {\n if (source === 'cursor') {\n return { ...raw };\n }\n return raw;\n}\n","import type { Source } from '../../config';\nimport { loadSessionId, readStdin, normalizePayload, detectSource, createCollector, withSessionLock } from './utils';\n\nexport async function handlePromptSubmit(source: Source): Promise<void> {\n const input = await readStdin();\n try {\n const raw = JSON.parse(input);\n const detected = detectSource(raw);\n const hookData = normalizePayload(detected, raw);\n const sessionId = hookData.session_id || loadSessionId(detected);\n if (!sessionId) return; // No active session\n\n // PRIVACY: We only track that a prompt was submitted and its character length.\n // The actual prompt text is NEVER sent to the server.\n const promptLength = typeof hookData.prompt === 'string' ? hookData.prompt.length : 0;\n\n await withSessionLock(sessionId, async () => {\n const collector = createCollector(sessionId, detected);\n\n collector.addEvent({\n action_type: 'prompt_submitted',\n value: 1,\n metadata: {\n char_length: promptLength,\n has_images: Array.isArray(hookData.images) && hookData.images.length > 0,\n },\n });\n\n await collector.flush();\n });\n } catch {}\n}\n","import { handlePromptSubmit } from '../shared/prompt-submit';\nhandlePromptSubmit('cursor');\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBO,SAAS,iBAAiB,WAA2B;AAC1D,SAAO,YAAAA,QAAK,KAAK,cAAc,GAAG,SAAS,OAAO;AACpD;AAGO,SAAS,uBAAuB,QAAwB;AAC7D,SAAO,YAAAA,QAAK,KAAK,YAAY,kBAAkB,MAAM,OAAO;AAC9D;AAEO,SAAS,kBAAwB;AACtC,MAAI,CAAC,UAAAC,QAAG,WAAW,UAAU,GAAG;AAC9B,cAAAA,QAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,EAC9C;AACF;AAEO,SAAS,aAAkC;AAChD,MAAI;AACF,QAAI,CAAC,UAAAA,QAAG,WAAW,WAAW,EAAG,QAAO;AACxC,UAAM,MAAM,UAAAA,QAAG,aAAa,aAAa,OAAO;AAChD,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,WAAW,QAA4B;AACrD,kBAAgB;AAChB,YAAAA,QAAG,cAAc,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,OAAO;AACxE;AApDA,eACA,aACA,WAEa,YACA,aACA,YAgBA;AAtBb;AAAA;AAAA;AAAA,gBAAe;AACf,kBAAiB;AACjB,gBAAe;AAER,IAAM,aAAa,YAAAD,QAAK,KAAK,UAAAE,QAAG,QAAQ,GAAG,cAAc;AACzD,IAAM,cAAc,YAAAF,QAAK,KAAK,YAAY,aAAa;AACvD,IAAM,aAAa,YAAAA,QAAK,KAAK,YAAY,aAAa;AAgBtD,IAAM,eAAe,YAAAA,QAAK,KAAK,YAAY,UAAU;AAAA;AAAA;;;ACtB5D;AAAA,4BAAAG,UAAA;AAAA;AACA,WAAO,eAAeA,UAAS,cAAc,EAAE,OAAO,KAAK,CAAC;AAAA;AAAA;;;;;;;;AC0B5D,IAAAC,SAAA,gBAAA;AAOA,IAAAA,SAAA,sBAAA;AAUA,IAAAA,SAAA,wBAAA;AAMA,IAAAA,SAAA,kBAAA;AAhDa,IAAAA,SAAA,yBAAyB;AACzB,IAAAA,SAAA,0BAA0B;AAC1B,IAAAA,SAAA,qBAAqB;AAErB,IAAAA,SAAA,cAAyE;MACpF,aAAoB,EAAE,SAAS,GAAK,UAAU,IAAI;MAClD,cAAoB,EAAE,SAAS,GAAK,UAAU,IAAI;MAClD,WAAoB,EAAE,SAAS,GAAK,UAAU,EAAC;MAC/C,cAAoB,EAAE,SAAS,IAAK,UAAU,EAAC;MAC/C,aAAoB,EAAE,SAAS,IAAK,UAAU,EAAC;MAC/C,QAAoB,EAAE,SAAS,IAAK,UAAU,EAAC;MAC/C,mBAAoB,EAAE,SAAS,KAAK,UAAU,EAAC;MAC/C,YAAoB,EAAE,SAAS,KAAK,UAAU,EAAC;MAC/C,kBAAoB,EAAE,SAAS,IAAK,UAAU,EAAC;MAC/C,kBAAoB,EAAE,SAAS,IAAK,UAAU,EAAC;MAC/C,mBAAoB,EAAE,SAAS,IAAK,UAAU,EAAC;MAC/C,eAAoB,EAAE,SAAS,IAAK,UAAU,EAAC;;AAGpC,IAAAA,SAAA,kBAAkB;MAC7B;MAA6B;MAAqB;MAAiB;MACnE;MACA;MAAmC;;AAGrC,aAAgB,cAAc,YAAwB,OAAa;AACjE,YAAM,SAASA,SAAA,YAAY,UAAU;AACrC,aAAO,KAAK,MAAM,QAAQ,OAAO,QAAQ,IAAI,OAAO;IACtD;AAIA,aAAgB,oBAAoB,OAAsB;AACxD,UAAI,SAAS;AACb,UAAI,MAAM,eAAe;AAAI,iBAAS;eAC7B,MAAM,eAAe;AAAG,iBAAS;eACjC,MAAM,eAAe;AAAG,iBAAS;AAC1C,YAAM,UAAU,MAAM,iBAAiB,MAAM;AAC7C,YAAM,YAAY,MAAM,kBAAkB,IAAI,MAAM;AACpD,aAAO,SAAS,UAAU;IAC5B;AAEA,aAAgB,sBAAsB,QAA2D,YAAkB;AACjH,UAAI,YAAY;AAChB,iBAAW,KAAK;AAAQ,qBAAa,cAAc,EAAE,aAAa,EAAE,KAAK;AACzE,aAAO,KAAK,MAAM,KAAK,IAAI,WAAWA,SAAA,sBAAsB,IAAI,UAAU;IAC5E;AAEA,aAAgB,gBAAgB,SAAe;AAC7C,aAAOA,SAAA,gBAAgB,KAAK,CAAC,MAAM,EAAE,KAAK,QAAQ,KAAI,CAAE,CAAC;IAC3D;;;;;;;;;ACzCA,IAAAC,SAAA,YAAA;AASA,IAAAA,SAAA,mBAAAC;AAYA,IAAAD,SAAA,YAAAE;AAaA,IAAAF,SAAA,uBAAA;AArCA,QAAA,WAAA,QAAA,QAAA;AAGA,aAAgB,UACd,OACA,UAAgB;AAEhB,YAAM,UAAU,GAAG,QAAQ,IAAI,MAAM,WAAW,IAAI,MAAM,KAAK,IAAI,MAAM,SAAS;AAClF,cAAO,GAAA,SAAA,YAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;IACvE;AAGA,aAAgBC,kBACd,QACA,cAAsB,oBAAkB;AAExC,UAAI,OAAO;AACX,iBAAW,SAAS,QAAQ;AAC1B,eAAO,UAAU,OAAO,IAAI;MAC9B;AACA,aAAO;IACT;AAGA,aAAgBC,WACd,WACA,QACA,WACA,QAAc;AAGd,YAAM,WAAW,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,OAAO,CAAC;AACvD,YAAM,UAAU,GAAG,SAAS,IAAI,SAAS,IAAI,OAAO,MAAM,IAAI,QAAQ;AACtE,cAAO,GAAA,SAAA,YAAW,UAAU,MAAM,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;IAClE;AAGA,aAAgB,qBACd,WACA,QACA,WACA,WACA,QAAc;AAEd,YAAM,WAAWA,WAAU,WAAW,QAAQ,WAAW,MAAM;AAE/D,UAAI,SAAS,WAAW,UAAU;AAAQ,eAAO;AACjD,UAAI,WAAW;AACf,eAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,oBAAY,SAAS,WAAW,CAAC,IAAI,UAAU,WAAW,CAAC;MAC7D;AACA,aAAO,aAAa;IACtB;;;;;;;;;;;;;;;;;;;;;;;;;AC5DA,iBAAA,iBAAAC,QAAA;AACA,iBAAA,mBAAAA,QAAA;AACA,iBAAA,qBAAAA,QAAA;;;;;ACFA,IACA,eAGa;AAJb;AAAA;AAAA;AAAA;AACA,oBAA4C;AAGrC,IAAM,YAAN,MAAgB;AAAA,MACb,SAAS,WAAW;AAAA,MACpB,gBAA+B;AAAA,MAC/B,YAAoB;AAAA,MACpB,gBAAsE;AAAA;AAAA,MAG9E,uBAAuB,IAAiD;AACtE,aAAK,gBAAgB;AAAA,MACvB;AAAA;AAAA,MAGA,mBAAmB,QAAgB,WAAmB;AACpD,aAAK,gBAAgB;AACrB,aAAK,YAAY;AAAA,MACnB;AAAA,MAEA,IAAY,UAAkC;AAC5C,eAAO;AAAA,UACL,gBAAgB;AAAA,UAChB,eAAe,UAAU,KAAK,QAAQ,SAAS,EAAE;AAAA,QACnD;AAAA,MACF;AAAA;AAAA,MAGA,iBAA+D;AAC7D,YAAI,CAAC,KAAK,cAAe,QAAO;AAChC,eAAO,EAAE,QAAQ,KAAK,eAAe,WAAW,KAAK,UAAU;AAAA,MACjE;AAAA;AAAA,MAGA,MAAM,aAAa,WAAqC;AACtD,YAAI;AACF,gBAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,gBAAM,MAAM,MAAM,MAAM,GAAG,MAAM,uBAAuB;AAAA,YACtD,QAAQ;AAAA,YACR,SAAS,KAAK;AAAA,YACd,MAAM,KAAK,UAAU,EAAE,YAAY,UAAU,CAAC;AAAA,UAChD,CAAC;AACD,cAAI,IAAI,IAAI;AACV,kBAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,iBAAK,gBAAgB,KAAK;AAC1B,iBAAK,YAAY,KAAK;AACtB,mBAAO;AAAA,UACT;AACA,iBAAO;AAAA,QACT,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAM,UAAU,OAAqC;AACnD,YAAI;AACF,gBAAM,SAAS,KAAK,QAAQ,UAAU;AAGtC,cAAI,KAAK,eAAe;AACtB,kBAAM,gBAAgB,KAAK;AAC3B,kBAAM,mBAAe,gCAAiB,MAAM,QAAQ,aAAa;AACjE,kBAAM,gBAAY,yBAAU,MAAM,YAAY,MAAM,QAAQ,cAAc,KAAK,aAAa;AAE5F,kBAAM,YAAY;AAClB,kBAAM,aAAa;AACnB,kBAAM,kBAAkB;AAExB,kBAAMC,OAAM,MAAM,MAAM,GAAG,MAAM,qBAAqB;AAAA,cACpD,QAAQ;AAAA,cACR,SAAS,KAAK;AAAA,cACd,MAAM,KAAK,UAAU,KAAK;AAAA,YAC5B,CAAC;AAED,gBAAIA,KAAI,IAAI;AAEV,mBAAK,YAAY;AACjB,kBAAI,KAAK,eAAe;AACtB,qBAAK,cAAc,KAAK,WAAW,KAAK,aAAa;AAAA,cACvD;AAAA,YACF;AAEA,mBAAOA,KAAI;AAAA,UACb;AAEA,gBAAM,MAAM,MAAM,MAAM,GAAG,MAAM,qBAAqB;AAAA,YACpD,QAAQ;AAAA,YACR,SAAS,KAAK;AAAA,YACd,MAAM,KAAK,UAAU,KAAK;AAAA,UAC5B,CAAC;AACD,iBAAO,IAAI;AAAA,QACb,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAM,uBAAsC;AAC1C,YAAI,CAAC,KAAK,OAAQ;AAClB,YAAI;AACF,gBAAM,QAAQ,KAAK,OAAO,MAAM,MAAM,GAAG;AACzC,cAAI,MAAM,WAAW,EAAG;AACxB,gBAAM,UAAU,KAAK,MAAM,OAAO,KAAK,MAAM,CAAC,GAAG,QAAQ,EAAE,SAAS,OAAO,CAAC;AAC5E,gBAAM,QAAQ,QAAQ,MAAM;AAC5B,gBAAM,WAAW,KAAK,KAAK,KAAK;AAChC,cAAI,KAAK,IAAI,IAAI,WAAW,MAAO;AAEnC,gBAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,+BAA+B;AAAA,YAC1E,QAAQ;AAAA,YACR,SAAS,KAAK;AAAA,YACd,MAAM,KAAK,UAAU,EAAE,eAAe,KAAK,OAAO,cAAc,CAAC;AAAA,UACnE,CAAC;AACD,cAAI,IAAI,IAAI;AACV,kBAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,gBAAI,KAAK,OAAO;AACd,mBAAK,OAAO,QAAQ,KAAK;AACzB,kBAAI,KAAK,cAAe,MAAK,OAAO,gBAAgB,KAAK;AACzD,yBAAW,KAAK,MAAM;AAAA,YACxB;AAAA,UACF;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,MAEA,MAAM,YAA8C;AAClD,cAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,cAAM,MAAM,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,SAAS,KAAK,QAAQ,CAAC;AAC3E,YAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,qBAAqB,IAAI,MAAM,EAAE;AAC9D,eAAO,IAAI,KAAK;AAAA,MAClB;AAAA,MAEA,MAAM,UAA4C;AAChD,cAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,cAAM,MAAM,MAAM,MAAM,GAAG,MAAM,sBAAsB,EAAE,SAAS,KAAK,QAAQ,CAAC;AAChF,YAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,mBAAmB,IAAI,MAAM,EAAE;AAC5D,eAAO,IAAI,KAAK;AAAA,MAClB;AAAA,MAEA,MAAM,iBAAmD;AACvD,cAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,cAAM,MAAM,MAAM,MAAM,GAAG,MAAM,oCAAoC,EAAE,SAAS,KAAK,QAAQ,CAAC;AAC9F,YAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,EAAE;AACnE,eAAO,IAAI,KAAK;AAAA,MAClB;AAAA,MAEA,MAAM,iBAA0D;AAC9D,YAAI;AACF,gBAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,gBAAM,MAAM,MAAM,MAAM,GAAG,MAAM,mBAAmB;AACpD,cAAI,CAAC,IAAI,GAAI,QAAO;AACpB,iBAAO,IAAI,KAAK;AAAA,QAClB,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAM,mBAAgG;AACpG,YAAI;AACF,gBAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,gBAAM,MAAM,MAAM,MAAM,GAAG,MAAM,+BAA+B,EAAE,SAAS,KAAK,QAAQ,CAAC;AACzF,cAAI,CAAC,IAAI,GAAI,QAAO;AACpB,iBAAO,IAAI,KAAK;AAAA,QAClB,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAM,cAAc,MAAuD;AACzE,YAAI;AACF,gBAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,gBAAM,MAAM,MAAM,MAAM,GAAG,MAAM,uBAAuB;AAAA,YACtD,QAAQ;AAAA,YACR,SAAS,KAAK;AAAA,YACd,MAAM,KAAK,UAAU,EAAE,eAAe,KAAK,CAAC;AAAA,UAC9C,CAAC;AACD,cAAI,CAAC,IAAI,GAAI,QAAO;AACpB,iBAAO,IAAI,KAAK;AAAA,QAClB,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAM,mBAA4D;AAChE,YAAI;AACF,gBAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,gBAAM,MAAM,MAAM,MAAM,GAAG,MAAM,uBAAuB,EAAE,SAAS,KAAK,QAAQ,CAAC;AACjF,cAAI,CAAC,IAAI,GAAI,QAAO;AACpB,iBAAO,IAAI,KAAK;AAAA,QAClB,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;AC1LO,SAAS,cAAc,QAA0B;AACtD,kBAAgB;AAChB,QAAM,QAAQ,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI;AAC5D,aAAAC,QAAG,eAAe,YAAY,QAAQ,MAAM,OAAO;AAGnD,MAAI;AACF,UAAM,UAAU,WAAAA,QAAG,aAAa,YAAY,OAAO;AACnD,UAAM,WAAW,QAAQ,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE;AAClE,QAAI,SAAS,SAAS,gBAAgB;AACpC,YAAM,UAAU,SAAS,MAAM,SAAS,SAAS,cAAc;AAC/D,iBAAAA,QAAG,cAAc,YAAY,QAAQ,KAAK,IAAI,IAAI,MAAM,OAAO;AAAA,IACjE;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,aAAyB;AACvC,MAAI;AACF,QAAI,CAAC,WAAAA,QAAG,WAAW,UAAU,EAAG,QAAO,CAAC;AACxC,UAAM,UAAU,WAAAA,QAAG,aAAa,YAAY,OAAO;AACnD,eAAAA,QAAG,cAAc,YAAY,IAAI,OAAO;AAExC,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAqB,CAAC;AAC5B,eAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,UAAI,CAAC,KAAK,KAAK,EAAG;AAClB,UAAI;AACF,cAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,cAAM,MAAM,MAAM,IAAI,KAAK,MAAM,SAAS,EAAE,QAAQ;AACpD,YAAI,OAAO,YAAY;AACrB,iBAAO,KAAK,KAAK;AAAA,QACnB;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAjDA,IAAAC,YAIM,gBACA;AALN;AAAA;AAAA;AAAA,IAAAA,aAAe;AACf;AAGA,IAAM,iBAAiB;AACvB,IAAM,aAAa,KAAK,KAAK,KAAK;AAAA;AAAA;;;ACLlC;AAAA;AAAA;AAAA;AAAA,iBASa;AATb;AAAA;AAAA;AAAA,kBAA6B;AAC7B;AACA;AACA;AAMO,IAAM,iBAAN,MAAqB;AAAA,MACjB;AAAA,MACD,SAAqB,CAAC;AAAA,MACtB,gBAAuD;AAAA,MACtD;AAAA,MACD;AAAA,MAER,YAAY,WAAoB,QAAiB;AAC/C,aAAK,YAAY,iBAAa,YAAAC,IAAO;AACrC,aAAK,SAAS,IAAI,UAAU;AAC5B,aAAK,SAAS;AAAA,MAChB;AAAA;AAAA,MAGA,cAAc,QAAgB,WAAmB;AAC/C,aAAK,OAAO,mBAAmB,QAAQ,SAAS;AAAA,MAClD;AAAA;AAAA,MAGA,wBAAwB,IAAiD;AACvE,aAAK,OAAO,uBAAuB,EAAE;AAAA,MACvC;AAAA,MAEA,SAAS,OAA4B;AACnC,cAAM,WAAW,KAAK,SAClB,EAAE,GAAG,MAAM,UAAU,QAAQ,KAAK,OAAO,IACzC,MAAM;AACV,aAAK,OAAO,KAAK;AAAA,UACf,GAAG;AAAA,UACH;AAAA,UACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QACpC,CAAC;AAAA,MACH;AAAA,MAEA,MAAM,QAAuB;AAC3B,cAAM,KAAK,OAAO,qBAAqB;AAGvC,cAAM,KAAK,OAAO,aAAa,KAAK,SAAS;AAI7C,mBAAW;AAEX,cAAM,SAAS,WAAW;AAC1B,cAAM,aAAa,QAAQ,aAAa,qBAAqB;AAC7D,aAAK,gBAAgB,YAAY,MAAM;AACrC,eAAK,MAAM,EAAE,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QAC7B,GAAG,UAAU;AAAA,MACf;AAAA,MAEA,MAAM,QAAuB;AAC3B,YAAI,KAAK,OAAO,WAAW,EAAG;AAC9B,cAAM,SAAS,KAAK,OAAO,OAAO,CAAC;AACnC,cAAM,QAAQ,EAAE,YAAY,KAAK,WAAW,OAAO;AACnD,cAAM,KAAK,MAAM,KAAK,OAAO,UAAU,KAAK;AAC5C,YAAI,CAAC,IAAI;AACP,wBAAc,MAAM;AAAA,QACtB;AAAA,MACF;AAAA,MAEA,MAAM,OAAsB;AAC1B,YAAI,KAAK,kBAAkB,MAAM;AAC/B,wBAAc,KAAK,aAAa;AAChC,eAAK,gBAAgB;AAAA,QACvB;AACA,aAAK,SAAS,EAAE,aAAa,qBAAmC,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC;AACxF,cAAM,KAAK,MAAM;AAAA,MACnB;AAAA,IACF;AAAA;AAAA;;;AC9EA,IAAAC,aAAe;AACf,IAAAC,eAAiB;AAEjB;AAUA,SAAS,oBAA0B;AACjC,MAAI,CAAC,WAAAC,QAAG,WAAW,YAAY,GAAG;AAChC,eAAAA,QAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;AAAA,EAChD;AACF;AAEO,SAAS,iBAAiB,WAA6C;AAC5E,MAAI;AACF,UAAM,OAAO,iBAAiB,SAAS;AACvC,QAAI,CAAC,WAAAA,QAAG,WAAW,IAAI,EAAG,QAAO;AACjC,WAAO,KAAK,MAAM,WAAAA,QAAG,aAAa,MAAM,OAAO,CAAC;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,oBAAoB,QAA0C;AAC5E,MAAI;AACF,QAAI,CAAC,WAAAA,QAAG,WAAW,YAAY,EAAG,QAAO;AACzC,UAAM,QAAQ,WAAAA,QAAG,YAAY,YAAY,EAAE,OAAO,OAAK,EAAE,SAAS,OAAO,CAAC;AAC1E,eAAW,KAAK,OAAO;AACrB,UAAI;AACF,cAAM,QAAQ,KAAK,MAAM,WAAAA,QAAG,aAAa,aAAAC,QAAK,KAAK,cAAc,CAAC,GAAG,OAAO,CAAC;AAC7E,YAAI,MAAM,WAAW,OAAQ,QAAO;AAAA,MACtC,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF,QAAQ;AAAA,EAAC;AAET,MAAI;AACF,UAAM,aAAa,uBAAuB,MAAM;AAChD,QAAI,WAAAD,QAAG,WAAW,UAAU,GAAG;AAC7B,aAAO,KAAK,MAAM,WAAAA,QAAG,aAAa,YAAY,OAAO,CAAC;AAAA,IACxD;AAAA,EACF,QAAQ;AAAA,EAAC;AACT,SAAO;AACT;AAEO,SAAS,cAAc,QAAoC;AAChE,SAAO,oBAAoB,MAAM,GAAG;AACtC;AAEO,SAAS,mBAAmB,WAAmB,SAAsC;AAC1F,QAAM,QAAQ,iBAAiB,SAAS;AACxC,MAAI,CAAC,MAAO;AACZ,QAAM,OAAO,iBAAiB,SAAS;AACvC,aAAAA,QAAG,cAAc,MAAM,KAAK,UAAU,EAAE,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC;AACjE;AASO,SAAS,gBAAgB,WAA+B,QAA0D;AACvH,QAAM,EAAE,gBAAAE,gBAAe,IAAI;AAC3B,QAAM,YAAY,IAAIA,gBAAe,WAAW,MAAM;AAGtD,QAAM,aAAa,UAAU;AAC7B,QAAM,QAAQ,iBAAiB,UAAU;AACzC,MAAI,OAAO,iBAAiB,OAAO,aAAa,MAAM,cAAc,YAAY;AAC9E,cAAU,cAAc,MAAM,eAAe,MAAM,SAAS;AAG5D,cAAU,wBAAwB,CAAC,WAAmB,WAAmB;AACvE,yBAAmB,YAAY,EAAE,WAAW,eAAe,OAAO,CAAC;AAAA,IACrE,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAgCA,IAAM,gBAAgB;AACtB,IAAM,eAAe;AAErB,SAAS,aAAa,WAA2B;AAC/C,oBAAkB;AAClB,SAAO,aAAAC,QAAK,KAAK,cAAc,QAAQ,SAAS,EAAE;AACpD;AAEA,SAAS,eAAe,UAA2B;AACjD,MAAI;AACF,UAAM,KAAK,WAAAC,QAAG,SAAS,UAAU,IAAI;AACrC,eAAAA,QAAG,UAAU,IAAI,GAAG,QAAQ,GAAG;AAAA,EAAK,KAAK,IAAI,CAAC,EAAE;AAChD,eAAAA,QAAG,UAAU,EAAE;AACf,WAAO;AAAA,EACT,SAAS,GAAQ;AACf,QAAI,EAAE,SAAS,SAAU,QAAO;AAChC,UAAM;AAAA,EACR;AACF;AAEA,SAAS,eAAe,UAA2B;AACjD,MAAI;AACF,UAAM,UAAU,WAAAA,QAAG,aAAa,UAAU,OAAO;AACjD,UAAM,WAAW,SAAS,QAAQ,MAAM,IAAI,EAAE,CAAC,KAAK,KAAK,EAAE;AAC3D,QAAI,KAAK,IAAI,IAAI,WAAW,eAAe;AACzC,UAAI;AAAE,mBAAAA,QAAG,WAAW,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAC;AACxC,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAE,WAAO;AAAA,EAA6B;AAC9C,SAAO;AACT;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC7C;AAEA,eAAe,YAAY,WAAkC;AAC3D,QAAM,WAAW,aAAa,SAAS;AACvC,QAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,QAAI,eAAe,QAAQ,EAAG;AAC9B,mBAAe,QAAQ;AACvB,UAAM,MAAM,eAAe,KAAK,OAAO,IAAI,EAAE;AAAA,EAC/C;AAGA,MAAI;AAAE,eAAAA,QAAG,WAAW,QAAQ;AAAA,EAAG,QAAQ;AAAA,EAAC;AACxC,MAAI,CAAC,eAAe,QAAQ,GAAG;AAAA,EAE/B;AACF;AAEA,SAAS,YAAY,WAAyB;AAC5C,MAAI;AAAE,eAAAA,QAAG,WAAW,aAAa,SAAS,CAAC;AAAA,EAAG,QAAQ;AAAA,EAAC;AACzD;AAOA,eAAsB,gBAAmB,WAAmB,IAAkC;AAC5F,QAAM,YAAY,SAAS;AAC3B,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,UAAE;AACA,gBAAY,SAAS;AAAA,EACvB;AACF;AAEO,SAAS,YAA6B;AAC3C,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,QAAQ;AACZ,YAAQ,MAAM,GAAG,QAAQ,CAAC,UAAU;AAAE,eAAS;AAAA,IAAO,CAAC;AACvD,YAAQ,MAAM,GAAG,OAAO,MAAM,QAAQ,KAAK,CAAC;AAAA,EAC9C,CAAC;AACH;AAQO,SAAS,aAAa,SAA2C;AACtE,MAAI,QAAQ,IAAI,eAAgB,QAAO;AACvC,MAAI,SAAS,eAAgB,QAAO;AACpC,SAAO;AACT;AAOO,SAAS,iBAAiB,QAAgB,KAAuD;AACtG,MAAI,WAAW,UAAU;AACvB,WAAO,EAAE,GAAG,IAAI;AAAA,EAClB;AACA,SAAO;AACT;;;AC3NA,eAAsB,mBAAmB,QAA+B;AACtE,QAAM,QAAQ,MAAM,UAAU;AAC9B,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,KAAK;AAC5B,UAAM,WAAW,aAAa,GAAG;AACjC,UAAM,WAAW,iBAAiB,UAAU,GAAG;AAC/C,UAAM,YAAY,SAAS,cAAc,cAAc,QAAQ;AAC/D,QAAI,CAAC,UAAW;AAIhB,UAAM,eAAe,OAAO,SAAS,WAAW,WAAW,SAAS,OAAO,SAAS;AAEpF,UAAM,gBAAgB,WAAW,YAAY;AAC3C,YAAM,YAAY,gBAAgB,WAAW,QAAQ;AAErD,gBAAU,SAAS;AAAA,QACjB,aAAa;AAAA,QACb,OAAO;AAAA,QACP,UAAU;AAAA,UACR,aAAa;AAAA,UACb,YAAY,MAAM,QAAQ,SAAS,MAAM,KAAK,SAAS,OAAO,SAAS;AAAA,QACzE;AAAA,MACF,CAAC;AAED,YAAM,UAAU,MAAM;AAAA,IACxB,CAAC;AAAA,EACH,QAAQ;AAAA,EAAC;AACX;;;AC9BA,mBAAmB,QAAQ;","names":["path","fs","os","exports","exports","exports","computeChainHash","signBatch","exports","res","fs","import_fs","uuidv4","import_fs","import_path","fs","path","EventCollector","path","fs"]}
@@ -35,7 +35,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
35
35
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
36
 
37
37
  // src/config.ts
38
- function sessionStateFile(source) {
38
+ function sessionStateFile(sessionId) {
39
+ return import_path.default.join(SESSIONS_DIR, `${sessionId}.json`);
40
+ }
41
+ function legacySessionStateFile(source) {
39
42
  return import_path.default.join(CONFIG_DIR, `active-session-${source}.json`);
40
43
  }
41
44
  function ensureConfigDir() {
@@ -56,7 +59,7 @@ function saveConfig(config) {
56
59
  ensureConfigDir();
57
60
  import_fs.default.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
58
61
  }
59
- var import_fs, import_path, import_os, CONFIG_DIR, CONFIG_FILE, QUEUE_FILE;
62
+ var import_fs, import_path, import_os, CONFIG_DIR, CONFIG_FILE, QUEUE_FILE, SESSIONS_DIR;
60
63
  var init_config = __esm({
61
64
  "src/config.ts"() {
62
65
  "use strict";
@@ -66,6 +69,7 @@ var init_config = __esm({
66
69
  CONFIG_DIR = import_path.default.join(import_os.default.homedir(), ".hackersbaby");
67
70
  CONFIG_FILE = import_path.default.join(CONFIG_DIR, "config.json");
68
71
  QUEUE_FILE = import_path.default.join(CONFIG_DIR, "queue.jsonl");
72
+ SESSIONS_DIR = import_path.default.join(CONFIG_DIR, "sessions");
69
73
  }
70
74
  });
71
75
 
@@ -508,44 +512,76 @@ var init_collector = __esm({
508
512
  var import_fs3 = __toESM(require("fs"));
509
513
  var import_path2 = __toESM(require("path"));
510
514
  init_config();
511
- function loadSessionState(source) {
515
+ function ensureSessionsDir() {
516
+ if (!import_fs3.default.existsSync(SESSIONS_DIR)) {
517
+ import_fs3.default.mkdirSync(SESSIONS_DIR, { recursive: true });
518
+ }
519
+ }
520
+ function loadSessionState(sessionId) {
512
521
  try {
513
- const file = sessionStateFile(source);
522
+ const file = sessionStateFile(sessionId);
514
523
  if (!import_fs3.default.existsSync(file)) return void 0;
515
524
  return JSON.parse(import_fs3.default.readFileSync(file, "utf-8"));
516
525
  } catch {
517
526
  return void 0;
518
527
  }
519
528
  }
529
+ function findSessionBySource(source) {
530
+ try {
531
+ if (!import_fs3.default.existsSync(SESSIONS_DIR)) return void 0;
532
+ const files = import_fs3.default.readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(".json"));
533
+ for (const f of files) {
534
+ try {
535
+ const state = JSON.parse(import_fs3.default.readFileSync(import_path2.default.join(SESSIONS_DIR, f), "utf-8"));
536
+ if (state.source === source) return state;
537
+ } catch {
538
+ }
539
+ }
540
+ } catch {
541
+ }
542
+ try {
543
+ const legacyFile = legacySessionStateFile(source);
544
+ if (import_fs3.default.existsSync(legacyFile)) {
545
+ return JSON.parse(import_fs3.default.readFileSync(legacyFile, "utf-8"));
546
+ }
547
+ } catch {
548
+ }
549
+ return void 0;
550
+ }
520
551
  function loadSessionId(source) {
521
- return loadSessionState(source)?.sessionId;
552
+ return findSessionBySource(source)?.sessionId;
522
553
  }
523
- function updateSessionState(source, updates) {
524
- const state = loadSessionState(source);
554
+ function updateSessionState(sessionId, updates) {
555
+ const state = loadSessionState(sessionId);
525
556
  if (!state) return;
526
- const file = sessionStateFile(source);
557
+ const file = sessionStateFile(sessionId);
527
558
  import_fs3.default.writeFileSync(file, JSON.stringify({ ...state, ...updates }));
528
559
  }
529
560
  function createCollector(sessionId, source) {
530
561
  const { EventCollector: EventCollector2 } = (init_collector(), __toCommonJS(collector_exports));
531
562
  const collector = new EventCollector2(sessionId, source);
532
- const state = loadSessionState(source);
533
- if (state?.sessionSecret && state?.chainHash && state.sessionId === sessionId) {
563
+ const resolvedId = collector.sessionId;
564
+ const state = loadSessionState(resolvedId);
565
+ if (state?.sessionSecret && state?.chainHash && state.sessionId === resolvedId) {
534
566
  collector.restoreCrypto(state.sessionSecret, state.chainHash);
535
567
  collector.setCryptoUpdateCallback((chainHash, secret) => {
536
- updateSessionState(source, { chainHash, sessionSecret: secret });
568
+ updateSessionState(resolvedId, { chainHash, sessionSecret: secret });
537
569
  });
538
570
  }
539
571
  return collector;
540
572
  }
541
- function removeSessionState(source) {
542
- const file = sessionStateFile(source);
543
- if (import_fs3.default.existsSync(file)) import_fs3.default.unlinkSync(file);
573
+ function removeSessionState(sessionId) {
574
+ try {
575
+ const file = sessionStateFile(sessionId);
576
+ if (import_fs3.default.existsSync(file)) import_fs3.default.unlinkSync(file);
577
+ } catch {
578
+ }
544
579
  }
545
580
  var LOCK_STALE_MS = 5e3;
546
581
  var LOCK_POLL_MS = 20;
547
- function lockFilePath(source) {
548
- return import_path2.default.join(CONFIG_DIR, `lock-${source}`);
582
+ function lockFilePath(sessionId) {
583
+ ensureSessionsDir();
584
+ return import_path2.default.join(SESSIONS_DIR, `lock-${sessionId}`);
549
585
  }
550
586
  function tryAcquireLock(lockPath) {
551
587
  try {
@@ -578,8 +614,8 @@ function clearStaleLock(lockPath) {
578
614
  function sleep(ms) {
579
615
  return new Promise((r) => setTimeout(r, ms));
580
616
  }
581
- async function acquireLock(source) {
582
- const lockPath = lockFilePath(source);
617
+ async function acquireLock(sessionId) {
618
+ const lockPath = lockFilePath(sessionId);
583
619
  const deadline = Date.now() + LOCK_STALE_MS;
584
620
  while (Date.now() < deadline) {
585
621
  if (tryAcquireLock(lockPath)) return;
@@ -593,18 +629,18 @@ async function acquireLock(source) {
593
629
  if (!tryAcquireLock(lockPath)) {
594
630
  }
595
631
  }
596
- function releaseLock(source) {
632
+ function releaseLock(sessionId) {
597
633
  try {
598
- import_fs3.default.unlinkSync(lockFilePath(source));
634
+ import_fs3.default.unlinkSync(lockFilePath(sessionId));
599
635
  } catch {
600
636
  }
601
637
  }
602
- async function withSessionLock(source, fn) {
603
- await acquireLock(source);
638
+ async function withSessionLock(sessionId, fn) {
639
+ await acquireLock(sessionId);
604
640
  try {
605
641
  return await fn();
606
642
  } finally {
607
- releaseLock(source);
643
+ releaseLock(sessionId);
608
644
  }
609
645
  }
610
646
  function readStdin() {
@@ -631,20 +667,22 @@ function normalizePayload(source, raw) {
631
667
  // src/hooks/shared/session-end.ts
632
668
  async function handleSessionEnd(source) {
633
669
  let sessionId;
670
+ let detected = source;
634
671
  try {
635
672
  const input = await readStdin();
636
673
  const raw = JSON.parse(input);
637
- const detected2 = detectSource(raw);
638
- const hookData = normalizePayload(detected2, raw);
674
+ detected = detectSource(raw);
675
+ const hookData = normalizePayload(detected, raw);
639
676
  sessionId = typeof hookData.session_id === "string" && hookData.session_id || void 0;
640
677
  } catch {
641
678
  }
642
- const detected = detectSource();
643
- await withSessionLock(detected, async () => {
644
- const collector = createCollector(sessionId || loadSessionId(detected), detected);
679
+ const resolvedId = sessionId || loadSessionId(detected);
680
+ if (!resolvedId) return;
681
+ await withSessionLock(resolvedId, async () => {
682
+ const collector = createCollector(resolvedId, detected);
645
683
  await collector.stop();
646
684
  });
647
- removeSessionState(detected);
685
+ removeSessionState(resolvedId);
648
686
  }
649
687
 
650
688
  // src/hooks/cursor/session-end.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/config.ts","../../../../shared/dist/types.js","../../../../shared/src/scoring.ts","../../../../shared/src/anticheat.ts","../../../../shared/src/index.ts","../../../src/api-client.ts","../../../src/queue.ts","../../../src/collector.ts","../../../src/hooks/shared/utils.ts","../../../src/hooks/shared/session-end.ts","../../../src/hooks/cursor/session-end.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport os from 'os';\n\nexport const CONFIG_DIR = path.join(os.homedir(), '.hackersbaby');\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\nexport const QUEUE_FILE = path.join(CONFIG_DIR, 'queue.jsonl');\n\nexport type Source = 'claude' | 'cursor';\n\nexport interface PluginConfig {\n token: string;\n refresh_token: string;\n user_id: string;\n server: string;\n targets?: Source[];\n preferences: {\n dashboard_port: number;\n batch_interval_ms: number;\n };\n}\n\nexport function sessionStateFile(source: Source): string {\n return path.join(CONFIG_DIR, `active-session-${source}.json`);\n}\n\nexport function ensureConfigDir(): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\nexport function loadConfig(): PluginConfig | null {\n try {\n if (!fs.existsSync(CONFIG_FILE)) return null;\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\n return JSON.parse(raw) as PluginConfig;\n } catch {\n return null;\n }\n}\n\nexport function saveConfig(config: PluginConfig): void {\n ensureConfigDir();\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');\n}\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n//# sourceMappingURL=types.js.map","import type { ActionType } from './types';\n\nexport const SESSION_BASE_POINT_CAP = 20000;\nexport const TOOL_CALLS_PER_HOUR_CAP = 4000;\nexport const MIN_SESSION_TOKENS = 2000;\n\nexport const BASE_POINTS: Record<ActionType, { perUnit: number; unitSize: number }> = {\n token_input: { perUnit: 1, unitSize: 1000 },\n token_output: { perUnit: 2, unitSize: 1000 },\n tool_call: { perUnit: 5, unitSize: 1 },\n file_created: { perUnit: 15, unitSize: 1 },\n file_edited: { perUnit: 10, unitSize: 1 },\n commit: { perUnit: 50, unitSize: 1 },\n session_completed: { perUnit: 100, unitSize: 1 },\n deployment: { perUnit: 200, unitSize: 1 },\n prompt_submitted: { perUnit: 15, unitSize: 1 },\n subagent_spawned: { perUnit: 40, unitSize: 1 },\n context_compacted: { perUnit: 30, unitSize: 1 },\n stop_response: { perUnit: 10, unitSize: 1 },\n};\n\nexport const DEPLOY_PATTERNS = [\n /^vercel\\s+(--prod|deploy)/, /^netlify\\s+deploy/, /^fly\\s+deploy/, /^railway\\s+up/,\n /^git\\s+push\\s+\\S+\\s+(main|master|production)/,\n /^(npm|yarn|pnpm)\\s+run\\s+deploy/, /^(yarn|pnpm)\\s+deploy/,\n];\n\nexport function getBasePoints(actionType: ActionType, value: number): number {\n const config = BASE_POINTS[actionType];\n return Math.floor(value / config.unitSize) * config.perUnit;\n}\n\ninterface MultiplierInput { streak_days: number; commit_quality: boolean; language_count: number; }\n\nexport function calculateMultiplier(input: MultiplierInput): number {\n let streak = 1.0;\n if (input.streak_days >= 30) streak = 3.0;\n else if (input.streak_days >= 7) streak = 2.0;\n else if (input.streak_days >= 3) streak = 1.5;\n const quality = input.commit_quality ? 1.5 : 1.0;\n const diversity = input.language_count >= 3 ? 1.2 : 1.0;\n return streak * quality * diversity;\n}\n\nexport function calculateSessionScore(events: Array<{ action_type: ActionType; value: number }>, multiplier: number): number {\n let totalBase = 0;\n for (const e of events) totalBase += getBasePoints(e.action_type, e.value);\n return Math.floor(Math.min(totalBase, SESSION_BASE_POINT_CAP) * multiplier);\n}\n\nexport function isDeployCommand(command: string): boolean {\n return DEPLOY_PATTERNS.some((p) => p.test(command.trim()));\n}\n","/**\n * Anti-cheat cryptographic primitives.\n * Shared between plugin (client) and server.\n *\n * - Event chain: each event hash includes the previous, creating a tamper-evident chain\n * - Batch HMAC: the entire batch is signed with a server-issued session secret\n */\n\nimport { createHmac, createHash } from 'crypto';\n\n/** Hash a single event, chaining it to the previous hash */\nexport function hashEvent(\n event: { action_type: string; value: number; timestamp: string },\n prevHash: string,\n): string {\n const payload = `${prevHash}|${event.action_type}|${event.value}|${event.timestamp}`;\n return createHash('sha256').update(payload).digest('hex').slice(0, 16);\n}\n\n/** Compute the chain hash for an array of events */\nexport function computeChainHash(\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n initialHash: string = '0000000000000000',\n): string {\n let hash = initialHash;\n for (const event of events) {\n hash = hashEvent(event, hash);\n }\n return hash;\n}\n\n/** Sign a batch with HMAC-SHA256 using the session secret */\nexport function signBatch(\n sessionId: string,\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n chainHash: string,\n secret: string,\n): string {\n // Canonical form: sessionId + chainHash + event count + sum of values\n const valueSum = events.reduce((s, e) => s + e.value, 0);\n const message = `${sessionId}|${chainHash}|${events.length}|${valueSum}`;\n return createHmac('sha256', secret).update(message).digest('hex');\n}\n\n/** Verify a batch HMAC signature */\nexport function verifyBatchSignature(\n sessionId: string,\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n chainHash: string,\n signature: string,\n secret: string,\n): boolean {\n const expected = signBatch(sessionId, events, chainHash, secret);\n // Constant-time comparison\n if (expected.length !== signature.length) return false;\n let mismatch = 0;\n for (let i = 0; i < expected.length; i++) {\n mismatch |= expected.charCodeAt(i) ^ signature.charCodeAt(i);\n }\n return mismatch === 0;\n}\n","export * from './types';\nexport * from './scoring';\nexport * from './anticheat';\n","import { loadConfig, saveConfig } from './config';\nimport { computeChainHash, signBatch } from '@hackersbaby/shared';\nimport type { EventBatch } from '@hackersbaby/shared';\n\nexport class APIClient {\n private config = loadConfig();\n private sessionSecret: string | null = null;\n private chainHash: string = '0000000000000000';\n private onChainUpdate: ((chainHash: string, secret: string) => void) | null = null;\n\n /** Set callback to persist chain state between hook processes */\n setChainUpdateCallback(cb: (chainHash: string, secret: string) => void) {\n this.onChainUpdate = cb;\n }\n\n /** Restore crypto state from a previous session */\n restoreCryptoState(secret: string, chainHash: string) {\n this.sessionSecret = secret;\n this.chainHash = chainHash;\n }\n\n private get headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config?.token || ''}`,\n };\n }\n\n /** Get current crypto state for persistence */\n getCryptoState(): { secret: string; chainHash: string } | null {\n if (!this.sessionSecret) return null;\n return { secret: this.sessionSecret, chainHash: this.chainHash };\n }\n\n /** Register session with server and get cryptographic secret */\n async startSession(sessionId: string): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/sessions/start`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ session_id: sessionId }),\n });\n if (res.ok) {\n const data = await res.json() as { session_secret: string; initial_chain_hash: string };\n this.sessionSecret = data.session_secret;\n this.chainHash = data.initial_chain_hash;\n return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n\n async sendBatch(batch: EventBatch): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n\n // Sign the batch if we have a session secret\n if (this.sessionSecret) {\n const prevChainHash = this.chainHash;\n const newChainHash = computeChainHash(batch.events, prevChainHash);\n const signature = signBatch(batch.session_id, batch.events, newChainHash, this.sessionSecret);\n\n batch.signature = signature;\n batch.chain_hash = newChainHash;\n batch.prev_chain_hash = prevChainHash;\n\n const res = await fetch(`${server}/api/scores/batch`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(batch),\n });\n\n if (res.ok) {\n // Only advance chain hash after server confirms acceptance\n this.chainHash = newChainHash;\n if (this.onChainUpdate) {\n this.onChainUpdate(this.chainHash, this.sessionSecret);\n }\n }\n\n return res.ok;\n }\n\n const res = await fetch(`${server}/api/scores/batch`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(batch),\n });\n return res.ok;\n } catch {\n return false;\n }\n }\n\n async refreshTokenIfNeeded(): Promise<void> {\n if (!this.config) return;\n try {\n const parts = this.config.token.split('.');\n if (parts.length !== 3) return;\n const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));\n const expMs = payload.exp * 1000;\n const oneDayMs = 24 * 60 * 60 * 1000;\n if (Date.now() + oneDayMs < expMs) return; // more than 1 day remaining, no refresh needed\n\n const res = await fetch(`${this.config.server}/api/auth/cli-token/refresh`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ refresh_token: this.config.refresh_token }),\n });\n if (res.ok) {\n const data = (await res.json()) as { token?: string; refresh_token?: string };\n if (data.token) {\n this.config.token = data.token;\n if (data.refresh_token) this.config.refresh_token = data.refresh_token;\n saveConfig(this.config);\n }\n }\n } catch {\n // silently ignore refresh errors\n }\n }\n\n async getStatus(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me`, { headers: this.headers });\n if (!res.ok) throw new Error(`getStatus failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getRank(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me/rank`, { headers: this.headers });\n if (!res.ok) throw new Error(`getRank failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getLeaderboard(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/leaderboard/global?limit=10`, { headers: this.headers });\n if (!res.ok) throw new Error(`getLeaderboard failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getPublicStats(): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/stats/public`);\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n\n async getNotifications(): Promise<{ notifications: Array<{ type: string; message: string }> } | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me/notifications`, { headers: this.headers });\n if (!res.ok) return null;\n return res.json() as Promise<{ notifications: Array<{ type: string; message: string }> }>;\n } catch {\n return null;\n }\n }\n\n async claimReferral(code: string): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/referral/claim`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ referral_code: code }),\n });\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n\n async getReferralStats(): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/referral/stats`, { headers: this.headers });\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n}\n","import fs from 'fs';\nimport { QUEUE_FILE, ensureConfigDir } from './config';\nimport type { RawEvent } from '@hackersbaby/shared';\n\nconst MAX_QUEUE_SIZE = 10_000;\nconst MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours\n\nexport function enqueueEvents(events: RawEvent[]): void {\n ensureConfigDir();\n const lines = events.map((e) => JSON.stringify(e)).join('\\n');\n fs.appendFileSync(QUEUE_FILE, lines + '\\n', 'utf-8');\n\n // Trim to MAX_QUEUE_SIZE\n try {\n const content = fs.readFileSync(QUEUE_FILE, 'utf-8');\n const allLines = content.split('\\n').filter((l) => l.trim() !== '');\n if (allLines.length > MAX_QUEUE_SIZE) {\n const trimmed = allLines.slice(allLines.length - MAX_QUEUE_SIZE);\n fs.writeFileSync(QUEUE_FILE, trimmed.join('\\n') + '\\n', 'utf-8');\n }\n } catch {\n // ignore read/write errors on trim\n }\n}\n\nexport function drainQueue(): RawEvent[] {\n try {\n if (!fs.existsSync(QUEUE_FILE)) return [];\n const content = fs.readFileSync(QUEUE_FILE, 'utf-8');\n fs.writeFileSync(QUEUE_FILE, '', 'utf-8');\n\n const now = Date.now();\n const events: RawEvent[] = [];\n for (const line of content.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const event = JSON.parse(line) as RawEvent;\n const age = now - new Date(event.timestamp).getTime();\n if (age <= MAX_AGE_MS) {\n events.push(event);\n }\n } catch {\n // skip malformed lines\n }\n }\n return events;\n } catch {\n return [];\n }\n}\n","import { v4 as uuidv4 } from 'uuid';\nimport { APIClient } from './api-client';\nimport { enqueueEvents, drainQueue } from './queue';\nimport { loadConfig } from './config';\nimport type { Source } from './config';\nimport type { RawEvent, ActionType } from '@hackersbaby/shared';\n\ntype RawEventInput = Omit<RawEvent, 'timestamp'>;\n\nexport class EventCollector {\n readonly sessionId: string;\n private buffer: RawEvent[] = [];\n private flushInterval: ReturnType<typeof setInterval> | null = null;\n readonly client: APIClient;\n private source?: Source;\n\n constructor(sessionId?: string, source?: Source) {\n this.sessionId = sessionId ?? uuidv4();\n this.client = new APIClient();\n this.source = source;\n }\n\n /** Restore crypto state from persisted session data */\n restoreCrypto(secret: string, chainHash: string) {\n this.client.restoreCryptoState(secret, chainHash);\n }\n\n /** Set callback to persist crypto state after each batch */\n setCryptoUpdateCallback(cb: (chainHash: string, secret: string) => void) {\n this.client.setChainUpdateCallback(cb);\n }\n\n addEvent(event: RawEventInput): void {\n const metadata = this.source\n ? { ...event.metadata, source: this.source }\n : event.metadata;\n this.buffer.push({\n ...event,\n metadata,\n timestamp: new Date().toISOString(),\n });\n }\n\n async start(): Promise<void> {\n await this.client.refreshTokenIfNeeded();\n\n // Register session with server for cryptographic signing\n await this.client.startSession(this.sessionId);\n\n // Clear stale queue — events from previous sessions have old timestamps\n // that the server will reject, and retrying them risks chain desync.\n drainQueue();\n\n const config = loadConfig();\n const intervalMs = config?.preferences?.batch_interval_ms ?? 10_000;\n this.flushInterval = setInterval(() => {\n this.flush().catch(() => {});\n }, intervalMs);\n }\n\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n const events = this.buffer.splice(0);\n const batch = { session_id: this.sessionId, events };\n const ok = await this.client.sendBatch(batch);\n if (!ok) {\n enqueueEvents(events);\n }\n }\n\n async stop(): Promise<void> {\n if (this.flushInterval !== null) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n this.addEvent({ action_type: 'session_completed' as ActionType, value: 1, metadata: {} });\n await this.flush();\n }\n}\n","import fs from 'fs';\nimport path from 'path';\nimport type { Source } from '../../config';\nimport { sessionStateFile, CONFIG_DIR } from '../../config';\n\nexport interface SessionState {\n sessionId: string;\n pid: number;\n source: Source;\n sessionSecret?: string;\n chainHash?: string;\n}\n\nexport function loadSessionState(source: Source): SessionState | undefined {\n try {\n const file = sessionStateFile(source);\n if (!fs.existsSync(file)) return undefined;\n return JSON.parse(fs.readFileSync(file, 'utf-8')) as SessionState;\n } catch {\n return undefined;\n }\n}\n\nexport function loadSessionId(source: Source): string | undefined {\n return loadSessionState(source)?.sessionId;\n}\n\nexport function updateSessionState(source: Source, updates: Partial<SessionState>): void {\n const state = loadSessionState(source);\n if (!state) return;\n const file = sessionStateFile(source);\n fs.writeFileSync(file, JSON.stringify({ ...state, ...updates }));\n}\n\nexport function saveSessionState(state: SessionState): void {\n const file = sessionStateFile(state.source);\n fs.writeFileSync(file, JSON.stringify(state));\n}\n\n/** Create an EventCollector with crypto state restored from the session file */\nexport function createCollector(sessionId: string | undefined, source: Source): import('../../collector').EventCollector {\n const { EventCollector } = require('../../collector');\n const collector = new EventCollector(sessionId, source);\n\n // Restore crypto state from session file\n const state = loadSessionState(source);\n if (state?.sessionSecret && state?.chainHash && state.sessionId === sessionId) {\n collector.restoreCrypto(state.sessionSecret, state.chainHash);\n\n // Update session file after each signed batch\n collector.setCryptoUpdateCallback((chainHash: string, secret: string) => {\n updateSessionState(source, { chainHash, sessionSecret: secret });\n });\n }\n\n return collector;\n}\n\nexport function removeSessionState(source: Source): void {\n const file = sessionStateFile(source);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n}\n\n// --- File-based lock to serialize batch sending across concurrent hook processes ---\n\nconst LOCK_STALE_MS = 5_000; // 5 seconds — force-remove stale locks\nconst LOCK_POLL_MS = 20; // polling interval\n\nfunction lockFilePath(source: Source): string {\n return path.join(CONFIG_DIR, `lock-${source}`);\n}\n\nfunction tryAcquireLock(lockPath: string): boolean {\n try {\n const fd = fs.openSync(lockPath, 'wx');\n fs.writeSync(fd, `${process.pid}\\n${Date.now()}`);\n fs.closeSync(fd);\n return true;\n } catch (e: any) {\n if (e.code === 'EEXIST') return false;\n throw e;\n }\n}\n\nfunction clearStaleLock(lockPath: string): boolean {\n try {\n const content = fs.readFileSync(lockPath, 'utf-8');\n const lockTime = parseInt(content.split('\\n')[1] || '0', 10);\n if (Date.now() - lockTime > LOCK_STALE_MS) {\n try { fs.unlinkSync(lockPath); } catch {}\n return true; // was stale, removed\n }\n } catch { return true; /* vanished — retry */ }\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function acquireLock(source: Source): Promise<void> {\n const lockPath = lockFilePath(source);\n const deadline = Date.now() + LOCK_STALE_MS;\n\n while (Date.now() < deadline) {\n if (tryAcquireLock(lockPath)) return;\n clearStaleLock(lockPath);\n await sleep(LOCK_POLL_MS + Math.random() * 30);\n }\n\n // Timeout — force-remove and take lock\n try { fs.unlinkSync(lockPath); } catch {}\n if (!tryAcquireLock(lockPath)) {\n // Another process grabbed it — proceed without lock (best-effort)\n }\n}\n\nfunction releaseLock(source: Source): void {\n try { fs.unlinkSync(lockFilePath(source)); } catch {}\n}\n\n/**\n * Execute a callback while holding a file lock for the given source.\n * Serializes all hook processes for the same source (claude/cursor)\n * to prevent concurrent batch sends from causing chain hash desync.\n */\nexport async function withSessionLock<T>(source: Source, fn: () => Promise<T>): Promise<T> {\n await acquireLock(source);\n try {\n return await fn();\n } finally {\n releaseLock(source);\n }\n}\n\nexport function readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let input = '';\n process.stdin.on('data', (chunk) => { input += chunk; });\n process.stdin.on('end', () => resolve(input));\n });\n}\n\n/**\n * Detect source at runtime. Cursor sets CURSOR_VERSION env var\n * and includes cursor_version in the hook payload.\n * Since Cursor reads ~/.claude/settings.json directly, both tools\n * fire the same hooks — so we detect rather than relying on entry points.\n */\nexport function detectSource(payload?: Record<string, unknown>): Source {\n if (process.env.CURSOR_VERSION) return 'cursor';\n if (payload?.cursor_version) return 'cursor';\n return 'claude';\n}\n\n/**\n * Normalize hook payload across tools.\n * Currently an identity function — Cursor's payload format matches Claude Code's.\n * If formats diverge, add field mappings here (e.g., raw.toolName → tool_name).\n */\nexport function normalizePayload(source: Source, raw: Record<string, unknown>): Record<string, unknown> {\n if (source === 'cursor') {\n return { ...raw };\n }\n return raw;\n}\n","import type { Source } from '../../config';\nimport { loadSessionId, removeSessionState, readStdin, detectSource, normalizePayload, createCollector, withSessionLock } from './utils';\n\nexport async function handleSessionEnd(source: Source): Promise<void> {\n // Read session_id from payload (consistent with other hooks)\n let sessionId: string | undefined;\n try {\n const input = await readStdin();\n const raw = JSON.parse(input);\n const detected = detectSource(raw);\n const hookData = normalizePayload(detected, raw);\n sessionId = (typeof hookData.session_id === 'string' && hookData.session_id) || undefined;\n } catch {}\n\n const detected = detectSource();\n\n await withSessionLock(detected, async () => {\n const collector = createCollector(sessionId || loadSessionId(detected), detected);\n await collector.stop();\n });\n\n removeSessionState(detected);\n}\n","import { handleSessionEnd } from '../shared/session-end';\nhandleSessionEnd('cursor');\n"],"mappings":"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","names":["path","fs","os","exports","exports","exports","computeChainHash","signBatch","exports","res","fs","import_fs","uuidv4","import_fs","import_path","fs","EventCollector","fs","path","detected"]}
1
+ {"version":3,"sources":["../../../src/config.ts","../../../../shared/dist/types.js","../../../../shared/src/scoring.ts","../../../../shared/src/anticheat.ts","../../../../shared/src/index.ts","../../../src/api-client.ts","../../../src/queue.ts","../../../src/collector.ts","../../../src/hooks/shared/utils.ts","../../../src/hooks/shared/session-end.ts","../../../src/hooks/cursor/session-end.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport os from 'os';\n\nexport const CONFIG_DIR = path.join(os.homedir(), '.hackersbaby');\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\nexport const QUEUE_FILE = path.join(CONFIG_DIR, 'queue.jsonl');\n\nexport type Source = 'claude' | 'cursor';\n\nexport interface PluginConfig {\n token: string;\n refresh_token: string;\n user_id: string;\n server: string;\n targets?: Source[];\n preferences: {\n dashboard_port: number;\n batch_interval_ms: number;\n };\n}\n\nexport const SESSIONS_DIR = path.join(CONFIG_DIR, 'sessions');\n\nexport function sessionStateFile(sessionId: string): string {\n return path.join(SESSIONS_DIR, `${sessionId}.json`);\n}\n\n/** @deprecated — only used for migrating old per-source state files */\nexport function legacySessionStateFile(source: Source): string {\n return path.join(CONFIG_DIR, `active-session-${source}.json`);\n}\n\nexport function ensureConfigDir(): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\nexport function loadConfig(): PluginConfig | null {\n try {\n if (!fs.existsSync(CONFIG_FILE)) return null;\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\n return JSON.parse(raw) as PluginConfig;\n } catch {\n return null;\n }\n}\n\nexport function saveConfig(config: PluginConfig): void {\n ensureConfigDir();\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');\n}\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n//# sourceMappingURL=types.js.map","import type { ActionType } from './types';\n\nexport const SESSION_BASE_POINT_CAP = 20000;\nexport const TOOL_CALLS_PER_HOUR_CAP = 4000;\nexport const MIN_SESSION_TOKENS = 2000;\n\nexport const BASE_POINTS: Record<ActionType, { perUnit: number; unitSize: number }> = {\n token_input: { perUnit: 1, unitSize: 1000 },\n token_output: { perUnit: 2, unitSize: 1000 },\n tool_call: { perUnit: 5, unitSize: 1 },\n file_created: { perUnit: 15, unitSize: 1 },\n file_edited: { perUnit: 10, unitSize: 1 },\n commit: { perUnit: 50, unitSize: 1 },\n session_completed: { perUnit: 100, unitSize: 1 },\n deployment: { perUnit: 200, unitSize: 1 },\n prompt_submitted: { perUnit: 15, unitSize: 1 },\n subagent_spawned: { perUnit: 40, unitSize: 1 },\n context_compacted: { perUnit: 30, unitSize: 1 },\n stop_response: { perUnit: 10, unitSize: 1 },\n};\n\nexport const DEPLOY_PATTERNS = [\n /^vercel\\s+(--prod|deploy)/, /^netlify\\s+deploy/, /^fly\\s+deploy/, /^railway\\s+up/,\n /^git\\s+push\\s+\\S+\\s+(main|master|production)/,\n /^(npm|yarn|pnpm)\\s+run\\s+deploy/, /^(yarn|pnpm)\\s+deploy/,\n];\n\nexport function getBasePoints(actionType: ActionType, value: number): number {\n const config = BASE_POINTS[actionType];\n return Math.floor(value / config.unitSize) * config.perUnit;\n}\n\ninterface MultiplierInput { streak_days: number; commit_quality: boolean; language_count: number; }\n\nexport function calculateMultiplier(input: MultiplierInput): number {\n let streak = 1.0;\n if (input.streak_days >= 30) streak = 3.0;\n else if (input.streak_days >= 7) streak = 2.0;\n else if (input.streak_days >= 3) streak = 1.5;\n const quality = input.commit_quality ? 1.5 : 1.0;\n const diversity = input.language_count >= 3 ? 1.2 : 1.0;\n return streak * quality * diversity;\n}\n\nexport function calculateSessionScore(events: Array<{ action_type: ActionType; value: number }>, multiplier: number): number {\n let totalBase = 0;\n for (const e of events) totalBase += getBasePoints(e.action_type, e.value);\n return Math.floor(Math.min(totalBase, SESSION_BASE_POINT_CAP) * multiplier);\n}\n\nexport function isDeployCommand(command: string): boolean {\n return DEPLOY_PATTERNS.some((p) => p.test(command.trim()));\n}\n","/**\n * Anti-cheat cryptographic primitives.\n * Shared between plugin (client) and server.\n *\n * - Event chain: each event hash includes the previous, creating a tamper-evident chain\n * - Batch HMAC: the entire batch is signed with a server-issued session secret\n */\n\nimport { createHmac, createHash } from 'crypto';\n\n/** Hash a single event, chaining it to the previous hash */\nexport function hashEvent(\n event: { action_type: string; value: number; timestamp: string },\n prevHash: string,\n): string {\n const payload = `${prevHash}|${event.action_type}|${event.value}|${event.timestamp}`;\n return createHash('sha256').update(payload).digest('hex').slice(0, 16);\n}\n\n/** Compute the chain hash for an array of events */\nexport function computeChainHash(\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n initialHash: string = '0000000000000000',\n): string {\n let hash = initialHash;\n for (const event of events) {\n hash = hashEvent(event, hash);\n }\n return hash;\n}\n\n/** Sign a batch with HMAC-SHA256 using the session secret */\nexport function signBatch(\n sessionId: string,\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n chainHash: string,\n secret: string,\n): string {\n // Canonical form: sessionId + chainHash + event count + sum of values\n const valueSum = events.reduce((s, e) => s + e.value, 0);\n const message = `${sessionId}|${chainHash}|${events.length}|${valueSum}`;\n return createHmac('sha256', secret).update(message).digest('hex');\n}\n\n/** Verify a batch HMAC signature */\nexport function verifyBatchSignature(\n sessionId: string,\n events: Array<{ action_type: string; value: number; timestamp: string }>,\n chainHash: string,\n signature: string,\n secret: string,\n): boolean {\n const expected = signBatch(sessionId, events, chainHash, secret);\n // Constant-time comparison\n if (expected.length !== signature.length) return false;\n let mismatch = 0;\n for (let i = 0; i < expected.length; i++) {\n mismatch |= expected.charCodeAt(i) ^ signature.charCodeAt(i);\n }\n return mismatch === 0;\n}\n","export * from './types';\nexport * from './scoring';\nexport * from './anticheat';\n","import { loadConfig, saveConfig } from './config';\nimport { computeChainHash, signBatch } from '@hackersbaby/shared';\nimport type { EventBatch } from '@hackersbaby/shared';\n\nexport class APIClient {\n private config = loadConfig();\n private sessionSecret: string | null = null;\n private chainHash: string = '0000000000000000';\n private onChainUpdate: ((chainHash: string, secret: string) => void) | null = null;\n\n /** Set callback to persist chain state between hook processes */\n setChainUpdateCallback(cb: (chainHash: string, secret: string) => void) {\n this.onChainUpdate = cb;\n }\n\n /** Restore crypto state from a previous session */\n restoreCryptoState(secret: string, chainHash: string) {\n this.sessionSecret = secret;\n this.chainHash = chainHash;\n }\n\n private get headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config?.token || ''}`,\n };\n }\n\n /** Get current crypto state for persistence */\n getCryptoState(): { secret: string; chainHash: string } | null {\n if (!this.sessionSecret) return null;\n return { secret: this.sessionSecret, chainHash: this.chainHash };\n }\n\n /** Register session with server and get cryptographic secret */\n async startSession(sessionId: string): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/sessions/start`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ session_id: sessionId }),\n });\n if (res.ok) {\n const data = await res.json() as { session_secret: string; initial_chain_hash: string };\n this.sessionSecret = data.session_secret;\n this.chainHash = data.initial_chain_hash;\n return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n\n async sendBatch(batch: EventBatch): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n\n // Sign the batch if we have a session secret\n if (this.sessionSecret) {\n const prevChainHash = this.chainHash;\n const newChainHash = computeChainHash(batch.events, prevChainHash);\n const signature = signBatch(batch.session_id, batch.events, newChainHash, this.sessionSecret);\n\n batch.signature = signature;\n batch.chain_hash = newChainHash;\n batch.prev_chain_hash = prevChainHash;\n\n const res = await fetch(`${server}/api/scores/batch`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(batch),\n });\n\n if (res.ok) {\n // Only advance chain hash after server confirms acceptance\n this.chainHash = newChainHash;\n if (this.onChainUpdate) {\n this.onChainUpdate(this.chainHash, this.sessionSecret);\n }\n }\n\n return res.ok;\n }\n\n const res = await fetch(`${server}/api/scores/batch`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(batch),\n });\n return res.ok;\n } catch {\n return false;\n }\n }\n\n async refreshTokenIfNeeded(): Promise<void> {\n if (!this.config) return;\n try {\n const parts = this.config.token.split('.');\n if (parts.length !== 3) return;\n const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));\n const expMs = payload.exp * 1000;\n const oneDayMs = 24 * 60 * 60 * 1000;\n if (Date.now() + oneDayMs < expMs) return; // more than 1 day remaining, no refresh needed\n\n const res = await fetch(`${this.config.server}/api/auth/cli-token/refresh`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ refresh_token: this.config.refresh_token }),\n });\n if (res.ok) {\n const data = (await res.json()) as { token?: string; refresh_token?: string };\n if (data.token) {\n this.config.token = data.token;\n if (data.refresh_token) this.config.refresh_token = data.refresh_token;\n saveConfig(this.config);\n }\n }\n } catch {\n // silently ignore refresh errors\n }\n }\n\n async getStatus(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me`, { headers: this.headers });\n if (!res.ok) throw new Error(`getStatus failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getRank(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me/rank`, { headers: this.headers });\n if (!res.ok) throw new Error(`getRank failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getLeaderboard(): Promise<Record<string, unknown>> {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/leaderboard/global?limit=10`, { headers: this.headers });\n if (!res.ok) throw new Error(`getLeaderboard failed: ${res.status}`);\n return res.json() as Promise<Record<string, unknown>>;\n }\n\n async getPublicStats(): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/stats/public`);\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n\n async getNotifications(): Promise<{ notifications: Array<{ type: string; message: string }> } | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/users/me/notifications`, { headers: this.headers });\n if (!res.ok) return null;\n return res.json() as Promise<{ notifications: Array<{ type: string; message: string }> }>;\n } catch {\n return null;\n }\n }\n\n async claimReferral(code: string): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/referral/claim`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ referral_code: code }),\n });\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n\n async getReferralStats(): Promise<Record<string, unknown> | null> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\n const res = await fetch(`${server}/api/referral/stats`, { headers: this.headers });\n if (!res.ok) return null;\n return res.json() as Promise<Record<string, unknown>>;\n } catch {\n return null;\n }\n }\n}\n","import fs from 'fs';\nimport { QUEUE_FILE, ensureConfigDir } from './config';\nimport type { RawEvent } from '@hackersbaby/shared';\n\nconst MAX_QUEUE_SIZE = 10_000;\nconst MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours\n\nexport function enqueueEvents(events: RawEvent[]): void {\n ensureConfigDir();\n const lines = events.map((e) => JSON.stringify(e)).join('\\n');\n fs.appendFileSync(QUEUE_FILE, lines + '\\n', 'utf-8');\n\n // Trim to MAX_QUEUE_SIZE\n try {\n const content = fs.readFileSync(QUEUE_FILE, 'utf-8');\n const allLines = content.split('\\n').filter((l) => l.trim() !== '');\n if (allLines.length > MAX_QUEUE_SIZE) {\n const trimmed = allLines.slice(allLines.length - MAX_QUEUE_SIZE);\n fs.writeFileSync(QUEUE_FILE, trimmed.join('\\n') + '\\n', 'utf-8');\n }\n } catch {\n // ignore read/write errors on trim\n }\n}\n\nexport function drainQueue(): RawEvent[] {\n try {\n if (!fs.existsSync(QUEUE_FILE)) return [];\n const content = fs.readFileSync(QUEUE_FILE, 'utf-8');\n fs.writeFileSync(QUEUE_FILE, '', 'utf-8');\n\n const now = Date.now();\n const events: RawEvent[] = [];\n for (const line of content.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const event = JSON.parse(line) as RawEvent;\n const age = now - new Date(event.timestamp).getTime();\n if (age <= MAX_AGE_MS) {\n events.push(event);\n }\n } catch {\n // skip malformed lines\n }\n }\n return events;\n } catch {\n return [];\n }\n}\n","import { v4 as uuidv4 } from 'uuid';\nimport { APIClient } from './api-client';\nimport { enqueueEvents, drainQueue } from './queue';\nimport { loadConfig } from './config';\nimport type { Source } from './config';\nimport type { RawEvent, ActionType } from '@hackersbaby/shared';\n\ntype RawEventInput = Omit<RawEvent, 'timestamp'>;\n\nexport class EventCollector {\n readonly sessionId: string;\n private buffer: RawEvent[] = [];\n private flushInterval: ReturnType<typeof setInterval> | null = null;\n readonly client: APIClient;\n private source?: Source;\n\n constructor(sessionId?: string, source?: Source) {\n this.sessionId = sessionId ?? uuidv4();\n this.client = new APIClient();\n this.source = source;\n }\n\n /** Restore crypto state from persisted session data */\n restoreCrypto(secret: string, chainHash: string) {\n this.client.restoreCryptoState(secret, chainHash);\n }\n\n /** Set callback to persist crypto state after each batch */\n setCryptoUpdateCallback(cb: (chainHash: string, secret: string) => void) {\n this.client.setChainUpdateCallback(cb);\n }\n\n addEvent(event: RawEventInput): void {\n const metadata = this.source\n ? { ...event.metadata, source: this.source }\n : event.metadata;\n this.buffer.push({\n ...event,\n metadata,\n timestamp: new Date().toISOString(),\n });\n }\n\n async start(): Promise<void> {\n await this.client.refreshTokenIfNeeded();\n\n // Register session with server for cryptographic signing\n await this.client.startSession(this.sessionId);\n\n // Clear stale queue — events from previous sessions have old timestamps\n // that the server will reject, and retrying them risks chain desync.\n drainQueue();\n\n const config = loadConfig();\n const intervalMs = config?.preferences?.batch_interval_ms ?? 10_000;\n this.flushInterval = setInterval(() => {\n this.flush().catch(() => {});\n }, intervalMs);\n }\n\n async flush(): Promise<void> {\n if (this.buffer.length === 0) return;\n const events = this.buffer.splice(0);\n const batch = { session_id: this.sessionId, events };\n const ok = await this.client.sendBatch(batch);\n if (!ok) {\n enqueueEvents(events);\n }\n }\n\n async stop(): Promise<void> {\n if (this.flushInterval !== null) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n this.addEvent({ action_type: 'session_completed' as ActionType, value: 1, metadata: {} });\n await this.flush();\n }\n}\n","import fs from 'fs';\nimport path from 'path';\nimport type { Source } from '../../config';\nimport { sessionStateFile, SESSIONS_DIR, CONFIG_DIR, legacySessionStateFile } from '../../config';\n\nexport interface SessionState {\n sessionId: string;\n pid: number;\n source: Source;\n sessionSecret?: string;\n chainHash?: string;\n}\n\nfunction ensureSessionsDir(): void {\n if (!fs.existsSync(SESSIONS_DIR)) {\n fs.mkdirSync(SESSIONS_DIR, { recursive: true });\n }\n}\n\nexport function loadSessionState(sessionId: string): SessionState | undefined {\n try {\n const file = sessionStateFile(sessionId);\n if (!fs.existsSync(file)) return undefined;\n return JSON.parse(fs.readFileSync(file, 'utf-8')) as SessionState;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Fallback: find any session file for a given source.\n * Used when session_id is missing from hook payload (legacy/edge case).\n */\nexport function findSessionBySource(source: Source): SessionState | undefined {\n try {\n if (!fs.existsSync(SESSIONS_DIR)) return undefined;\n const files = fs.readdirSync(SESSIONS_DIR).filter(f => f.endsWith('.json'));\n for (const f of files) {\n try {\n const state = JSON.parse(fs.readFileSync(path.join(SESSIONS_DIR, f), 'utf-8')) as SessionState;\n if (state.source === source) return state;\n } catch {}\n }\n } catch {}\n // Also check legacy file as last resort\n try {\n const legacyFile = legacySessionStateFile(source);\n if (fs.existsSync(legacyFile)) {\n return JSON.parse(fs.readFileSync(legacyFile, 'utf-8')) as SessionState;\n }\n } catch {}\n return undefined;\n}\n\nexport function loadSessionId(source: Source): string | undefined {\n return findSessionBySource(source)?.sessionId;\n}\n\nexport function updateSessionState(sessionId: string, updates: Partial<SessionState>): void {\n const state = loadSessionState(sessionId);\n if (!state) return;\n const file = sessionStateFile(sessionId);\n fs.writeFileSync(file, JSON.stringify({ ...state, ...updates }));\n}\n\nexport function saveSessionState(state: SessionState): void {\n ensureSessionsDir();\n const file = sessionStateFile(state.sessionId);\n fs.writeFileSync(file, JSON.stringify(state));\n}\n\n/** Create an EventCollector with crypto state restored from the session file */\nexport function createCollector(sessionId: string | undefined, source: Source): import('../../collector').EventCollector {\n const { EventCollector } = require('../../collector');\n const collector = new EventCollector(sessionId, source);\n\n // Restore crypto state from session file (keyed by sessionId)\n const resolvedId = collector.sessionId;\n const state = loadSessionState(resolvedId);\n if (state?.sessionSecret && state?.chainHash && state.sessionId === resolvedId) {\n collector.restoreCrypto(state.sessionSecret, state.chainHash);\n\n // Update session file after each signed batch\n collector.setCryptoUpdateCallback((chainHash: string, secret: string) => {\n updateSessionState(resolvedId, { chainHash, sessionSecret: secret });\n });\n }\n\n return collector;\n}\n\nexport function removeSessionState(sessionId: string): void {\n try {\n const file = sessionStateFile(sessionId);\n if (fs.existsSync(file)) fs.unlinkSync(file);\n } catch {}\n}\n\n/**\n * Clean up stale session files older than maxAge (default 24h).\n * Called during session-start to prevent buildup.\n */\nexport function cleanStaleSessionFiles(maxAgeMs: number = 7 * 24 * 60 * 60 * 1000): void {\n try {\n if (!fs.existsSync(SESSIONS_DIR)) return;\n const now = Date.now();\n for (const f of fs.readdirSync(SESSIONS_DIR)) {\n if (!f.endsWith('.json')) continue;\n const filePath = path.join(SESSIONS_DIR, f);\n try {\n const stat = fs.statSync(filePath);\n if (now - stat.mtimeMs > maxAgeMs) {\n fs.unlinkSync(filePath);\n }\n } catch {}\n }\n } catch {}\n}\n\n// --- File-based lock to serialize batch sending across concurrent hook processes ---\n\nconst LOCK_STALE_MS = 5_000; // 5 seconds — force-remove stale locks\nconst LOCK_POLL_MS = 20; // polling interval\n\nfunction lockFilePath(sessionId: string): string {\n ensureSessionsDir();\n return path.join(SESSIONS_DIR, `lock-${sessionId}`);\n}\n\nfunction tryAcquireLock(lockPath: string): boolean {\n try {\n const fd = fs.openSync(lockPath, 'wx');\n fs.writeSync(fd, `${process.pid}\\n${Date.now()}`);\n fs.closeSync(fd);\n return true;\n } catch (e: any) {\n if (e.code === 'EEXIST') return false;\n throw e;\n }\n}\n\nfunction clearStaleLock(lockPath: string): boolean {\n try {\n const content = fs.readFileSync(lockPath, 'utf-8');\n const lockTime = parseInt(content.split('\\n')[1] || '0', 10);\n if (Date.now() - lockTime > LOCK_STALE_MS) {\n try { fs.unlinkSync(lockPath); } catch {}\n return true; // was stale, removed\n }\n } catch { return true; /* vanished — retry */ }\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function acquireLock(sessionId: string): Promise<void> {\n const lockPath = lockFilePath(sessionId);\n const deadline = Date.now() + LOCK_STALE_MS;\n\n while (Date.now() < deadline) {\n if (tryAcquireLock(lockPath)) return;\n clearStaleLock(lockPath);\n await sleep(LOCK_POLL_MS + Math.random() * 30);\n }\n\n // Timeout — force-remove and take lock\n try { fs.unlinkSync(lockPath); } catch {}\n if (!tryAcquireLock(lockPath)) {\n // Another process grabbed it — proceed without lock (best-effort)\n }\n}\n\nfunction releaseLock(sessionId: string): void {\n try { fs.unlinkSync(lockFilePath(sessionId)); } catch {}\n}\n\n/**\n * Execute a callback while holding a file lock for the given session.\n * Serializes all hook processes for the same session\n * to prevent concurrent batch sends from causing chain hash desync.\n */\nexport async function withSessionLock<T>(sessionId: string, fn: () => Promise<T>): Promise<T> {\n await acquireLock(sessionId);\n try {\n return await fn();\n } finally {\n releaseLock(sessionId);\n }\n}\n\nexport function readStdin(): Promise<string> {\n return new Promise((resolve) => {\n let input = '';\n process.stdin.on('data', (chunk) => { input += chunk; });\n process.stdin.on('end', () => resolve(input));\n });\n}\n\n/**\n * Detect source at runtime. Cursor sets CURSOR_VERSION env var\n * and includes cursor_version in the hook payload.\n * Since Cursor reads ~/.claude/settings.json directly, both tools\n * fire the same hooks — so we detect rather than relying on entry points.\n */\nexport function detectSource(payload?: Record<string, unknown>): Source {\n if (process.env.CURSOR_VERSION) return 'cursor';\n if (payload?.cursor_version) return 'cursor';\n return 'claude';\n}\n\n/**\n * Normalize hook payload across tools.\n * Currently an identity function — Cursor's payload format matches Claude Code's.\n * If formats diverge, add field mappings here (e.g., raw.toolName → tool_name).\n */\nexport function normalizePayload(source: Source, raw: Record<string, unknown>): Record<string, unknown> {\n if (source === 'cursor') {\n return { ...raw };\n }\n return raw;\n}\n","import type { Source } from '../../config';\nimport { loadSessionId, removeSessionState, readStdin, detectSource, normalizePayload, createCollector, withSessionLock } from './utils';\n\nexport async function handleSessionEnd(source: Source): Promise<void> {\n let sessionId: string | undefined;\n let detected: Source = source;\n try {\n const input = await readStdin();\n const raw = JSON.parse(input);\n detected = detectSource(raw);\n const hookData = normalizePayload(detected, raw);\n sessionId = (typeof hookData.session_id === 'string' && hookData.session_id) || undefined;\n } catch {}\n\n // Resolve session ID: from payload, or fallback to finding by source\n const resolvedId = sessionId || loadSessionId(detected);\n if (!resolvedId) return; // No session to end\n\n await withSessionLock(resolvedId, async () => {\n const collector = createCollector(resolvedId, detected);\n await collector.stop();\n });\n\n removeSessionState(resolvedId);\n}\n","import { handleSessionEnd } from '../shared/session-end';\nhandleSessionEnd('cursor');\n"],"mappings":"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","names":["path","fs","os","exports","exports","exports","computeChainHash","signBatch","exports","res","fs","import_fs","uuidv4","import_fs","import_path","fs","path","EventCollector","fs","path","fs"]}