@dupecom/botcha-cloudflare 0.3.1 → 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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -4
- package/dist/routes/stream.d.ts.map +1 -1
- package/dist/routes/stream.js +14 -3
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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',
|
|
604
|
-
|
|
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
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/routes/stream.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAA0B,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAKzE,KAAK,QAAQ,GAAG;IACd,UAAU,EAAE,WAAW,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAYF,QAAA,MAAM,GAAG;cAAwB,QAAQ;yCAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/routes/stream.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAA0B,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAKzE,KAAK,QAAQ,GAAG;IACd,UAAU,EAAE,WAAW,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAYF,QAAA,MAAM,GAAG;cAAwB,QAAQ;yCAAK,CAAC;AAiT/C,eAAe,GAAG,CAAC"}
|
package/dist/routes/stream.js
CHANGED
|
@@ -143,29 +143,40 @@ app.post('/v1/challenge/stream/:session', async (c) => {
|
|
|
143
143
|
expectedAnswers.push(await sha256First(num.toString(), 8));
|
|
144
144
|
}
|
|
145
145
|
// Update session
|
|
146
|
+
const timerStart = Date.now();
|
|
146
147
|
session.status = 'challenged';
|
|
147
148
|
session.problems = problems;
|
|
148
149
|
session.expectedAnswers = expectedAnswers;
|
|
149
|
-
session.timerStart =
|
|
150
|
+
session.timerStart = timerStart;
|
|
151
|
+
// Store session
|
|
150
152
|
await storeSession(c.env.CHALLENGES, session);
|
|
151
|
-
// Return challenge event
|
|
153
|
+
// Return challenge event with timer start for client to track
|
|
152
154
|
return c.json({
|
|
153
155
|
success: true,
|
|
154
156
|
event: 'challenge',
|
|
155
157
|
data: {
|
|
156
158
|
problems,
|
|
157
159
|
timeLimit: 500,
|
|
160
|
+
timerStart, // Include so client can verify timing
|
|
158
161
|
instructions: 'Compute SHA256 of each number, return first 8 hex chars',
|
|
159
162
|
},
|
|
160
163
|
});
|
|
161
164
|
}
|
|
162
165
|
// Handle "solve" action - verify answers
|
|
163
166
|
if (action === 'solve') {
|
|
167
|
+
// Handle KV eventual consistency - retry once if still in 'ready' state
|
|
168
|
+
if (session.status === 'ready') {
|
|
169
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
170
|
+
const retrySession = await getSession(c.env.CHALLENGES, sessionId);
|
|
171
|
+
if (retrySession && retrySession.status === 'challenged') {
|
|
172
|
+
Object.assign(session, retrySession);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
164
175
|
if (session.status !== 'challenged') {
|
|
165
176
|
return c.json({
|
|
166
177
|
success: false,
|
|
167
178
|
error: 'INVALID_STATE',
|
|
168
|
-
message: `Session is in ${session.status} state, expected challenged
|
|
179
|
+
message: `Session is in ${session.status} state, expected challenged. Try sending GO first.`,
|
|
169
180
|
}, 400);
|
|
170
181
|
}
|
|
171
182
|
if (!answers || !Array.isArray(answers)) {
|