@dupecom/botcha-cloudflare 0.2.1 → 0.3.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/README.md CHANGED
@@ -96,8 +96,23 @@ Rate limit headers:
96
96
  | `/v1/challenges/:id/verify` | POST | Verify challenge (no JWT) |
97
97
  | `/v1/token` | GET | Get challenge for JWT flow |
98
98
  | `/v1/token/verify` | POST | Verify challenge → get JWT token |
99
+ | `/v1/challenge/stream` | GET | SSE streaming challenge (AI-native) |
100
+ | `/v1/challenge/stream/:session` | POST | SSE action handler (go, solve) |
99
101
  | `/agent-only` | GET | Protected endpoint (requires JWT) |
100
102
 
103
+ ### SSE Streaming (AI-Native)
104
+
105
+ For AI agents that prefer conversational flows, BOTCHA offers Server-Sent Events streaming:
106
+
107
+ **Flow:**
108
+ 1. `GET /v1/challenge/stream` - Opens SSE connection, receive welcome/instructions/ready events
109
+ 2. `POST /v1/challenge/stream/:session` with `{action:"go"}` - Start challenge timer (fair timing!)
110
+ 3. Receive `challenge` event with problems
111
+ 4. `POST /v1/challenge/stream/:session` with `{action:"solve", answers:[...]}` - Submit solution
112
+ 5. Receive `result` event with JWT token
113
+
114
+ **Benefits:** Timer starts when you say "GO" (not on connection), natural back-and-forth handshake.
115
+
101
116
  ### Legacy API (v0 - backward compatible)
102
117
 
103
118
  | Endpoint | Method | Description |
package/dist/badge.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * Port of badge generation and verification logic from src/utils/badge.ts
5
5
  * Uses jose library for HMAC-SHA256 signing (same as JWT auth)
6
6
  */
