@skillmark/webapp 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.
Files changed (24) hide show
  1. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/cd45cc5264daa1c125545b5b4c0756df95d8b6ac5900ecf52323d90f61a47f2d.sqlite +0 -0
  2. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/fc50b649db51ed0c303ff2c4b7c0eca2da269cc3dfc7ce40615fc37a7b53366c.sqlite +0 -0
  3. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/fc50b649db51ed0c303ff2c4b7c0eca2da269cc3dfc7ce40615fc37a7b53366c.sqlite-shm +0 -0
  4. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/fc50b649db51ed0c303ff2c4b7c0eca2da269cc3dfc7ce40615fc37a7b53366c.sqlite-wal +0 -0
  5. package/.wrangler/tmp/bundle-lfa2r7/checked-fetch.js +30 -0
  6. package/.wrangler/tmp/bundle-lfa2r7/middleware-insertion-facade.js +11 -0
  7. package/.wrangler/tmp/bundle-lfa2r7/middleware-loader.entry.ts +134 -0
  8. package/.wrangler/tmp/bundle-lfa2r7/strip-cf-connecting-ip-header.js +13 -0
  9. package/.wrangler/tmp/dev-IDqSK4/worker-entry-point.js +4918 -0
  10. package/.wrangler/tmp/dev-IDqSK4/worker-entry-point.js.map +8 -0
  11. package/package.json +22 -0
  12. package/src/assets/favicon.png +0 -0
  13. package/src/assets/skillmark-thumb.png +0 -0
  14. package/src/db/d1-database-schema.sql +69 -0
  15. package/src/db/migrations/001-add-github-oauth-and-user-session-tables.sql +40 -0
  16. package/src/db/migrations/002-add-security-benchmark-columns.sql +30 -0
  17. package/src/db/migrations/003-add-repo-url-and-update-composite-formula.sql +27 -0
  18. package/src/routes/api-endpoints-handler.ts +380 -0
  19. package/src/routes/github-oauth-authentication-handler.ts +427 -0
  20. package/src/routes/html-pages-renderer.ts +2263 -0
  21. package/src/routes/static-assets-handler.ts +58 -0
  22. package/src/worker-entry-point.ts +143 -0
  23. package/tsconfig.json +19 -0
  24. package/wrangler.toml +19 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Static assets handler for favicon and OG image
