@hackersbaby/plugin 0.4.3 → 0.5.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 +192 -1
- package/dist/cli.js.map +1 -1
- package/dist/hooks/compact.js +428 -168
- package/dist/hooks/compact.js.map +1 -1
- package/dist/hooks/cursor/compact.js +428 -168
- package/dist/hooks/cursor/compact.js.map +1 -1
- package/dist/hooks/cursor/prompt-submit.js +428 -168
- package/dist/hooks/cursor/prompt-submit.js.map +1 -1
- package/dist/hooks/cursor/session-end.js +428 -168
- package/dist/hooks/cursor/session-end.js.map +1 -1
- package/dist/hooks/cursor/session-start.js +417 -165
- package/dist/hooks/cursor/session-start.js.map +1 -1
- package/dist/hooks/cursor/stop.js +428 -168
- package/dist/hooks/cursor/stop.js.map +1 -1
- package/dist/hooks/cursor/subagent.js +428 -168
- package/dist/hooks/cursor/subagent.js.map +1 -1
- package/dist/hooks/cursor/tool-call.js +337 -178
- package/dist/hooks/cursor/tool-call.js.map +1 -1
- package/dist/hooks/prompt-submit.js +428 -168
- package/dist/hooks/prompt-submit.js.map +1 -1
- package/dist/hooks/session-end.js +428 -168
- package/dist/hooks/session-end.js.map +1 -1
- package/dist/hooks/session-start.js +417 -165
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/hooks/stop.js +428 -168
- package/dist/hooks/stop.js.map +1 -1
- package/dist/hooks/subagent.js +428 -168
- package/dist/hooks/subagent.js.map +1 -1
- package/dist/hooks/tool-call.js +337 -178
- package/dist/hooks/tool-call.js.map +1 -1
- package/dist/index.d.ts +39 -21
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,12 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
13
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
14
|
+
};
|
|
9
15
|
var __copyProps = (to, from, except, desc) => {
|
|
10
16
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
17
|
for (let key of __getOwnPropNames(from))
|
|
@@ -23,16 +29,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
29
|
mod
|
|
24
30
|
));
|
|
25
31
|
|
|
26
|
-
// src/collector.ts
|
|
27
|
-
var import_uuid = require("uuid");
|
|
28
|
-
|
|
29
32
|
// 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
33
|
function sessionStateFile(source) {
|
|
37
34
|
return import_path.default.join(CONFIG_DIR, `active-session-${source}.json`);
|
|
38
35
|
}
|
|
@@ -54,122 +51,326 @@ function saveConfig(config) {
|
|
|
54
51
|
ensureConfigDir();
|
|
55
52
|
import_fs.default.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
56
53
|
}
|
|
54
|
+
var import_fs, import_path, import_os, CONFIG_DIR, CONFIG_FILE, QUEUE_FILE;
|
|
55
|
+
var init_config = __esm({
|
|
56
|
+
"src/config.ts"() {
|
|
57
|
+
"use strict";
|
|
58
|
+
import_fs = __toESM(require("fs"));
|
|
59
|
+
import_path = __toESM(require("path"));
|
|
60
|
+
import_os = __toESM(require("os"));
|
|
61
|
+
CONFIG_DIR = import_path.default.join(import_os.default.homedir(), ".hackersbaby");
|
|
62
|
+
CONFIG_FILE = import_path.default.join(CONFIG_DIR, "config.json");
|
|
63
|
+
QUEUE_FILE = import_path.default.join(CONFIG_DIR, "queue.jsonl");
|
|
64
|
+
}
|
|
65
|
+
});
|
|
57
66
|
|
|
58
|
-
//
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"Content-Type": "application/json",
|
|
64
|
-
Authorization: `Bearer ${this.config?.token || ""}`
|
|
65
|
-
};
|
|
67
|
+
// ../shared/dist/types.js
|
|
68
|
+
var require_types = __commonJS({
|
|
69
|
+
"../shared/dist/types.js"(exports2) {
|
|
70
|
+
"use strict";
|
|
71
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
66
72
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ../shared/dist/scoring.js
|
|
76
|
+
var require_scoring = __commonJS({
|
|
77
|
+
"../shared/dist/scoring.js"(exports2) {
|
|
78
|
+
"use strict";
|
|
79
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
80
|
+
exports2.DEPLOY_PATTERNS = exports2.BASE_POINTS = exports2.MIN_SESSION_TOKENS = exports2.TOOL_CALLS_PER_HOUR_CAP = exports2.SESSION_BASE_POINT_CAP = void 0;
|
|
81
|
+
exports2.getBasePoints = getBasePoints;
|
|
82
|
+
exports2.calculateMultiplier = calculateMultiplier;
|
|
83
|
+
exports2.calculateSessionScore = calculateSessionScore;
|
|
84
|
+
exports2.isDeployCommand = isDeployCommand;
|
|
85
|
+
exports2.SESSION_BASE_POINT_CAP = 2e4;
|
|
86
|
+
exports2.TOOL_CALLS_PER_HOUR_CAP = 4e3;
|
|
87
|
+
exports2.MIN_SESSION_TOKENS = 2e3;
|
|
88
|
+
exports2.BASE_POINTS = {
|
|
89
|
+
token_input: { perUnit: 1, unitSize: 1e3 },
|
|
90
|
+
token_output: { perUnit: 2, unitSize: 1e3 },
|
|
91
|
+
tool_call: { perUnit: 5, unitSize: 1 },
|
|
92
|
+
file_created: { perUnit: 15, unitSize: 1 },
|
|
93
|
+
file_edited: { perUnit: 10, unitSize: 1 },
|
|
94
|
+
commit: { perUnit: 50, unitSize: 1 },
|
|
95
|
+
session_completed: { perUnit: 100, unitSize: 1 },
|
|
96
|
+
deployment: { perUnit: 200, unitSize: 1 },
|
|
97
|
+
prompt_submitted: { perUnit: 15, unitSize: 1 },
|
|
98
|
+
subagent_spawned: { perUnit: 40, unitSize: 1 },
|
|
99
|
+
context_compacted: { perUnit: 30, unitSize: 1 },
|
|
100
|
+
stop_response: { perUnit: 10, unitSize: 1 }
|
|
101
|
+
};
|
|
102
|
+
exports2.DEPLOY_PATTERNS = [
|
|
103
|
+
/^vercel\s+(--prod|deploy)/,
|
|
104
|
+
/^netlify\s+deploy/,
|
|
105
|
+
/^fly\s+deploy/,
|
|
106
|
+
/^railway\s+up/,
|
|
107
|
+
/^git\s+push\s+\S+\s+(main|master|production)/,
|
|
108
|
+
/^(npm|yarn|pnpm)\s+run\s+deploy/,
|
|
109
|
+
/^(yarn|pnpm)\s+deploy/
|
|
110
|
+
];
|
|
111
|
+
function getBasePoints(actionType, value) {
|
|
112
|
+
const config = exports2.BASE_POINTS[actionType];
|
|
113
|
+
return Math.floor(value / config.unitSize) * config.perUnit;
|
|
114
|
+
}
|
|
115
|
+
function calculateMultiplier(input) {
|
|
116
|
+
let streak = 1;
|
|
117
|
+
if (input.streak_days >= 30)
|
|
118
|
+
streak = 3;
|
|
119
|
+
else if (input.streak_days >= 7)
|
|
120
|
+
streak = 2;
|
|
121
|
+
else if (input.streak_days >= 3)
|
|
122
|
+
streak = 1.5;
|
|
123
|
+
const quality = input.commit_quality ? 1.5 : 1;
|
|
124
|
+
const diversity = input.language_count >= 3 ? 1.2 : 1;
|
|
125
|
+
return streak * quality * diversity;
|
|
126
|
+
}
|
|
127
|
+
function calculateSessionScore(events, multiplier) {
|
|
128
|
+
let totalBase = 0;
|
|
129
|
+
for (const e of events)
|
|
130
|
+
totalBase += getBasePoints(e.action_type, e.value);
|
|
131
|
+
return Math.floor(Math.min(totalBase, exports2.SESSION_BASE_POINT_CAP) * multiplier);
|
|
132
|
+
}
|
|
133
|
+
function isDeployCommand(command) {
|
|
134
|
+
return exports2.DEPLOY_PATTERNS.some((p) => p.test(command.trim()));
|
|
78
135
|
}
|
|
79
136
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// ../shared/dist/anticheat.js
|
|
140
|
+
var require_anticheat = __commonJS({
|
|
141
|
+
"../shared/dist/anticheat.js"(exports2) {
|
|
142
|
+
"use strict";
|
|
143
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
144
|
+
exports2.hashEvent = hashEvent;
|
|
145
|
+
exports2.computeChainHash = computeChainHash2;
|
|
146
|
+
exports2.signBatch = signBatch2;
|
|
147
|
+
exports2.verifyBatchSignature = verifyBatchSignature;
|
|
148
|
+
var crypto_1 = require("crypto");
|
|
149
|
+
function hashEvent(event, prevHash) {
|
|
150
|
+
const payload = `${prevHash}|${event.action_type}|${event.value}|${event.timestamp}`;
|
|
151
|
+
return (0, crypto_1.createHash)("sha256").update(payload).digest("hex").slice(0, 16);
|
|
152
|
+
}
|
|
153
|
+
function computeChainHash2(events, initialHash = "0000000000000000") {
|
|
154
|
+
let hash = initialHash;
|
|
155
|
+
for (const event of events) {
|
|
156
|
+
hash = hashEvent(event, hash);
|
|
101
157
|
}
|
|
102
|
-
|
|
158
|
+
return hash;
|
|
103
159
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (!res.ok) throw new Error(`getStatus failed: ${res.status}`);
|
|
109
|
-
return res.json();
|
|
110
|
-
}
|
|
111
|
-
async getRank() {
|
|
112
|
-
const server = this.config?.server || "https://hackers.baby";
|
|
113
|
-
const res = await fetch(`${server}/api/users/me/rank`, { headers: this.headers });
|
|
114
|
-
if (!res.ok) throw new Error(`getRank failed: ${res.status}`);
|
|
115
|
-
return res.json();
|
|
116
|
-
}
|
|
117
|
-
async getLeaderboard() {
|
|
118
|
-
const server = this.config?.server || "https://hackers.baby";
|
|
119
|
-
const res = await fetch(`${server}/api/leaderboard/global?limit=10`, { headers: this.headers });
|
|
120
|
-
if (!res.ok) throw new Error(`getLeaderboard failed: ${res.status}`);
|
|
121
|
-
return res.json();
|
|
122
|
-
}
|
|
123
|
-
async getPublicStats() {
|
|
124
|
-
try {
|
|
125
|
-
const server = this.config?.server || "https://hackers.baby";
|
|
126
|
-
const res = await fetch(`${server}/api/stats/public`);
|
|
127
|
-
if (!res.ok) return null;
|
|
128
|
-
return res.json();
|
|
129
|
-
} catch {
|
|
130
|
-
return null;
|
|
160
|
+
function signBatch2(sessionId, events, chainHash, secret) {
|
|
161
|
+
const valueSum = events.reduce((s, e) => s + e.value, 0);
|
|
162
|
+
const message = `${sessionId}|${chainHash}|${events.length}|${valueSum}`;
|
|
163
|
+
return (0, crypto_1.createHmac)("sha256", secret).update(message).digest("hex");
|
|
131
164
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return
|
|
165
|
+
function verifyBatchSignature(sessionId, events, chainHash, signature, secret) {
|
|
166
|
+
const expected = signBatch2(sessionId, events, chainHash, secret);
|
|
167
|
+
if (expected.length !== signature.length)
|
|
168
|
+
return false;
|
|
169
|
+
let mismatch = 0;
|
|
170
|
+
for (let i = 0; i < expected.length; i++) {
|
|
171
|
+
mismatch |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
|
|
172
|
+
}
|
|
173
|
+
return mismatch === 0;
|
|
141
174
|
}
|
|
142
175
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// ../shared/dist/index.js
|
|
179
|
+
var require_dist = __commonJS({
|
|
180
|
+
"../shared/dist/index.js"(exports2) {
|
|
181
|
+
"use strict";
|
|
182
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
183
|
+
if (k2 === void 0) k2 = k;
|
|
184
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
185
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
186
|
+
desc = { enumerable: true, get: function() {
|
|
187
|
+
return m[k];
|
|
188
|
+
} };
|
|
189
|
+
}
|
|
190
|
+
Object.defineProperty(o, k2, desc);
|
|
191
|
+
}) : (function(o, m, k, k2) {
|
|
192
|
+
if (k2 === void 0) k2 = k;
|
|
193
|
+
o[k2] = m[k];
|
|
194
|
+
}));
|
|
195
|
+
var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
|
|
196
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
|
|
197
|
+
};
|
|
198
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
199
|
+
__exportStar(require_types(), exports2);
|
|
200
|
+
__exportStar(require_scoring(), exports2);
|
|
201
|
+
__exportStar(require_anticheat(), exports2);
|
|
156
202
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// src/api-client.ts
|
|
206
|
+
var import_shared, APIClient;
|
|
207
|
+
var init_api_client = __esm({
|
|
208
|
+
"src/api-client.ts"() {
|
|
209
|
+
"use strict";
|
|
210
|
+
init_config();
|
|
211
|
+
import_shared = __toESM(require_dist());
|
|
212
|
+
APIClient = class {
|
|
213
|
+
config = loadConfig();
|
|
214
|
+
sessionSecret = null;
|
|
215
|
+
chainHash = "0000000000000000";
|
|
216
|
+
onChainUpdate = null;
|
|
217
|
+
/** Set callback to persist chain state between hook processes */
|
|
218
|
+
setChainUpdateCallback(cb) {
|
|
219
|
+
this.onChainUpdate = cb;
|
|
220
|
+
}
|
|
221
|
+
/** Restore crypto state from a previous session */
|
|
222
|
+
restoreCryptoState(secret, chainHash) {
|
|
223
|
+
this.sessionSecret = secret;
|
|
224
|
+
this.chainHash = chainHash;
|
|
225
|
+
}
|
|
226
|
+
get headers() {
|
|
227
|
+
return {
|
|
228
|
+
"Content-Type": "application/json",
|
|
229
|
+
Authorization: `Bearer ${this.config?.token || ""}`
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/** Get current crypto state for persistence */
|
|
233
|
+
getCryptoState() {
|
|
234
|
+
if (!this.sessionSecret) return null;
|
|
235
|
+
return { secret: this.sessionSecret, chainHash: this.chainHash };
|
|
236
|
+
}
|
|
237
|
+
/** Register session with server and get cryptographic secret */
|
|
238
|
+
async startSession(sessionId) {
|
|
239
|
+
try {
|
|
240
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
241
|
+
const res = await fetch(`${server}/api/sessions/start`, {
|
|
242
|
+
method: "POST",
|
|
243
|
+
headers: this.headers,
|
|
244
|
+
body: JSON.stringify({ session_id: sessionId })
|
|
245
|
+
});
|
|
246
|
+
if (res.ok) {
|
|
247
|
+
const data = await res.json();
|
|
248
|
+
this.sessionSecret = data.session_secret;
|
|
249
|
+
this.chainHash = data.initial_chain_hash;
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
} catch {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async sendBatch(batch) {
|
|
258
|
+
try {
|
|
259
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
260
|
+
if (this.sessionSecret) {
|
|
261
|
+
const prevChainHash = this.chainHash;
|
|
262
|
+
const newChainHash = (0, import_shared.computeChainHash)(batch.events, prevChainHash);
|
|
263
|
+
const signature = (0, import_shared.signBatch)(batch.session_id, batch.events, newChainHash, this.sessionSecret);
|
|
264
|
+
batch.signature = signature;
|
|
265
|
+
batch.chain_hash = newChainHash;
|
|
266
|
+
batch.prev_chain_hash = prevChainHash;
|
|
267
|
+
this.chainHash = newChainHash;
|
|
268
|
+
if (this.onChainUpdate) {
|
|
269
|
+
this.onChainUpdate(this.chainHash, this.sessionSecret);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const res = await fetch(`${server}/api/scores/batch`, {
|
|
273
|
+
method: "POST",
|
|
274
|
+
headers: this.headers,
|
|
275
|
+
body: JSON.stringify(batch)
|
|
276
|
+
});
|
|
277
|
+
return res.ok;
|
|
278
|
+
} catch {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async refreshTokenIfNeeded() {
|
|
283
|
+
if (!this.config) return;
|
|
284
|
+
try {
|
|
285
|
+
const parts = this.config.token.split(".");
|
|
286
|
+
if (parts.length !== 3) return;
|
|
287
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64").toString("utf-8"));
|
|
288
|
+
const expMs = payload.exp * 1e3;
|
|
289
|
+
const oneDayMs = 24 * 60 * 60 * 1e3;
|
|
290
|
+
if (Date.now() + oneDayMs < expMs) return;
|
|
291
|
+
const res = await fetch(`${this.config.server}/api/auth/cli-token/refresh`, {
|
|
292
|
+
method: "POST",
|
|
293
|
+
headers: this.headers,
|
|
294
|
+
body: JSON.stringify({ refresh_token: this.config.refresh_token })
|
|
295
|
+
});
|
|
296
|
+
if (res.ok) {
|
|
297
|
+
const data = await res.json();
|
|
298
|
+
if (data.token) {
|
|
299
|
+
this.config.token = data.token;
|
|
300
|
+
if (data.refresh_token) this.config.refresh_token = data.refresh_token;
|
|
301
|
+
saveConfig(this.config);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} catch {
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async getStatus() {
|
|
308
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
309
|
+
const res = await fetch(`${server}/api/users/me`, { headers: this.headers });
|
|
310
|
+
if (!res.ok) throw new Error(`getStatus failed: ${res.status}`);
|
|
311
|
+
return res.json();
|
|
312
|
+
}
|
|
313
|
+
async getRank() {
|
|
314
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
315
|
+
const res = await fetch(`${server}/api/users/me/rank`, { headers: this.headers });
|
|
316
|
+
if (!res.ok) throw new Error(`getRank failed: ${res.status}`);
|
|
317
|
+
return res.json();
|
|
318
|
+
}
|
|
319
|
+
async getLeaderboard() {
|
|
320
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
321
|
+
const res = await fetch(`${server}/api/leaderboard/global?limit=10`, { headers: this.headers });
|
|
322
|
+
if (!res.ok) throw new Error(`getLeaderboard failed: ${res.status}`);
|
|
323
|
+
return res.json();
|
|
324
|
+
}
|
|
325
|
+
async getPublicStats() {
|
|
326
|
+
try {
|
|
327
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
328
|
+
const res = await fetch(`${server}/api/stats/public`);
|
|
329
|
+
if (!res.ok) return null;
|
|
330
|
+
return res.json();
|
|
331
|
+
} catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
async getNotifications() {
|
|
336
|
+
try {
|
|
337
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
338
|
+
const res = await fetch(`${server}/api/users/me/notifications`, { headers: this.headers });
|
|
339
|
+
if (!res.ok) return null;
|
|
340
|
+
return res.json();
|
|
341
|
+
} catch {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
async claimReferral(code) {
|
|
346
|
+
try {
|
|
347
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
348
|
+
const res = await fetch(`${server}/api/referral/claim`, {
|
|
349
|
+
method: "POST",
|
|
350
|
+
headers: this.headers,
|
|
351
|
+
body: JSON.stringify({ referral_code: code })
|
|
352
|
+
});
|
|
353
|
+
if (!res.ok) return null;
|
|
354
|
+
return res.json();
|
|
355
|
+
} catch {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
async getReferralStats() {
|
|
360
|
+
try {
|
|
361
|
+
const server = this.config?.server || "https://hackers.baby";
|
|
362
|
+
const res = await fetch(`${server}/api/referral/stats`, { headers: this.headers });
|
|
363
|
+
if (!res.ok) return null;
|
|
364
|
+
return res.json();
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
};
|
|
166
370
|
}
|
|
167
|
-
};
|
|
371
|
+
});
|
|
168
372
|
|
|
169
373
|
// src/queue.ts
|
|
170
|
-
var import_fs2 = __toESM(require("fs"));
|
|
171
|
-
var MAX_QUEUE_SIZE = 1e4;
|
|
172
|
-
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
173
374
|
function enqueueEvents(events) {
|
|
174
375
|
ensureConfigDir();
|
|
175
376
|
const lines = events.map((e) => JSON.stringify(e)).join("\n");
|
|
@@ -207,65 +408,100 @@ function drainQueue() {
|
|
|
207
408
|
return [];
|
|
208
409
|
}
|
|
209
410
|
}
|
|
411
|
+
var import_fs2, MAX_QUEUE_SIZE, MAX_AGE_MS;
|
|
412
|
+
var init_queue = __esm({
|
|
413
|
+
"src/queue.ts"() {
|
|
414
|
+
"use strict";
|
|
415
|
+
import_fs2 = __toESM(require("fs"));
|
|
416
|
+
init_config();
|
|
417
|
+
MAX_QUEUE_SIZE = 1e4;
|
|
418
|
+
MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
419
|
+
}
|
|
420
|
+
});
|
|
210
421
|
|
|
211
422
|
// src/collector.ts
|
|
212
|
-
var EventCollector
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
async start() {
|
|
232
|
-
await this.client.refreshTokenIfNeeded();
|
|
233
|
-
const queued = drainQueue();
|
|
234
|
-
if (queued.length > 0) {
|
|
235
|
-
const batch = { session_id: this.sessionId, events: queued };
|
|
236
|
-
const ok = await this.client.sendBatch(batch);
|
|
237
|
-
if (!ok) {
|
|
238
|
-
enqueueEvents(queued);
|
|
423
|
+
var import_uuid, EventCollector;
|
|
424
|
+
var init_collector = __esm({
|
|
425
|
+
"src/collector.ts"() {
|
|
426
|
+
"use strict";
|
|
427
|
+
import_uuid = require("uuid");
|
|
428
|
+
init_api_client();
|
|
429
|
+
init_queue();
|
|
430
|
+
init_config();
|
|
431
|
+
EventCollector = class {
|
|
432
|
+
sessionId;
|
|
433
|
+
buffer = [];
|
|
434
|
+
flushInterval = null;
|
|
435
|
+
client;
|
|
436
|
+
source;
|
|
437
|
+
constructor(sessionId, source) {
|
|
438
|
+
this.sessionId = sessionId ?? (0, import_uuid.v4)();
|
|
439
|
+
this.client = new APIClient();
|
|
440
|
+
this.source = source;
|
|
239
441
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
442
|
+
/** Restore crypto state from persisted session data */
|
|
443
|
+
restoreCrypto(secret, chainHash) {
|
|
444
|
+
this.client.restoreCryptoState(secret, chainHash);
|
|
445
|
+
}
|
|
446
|
+
/** Set callback to persist crypto state after each batch */
|
|
447
|
+
setCryptoUpdateCallback(cb) {
|
|
448
|
+
this.client.setChainUpdateCallback(cb);
|
|
449
|
+
}
|
|
450
|
+
addEvent(event) {
|
|
451
|
+
const metadata = this.source ? { ...event.metadata, source: this.source } : event.metadata;
|
|
452
|
+
this.buffer.push({
|
|
453
|
+
...event,
|
|
454
|
+
metadata,
|
|
455
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
async start() {
|
|
459
|
+
await this.client.refreshTokenIfNeeded();
|
|
460
|
+
await this.client.startSession(this.sessionId);
|
|
461
|
+
const queued = drainQueue();
|
|
462
|
+
if (queued.length > 0) {
|
|
463
|
+
const batch = { session_id: this.sessionId, events: queued };
|
|
464
|
+
const ok = await this.client.sendBatch(batch);
|
|
465
|
+
if (!ok) {
|
|
466
|
+
enqueueEvents(queued);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const config = loadConfig();
|
|
470
|
+
const intervalMs = config?.preferences?.batch_interval_ms ?? 1e4;
|
|
471
|
+
this.flushInterval = setInterval(() => {
|
|
472
|
+
this.flush().catch(() => {
|
|
473
|
+
});
|
|
474
|
+
}, intervalMs);
|
|
475
|
+
}
|
|
476
|
+
async flush() {
|
|
477
|
+
if (this.buffer.length === 0) return;
|
|
478
|
+
const events = this.buffer.splice(0);
|
|
479
|
+
const batch = { session_id: this.sessionId, events };
|
|
480
|
+
const ok = await this.client.sendBatch(batch);
|
|
481
|
+
if (!ok) {
|
|
482
|
+
enqueueEvents(events);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
async stop() {
|
|
486
|
+
if (this.flushInterval !== null) {
|
|
487
|
+
clearInterval(this.flushInterval);
|
|
488
|
+
this.flushInterval = null;
|
|
489
|
+
}
|
|
490
|
+
this.addEvent({ action_type: "session_completed", value: 1, metadata: {} });
|
|
491
|
+
await this.flush();
|
|
492
|
+
}
|
|
493
|
+
};
|
|
264
494
|
}
|
|
265
|
-
};
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// src/hooks/shared/session-start.ts
|
|
498
|
+
init_collector();
|
|
499
|
+
init_api_client();
|
|
500
|
+
init_config();
|
|
266
501
|
|
|
267
502
|
// src/hooks/shared/utils.ts
|
|
268
503
|
var import_fs3 = __toESM(require("fs"));
|
|
504
|
+
init_config();
|
|
269
505
|
function saveSessionState(state) {
|
|
270
506
|
const file = sessionStateFile(state.source);
|
|
271
507
|
import_fs3.default.writeFileSync(file, JSON.stringify(state));
|
|
@@ -280,8 +516,24 @@ function detectSource(payload) {
|
|
|
280
516
|
async function handleSessionStart(source) {
|
|
281
517
|
const detected = detectSource();
|
|
282
518
|
const collector = new EventCollector(void 0, detected);
|
|
519
|
+
collector.setCryptoUpdateCallback((chainHash, secret) => {
|
|
520
|
+
saveSessionState({
|
|
521
|
+
sessionId: collector.sessionId,
|
|
522
|
+
pid: process.pid,
|
|
523
|
+
source: detected,
|
|
524
|
+
sessionSecret: secret,
|
|
525
|
+
chainHash
|
|
526
|
+
});
|
|
527
|
+
});
|
|
283
528
|
await collector.start();
|
|
284
|
-
|
|
529
|
+
const crypto = collector.client.getCryptoState();
|
|
530
|
+
saveSessionState({
|
|
531
|
+
sessionId: collector.sessionId,
|
|
532
|
+
pid: process.pid,
|
|
533
|
+
source: detected,
|
|
534
|
+
sessionSecret: crypto?.secret,
|
|
535
|
+
chainHash: crypto?.chainHash
|
|
536
|
+
});
|
|
285
537
|
try {
|
|
286
538
|
const config = loadConfig();
|
|
287
539
|
if (config?.token) {
|