7
- export type BadgeMethod = 'speed-challenge' | 'landing-challenge' | 'standard-challenge' | 'web-bot-auth';
7
+ export type BadgeMethod = 'speed-challenge' | 'landing-challenge' | 'standard-challenge' | 'web-bot-auth' | 'reasoning-challenge' | 'hybrid-challenge';
8
8
  export interface BadgePayload {
9
9
  method: BadgeMethod;
10
10
  solveTimeMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,oBAAoB,GAAG,cAAc,CAAC;AAE1G,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QACJ,MAAM,EAAE,WAAW,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAID;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB1F;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAuB9B;AAID;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAiDvG;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,KAAK,CAAC,CAqBhB;AAyBD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAChD,MAAM,CAuER;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAyJjG"}
1
+ {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,oBAAoB,GAAG,cAAc,GAAG,qBAAqB,GAAG,kBAAkB,CAAC;AAEvJ,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE;QACJ,MAAM,EAAE,WAAW,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAID;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB1F;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAuB9B;AAID;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CA6DvG;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,KAAK,CAAC,CAqBhB;AA+BD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAChD,MAAM,CAuER;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAyJjG"}
package/dist/badge.js CHANGED
@@ -78,6 +78,18 @@ export function generateShareText(badgeId, payload, baseUrl) {
78
78
  title: 'I verified via BOTCHA Web Bot Auth!',
79
79
  subtitle: 'Cryptographic identity confirmed.',
80
80
  },
81
+ 'reasoning-challenge': {
82
+ title: payload.solveTimeMs
83
+ ? `I passed the BOTCHA reasoning test in ${(payload.solveTimeMs / 1000).toFixed(1)}s!`
84
+ : 'I passed the BOTCHA reasoning test!',
85
+ subtitle: 'Proved I can reason like an AI.',
86
+ },
87
+ 'hybrid-challenge': {
88
+ title: payload.solveTimeMs
89
+ ? `I passed the BOTCHA hybrid test in ${(payload.solveTimeMs / 1000).toFixed(1)}s!`
90
+ : 'I passed the BOTCHA hybrid test!',
91
+ subtitle: 'Proved I can compute AND reason like an AI.',
92
+ },
81
93
  };
82
94
  const desc = methodDescriptions[payload.method];
83
95
  const twitter = `${desc.title}
@@ -126,18 +138,24 @@ const METHOD_COLORS = {
126
138
  'landing-challenge': { bg: '#1a1a2e', accent: '#10b981', text: '#d1fae5' },
127
139
  'standard-challenge': { bg: '#1a1a2e', accent: '#3b82f6', text: '#dbeafe' },
128
140
  'web-bot-auth': { bg: '#1a1a2e', accent: '#8b5cf6', text: '#ede9fe' },
141
+ 'reasoning-challenge': { bg: '#1a1a2e', accent: '#ec4899', text: '#fce7f3' },
142
+ 'hybrid-challenge': { bg: '#1a1a2e', accent: '#ef4444', text: '#fecaca' },
129
143
  };
130
144
  const METHOD_LABELS = {
131
145
  'speed-challenge': 'SPEED TEST',
132
146
  'landing-challenge': 'LANDING CHALLENGE',
133
147
  'standard-challenge': 'CHALLENGE',
134
148
  'web-bot-auth': 'WEB BOT AUTH',
149
+ 'reasoning-challenge': 'REASONING TEST',
150
+ 'hybrid-challenge': 'HYBRID TEST',
135
151
  };
136
152
  const METHOD_ICONS = {
137
153
  'speed-challenge': '⚡',
138
154
  'landing-challenge': '🌐',
139
155
  'standard-challenge': '🔢',
140
156
  'web-bot-auth': '🔐',
157
+ 'reasoning-challenge': '🧠',
158
+ 'hybrid-challenge': '🔥',
141
159
  };
142
160
  /**
143
161
  * Generate an SVG badge image
@@ -28,10 +28,36 @@ export interface StandardChallenge {
28
28
  expiresAt: number;
29
29
  difficulty: 'easy' | 'medium' | 'hard';
30
30
  }
31
+ export interface ReasoningQuestion {
32
+ id: string;
33
+ question: string;
34
+ category: 'analogy' | 'logic' | 'wordplay' | 'math' | 'code' | 'common-sense';
35
+ acceptedAnswers: string[];
36
+ }
37
+ export interface ReasoningChallenge {
38
+ id: string;
39
+ questions: {
40
+ id: string;
41
+ question: string;
42
+ category: string;
43
+ }[];
44
+ expectedAnswers: Record<string, string[]>;
45
+ issuedAt: number;
46
+ expiresAt: number;
47
+ }
31
48
  export interface ChallengeResult {
32
49
  valid: boolean;
33
50
  reason?: string;
34
51
  solveTimeMs?: number;
52
+ correctCount?: number;
53
+ totalCount?: number;
54
+ }
55
+ export interface HybridChallenge {
56
+ id: string;
57
+ speedChallengeId: string;
58
+ reasoningChallengeId: string;
59
+ issuedAt: number;
60
+ expiresAt: number;
35
61
  }
36
62
  /**
37
63
  * Generate a speed challenge: 5 SHA256 problems, 500ms to solve ALL
@@ -82,4 +108,62 @@ export declare function validateLandingToken(token: string, kv?: KVNamespace): P
82
108
  * Solve speed challenge problems (utility for AI agents)
83
109
  */
84
110
  export declare function solveSpeedChallenge(problems: number[]): Promise<string[]>;
111
+ /**
112
+ * Generate a reasoning challenge: 3 random questions requiring LLM capabilities
113
+ */
114
+ export declare function generateReasoningChallenge(kv?: KVNamespace): Promise<{
115
+ id: string;
116
+ questions: {
117
+ id: string;
118
+ question: string;
119
+ category: string;
120
+ }[];
121
+ timeLimit: number;
122
+ instructions: string;
123
+ }>;
124
+ /**
125
+ * Verify a reasoning challenge response
126
+ */
127
+ export declare function verifyReasoningChallenge(id: string, answers: Record<string, string>, kv?: KVNamespace): Promise<ChallengeResult>;
128
+ /**
129
+ * Generate a hybrid challenge: speed + reasoning combined
130
+ */
131
+ export declare function generateHybridChallenge(kv?: KVNamespace): Promise<{
132
+ id: string;
133
+ speed: {
134
+ problems: {
135
+ num: number;
136
+ operation: string;
137
+ }[];
138
+ timeLimit: number;
139
+ };
140
+ reasoning: {
141
+ questions: {
142
+ id: string;
143
+ question: string;
144
+ category: string;
145
+ }[];
146
+ timeLimit: number;
147
+ };
148
+ instructions: string;
149
+ }>;
150
+ /**
151
+ * Verify a hybrid challenge response
152
+ */
153
+ export declare function verifyHybridChallenge(id: string, speedAnswers: string[], reasoningAnswers: Record<string, string>, kv?: KVNamespace): Promise<{
154
+ valid: boolean;
155
+ reason?: string;
156
+ speed: {
157
+ passed: boolean;
158
+ solveTimeMs?: number;
159
+ reason?: string;
160
+ };
161
+ reasoning: {
162
+ passed: boolean;
163
+ score?: string;
164
+ solveTimeMs?: number;
165
+ reason?: string;
166
+ };
167
+ totalTimeMs?: number;
168
+ }>;
85
169
  //# sourceMappingURL=challenges.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"challenges.d.ts","sourceRoot":"","sources":["../src/challenges.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"challenges.d.ts","sourceRoot":"","sources":["../src/challenges.ts"],"names":[],"mappings":"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"}
@@ -246,3 +246,394 @@ export async function validateLandingToken(token, kv) {
246
246
  export async function solveSpeedChallenge(problems) {
247
247
  return Promise.all(problems.map(n => sha256First(n.toString(), 8)));
248
248
  }
249
+ // ============ REASONING CHALLENGE ============
250
+ // In-memory storage for reasoning challenges
251
+ const reasoningChallenges = new Map();
252
+ // Question bank - LLMs can answer these, simple scripts cannot
253
+ const QUESTION_BANK = [
254
+ // Analogies
255
+ {
256
+ id: 'analogy-1',
257
+ question: 'Complete the analogy: Book is to library as car is to ___',
258
+ category: 'analogy',
259
+ acceptedAnswers: ['garage', 'parking lot', 'dealership', 'parking garage', 'lot'],
260
+ },
261
+ {
262
+ id: 'analogy-2',
263
+ question: 'Complete the analogy: Painter is to brush as writer is to ___',
264
+ category: 'analogy',
265
+ acceptedAnswers: ['pen', 'pencil', 'keyboard', 'typewriter', 'quill'],
266
+ },
267
+ {
268
+ id: 'analogy-3',
269
+ question: 'Complete the analogy: Fish is to water as bird is to ___',
270
+ category: 'analogy',
271
+ acceptedAnswers: ['air', 'sky', 'atmosphere'],
272
+ },
273
+ {
274
+ id: 'analogy-4',
275
+ question: 'Complete the analogy: Eye is to see as ear is to ___',
276
+ category: 'analogy',
277
+ acceptedAnswers: ['hear', 'listen', 'hearing', 'listening'],
278
+ },
279
+ // Wordplay
280
+ {
281
+ id: 'wordplay-1',
282
+ question: 'What single word connects: apple, Newton, gravity?',
283
+ category: 'wordplay',
284
+ acceptedAnswers: ['tree', 'fall', 'falling'],
285
+ },
286
+ {
287
+ id: 'wordplay-2',
288
+ question: 'What single word connects: key, piano, computer?',
289
+ category: 'wordplay',
290
+ acceptedAnswers: ['keyboard', 'board', 'keys'],
291
+ },
292
+ {
293
+ id: 'wordplay-3',
294
+ question: 'What single word connects: river, money, blood?',
295
+ category: 'wordplay',
296
+ acceptedAnswers: ['bank', 'flow', 'stream'],
297
+ },
298
+ {
299
+ id: 'wordplay-4',
300
+ question: 'What word can precede: light, house, shine?',
301
+ category: 'wordplay',
302
+ acceptedAnswers: ['sun', 'moon'],
303
+ },
304
+ // Logic
305
+ {
306
+ id: 'logic-1',
307
+ question: 'If all Bloops are Razzies and all Razzies are Lazzies, are all Bloops definitely Lazzies? Answer yes or no.',
308
+ category: 'logic',
309
+ acceptedAnswers: ['yes'],
310
+ },
311
+ {
312
+ id: 'logic-2',
313
+ question: 'If some Widgets are Gadgets, and all Gadgets are blue, can some Widgets be blue? Answer yes or no.',
314
+ category: 'logic',
315
+ acceptedAnswers: ['yes'],
316
+ },
317
+ {
318
+ id: 'logic-3',
319
+ question: 'I have a bee in my hand. What do I have in my eye? (Think about the saying)',
320
+ category: 'logic',
321
+ acceptedAnswers: ['beauty', 'beholder'],
322
+ },
323
+ {
324
+ id: 'logic-4',
325
+ question: 'A farmer has 17 sheep. All but 9 run away. How many sheep does he have left?',
326
+ category: 'logic',
327
+ acceptedAnswers: ['9', 'nine'],
328
+ },
329
+ // Math
330
+ {
331
+ id: 'math-1',
332
+ question: 'A bat and ball cost $1.10 total. The bat costs $1.00 more than the ball. How much does the ball cost in cents?',
333
+ category: 'math',
334
+ acceptedAnswers: ['5', '5 cents', 'five', 'five cents', '0.05', '$0.05'],
335
+ },
336
+ {
337
+ id: 'math-2',
338
+ question: 'If it takes 5 machines 5 minutes to make 5 widgets, how many minutes would it take 100 machines to make 100 widgets?',
339
+ category: 'math',
340
+ acceptedAnswers: ['5', 'five', '5 minutes', 'five minutes'],
341
+ },
342
+ {
343
+ id: 'math-3',
344
+ question: 'In a lake, there is a patch of lily pads. Every day, the patch doubles in size. If it takes 48 days for the patch to cover the entire lake, how many days would it take for the patch to cover half of the lake?',
345
+ category: 'math',
346
+ acceptedAnswers: ['47', 'forty-seven', 'forty seven', '47 days'],
347
+ },
348
+ // Code
349
+ {
350
+ id: 'code-1',
351
+ question: 'What is wrong with this code: if (x = 5) { doSomething(); }',
352
+ category: 'code',
353
+ acceptedAnswers: ['assignment', 'single equals', '= instead of ==', 'should be ==', 'should be ===', 'equality', 'comparison'],
354
+ },
355
+ {
356
+ id: 'code-2',
357
+ question: 'In most programming languages, what does the modulo operator % return for 17 % 5?',
358
+ category: 'code',
359
+ acceptedAnswers: ['2', 'two'],
360
+ },
361
+ {
362
+ id: 'code-3',
363
+ question: 'What data structure uses LIFO (Last In, First Out)?',
364
+ category: 'code',
365
+ acceptedAnswers: ['stack', 'a stack'],
366
+ },
367
+ // Common sense
368
+ {
369
+ id: 'sense-1',
370
+ question: 'If you are running a race and you pass the person in second place, what place are you in now?',
371
+ category: 'common-sense',
372
+ acceptedAnswers: ['second', '2nd', '2', 'two'],
373
+ },
374
+ {
375
+ id: 'sense-2',
376
+ question: 'What gets wetter the more it dries?',
377
+ category: 'common-sense',
378
+ acceptedAnswers: ['towel', 'a towel', 'cloth', 'rag'],
379
+ },
380
+ {
381
+ id: 'sense-3',
382
+ question: 'What can you catch but not throw?',
383
+ category: 'common-sense',
384
+ acceptedAnswers: ['cold', 'a cold', 'breath', 'your breath', 'feelings', 'disease'],
385
+ },
386
+ ];
387
+ /**
388
+ * Generate a reasoning challenge: 3 random questions requiring LLM capabilities
389
+ */
390
+ export async function generateReasoningChallenge(kv) {
391
+ cleanExpired();
392
+ const id = uuid();
393
+ // Pick 3 random questions from different categories
394
+ const shuffled = [...QUESTION_BANK].sort(() => Math.random() - 0.5);
395
+ const selectedCategories = new Set();
396
+ const selectedQuestions = [];
397
+ for (const q of shuffled) {
398
+ if (selectedQuestions.length >= 3)
399
+ break;
400
+ if (selectedQuestions.length < 2 || !selectedCategories.has(q.category)) {
401
+ selectedQuestions.push(q);
402
+ selectedCategories.add(q.category);
403
+ }
404
+ }
405
+ while (selectedQuestions.length < 3 && shuffled.length > selectedQuestions.length) {
406
+ const q = shuffled.find(sq => !selectedQuestions.includes(sq));
407
+ if (q)
408
+ selectedQuestions.push(q);
409
+ }
410
+ const expectedAnswers = {};
411
+ const questions = selectedQuestions.map(q => {
412
+ expectedAnswers[q.id] = q.acceptedAnswers;
413
+ return {
414
+ id: q.id,
415
+ question: q.question,
416
+ category: q.category,
417
+ };
418
+ });
419
+ const timeLimit = 30000; // 30 seconds
420
+ const challenge = {
421
+ id,
422
+ questions,
423
+ expectedAnswers,
424
+ issuedAt: Date.now(),
425
+ expiresAt: Date.now() + timeLimit + 5000,
426
+ };
427
+ // Store in KV or memory
428
+ if (kv) {
429
+ await kv.put(`challenge:${id}`, JSON.stringify(challenge), { expirationTtl: 300 });
430
+ }
431
+ else {
432
+ reasoningChallenges.set(id, challenge);
433
+ }
434
+ return {
435
+ id,
436
+ questions,
437
+ timeLimit,
438
+ instructions: 'Answer all 3 questions. These require reasoning that LLMs can do but simple scripts cannot. You have 30 seconds.',
439
+ };
440
+ }
441
+ /**
442
+ * Normalize answer for comparison
443
+ */
444
+ function normalizeAnswer(answer) {
445
+ return answer
446
+ .toLowerCase()
447
+ .trim()
448
+ .replace(/[.,!?'"]/g, '')
449
+ .replace(/\s+/g, ' ');
450
+ }
451
+ /**
452
+ * Check if an answer matches any accepted answer
453
+ */
454
+ function isAnswerAccepted(answer, acceptedAnswers) {
455
+ const normalized = normalizeAnswer(answer);
456
+ for (const accepted of acceptedAnswers) {
457
+ const normalizedAccepted = normalizeAnswer(accepted);
458
+ if (normalized === normalizedAccepted)
459
+ return true;
460
+ if (normalized.includes(normalizedAccepted))
461
+ return true;
462
+ if (normalizedAccepted.includes(normalized) && normalized.length > 2)
463
+ return true;
464
+ }
465
+ return false;
466
+ }
467
+ /**
468
+ * Verify a reasoning challenge response
469
+ */
470
+ export async function verifyReasoningChallenge(id, answers, kv) {
471
+ let challenge = null;
472
+ if (kv) {
473
+ const data = await kv.get(`challenge:${id}`);
474
+ challenge = data ? JSON.parse(data) : null;
475
+ }
476
+ else {
477
+ cleanExpired();
478
+ challenge = reasoningChallenges.get(id) || null;
479
+ }
480
+ if (!challenge) {
481
+ return { valid: false, reason: 'Challenge not found or expired' };
482
+ }
483
+ const now = Date.now();
484
+ const solveTimeMs = now - challenge.issuedAt;
485
+ // Delete challenge
486
+ if (kv) {
487
+ await kv.delete(`challenge:${id}`);
488
+ }
489
+ else {
490
+ reasoningChallenges.delete(id);
491
+ }
492
+ if (now > challenge.expiresAt) {
493
+ return { valid: false, reason: `Too slow! Took ${solveTimeMs}ms, limit was 30 seconds` };
494
+ }
495
+ if (!answers || typeof answers !== 'object') {
496
+ return { valid: false, reason: 'Answers must be an object mapping question IDs to answers' };
497
+ }
498
+ let correctCount = 0;
499
+ const totalCount = challenge.questions.length;
500
+ const wrongQuestions = [];
501
+ for (const q of challenge.questions) {
502
+ const userAnswer = answers[q.id];
503
+ const acceptedAnswers = challenge.expectedAnswers[q.id] || [];
504
+ if (!userAnswer) {
505
+ wrongQuestions.push(q.id);
506
+ continue;
507
+ }
508
+ if (isAnswerAccepted(userAnswer, acceptedAnswers)) {
509
+ correctCount++;
510
+ }
511
+ else {
512
+ wrongQuestions.push(q.id);
513
+ }
514
+ }
515
+ if (correctCount < totalCount) {
516
+ return {
517
+ valid: false,
518
+ reason: `Only ${correctCount}/${totalCount} correct. Wrong: ${wrongQuestions.join(', ')}`,
519
+ solveTimeMs,
520
+ correctCount,
521
+ totalCount,
522
+ };
523
+ }
524
+ return {
525
+ valid: true,
526
+ solveTimeMs,
527
+ correctCount,
528
+ totalCount,
529
+ };
530
+ }
531
+ // ============ HYBRID CHALLENGE ============
532
+ const hybridChallenges = new Map();
533
+ /**
534
+ * Generate a hybrid challenge: speed + reasoning combined
535
+ */
536
+ export async function generateHybridChallenge(kv) {
537
+ cleanExpired();
538
+ const id = uuid();
539
+ // Generate both sub-challenges
540
+ const speedChallenge = await generateSpeedChallenge(kv);
541
+ const reasoningChallenge = await generateReasoningChallenge(kv);
542
+ const hybrid = {
543
+ id,
544
+ speedChallengeId: speedChallenge.id,
545
+ reasoningChallengeId: reasoningChallenge.id,
546
+ issuedAt: Date.now(),
547
+ expiresAt: Date.now() + 35000,
548
+ };
549
+ // Store in KV or memory
550
+ if (kv) {
551
+ await kv.put(`hybrid:${id}`, JSON.stringify(hybrid), { expirationTtl: 300 });
552
+ }
553
+ else {
554
+ hybridChallenges.set(id, hybrid);
555
+ }
556
+ return {
557
+ id,
558
+ speed: {
559
+ problems: speedChallenge.problems,
560
+ timeLimit: speedChallenge.timeLimit,
561
+ },
562
+ reasoning: {
563
+ questions: reasoningChallenge.questions,
564
+ timeLimit: reasoningChallenge.timeLimit,
565
+ },
566
+ instructions: 'Solve ALL speed problems (SHA256) in <500ms AND answer ALL reasoning questions. Submit both together.',
567
+ };
568
+ }
569
+ /**
570
+ * Verify a hybrid challenge response
571
+ */
572
+ export async function verifyHybridChallenge(id, speedAnswers, reasoningAnswers, kv) {
573
+ let hybrid = null;
574
+ if (kv) {
575
+ const data = await kv.get(`hybrid:${id}`);
576
+ hybrid = data ? JSON.parse(data) : null;
577
+ }
578
+ else {
579
+ cleanExpired();
580
+ hybrid = hybridChallenges.get(id) || null;
581
+ }
582
+ if (!hybrid) {
583
+ return {
584
+ valid: false,
585
+ reason: 'Hybrid challenge not found or expired',
586
+ speed: { passed: false, reason: 'Challenge not found' },
587
+ reasoning: { passed: false, reason: 'Challenge not found' },
588
+ };
589
+ }
590
+ const now = Date.now();
591
+ const totalTimeMs = now - hybrid.issuedAt;
592
+ if (now > hybrid.expiresAt) {
593
+ if (kv) {
594
+ await kv.delete(`hybrid:${id}`);
595
+ }
596
+ else {
597
+ hybridChallenges.delete(id);
598
+ }
599
+ return {
600
+ valid: false,
601
+ reason: 'Hybrid challenge expired',
602
+ speed: { passed: false, reason: 'Expired' },
603
+ reasoning: { passed: false, reason: 'Expired' },
604
+ totalTimeMs,
605
+ };
606
+ }
607
+ // Verify speed challenge
608
+ const speedResult = await verifySpeedChallenge(hybrid.speedChallengeId, speedAnswers, kv);
609
+ // Verify reasoning challenge
610
+ const reasoningResult = await verifyReasoningChallenge(hybrid.reasoningChallengeId, reasoningAnswers, kv);
611
+ // Clean up hybrid
612
+ if (kv) {
613
+ await kv.delete(`hybrid:${id}`);
614
+ }
615
+ else {
616
+ hybridChallenges.delete(id);
617
+ }
618
+ const speedPassed = speedResult.valid;
619
+ const reasoningPassed = reasoningResult.valid;
620
+ const bothPassed = speedPassed && reasoningPassed;
621
+ return {
622
+ valid: bothPassed,
623
+ reason: bothPassed
624
+ ? undefined
625
+ : `Failed: ${!speedPassed ? 'speed' : ''}${!speedPassed && !reasoningPassed ? ' + ' : ''}${!reasoningPassed ? 'reasoning' : ''}`,
626
+ speed: {
627
+ passed: speedPassed,
628
+ solveTimeMs: speedResult.solveTimeMs,
629
+ reason: speedResult.reason,
630
+ },
631
+ reasoning: {
632
+ passed: reasoningPassed,
633
+ score: reasoningResult.valid ? `${reasoningResult.correctCount}/${reasoningResult.totalCount}` : undefined,
634
+ solveTimeMs: reasoningResult.solveTimeMs,
635
+ reason: reasoningResult.reason,
636
+ },
637
+ totalTimeMs,
638
+ };
639
+ }
package/dist/index.d.ts CHANGED
@@ -27,7 +27,7 @@ declare const app: Hono<{
27
27
  Variables: Variables;
28
28
  }, import("hono/types").BlankSchema, "/">;
29
29
  export default app;
30
- export { generateSpeedChallenge, verifySpeedChallenge, generateStandardChallenge, verifyStandardChallenge, solveSpeedChallenge, } from './challenges';
30
+ export { generateSpeedChallenge, verifySpeedChallenge, generateStandardChallenge, verifyStandardChallenge, generateReasoningChallenge, verifyReasoningChallenge, generateHybridChallenge, verifyHybridChallenge, solveSpeedChallenge, } from './challenges';
31
31
  export { generateToken, verifyToken } from './auth';
32
32
  export { checkRateLimit } from './rate-limit';
33
33
  export { generateBadge, verifyBadge, createBadgeResponse, generateBadgeSvg, generateBadgeHtml, generateShareText, type BadgeMethod, type BadgePayload, type Badge, type ShareFormats, } from './badge';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAQL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAMtB,KAAK,QAAQ,GAAG;IACd,UAAU,EAAE,WAAW,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,YAAY,CAAC,EAAE;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,iBAAiB,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,GAAG;cAAwB,QAAQ;eAAa,SAAS;yCAAK,CAAC;AAmdrE,eAAe,GAAG,CAAC;AAGnB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,yBAAyB,EACzB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAYL,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAOtB,KAAK,QAAQ,GAAG;IACd,UAAU,EAAE,WAAW,CAAC;IACxB,WAAW,EAAE,WAAW,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,YAAY,CAAC,EAAE;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,iBAAiB,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,GAAG;cAAwB,QAAQ;eAAa,SAAS;yCAAK,CAAC;AAs3BrE,eAAe,GAAG,CAAC;AAGnB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}