@hackersbaby/plugin 0.1.0

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