@hackersbaby/plugin 0.3.0 → 0.4.1
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.
- package/dist/cli.js +214 -115
- package/dist/cli.js.map +1 -1
- package/dist/hooks/compact.js +40 -17
- package/dist/hooks/compact.js.map +1 -1
- package/dist/hooks/cursor/compact.js +314 -0
- package/dist/hooks/cursor/compact.js.map +1 -0
- package/dist/hooks/cursor/prompt-submit.js +318 -0
- package/dist/hooks/cursor/prompt-submit.js.map +1 -0
- package/dist/hooks/cursor/session-end.js +293 -0
- package/dist/hooks/cursor/session-end.js.map +1 -0
- package/dist/hooks/cursor/session-start.js +306 -0
- package/dist/hooks/cursor/session-start.js.map +1 -0
- package/dist/hooks/cursor/stop.js +316 -0
- package/dist/hooks/cursor/stop.js.map +1 -0
- package/dist/hooks/cursor/subagent.js +316 -0
- package/dist/hooks/cursor/subagent.js.map +1 -0
- package/dist/hooks/cursor/tool-call.js +431 -0
- package/dist/hooks/cursor/tool-call.js.map +1 -0
- package/dist/hooks/prompt-submit.js +40 -17
- package/dist/hooks/prompt-submit.js.map +1 -1
- package/dist/hooks/session-end.js +26 -12
- package/dist/hooks/session-end.js.map +1 -1
- package/dist/hooks/session-start.js +22 -9
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/hooks/stop.js +40 -17
- package/dist/hooks/stop.js.map +1 -1
- package/dist/hooks/subagent.js +40 -17
- package/dist/hooks/subagent.js.map +1 -1
- package/dist/hooks/tool-call.js +46 -24
- package/dist/hooks/tool-call.js.map +1 -1
- package/dist/index.d.ts +17 -14
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/collector.ts","../../../src/config.ts","../../../src/api-client.ts","../../../src/queue.ts","../../../src/hooks/shared/utils.ts","../../../src/hooks/shared/subagent.ts","../../../src/hooks/cursor/subagent.ts"],"sourcesContent":["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 private 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 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 const queued = drainQueue();\n if (queued.length > 0) {\n const batch = { session_id: this.sessionId, events: queued };\n const ok = await this.client.sendBatch(batch);\n if (!ok) {\n enqueueEvents(queued);\n }\n }\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 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","import { loadConfig, saveConfig } from './config';\nimport type { EventBatch } from '@hackersbaby/shared';\n\nexport class APIClient {\n private config = loadConfig();\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 async sendBatch(batch: EventBatch): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\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 fs from 'fs';\nimport type { Source } from '../../config';\nimport { sessionStateFile } from '../../config';\n\nexport interface SessionState {\n sessionId: string;\n pid: number;\n source: Source;\n}\n\nexport function loadSessionId(source: Source): string | undefined {\n try {\n const file = sessionStateFile(source);\n if (!fs.existsSync(file)) return undefined;\n const data = JSON.parse(fs.readFileSync(file, 'utf-8'));\n return data.sessionId;\n } catch {\n return undefined;\n }\n}\n\nexport function saveSessionState(state: SessionState): void {\n const file = sessionStateFile(state.source);\n fs.writeFileSync(file, JSON.stringify(state));\n}\n\nexport function removeSessionState(source: Source): void {\n const file = sessionStateFile(source);\n if (fs.existsSync(file)) fs.unlinkSync(file);\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 * 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 { EventCollector } from '../../collector';\nimport type { Source } from '../../config';\nimport { loadSessionId, readStdin, normalizePayload } from './utils';\n\nexport async function handleSubagent(source: Source): Promise<void> {\n const input = await readStdin();\n try {\n const hookData = normalizePayload(source, JSON.parse(input));\n const sessionId = hookData.session_id || loadSessionId(source);\n const collector = new EventCollector(sessionId, source);\n\n // PRIVACY: Only track agent type. No task content sent.\n collector.addEvent({\n action_type: 'subagent_spawned',\n value: 1,\n metadata: {\n agent_type: hookData.agent_type || 'unknown',\n },\n });\n\n await collector.flush();\n } catch {}\n}\n","import { handleSubagent } from '../shared/subagent';\nhandleSubagent('cursor');\n"],"mappings":"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","names":["path","os","fs","import_fs","fs","uuidv4","import_fs","fs"]}
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
|
|
29
|
+
// ../shared/dist/types.js
|
|
30
|
+
var require_types = __commonJS({
|
|
31
|
+
"../shared/dist/types.js"(exports2) {
|
|
32
|
+
"use strict";
|
|
33
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// ../shared/dist/scoring.js
|
|
38
|
+
var require_scoring = __commonJS({
|
|
39
|
+
"../shared/dist/scoring.js"(exports2) {
|
|
40
|
+
"use strict";
|
|
41
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
42
|
+
exports2.DEPLOY_PATTERNS = exports2.BASE_POINTS = exports2.MIN_SESSION_TOKENS = exports2.TOOL_CALLS_PER_HOUR_CAP = exports2.SESSION_BASE_POINT_CAP = void 0;
|
|
43
|
+
exports2.getBasePoints = getBasePoints;
|
|
44
|
+
exports2.calculateMultiplier = calculateMultiplier;
|
|
45
|
+
exports2.calculateSessionScore = calculateSessionScore;
|
|
46
|
+
exports2.isDeployCommand = isDeployCommand2;
|
|
47
|
+
exports2.SESSION_BASE_POINT_CAP = 2e4;
|
|
48
|
+
exports2.TOOL_CALLS_PER_HOUR_CAP = 4e3;
|
|
49
|
+
exports2.MIN_SESSION_TOKENS = 2e3;
|
|
50
|
+
exports2.BASE_POINTS = {
|
|
51
|
+
token_input: { perUnit: 1, unitSize: 1e3 },
|
|
52
|
+
token_output: { perUnit: 2, unitSize: 1e3 },
|
|
53
|
+
tool_call: { perUnit: 5, unitSize: 1 },
|
|
54
|
+
file_created: { perUnit: 15, unitSize: 1 },
|
|
55
|
+
file_edited: { perUnit: 10, unitSize: 1 },
|
|
56
|
+
commit: { perUnit: 50, unitSize: 1 },
|
|
57
|
+
session_completed: { perUnit: 100, unitSize: 1 },
|
|
58
|
+
deployment: { perUnit: 200, unitSize: 1 },
|
|
59
|
+
prompt_submitted: { perUnit: 15, unitSize: 1 },
|
|
60
|
+
subagent_spawned: { perUnit: 40, unitSize: 1 },
|
|
61
|
+
context_compacted: { perUnit: 30, unitSize: 1 },
|
|
62
|
+
stop_response: { perUnit: 10, unitSize: 1 }
|
|
63
|
+
};
|
|
64
|
+
exports2.DEPLOY_PATTERNS = [
|
|
65
|
+
/^vercel\s+(--prod|deploy)/,
|
|
66
|
+
/^netlify\s+deploy/,
|
|
67
|
+
/^fly\s+deploy/,
|
|
68
|
+
/^railway\s+up/,
|
|
69
|
+
/^git\s+push\s+\S+\s+(main|master|production)/,
|
|
70
|
+
/^(npm|yarn|pnpm)\s+run\s+deploy/,
|
|
71
|
+
/^(yarn|pnpm)\s+deploy/
|
|
72
|
+
];
|
|
73
|
+
function getBasePoints(actionType, value) {
|
|
74
|
+
const config = exports2.BASE_POINTS[actionType];
|
|
75
|
+
return Math.floor(value / config.unitSize) * config.perUnit;
|
|
76
|
+
}
|
|
77
|
+
function calculateMultiplier(input) {
|
|
78
|
+
let streak = 1;
|
|
79
|
+
if (input.streak_days >= 30)
|
|
80
|
+
streak = 3;
|
|
81
|
+
else if (input.streak_days >= 7)
|
|
82
|
+
streak = 2;
|
|
83
|
+
else if (input.streak_days >= 3)
|
|
84
|
+
streak = 1.5;
|
|
85
|
+
const quality = input.commit_quality ? 1.5 : 1;
|
|
86
|
+
const diversity = input.language_count >= 3 ? 1.2 : 1;
|
|
87
|
+
return streak * quality * diversity;
|
|
88
|
+
}
|
|
89
|
+
function calculateSessionScore(events, multiplier) {
|
|
90
|
+
let totalBase = 0;
|
|
91
|
+
for (const e of events)
|
|
92
|
+
totalBase += getBasePoints(e.action_type, e.value);
|
|
93
|
+
return Math.floor(Math.min(totalBase, exports2.SESSION_BASE_POINT_CAP) * multiplier);
|
|
94
|
+
}
|
|
95
|
+
function isDeployCommand2(command) {
|
|
96
|
+
return exports2.DEPLOY_PATTERNS.some((p) => p.test(command.trim()));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ../shared/dist/index.js
|
|
102
|
+
var require_dist = __commonJS({
|
|
103
|
+
"../shared/dist/index.js"(exports2) {
|
|
104
|
+
"use strict";
|
|
105
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
106
|
+
if (k2 === void 0) k2 = k;
|
|
107
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
108
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
109
|
+
desc = { enumerable: true, get: function() {
|
|
110
|
+
return m[k];
|
|
111
|
+
} };
|
|
112
|
+
}
|
|
113
|
+
Object.defineProperty(o, k2, desc);
|
|
114
|
+
}) : (function(o, m, k, k2) {
|
|
115
|
+
if (k2 === void 0) k2 = k;
|
|
116
|
+
o[k2] = m[k];
|
|
117
|
+
}));
|
|
118
|
+
var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
|
|
119
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
|
|
120
|
+
};
|
|
121
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
122
|
+
__exportStar(require_types(), exports2);
|
|
123
|
+
__exportStar(require_scoring(), exports2);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// src/hooks/shared/tool-call.ts
|
|
128
|
+
var import_shared = __toESM(require_dist());
|
|
129
|
+
|
|
130
|
+
// src/collector.ts
|
|
131
|
+
var import_uuid = require("uuid");
|
|
132
|
+
|
|
133
|
+
// src/config.ts
|
|
134
|
+
var import_fs = __toESM(require("fs"));
|
|
135
|
+
var import_path = __toESM(require("path"));
|
|
136
|
+
var import_os = __toESM(require("os"));
|
|
137
|
+
var CONFIG_DIR = import_path.default.join(import_os.default.homedir(), ".hackersbaby");
|
|
138
|
+
var CONFIG_FILE = import_path.default.join(CONFIG_DIR, "config.json");
|
|
139
|
+
var QUEUE_FILE = import_path.default.join(CONFIG_DIR, "queue.jsonl");
|
|
140
|
+
function sessionStateFile(source) {
|
|
141
|
+
return import_path.default.join(CONFIG_DIR, `active-session-${source}.json`);
|
|
142
|
+
}
|
|
143
|
+
function ensureConfigDir() {
|
|
144
|
+
if (!import_fs.default.existsSync(CONFIG_DIR)) {
|
|
145
|
+
import_fs.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function loadConfig() {
|
|
149
|
+
try {
|
|
150
|
+
if (!import_fs.default.existsSync(CONFIG_FILE)) return null;
|
|
151
|
+
const raw = import_fs.default.readFileSync(CONFIG_FILE, "utf-8");
|
|
152
|
+
return JSON.parse(raw);
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function saveConfig(config) {
|
|
158
|
+
ensureConfigDir();
|
|
159
|
+
import_fs.default.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/api-client.ts
|
|
163
|
+
var APIClient = class {
|
|
164
|
+
config = loadConfig();
|
|
165
|
+
get headers() {
|
|
166
|
+
return {
|
|
167
|
+
"Content-Type": "application/json",
|
|
168
|
+
Authorization: `Bearer ${this.config?.token || ""}`
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async sendBatch(batch) {
|
|
172
|
+
try {
|
|
173
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
174
|
+
const res = await fetch(`${server}/api/scores/batch`, {
|
|
175
|
+
method: "POST",
|
|
176
|
+
headers: this.headers,
|
|
177
|
+
body: JSON.stringify(batch)
|
|
178
|
+
});
|
|
179
|
+
return res.ok;
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async refreshTokenIfNeeded() {
|
|
185
|
+
if (!this.config) return;
|
|
186
|
+
try {
|
|
187
|
+
const parts = this.config.token.split(".");
|
|
188
|
+
if (parts.length !== 3) return;
|
|
189
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64").toString("utf-8"));
|
|
190
|
+
const expMs = payload.exp * 1e3;
|
|
191
|
+
const oneDayMs = 24 * 60 * 60 * 1e3;
|
|
192
|
+
if (Date.now() + oneDayMs < expMs) return;
|
|
193
|
+
const res = await fetch(`${this.config.server}/api/auth/cli-token/refresh`, {
|
|
194
|
+
method: "POST",
|
|
195
|
+
headers: this.headers,
|
|
196
|
+
body: JSON.stringify({ refresh_token: this.config.refresh_token })
|
|
197
|
+
});
|
|
198
|
+
if (res.ok) {
|
|
199
|
+
const data = await res.json();
|
|
200
|
+
if (data.token) {
|
|
201
|
+
this.config.token = data.token;
|
|
202
|
+
if (data.refresh_token) this.config.refresh_token = data.refresh_token;
|
|
203
|
+
saveConfig(this.config);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch {
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async getStatus() {
|
|
210
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
211
|
+
const res = await fetch(`${server}/api/users/me`, { headers: this.headers });
|
|
212
|
+
if (!res.ok) throw new Error(`getStatus failed: ${res.status}`);
|
|
213
|
+
return res.json();
|
|
214
|
+
}
|
|
215
|
+
async getRank() {
|
|
216
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
217
|
+
const res = await fetch(`${server}/api/users/me/rank`, { headers: this.headers });
|
|
218
|
+
if (!res.ok) throw new Error(`getRank failed: ${res.status}`);
|
|
219
|
+
return res.json();
|
|
220
|
+
}
|
|
221
|
+
async getLeaderboard() {
|
|
222
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
223
|
+
const res = await fetch(`${server}/api/leaderboard/global?limit=10`, { headers: this.headers });
|
|
224
|
+
if (!res.ok) throw new Error(`getLeaderboard failed: ${res.status}`);
|
|
225
|
+
return res.json();
|
|
226
|
+
}
|
|
227
|
+
async getPublicStats() {
|
|
228
|
+
try {
|
|
229
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
230
|
+
const res = await fetch(`${server}/api/stats/public`);
|
|
231
|
+
if (!res.ok) return null;
|
|
232
|
+
return res.json();
|
|
233
|
+
} catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async getNotifications() {
|
|
238
|
+
try {
|
|
239
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
240
|
+
const res = await fetch(`${server}/api/users/me/notifications`, { headers: this.headers });
|
|
241
|
+
if (!res.ok) return null;
|
|
242
|
+
return res.json();
|
|
243
|
+
} catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async claimReferral(code) {
|
|
248
|
+
try {
|
|
249
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
250
|
+
const res = await fetch(`${server}/api/referral/claim`, {
|
|
251
|
+
method: "POST",
|
|
252
|
+
headers: this.headers,
|
|
253
|
+
body: JSON.stringify({ referral_code: code })
|
|
254
|
+
});
|
|
255
|
+
if (!res.ok) return null;
|
|
256
|
+
return res.json();
|
|
257
|
+
} catch {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async getReferralStats() {
|
|
262
|
+
try {
|
|
263
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
264
|
+
const res = await fetch(`${server}/api/referral/stats`, { headers: this.headers });
|
|
265
|
+
if (!res.ok) return null;
|
|
266
|
+
return res.json();
|
|
267
|
+
} catch {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// src/queue.ts
|
|
274
|
+
var import_fs2 = __toESM(require("fs"));
|
|
275
|
+
var MAX_QUEUE_SIZE = 1e4;
|
|
276
|
+
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
277
|
+
function enqueueEvents(events) {
|
|
278
|
+
ensureConfigDir();
|
|
279
|
+
const lines = events.map((e) => JSON.stringify(e)).join("\n");
|
|
280
|
+
import_fs2.default.appendFileSync(QUEUE_FILE, lines + "\n", "utf-8");
|
|
281
|
+
try {
|
|
282
|
+
const content = import_fs2.default.readFileSync(QUEUE_FILE, "utf-8");
|
|
283
|
+
const allLines = content.split("\n").filter((l) => l.trim() !== "");
|
|
284
|
+
if (allLines.length > MAX_QUEUE_SIZE) {
|
|
285
|
+
const trimmed = allLines.slice(allLines.length - MAX_QUEUE_SIZE);
|
|
286
|
+
import_fs2.default.writeFileSync(QUEUE_FILE, trimmed.join("\n") + "\n", "utf-8");
|
|
287
|
+
}
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function drainQueue() {
|
|
292
|
+
try {
|
|
293
|
+
if (!import_fs2.default.existsSync(QUEUE_FILE)) return [];
|
|
294
|
+
const content = import_fs2.default.readFileSync(QUEUE_FILE, "utf-8");
|
|
295
|
+
import_fs2.default.writeFileSync(QUEUE_FILE, "", "utf-8");
|
|
296
|
+
const now = Date.now();
|
|
297
|
+
const events = [];
|
|
298
|
+
for (const line of content.split("\n")) {
|
|
299
|
+
if (!line.trim()) continue;
|
|
300
|
+
try {
|
|
301
|
+
const event = JSON.parse(line);
|
|
302
|
+
const age = now - new Date(event.timestamp).getTime();
|
|
303
|
+
if (age <= MAX_AGE_MS) {
|
|
304
|
+
events.push(event);
|
|
305
|
+
}
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return events;
|
|
310
|
+
} catch {
|
|
311
|
+
return [];
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/collector.ts
|
|
316
|
+
var EventCollector = class {
|
|
317
|
+
sessionId;
|
|
318
|
+
buffer = [];
|
|
319
|
+
flushInterval = null;
|
|
320
|
+
client;
|
|
321
|
+
source;
|
|
322
|
+
constructor(sessionId, source) {
|
|
323
|
+
this.sessionId = sessionId ?? (0, import_uuid.v4)();
|
|
324
|
+
this.client = new APIClient();
|
|
325
|
+
this.source = source;
|
|
326
|
+
}
|
|
327
|
+
addEvent(event) {
|
|
328
|
+
const metadata = this.source ? { ...event.metadata, source: this.source } : event.metadata;
|
|
329
|
+
this.buffer.push({
|
|
330
|
+
...event,
|
|
331
|
+
metadata,
|
|
332
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
async start() {
|
|
336
|
+
await this.client.refreshTokenIfNeeded();
|
|
337
|
+
const queued = drainQueue();
|
|
338
|
+
if (queued.length > 0) {
|
|
339
|
+
const batch = { session_id: this.sessionId, events: queued };
|
|
340
|
+
const ok = await this.client.sendBatch(batch);
|
|
341
|
+
if (!ok) {
|
|
342
|
+
enqueueEvents(queued);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const config = loadConfig();
|
|
346
|
+
const intervalMs = config?.preferences?.batch_interval_ms ?? 1e4;
|
|
347
|
+
this.flushInterval = setInterval(() => {
|
|
348
|
+
this.flush().catch(() => {
|
|
349
|
+
});
|
|
350
|
+
}, intervalMs);
|
|
351
|
+
}
|
|
352
|
+
async flush() {
|
|
353
|
+
if (this.buffer.length === 0) return;
|
|
354
|
+
const events = this.buffer.splice(0);
|
|
355
|
+
const batch = { session_id: this.sessionId, events };
|
|
356
|
+
const ok = await this.client.sendBatch(batch);
|
|
357
|
+
if (!ok) {
|
|
358
|
+
enqueueEvents(events);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async stop() {
|
|
362
|
+
if (this.flushInterval !== null) {
|
|
363
|
+
clearInterval(this.flushInterval);
|
|
364
|
+
this.flushInterval = null;
|
|
365
|
+
}
|
|
366
|
+
this.addEvent({ action_type: "session_completed", value: 1, metadata: {} });
|
|
367
|
+
await this.flush();
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// src/hooks/shared/utils.ts
|
|
372
|
+
var import_fs3 = __toESM(require("fs"));
|
|
373
|
+
function loadSessionId(source) {
|
|
374
|
+
try {
|
|
375
|
+
const file = sessionStateFile(source);
|
|
376
|
+
if (!import_fs3.default.existsSync(file)) return void 0;
|
|
377
|
+
const data = JSON.parse(import_fs3.default.readFileSync(file, "utf-8"));
|
|
378
|
+
return data.sessionId;
|
|
379
|
+
} catch {
|
|
380
|
+
return void 0;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function readStdin() {
|
|
384
|
+
return new Promise((resolve) => {
|
|
385
|
+
let input = "";
|
|
386
|
+
process.stdin.on("data", (chunk) => {
|
|
387
|
+
input += chunk;
|
|
388
|
+
});
|
|
389
|
+
process.stdin.on("end", () => resolve(input));
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
function normalizePayload(source, raw) {
|
|
393
|
+
if (source === "cursor") {
|
|
394
|
+
return { ...raw };
|
|
395
|
+
}
|
|
396
|
+
return raw;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// src/hooks/shared/tool-call.ts
|
|
400
|
+
async function handleToolCall(source) {
|
|
401
|
+
const input = await readStdin();
|
|
402
|
+
try {
|
|
403
|
+
const hookData = normalizePayload(source, JSON.parse(input));
|
|
404
|
+
const toolName = hookData.tool_name || "unknown";
|
|
405
|
+
const toolInput = hookData.tool_input || {};
|
|
406
|
+
const sessionId = hookData.session_id || loadSessionId(source);
|
|
407
|
+
const collector = new EventCollector(sessionId, source);
|
|
408
|
+
collector.addEvent({ action_type: "tool_call", value: 1, metadata: { tool_name: toolName } });
|
|
409
|
+
if (hookData.input_tokens) {
|
|
410
|
+
collector.addEvent({ action_type: "token_input", value: hookData.input_tokens, metadata: {} });
|
|
411
|
+
}
|
|
412
|
+
if (hookData.output_tokens) {
|
|
413
|
+
collector.addEvent({ action_type: "token_output", value: hookData.output_tokens, metadata: {} });
|
|
414
|
+
}
|
|
415
|
+
if (toolName === "Bash" && toolInput.command && (0, import_shared.isDeployCommand)(toolInput.command)) {
|
|
416
|
+
collector.addEvent({ action_type: "deployment", value: 1, metadata: {} });
|
|
417
|
+
}
|
|
418
|
+
if (toolName === "Write") {
|
|
419
|
+
collector.addEvent({ action_type: "file_created", value: 1, metadata: {} });
|
|
420
|
+
}
|
|
421
|
+
if (toolName === "Edit") {
|
|
422
|
+
collector.addEvent({ action_type: "file_edited", value: 1, metadata: {} });
|
|
423
|
+
}
|
|
424
|
+
await collector.flush();
|
|
425
|
+
} catch {
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/hooks/cursor/tool-call.ts
|
|
430
|
+
handleToolCall("cursor");
|
|
431
|
+
//# sourceMappingURL=tool-call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../shared/dist/types.js","../../../../shared/src/scoring.ts","../../../../shared/src/index.ts","../../../src/hooks/shared/tool-call.ts","../../../src/collector.ts","../../../src/config.ts","../../../src/api-client.ts","../../../src/queue.ts","../../../src/hooks/shared/utils.ts","../../../src/hooks/cursor/tool-call.ts"],"sourcesContent":["\"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","export * from './types';\nexport * from './scoring';\n","import { isDeployCommand } from '@hackersbaby/shared';\nimport { EventCollector } from '../../collector';\nimport type { Source } from '../../config';\nimport { loadSessionId, readStdin, normalizePayload } from './utils';\n\nexport async function handleToolCall(source: Source): Promise<void> {\n const input = await readStdin();\n try {\n const hookData = normalizePayload(source, JSON.parse(input));\n const toolName = hookData.tool_name || 'unknown';\n const toolInput = hookData.tool_input || {};\n const sessionId = hookData.session_id || loadSessionId(source);\n const collector = new EventCollector(sessionId, source);\n\n // PRIVACY: Only send tool name — never send tool_input content, file paths, or commands\n collector.addEvent({ action_type: 'tool_call', value: 1, metadata: { tool_name: toolName } });\n\n // Track token usage from hook payload\n if (hookData.input_tokens) {\n collector.addEvent({ action_type: 'token_input', value: hookData.input_tokens, metadata: {} });\n }\n if (hookData.output_tokens) {\n collector.addEvent({ action_type: 'token_output', value: hookData.output_tokens, metadata: {} });\n }\n\n // Track Bash commands for deployment detection — PRIVACY: only send boolean, not the command\n if (toolName === 'Bash' && toolInput.command && isDeployCommand(toolInput.command)) {\n collector.addEvent({ action_type: 'deployment', value: 1, metadata: {} });\n }\n\n // Track file creation — PRIVACY: no file path sent\n if (toolName === 'Write') {\n collector.addEvent({ action_type: 'file_created', value: 1, metadata: {} });\n }\n\n // Track file edits — PRIVACY: no file path sent\n if (toolName === 'Edit') {\n collector.addEvent({ action_type: 'file_edited', value: 1, metadata: {} });\n }\n\n await collector.flush();\n } catch {}\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 private 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 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 const queued = drainQueue();\n if (queued.length > 0) {\n const batch = { session_id: this.sessionId, events: queued };\n const ok = await this.client.sendBatch(batch);\n if (!ok) {\n enqueueEvents(queued);\n }\n }\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 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","import { loadConfig, saveConfig } from './config';\nimport type { EventBatch } from '@hackersbaby/shared';\n\nexport class APIClient {\n private config = loadConfig();\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 async sendBatch(batch: EventBatch): Promise<boolean> {\n try {\n const server = this.config?.server || 'https://hackers.baby';\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 fs from 'fs';\nimport type { Source } from '../../config';\nimport { sessionStateFile } from '../../config';\n\nexport interface SessionState {\n sessionId: string;\n pid: number;\n source: Source;\n}\n\nexport function loadSessionId(source: Source): string | undefined {\n try {\n const file = sessionStateFile(source);\n if (!fs.existsSync(file)) return undefined;\n const data = JSON.parse(fs.readFileSync(file, 'utf-8'));\n return data.sessionId;\n } catch {\n return undefined;\n }\n}\n\nexport function saveSessionState(state: SessionState): void {\n const file = sessionStateFile(state.source);\n fs.writeFileSync(file, JSON.stringify(state));\n}\n\nexport function removeSessionState(source: Source): void {\n const file = sessionStateFile(source);\n if (fs.existsSync(file)) fs.unlinkSync(file);\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 * 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 { handleToolCall } from '../shared/tool-call';\nhandleToolCall('cursor');\n"],"mappings":"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","names":["exports","exports","isDeployCommand","exports","path","os","fs","import_fs","fs","uuidv4","import_fs","fs"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
"use strict";
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
@@ -32,6 +33,9 @@ var import_os = __toESM(require("os"));
|
|
|
32
33
|
var CONFIG_DIR = import_path.default.join(import_os.default.homedir(), ".hackersbaby");
|
|
33
34
|
var CONFIG_FILE = import_path.default.join(CONFIG_DIR, "config.json");
|
|
34
35
|
var QUEUE_FILE = import_path.default.join(CONFIG_DIR, "queue.jsonl");
|
|
36
|
+
function sessionStateFile(source) {
|
|
37
|
+
return import_path.default.join(CONFIG_DIR, `active-session-${source}.json`);
|
|
38
|
+
}
|
|
35
39
|
function ensureConfigDir() {
|
|
36
40
|
if (!import_fs.default.existsSync(CONFIG_DIR)) {
|
|
37
41
|
import_fs.default.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
@@ -210,13 +214,17 @@ var EventCollector = class {
|
|
|
210
214
|
buffer = [];
|
|
211
215
|
flushInterval = null;
|
|
212
216
|
client;
|
|
213
|
-
|
|
217
|
+
source;
|
|
218
|
+
constructor(sessionId, source) {
|
|
214
219
|
this.sessionId = sessionId ?? (0, import_uuid.v4)();
|
|
215
220
|
this.client = new APIClient();
|
|
221
|
+
this.source = source;
|
|
216
222
|
}
|
|
217
223
|
addEvent(event) {
|
|
224
|
+
const metadata = this.source ? { ...event.metadata, source: this.source } : event.metadata;
|
|
218
225
|
this.buffer.push({
|
|
219
226
|
...event,
|
|
227
|
+
metadata,
|
|
220
228
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
221
229
|
});
|
|
222
230
|
}
|
|
@@ -256,29 +264,41 @@ var EventCollector = class {
|
|
|
256
264
|
}
|
|
257
265
|
};
|
|
258
266
|
|
|
259
|
-
// src/hooks/
|
|
267
|
+
// src/hooks/shared/utils.ts
|
|
260
268
|
var import_fs3 = __toESM(require("fs"));
|
|
261
|
-
|
|
262
|
-
var import_os2 = __toESM(require("os"));
|
|
263
|
-
var stateFile = import_path2.default.join(import_os2.default.homedir(), ".hackersbaby", "active-session.json");
|
|
264
|
-
function loadSessionId() {
|
|
269
|
+
function loadSessionId(source) {
|
|
265
270
|
try {
|
|
266
|
-
|
|
267
|
-
|
|
271
|
+
const file = sessionStateFile(source);
|
|
272
|
+
if (!import_fs3.default.existsSync(file)) return void 0;
|
|
273
|
+
const data = JSON.parse(import_fs3.default.readFileSync(file, "utf-8"));
|
|
268
274
|
return data.sessionId;
|
|
269
275
|
} catch {
|
|
270
276
|
return void 0;
|
|
271
277
|
}
|
|
272
278
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
279
|
+
function readStdin() {
|
|
280
|
+
return new Promise((resolve) => {
|
|
281
|
+
let input = "";
|
|
282
|
+
process.stdin.on("data", (chunk) => {
|
|
283
|
+
input += chunk;
|
|
284
|
+
});
|
|
285
|
+
process.stdin.on("end", () => resolve(input));
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
function normalizePayload(source, raw) {
|
|
289
|
+
if (source === "cursor") {
|
|
290
|
+
return { ...raw };
|
|
291
|
+
}
|
|
292
|
+
return raw;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/hooks/shared/prompt-submit.ts
|
|
296
|
+
async function handlePromptSubmit(source) {
|
|
297
|
+
const input = await readStdin();
|
|
278
298
|
try {
|
|
279
|
-
const hookData = JSON.parse(input);
|
|
280
|
-
const sessionId = hookData.session_id || loadSessionId();
|
|
281
|
-
const collector = new EventCollector(sessionId);
|
|
299
|
+
const hookData = normalizePayload(source, JSON.parse(input));
|
|
300
|
+
const sessionId = hookData.session_id || loadSessionId(source);
|
|
301
|
+
const collector = new EventCollector(sessionId, source);
|
|
282
302
|
const promptLength = typeof hookData.prompt === "string" ? hookData.prompt.length : 0;
|
|
283
303
|
collector.addEvent({
|
|
284
304
|
action_type: "prompt_submitted",
|
|
@@ -291,5 +311,8 @@ process.stdin.on("end", async () => {
|
|
|
291
311
|
await collector.flush();
|
|
292
312
|
} catch {
|
|
293
313
|
}
|
|
294
|
-
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/hooks/prompt-submit.ts
|
|
317
|
+
handlePromptSubmit("claude");
|
|
295
318
|
//# sourceMappingURL=prompt-submit.js.map
|