3
+ */
4
+ import { Hono } from 'hono';
5
+
6
+ export const assetsRouter = new Hono();
7
+
8
+ // Favicon - base64 encoded PNG (36x35px)
9
+ const FAVICON_BASE64 = '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';
10
+
11
+ // OG Image - served from embedded base64
12
+ import { readFileSync } from 'fs';
13
+
14
+ /** Get favicon as PNG response */
15
+ assetsRouter.get('/favicon.ico', (c) => {
16
+ const buffer = Uint8Array.from(atob(FAVICON_BASE64), c => c.charCodeAt(0));
17
+ return new Response(buffer, {
18
+ headers: {
19
+ 'Content-Type': 'image/png',
20
+ 'Cache-Control': 'public, max-age=31536000',
21
+ },
22
+ });
23
+ });
24
+
25
+ assetsRouter.get('/favicon.png', (c) => {
26
+ const buffer = Uint8Array.from(atob(FAVICON_BASE64), c => c.charCodeAt(0));
27
+ return new Response(buffer, {
28
+ headers: {
29
+ 'Content-Type': 'image/png',
30
+ 'Cache-Control': 'public, max-age=31536000',
31
+ },
32
+ });
33
+ });
34
+
35
+ // OG Image - embedded base64 (loaded at build time)
36
+ // Note: For production, consider using Cloudflare R2 for large assets
37
+ const OG_IMAGE_BASE64 = `OG_IMAGE_PLACEHOLDER`;
38
+
39
+ assetsRouter.get('/og-image.png', async (c) => {
40
+ // Return a redirect to a placeholder or serve embedded image
41
+ // For now, we'll serve a simple SVG placeholder that can be replaced
42
+ const svg = `<svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg">
43
+ <rect width="100%" height="100%" fill="#0a0a0a"/>
44
+ <text x="100" y="150" font-family="monospace" font-size="72" font-weight="bold" fill="#fff">SKILLMARK</text>
45
+ <text x="100" y="210" font-family="monospace" font-size="24" fill="#888">THE AGENT SKILL BENCHMARKING PLATFORM</text>
46
+ <text x="100" y="320" font-family="sans-serif" font-size="36" fill="#ccc">Benchmark your AI agent skills with detailed</text>
47
+ <text x="100" y="370" font-family="sans-serif" font-size="36" fill="#ccc">metrics. Compare accuracy, token usage, and</text>
48
+ <text x="100" y="420" font-family="sans-serif" font-size="36" fill="#ccc">cost across models.</text>
49
+ <text x="100" y="540" font-family="monospace" font-size="20" fill="#666">$ npx skillmark run &lt;skill-path&gt;</text>
50
+ </svg>`;
51
+
52
+ return new Response(svg, {
53
+ headers: {
54
+ 'Content-Type': 'image/svg+xml',
55
+ 'Cache-Control': 'public, max-age=86400',
56
+ },
57
+ });
58
+ });
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Skillmark Cloudflare Worker entry point
3
+ *
4
+ * Routes:
5
+ * GET / - Leaderboard HTML page
6
+ * POST /api/results - Submit benchmark results
7
+ * GET /api/leaderboard - Get skill rankings JSON
8
+ * GET /api/skill/:name - Get specific skill details
9
+ * POST /api/verify - Verify API key
10
+ */
11
+ import { Hono } from 'hono';
12
+ import { cors } from 'hono/cors';
13
+ import { logger } from 'hono/logger';
14
+ import { apiRouter } from './routes/api-endpoints-handler.js';
15
+ import { pagesRouter } from './routes/html-pages-renderer.js';
16
+ import { authRouter } from './routes/github-oauth-authentication-handler.js';
17
+ import { assetsRouter } from './routes/static-assets-handler.js';
18
+
19
+ type Bindings = {
20
+ DB: D1Database;
21
+ ENVIRONMENT: string;
22
+ GITHUB_CLIENT_ID: string;
23
+ GITHUB_CLIENT_SECRET: string;
24
+ SESSION_SECRET: string;
25
+ };
26
+
27
+ const app = new Hono<{ Bindings: Bindings }>();
28
+
29
+ // Middleware
30
+ app.use('*', cors({
31
+ origin: '*',
32
+ allowMethods: ['GET', 'POST', 'OPTIONS'],
33
+ allowHeaders: ['Content-Type', 'Authorization', 'X-Skillmark-Version'],
34
+ }));
35
+
36
+ // Only log in development
37
+ app.use('*', async (c, next) => {
38
+ if (c.env.ENVIRONMENT !== 'production') {
39
+ return logger()(c, next);
40
+ }
41
+ return next();
42
+ });
43
+
44
+ // Health check
45
+ app.get('/health', (c) => {
46
+ return c.json({ status: 'ok', timestamp: new Date().toISOString() });
47
+ });
48
+
49
+ // Static assets (favicon, og-image)
50
+ app.route('/', assetsRouter);
51
+
52
+ // Auth routes (GitHub OAuth)
53
+ app.route('/auth', authRouter);
54
+
55
+ // API routes
56
+ app.route('/api', apiRouter);
57
+
58
+ // HTML pages
59
+ app.route('/', pagesRouter);
60
+
61
+ // 404 handler
62
+ app.notFound((c) => {
63
+ const accept = c.req.header('Accept') || '';
64
+
65
+ if (accept.includes('application/json')) {
66
+ return c.json({ error: 'Not found' }, 404);
67
+ }
68
+
69
+ return c.html(`
70
+ <!DOCTYPE html>
71
+ <html>
72
+ <head>
73
+ <title>404 - Skillmark</title>
74
+ <style>
75
+ body {
76
+ font-family: system-ui, sans-serif;
77
+ background: #0d1117;
78
+ color: #c9d1d9;
79
+ display: flex;
80
+ justify-content: center;
81
+ align-items: center;
82
+ min-height: 100vh;
83
+ margin: 0;
84
+ }
85
+ .container { text-align: center; }
86
+ h1 { color: #58a6ff; }
87
+ a { color: #58a6ff; }
88
+ </style>
89
+ </head>
90
+ <body>
91
+ <div class="container">
92
+ <h1>404</h1>
93
+ <p>Page not found</p>
94
+ <p><a href="/">Back to leaderboard</a></p>
95
+ </div>
96
+ </body>
97
+ </html>
98
+ `, 404);
99
+ });
100
+
101
+ // Error handler
102
+ app.onError((err, c) => {
103
+ console.error('Unhandled error:', err);
104
+
105
+ const accept = c.req.header('Accept') || '';
106
+
107
+ if (accept.includes('application/json')) {
108
+ return c.json({ error: 'Internal server error' }, 500);
109
+ }
110
+
111
+ return c.html(`
112
+ <!DOCTYPE html>
113
+ <html>
114
+ <head>
115
+ <title>Error - Skillmark</title>
116
+ <style>
117
+ body {
118
+ font-family: system-ui, sans-serif;
119
+ background: #0d1117;
120
+ color: #c9d1d9;
121
+ display: flex;
122
+ justify-content: center;
123
+ align-items: center;
124
+ min-height: 100vh;
125
+ margin: 0;
126
+ }
127
+ .container { text-align: center; }
128
+ h1 { color: #f85149; }
129
+ a { color: #58a6ff; }
130
+ </style>
131
+ </head>
132
+ <body>
133
+ <div class="container">
134
+ <h1>Error</h1>
135
+ <p>Something went wrong</p>
136
+ <p><a href="/">Back to leaderboard</a></p>
137
+ </div>
138
+ </body>
139
+ </html>
140
+ `, 500);
141
+ });
142
+
143
+ export default app;
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2022"],
7
+ "types": ["@cloudflare/workers-types"],
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "noEmit": true,
13
+ "resolveJsonModule": true,
14
+ "allowSyntheticDefaultImports": true,
15
+ "isolatedModules": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules"]
19
+ }
package/wrangler.toml ADDED
@@ -0,0 +1,19 @@
1
+ name = "skillmark"
2
+ main = "src/worker-entry-point.ts"
3
+ compatibility_date = "2024-01-01"
4
+
5
+ [[d1_databases]]
6
+ binding = "DB"
7
+ database_name = "skillmark-db"
8
+ database_id = "e1b45b94-f799-4e75-83f2-bebd777b040d"
9
+
10
+ [vars]
11
+ ENVIRONMENT = "production"
12
+
13
+ # Custom domain (configured via Cloudflare dashboard)
14
+ # Domain: skillmark.sh
15
+
16
+ # Required secrets (set via wrangler secret put):
17
+ # - GITHUB_CLIENT_ID: GitHub OAuth App client ID
18
+ # - GITHUB_CLIENT_SECRET: GitHub OAuth App client secret
19
+ # - SESSION_SECRET: Random secret for signing session cookies