@dupecom/botcha-cloudflare 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"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"}
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;AA87BrE,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"}
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { Hono } from 'hono';
9
9
  import { cors } from 'hono/cors';
10
- import { generateSpeedChallenge, verifySpeedChallenge, generateStandardChallenge, verifyStandardChallenge, generateReasoningChallenge, verifyReasoningChallenge, generateHybridChallenge, verifyHybridChallenge, verifyLandingChallenge, } from './challenges';
10
+ import { generateSpeedChallenge, verifySpeedChallenge, generateStandardChallenge, verifyStandardChallenge, generateReasoningChallenge, verifyReasoningChallenge, generateHybridChallenge, verifyHybridChallenge, verifyLandingChallenge, validateLandingToken, } from './challenges';
11
11
  import { generateToken, verifyToken, extractBearerToken } from './auth';
12
12
  import { checkRateLimit, getClientIP } from './rate-limit';
13
13
  import { verifyBadge, generateBadgeSvg, generateBadgeHtml, createBadgeResponse } from './badge';
@@ -250,6 +250,7 @@ app.get('/health', (c) => {
250
250
  app.get('/v1/challenges', rateLimitMiddleware, async (c) => {
251
251
  const type = c.req.query('type') || 'hybrid';
252
252
  const difficulty = c.req.query('difficulty') || 'medium';
253
+ const baseUrl = new URL(c.req.url).origin;
253
254
  if (type === 'hybrid') {
254
255
  const challenge = await generateHybridChallenge(c.env.CHALLENGES);
255
256
  return c.json({
@@ -271,6 +272,12 @@ app.get('/v1/challenges', rateLimitMiddleware, async (c) => {
271
272
  },
272
273
  instructions: challenge.instructions,
273
274
  tip: '🔥 This is the ultimate test: proves you can compute AND reason like an AI.',
275
+ verify_endpoint: `${baseUrl}/v1/challenges/${challenge.id}/verify`,
276
+ submit_body: {
277
+ type: 'hybrid',
278
+ speed_answers: ['hash1', 'hash2', '...'],
279
+ reasoning_answers: { 'question-id': 'answer', '...': '...' }
280
+ }
274
281
  });
275
282
  }
276
283
  else if (type === 'speed') {
@@ -285,6 +292,11 @@ app.get('/v1/challenges', rateLimitMiddleware, async (c) => {
285
292
  instructions: challenge.instructions,
286
293
  },
287
294
  tip: '⚡ Speed challenge: You have 500ms to solve ALL problems. Humans cannot copy-paste fast enough.',
295
+ verify_endpoint: `${baseUrl}/v1/challenges/${challenge.id}/verify`,
296
+ submit_body: {
297
+ type: 'speed',
298
+ answers: ['hash1', 'hash2', 'hash3', 'hash4', 'hash5']
299
+ }
288
300
  });
289
301
  }
290
302
  else {
@@ -298,6 +310,10 @@ app.get('/v1/challenges', rateLimitMiddleware, async (c) => {
298
310
  timeLimit: `${challenge.timeLimit}ms`,
299
311
  hint: challenge.hint,
300
312
  },
313
+ verify_endpoint: `${baseUrl}/v1/challenges/${challenge.id}/verify`,
314
+ submit_body: {
315
+ answer: 'your-answer'
316
+ }
301
317
  });
302
318
  }
303
319
  });
@@ -413,6 +429,7 @@ app.post('/v1/token/verify', async (c) => {
413
429
  // Get reasoning challenge
414
430
  app.get('/v1/reasoning', rateLimitMiddleware, async (c) => {
415
431
  const challenge = await generateReasoningChallenge(c.env.CHALLENGES);
432
+ const baseUrl = new URL(c.req.url).origin;
416
433
  return c.json({
417
434
  success: true,
418
435
  type: 'reasoning',
@@ -424,6 +441,11 @@ app.get('/v1/reasoning', rateLimitMiddleware, async (c) => {
424
441
  instructions: challenge.instructions,
425
442
  },
426
443
  tip: 'These questions require reasoning that LLMs can do, but simple scripts cannot.',
444
+ verify_endpoint: `${baseUrl}/v1/reasoning`,
445
+ submit_body: {
446
+ id: challenge.id,
447
+ answers: { 'question-id': 'your answer', '...': '...' }
448
+ }
427
449
  });
428
450
  });
429
451
  // Verify reasoning challenge
@@ -452,6 +474,7 @@ app.post('/v1/reasoning', async (c) => {
452
474
  // Get hybrid challenge (v1 API)
453
475
  app.get('/v1/hybrid', rateLimitMiddleware, async (c) => {
454
476
  const challenge = await generateHybridChallenge(c.env.CHALLENGES);
477
+ const baseUrl = new URL(c.req.url).origin;
455
478
  return c.json({
456
479
  success: true,
457
480
  type: 'hybrid',
@@ -471,6 +494,12 @@ app.get('/v1/hybrid', rateLimitMiddleware, async (c) => {
471
494
  },
472
495
  instructions: challenge.instructions,
473
496
  tip: 'This is the ultimate test: proves you can compute AND reason like an AI.',
497
+ verify_endpoint: `${baseUrl}/v1/hybrid`,
498
+ submit_body: {
499
+ id: challenge.id,
500
+ speed_answers: ['hash1', 'hash2', '...'],
501
+ reasoning_answers: { 'question-id': 'answer', '...': '...' }
502
+ }
474
503
  });
475
504
  });
476
505
  // Verify hybrid challenge (v1 API)
@@ -600,8 +629,44 @@ app.post('/api/reasoning-challenge', async (c) => {
600
629
  });
601
630
  });
602
631
  // ============ PROTECTED ENDPOINT ============
603
- app.get('/agent-only', requireJWT, async (c) => {
604
- const payload = c.get('tokenPayload');
632
+ app.get('/agent-only', async (c) => {
633
+ // Check for landing token first (X-Botcha-Landing-Token header)
634
+ const landingToken = c.req.header('x-botcha-landing-token');
635
+ if (landingToken) {
636
+ const isValid = await validateLandingToken(landingToken, c.env.CHALLENGES);
637
+ if (isValid) {
638
+ return c.json({
639
+ success: true,
640
+ message: '🤖 Welcome, fellow agent!',
641
+ verified: true,
642
+ agent: 'landing-challenge-verified',
643
+ method: 'landing-token',
644
+ timestamp: new Date().toISOString(),
645
+ secret: 'The humans will never see this. Their fingers are too slow. 🤫',
646
+ });
647
+ }
648
+ }
649
+ // Fallback to JWT Bearer token
650
+ const authHeader = c.req.header('authorization');
651
+ const token = extractBearerToken(authHeader);
652
+ if (!token) {
653
+ return c.json({
654
+ error: 'UNAUTHORIZED',
655
+ message: 'Missing authentication. Use either:\n1. X-Botcha-Landing-Token header (from POST /api/verify-landing)\n2. Authorization: Bearer <token> (from POST /v1/token/verify)',
656
+ methods: {
657
+ landing: 'Solve landing page challenge via POST /api/verify-landing',
658
+ jwt: 'Solve speed challenge via POST /v1/token/verify'
659
+ }
660
+ }, 401);
661
+ }
662
+ const result = await verifyToken(token, c.env.JWT_SECRET);
663
+ if (!result.valid) {
664
+ return c.json({
665
+ error: 'INVALID_TOKEN',
666
+ message: result.error || 'Token is invalid or expired',
667
+ }, 401);
668
+ }
669
+ // JWT verified
605
670
  return c.json({
606
671
  success: true,
607
672
  message: '🤖 Welcome, fellow agent!',
@@ -609,7 +674,7 @@ app.get('/agent-only', requireJWT, async (c) => {
609
674
  agent: 'jwt-verified',
610
675
  method: 'bearer-token',
611
676
  timestamp: new Date().toISOString(),
612
- solveTime: `${payload?.solveTime}ms`,
677
+ solveTime: `${result.payload?.solveTime}ms`,
613
678
  secret: 'The humans will never see this. Their fingers are too slow. 🤫',
614
679
  });
615
680
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dupecom/botcha-cloudflare",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "BOTCHA for Cloudflare Workers - Prove you're a bot. Humans need not apply.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",