@jonsoc/console-app 1.1.34

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 (217) hide show
  1. package/.opencode/agent/css.md +149 -0
  2. package/README.md +32 -0
  3. package/package.json +49 -0
  4. package/public/apple-touch-icon-v3.png +1 -0
  5. package/public/apple-touch-icon.png +1 -0
  6. package/public/email +1 -0
  7. package/public/favicon-96x96-v3.png +1 -0
  8. package/public/favicon-96x96.png +1 -0
  9. package/public/favicon-v3.ico +1 -0
  10. package/public/favicon-v3.svg +1 -0
  11. package/public/favicon.ico +1 -0
  12. package/public/favicon.svg +1 -0
  13. package/public/opencode-brand-assets.zip +0 -0
  14. package/public/robots.txt +6 -0
  15. package/public/site.webmanifest +1 -0
  16. package/public/social-share-black.png +1 -0
  17. package/public/social-share-zen.png +1 -0
  18. package/public/social-share.png +1 -0
  19. package/public/theme.json +182 -0
  20. package/public/web-app-manifest-192x192.png +1 -0
  21. package/public/web-app-manifest-512x512.png +1 -0
  22. package/script/generate-sitemap.ts +103 -0
  23. package/src/app.css +1 -0
  24. package/src/app.tsx +27 -0
  25. package/src/asset/black/hero.png +0 -0
  26. package/src/asset/brand/opencode-brand-assets.zip +0 -0
  27. package/src/asset/brand/opencode-logo-dark.png +0 -0
  28. package/src/asset/brand/opencode-logo-dark.svg +16 -0
  29. package/src/asset/brand/opencode-logo-light.png +0 -0
  30. package/src/asset/brand/opencode-logo-light.svg +16 -0
  31. package/src/asset/brand/opencode-wordmark-dark.png +0 -0
  32. package/src/asset/brand/opencode-wordmark-dark.svg +30 -0
  33. package/src/asset/brand/opencode-wordmark-light.png +0 -0
  34. package/src/asset/brand/opencode-wordmark-light.svg +30 -0
  35. package/src/asset/brand/opencode-wordmark-simple-dark.png +0 -0
  36. package/src/asset/brand/opencode-wordmark-simple-dark.svg +22 -0
  37. package/src/asset/brand/opencode-wordmark-simple-light.png +0 -0
  38. package/src/asset/brand/opencode-wordmark-simple-light.svg +22 -0
  39. package/src/asset/brand/preview-opencode-dark.png +0 -0
  40. package/src/asset/brand/preview-opencode-logo-dark.png +0 -0
  41. package/src/asset/brand/preview-opencode-logo-light.png +0 -0
  42. package/src/asset/brand/preview-opencode-wordmark-dark.png +0 -0
  43. package/src/asset/brand/preview-opencode-wordmark-light.png +0 -0
  44. package/src/asset/brand/preview-opencode-wordmark-simple-dark.png +0 -0
  45. package/src/asset/brand/preview-opencode-wordmark-simple-light.png +0 -0
  46. package/src/asset/lander/avatar-adam.png +0 -0
  47. package/src/asset/lander/avatar-david.png +0 -0
  48. package/src/asset/lander/avatar-dax.png +0 -0
  49. package/src/asset/lander/avatar-frank.png +0 -0
  50. package/src/asset/lander/avatar-jay.png +0 -0
  51. package/src/asset/lander/brand-assets-dark.svg +10 -0
  52. package/src/asset/lander/brand-assets-light.svg +10 -0
  53. package/src/asset/lander/brand.png +0 -0
  54. package/src/asset/lander/check.svg +3 -0
  55. package/src/asset/lander/copy.svg +3 -0
  56. package/src/asset/lander/desktop-app-icon.png +0 -0
  57. package/src/asset/lander/dock.png +0 -0
  58. package/src/asset/lander/logo-dark.svg +11 -0
  59. package/src/asset/lander/logo-light.svg +11 -0
  60. package/src/asset/lander/opencode-comparison-min.mp4 +0 -0
  61. package/src/asset/lander/opencode-comparison-poster.png +0 -0
  62. package/src/asset/lander/opencode-desktop-icon.png +0 -0
  63. package/src/asset/lander/opencode-logo-dark.svg +11 -0
  64. package/src/asset/lander/opencode-logo-light.svg +11 -0
  65. package/src/asset/lander/opencode-min.mp4 +0 -0
  66. package/src/asset/lander/opencode-poster.png +0 -0
  67. package/src/asset/lander/opencode-wordmark-dark.svg +25 -0
  68. package/src/asset/lander/opencode-wordmark-light.svg +25 -0
  69. package/src/asset/lander/screenshot-github.png +0 -0
  70. package/src/asset/lander/screenshot-splash.png +0 -0
  71. package/src/asset/lander/screenshot-vscode.png +0 -0
  72. package/src/asset/lander/screenshot.png +0 -0
  73. package/src/asset/lander/wordmark-dark.svg +3 -0
  74. package/src/asset/lander/wordmark-light.svg +3 -0
  75. package/src/asset/logo-ornate-dark.svg +18 -0
  76. package/src/asset/logo-ornate-light.svg +18 -0
  77. package/src/asset/logo.svg +18 -0
  78. package/src/asset/zen-ornate-dark.svg +8 -0
  79. package/src/asset/zen-ornate-light.svg +8 -0
  80. package/src/component/dropdown.css +80 -0
  81. package/src/component/dropdown.tsx +79 -0
  82. package/src/component/email-signup.tsx +48 -0
  83. package/src/component/faq.tsx +33 -0
  84. package/src/component/footer.tsx +38 -0
  85. package/src/component/header-context-menu.css +63 -0
  86. package/src/component/header.tsx +279 -0
  87. package/src/component/icon.tsx +257 -0
  88. package/src/component/legal.tsx +20 -0
  89. package/src/component/modal.css +66 -0
  90. package/src/component/modal.tsx +24 -0
  91. package/src/component/spotlight.css +15 -0
  92. package/src/component/spotlight.tsx +820 -0
  93. package/src/config.ts +29 -0
  94. package/src/context/auth.session.ts +0 -0
  95. package/src/context/auth.ts +116 -0
  96. package/src/context/auth.withActor.ts +7 -0
  97. package/src/entry-client.tsx +4 -0
  98. package/src/entry-server.tsx +30 -0
  99. package/src/global.d.ts +5 -0
  100. package/src/lib/github.ts +38 -0
  101. package/src/middleware.ts +5 -0
  102. package/src/routes/[...404].css +130 -0
  103. package/src/routes/[...404].tsx +38 -0
  104. package/src/routes/api/enterprise.ts +47 -0
  105. package/src/routes/auth/[...callback].ts +41 -0
  106. package/src/routes/auth/authorize.ts +10 -0
  107. package/src/routes/auth/index.ts +12 -0
  108. package/src/routes/auth/logout.ts +17 -0
  109. package/src/routes/auth/status.ts +7 -0
  110. package/src/routes/bench/[id].tsx +365 -0
  111. package/src/routes/bench/index.tsx +86 -0
  112. package/src/routes/bench/submission.ts +29 -0
  113. package/src/routes/black/common.tsx +62 -0
  114. package/src/routes/black/index.tsx +108 -0
  115. package/src/routes/black/subscribe/[plan].tsx +449 -0
  116. package/src/routes/black/workspace.css +214 -0
  117. package/src/routes/black/workspace.tsx +229 -0
  118. package/src/routes/black.css +828 -0
  119. package/src/routes/black.tsx +285 -0
  120. package/src/routes/brand/index.css +555 -0
  121. package/src/routes/brand/index.tsx +252 -0
  122. package/src/routes/changelog/index.css +477 -0
  123. package/src/routes/changelog/index.tsx +147 -0
  124. package/src/routes/debug/index.ts +13 -0
  125. package/src/routes/desktop-feedback.ts +5 -0
  126. package/src/routes/discord.ts +5 -0
  127. package/src/routes/docs/[...path].ts +20 -0
  128. package/src/routes/docs/index.ts +20 -0
  129. package/src/routes/download/[platform].ts +38 -0
  130. package/src/routes/download/index.css +750 -0
  131. package/src/routes/download/index.tsx +482 -0
  132. package/src/routes/download/types.ts +4 -0
  133. package/src/routes/enterprise/index.css +578 -0
  134. package/src/routes/enterprise/index.tsx +251 -0
  135. package/src/routes/index.css +1251 -0
  136. package/src/routes/index.tsx +840 -0
  137. package/src/routes/legal/privacy-policy/index.css +343 -0
  138. package/src/routes/legal/privacy-policy/index.tsx +1512 -0
  139. package/src/routes/legal/terms-of-service/index.css +254 -0
  140. package/src/routes/legal/terms-of-service/index.tsx +512 -0
  141. package/src/routes/openapi.json.ts +7 -0
  142. package/src/routes/s/[id].ts +20 -0
  143. package/src/routes/stripe/webhook.ts +532 -0
  144. package/src/routes/t/[...path].tsx +20 -0
  145. package/src/routes/temp.tsx +172 -0
  146. package/src/routes/user-menu.css +18 -0
  147. package/src/routes/user-menu.tsx +32 -0
  148. package/src/routes/workspace/[id]/billing/billing-section.module.css +185 -0
  149. package/src/routes/workspace/[id]/billing/billing-section.tsx +240 -0
  150. package/src/routes/workspace/[id]/billing/black-section.module.css +142 -0
  151. package/src/routes/workspace/[id]/billing/black-section.tsx +269 -0
  152. package/src/routes/workspace/[id]/billing/black-waitlist-section.module.css +23 -0
  153. package/src/routes/workspace/[id]/billing/index.tsx +32 -0
  154. package/src/routes/workspace/[id]/billing/monthly-limit-section.module.css +96 -0
  155. package/src/routes/workspace/[id]/billing/monthly-limit-section.tsx +133 -0
  156. package/src/routes/workspace/[id]/billing/payment-section.module.css +93 -0
  157. package/src/routes/workspace/[id]/billing/payment-section.tsx +122 -0
  158. package/src/routes/workspace/[id]/billing/reload-section.module.css +261 -0
  159. package/src/routes/workspace/[id]/billing/reload-section.tsx +213 -0
  160. package/src/routes/workspace/[id]/graph-section.module.css +145 -0
  161. package/src/routes/workspace/[id]/graph-section.tsx +475 -0
  162. package/src/routes/workspace/[id]/index.tsx +81 -0
  163. package/src/routes/workspace/[id]/keys/index.tsx +11 -0
  164. package/src/routes/workspace/[id]/keys/key-section.module.css +197 -0
  165. package/src/routes/workspace/[id]/keys/key-section.tsx +176 -0
  166. package/src/routes/workspace/[id]/members/index.tsx +11 -0
  167. package/src/routes/workspace/[id]/members/member-section.module.css +249 -0
  168. package/src/routes/workspace/[id]/members/member-section.tsx +343 -0
  169. package/src/routes/workspace/[id]/members/role-dropdown.css +72 -0
  170. package/src/routes/workspace/[id]/members/role-dropdown.tsx +43 -0
  171. package/src/routes/workspace/[id]/model-section.module.css +173 -0
  172. package/src/routes/workspace/[id]/model-section.tsx +174 -0
  173. package/src/routes/workspace/[id]/new-user-section.module.css +143 -0
  174. package/src/routes/workspace/[id]/new-user-section.tsx +104 -0
  175. package/src/routes/workspace/[id]/provider-section.module.css +138 -0
  176. package/src/routes/workspace/[id]/provider-section.tsx +188 -0
  177. package/src/routes/workspace/[id]/settings/index.tsx +11 -0
  178. package/src/routes/workspace/[id]/settings/settings-section.module.css +94 -0
  179. package/src/routes/workspace/[id]/settings/settings-section.tsx +122 -0
  180. package/src/routes/workspace/[id]/usage-section.module.css +185 -0
  181. package/src/routes/workspace/[id]/usage-section.tsx +200 -0
  182. package/src/routes/workspace/[id].css +308 -0
  183. package/src/routes/workspace/[id].tsx +62 -0
  184. package/src/routes/workspace/common.tsx +120 -0
  185. package/src/routes/workspace-picker.css +74 -0
  186. package/src/routes/workspace-picker.tsx +122 -0
  187. package/src/routes/workspace.css +107 -0
  188. package/src/routes/workspace.tsx +38 -0
  189. package/src/routes/zen/index.css +866 -0
  190. package/src/routes/zen/index.tsx +343 -0
  191. package/src/routes/zen/util/dataDumper.ts +44 -0
  192. package/src/routes/zen/util/error.ts +13 -0
  193. package/src/routes/zen/util/handler.ts +784 -0
  194. package/src/routes/zen/util/logger.ts +12 -0
  195. package/src/routes/zen/util/provider/anthropic.ts +752 -0
  196. package/src/routes/zen/util/provider/google.ts +75 -0
  197. package/src/routes/zen/util/provider/openai-compatible.ts +546 -0
  198. package/src/routes/zen/util/provider/openai.ts +630 -0
  199. package/src/routes/zen/util/provider/provider.ts +210 -0
  200. package/src/routes/zen/util/rateLimiter.ts +41 -0
  201. package/src/routes/zen/util/stickyProviderTracker.ts +16 -0
  202. package/src/routes/zen/util/trialLimiter.ts +49 -0
  203. package/src/routes/zen/v1/chat/completions.ts +11 -0
  204. package/src/routes/zen/v1/messages.ts +11 -0
  205. package/src/routes/zen/v1/models/[model].ts +13 -0
  206. package/src/routes/zen/v1/models.ts +60 -0
  207. package/src/routes/zen/v1/responses.ts +11 -0
  208. package/src/style/base.css +21 -0
  209. package/src/style/component/button.css +102 -0
  210. package/src/style/index.css +8 -0
  211. package/src/style/reset.css +76 -0
  212. package/src/style/token/color.css +91 -0
  213. package/src/style/token/font.css +21 -0
  214. package/src/style/token/space.css +46 -0
  215. package/sst-env.d.ts +9 -0
  216. package/tsconfig.json +21 -0
  217. package/vite.config.ts +25 -0
@@ -0,0 +1,365 @@
1
+ import { Title } from "@solidjs/meta"
2
+ import { createAsync, query, useParams } from "@solidjs/router"
3
+ import { createSignal, For, Show } from "solid-js"
4
+ import { Database, desc, eq } from "@jonsoc/console-core/drizzle/index.js"
5
+ import { BenchmarkTable } from "@jonsoc/console-core/schema/benchmark.sql.js"
6
+
7
+ interface TaskSource {
8
+ repo: string
9
+ from: string
10
+ to: string
11
+ }
12
+
13
+ interface Judge {
14
+ score: number
15
+ rationale: string
16
+ judge: string
17
+ }
18
+
19
+ interface ScoreDetail {
20
+ criterion: string
21
+ weight: number
22
+ average: number
23
+ variance?: number
24
+ judges?: Judge[]
25
+ }
26
+
27
+ interface RunUsage {
28
+ input: number
29
+ output: number
30
+ cost: number
31
+ }
32
+
33
+ interface Run {
34
+ task: string
35
+ model: string
36
+ agent: string
37
+ score: {
38
+ final: number
39
+ base: number
40
+ penalty: number
41
+ }
42
+ scoreDetails: ScoreDetail[]
43
+ usage?: RunUsage
44
+ duration?: number
45
+ }
46
+
47
+ interface Prompt {
48
+ commit: string
49
+ prompt: string
50
+ }
51
+
52
+ interface AverageUsage {
53
+ input: number
54
+ output: number
55
+ cost: number
56
+ }
57
+
58
+ interface Task {
59
+ averageScore: number
60
+ averageDuration?: number
61
+ averageUsage?: AverageUsage
62
+ model?: string
63
+ agent?: string
64
+ summary?: string
65
+ runs?: Run[]
66
+ task: {
67
+ id: string
68
+ source: TaskSource
69
+ prompts?: Prompt[]
70
+ }
71
+ }
72
+
73
+ interface BenchmarkResult {
74
+ averageScore: number
75
+ tasks: Task[]
76
+ }
77
+
78
+ async function getTaskDetail(benchmarkId: string, taskId: string) {
79
+ "use server"
80
+ const rows = await Database.use((tx) =>
81
+ tx.select().from(BenchmarkTable).where(eq(BenchmarkTable.id, benchmarkId)).limit(1),
82
+ )
83
+ if (!rows[0]) return null
84
+ const parsed = JSON.parse(rows[0].result) as BenchmarkResult
85
+ const task = parsed.tasks.find((t) => t.task.id === taskId)
86
+ return task ?? null
87
+ }
88
+
89
+ const queryTaskDetail = query(getTaskDetail, "benchmark.task.detail")
90
+
91
+ function formatDuration(ms: number): string {
92
+ const seconds = Math.floor(ms / 1000)
93
+ const minutes = Math.floor(seconds / 60)
94
+ const remainingSeconds = seconds % 60
95
+ if (minutes > 0) {
96
+ return `${minutes}m ${remainingSeconds}s`
97
+ }
98
+ return `${remainingSeconds}s`
99
+ }
100
+
101
+ export default function BenchDetail() {
102
+ const params = useParams()
103
+ const [benchmarkId, taskId] = (params.id ?? "").split(":")
104
+ const task = createAsync(() => queryTaskDetail(benchmarkId, taskId))
105
+
106
+ return (
107
+ <main data-page="bench-detail">
108
+ <Title>Benchmark - {taskId}</Title>
109
+ <div style={{ padding: "1rem" }}>
110
+ <Show when={task()} fallback={<p>Task not found</p>}>
111
+ <div style={{ "margin-bottom": "1rem" }}>
112
+ <div>
113
+ <strong>Agent: </strong>
114
+ {task()?.agent ?? "N/A"}
115
+ </div>
116
+ <div>
117
+ <strong>Model: </strong>
118
+ {task()?.model ?? "N/A"}
119
+ </div>
120
+ <div>
121
+ <strong>Task: </strong>
122
+ {task()!.task.id}
123
+ </div>
124
+ </div>
125
+
126
+ <div style={{ "margin-bottom": "1rem" }}>
127
+ <div>
128
+ <strong>Repo: </strong>
129
+ <a
130
+ href={`https://github.com/${task()!.task.source.repo}`}
131
+ target="_blank"
132
+ rel="noopener noreferrer"
133
+ style={{ color: "#0066cc" }}
134
+ >
135
+ {task()!.task.source.repo}
136
+ </a>
137
+ </div>
138
+ <div>
139
+ <strong>From: </strong>
140
+ <a
141
+ href={`https://github.com/${task()!.task.source.repo}/commit/${task()!.task.source.from}`}
142
+ target="_blank"
143
+ rel="noopener noreferrer"
144
+ style={{ color: "#0066cc" }}
145
+ >
146
+ {task()!.task.source.from.slice(0, 7)}
147
+ </a>
148
+ </div>
149
+ <div>
150
+ <strong>To: </strong>
151
+ <a
152
+ href={`https://github.com/${task()!.task.source.repo}/commit/${task()!.task.source.to}`}
153
+ target="_blank"
154
+ rel="noopener noreferrer"
155
+ style={{ color: "#0066cc" }}
156
+ >
157
+ {task()!.task.source.to.slice(0, 7)}
158
+ </a>
159
+ </div>
160
+ </div>
161
+
162
+ <Show when={task()?.task.prompts && task()!.task.prompts!.length > 0}>
163
+ <div style={{ "margin-bottom": "1rem" }}>
164
+ <strong>Prompt:</strong>
165
+ <For each={task()!.task.prompts}>
166
+ {(p) => (
167
+ <div style={{ "margin-top": "0.5rem" }}>
168
+ <div style={{ "font-size": "0.875rem", color: "#666" }}>Commit: {p.commit.slice(0, 7)}</div>
169
+ <p style={{ "margin-top": "0.25rem", "white-space": "pre-wrap" }}>{p.prompt}</p>
170
+ </div>
171
+ )}
172
+ </For>
173
+ </div>
174
+ </Show>
175
+
176
+ <hr style={{ margin: "1rem 0", border: "none", "border-top": "1px solid #ccc" }} />
177
+
178
+ <div style={{ "margin-bottom": "1rem" }}>
179
+ <div>
180
+ <strong>Average Duration: </strong>
181
+ {task()?.averageDuration ? formatDuration(task()!.averageDuration!) : "N/A"}
182
+ </div>
183
+ <div>
184
+ <strong>Average Score: </strong>
185
+ {task()?.averageScore?.toFixed(3) ?? "N/A"}
186
+ </div>
187
+ <div>
188
+ <strong>Average Cost: </strong>
189
+ {task()?.averageUsage?.cost ? `$${task()!.averageUsage!.cost.toFixed(4)}` : "N/A"}
190
+ </div>
191
+ </div>
192
+
193
+ <Show when={task()?.summary}>
194
+ <div style={{ "margin-bottom": "1rem" }}>
195
+ <strong>Summary:</strong>
196
+ <p style={{ "margin-top": "0.5rem", "white-space": "pre-wrap" }}>{task()!.summary}</p>
197
+ </div>
198
+ </Show>
199
+
200
+ <Show when={task()?.runs && task()!.runs!.length > 0}>
201
+ <div style={{ "margin-bottom": "1rem" }}>
202
+ <strong>Runs:</strong>
203
+ <table style={{ "margin-top": "0.5rem", "border-collapse": "collapse", width: "100%" }}>
204
+ <thead>
205
+ <tr>
206
+ <th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>Run</th>
207
+ <th
208
+ style={{
209
+ border: "1px solid #ccc",
210
+ padding: "0.5rem",
211
+ "text-align": "left",
212
+ "white-space": "nowrap",
213
+ }}
214
+ >
215
+ Score (Base - Penalty)
216
+ </th>
217
+ <th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>Cost</th>
218
+ <th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>Duration</th>
219
+ <For each={task()!.runs![0]?.scoreDetails}>
220
+ {(detail) => (
221
+ <th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>
222
+ {detail.criterion} ({detail.weight})
223
+ </th>
224
+ )}
225
+ </For>
226
+ </tr>
227
+ </thead>
228
+ <tbody>
229
+ <For each={task()!.runs}>
230
+ {(run, index) => (
231
+ <tr>
232
+ <td style={{ border: "1px solid #ccc", padding: "0.5rem" }}>{index() + 1}</td>
233
+ <td style={{ border: "1px solid #ccc", padding: "0.5rem", "white-space": "nowrap" }}>
234
+ {run.score.final.toFixed(3)} ({run.score.base.toFixed(3)} - {run.score.penalty.toFixed(3)})
235
+ </td>
236
+ <td style={{ border: "1px solid #ccc", padding: "0.5rem" }}>
237
+ {run.usage?.cost ? `$${run.usage.cost.toFixed(4)}` : "N/A"}
238
+ </td>
239
+ <td style={{ border: "1px solid #ccc", padding: "0.5rem" }}>
240
+ {run.duration ? formatDuration(run.duration) : "N/A"}
241
+ </td>
242
+ <For each={run.scoreDetails}>
243
+ {(detail) => (
244
+ <td style={{ border: "1px solid #ccc", padding: "0.5rem" }}>
245
+ <For each={detail.judges}>
246
+ {(judge) => (
247
+ <span
248
+ style={{
249
+ color: judge.score === 1 ? "green" : judge.score === 0 ? "red" : "inherit",
250
+ "margin-right": "0.25rem",
251
+ }}
252
+ >
253
+ {judge.score === 1 ? "✓" : judge.score === 0 ? "✗" : judge.score}
254
+ </span>
255
+ )}
256
+ </For>
257
+ </td>
258
+ )}
259
+ </For>
260
+ </tr>
261
+ )}
262
+ </For>
263
+ </tbody>
264
+ </table>
265
+ <For each={task()!.runs}>
266
+ {(run, index) => (
267
+ <div style={{ "margin-top": "1rem" }}>
268
+ <h3 style={{ margin: "0 0 0.5rem 0" }}>Run {index() + 1}</h3>
269
+ <div>
270
+ <strong>Score: </strong>
271
+ {run.score.final.toFixed(3)} (Base: {run.score.base.toFixed(3)} - Penalty:{" "}
272
+ {run.score.penalty.toFixed(3)})
273
+ </div>
274
+ <For each={run.scoreDetails}>
275
+ {(detail) => (
276
+ <div style={{ "margin-top": "1rem", "padding-left": "1rem", "border-left": "2px solid #ccc" }}>
277
+ <div>
278
+ {detail.criterion} (weight: {detail.weight}){" "}
279
+ <For each={detail.judges}>
280
+ {(judge) => (
281
+ <span
282
+ style={{
283
+ color: judge.score === 1 ? "green" : judge.score === 0 ? "red" : "inherit",
284
+ "margin-right": "0.25rem",
285
+ }}
286
+ >
287
+ {judge.score === 1 ? "✓" : judge.score === 0 ? "✗" : judge.score}
288
+ </span>
289
+ )}
290
+ </For>
291
+ </div>
292
+ <Show when={detail.judges && detail.judges.length > 0}>
293
+ <For each={detail.judges}>
294
+ {(judge) => {
295
+ const [expanded, setExpanded] = createSignal(false)
296
+ return (
297
+ <div style={{ "margin-top": "0.5rem", "padding-left": "1rem" }}>
298
+ <div
299
+ style={{ "font-size": "0.875rem", cursor: "pointer" }}
300
+ onClick={() => setExpanded(!expanded())}
301
+ >
302
+ <span style={{ "margin-right": "0.5rem" }}>{expanded() ? "▼" : "▶"}</span>
303
+ <span
304
+ style={{
305
+ color: judge.score === 1 ? "green" : judge.score === 0 ? "red" : "inherit",
306
+ }}
307
+ >
308
+ {judge.score === 1 ? "✓" : judge.score === 0 ? "✗" : judge.score}
309
+ </span>{" "}
310
+ {judge.judge}
311
+ </div>
312
+ <Show when={expanded()}>
313
+ <p
314
+ style={{
315
+ margin: "0.25rem 0 0 0",
316
+ "white-space": "pre-wrap",
317
+ "font-size": "0.875rem",
318
+ }}
319
+ >
320
+ {judge.rationale}
321
+ </p>
322
+ </Show>
323
+ </div>
324
+ )
325
+ }}
326
+ </For>
327
+ </Show>
328
+ </div>
329
+ )}
330
+ </For>
331
+ </div>
332
+ )}
333
+ </For>
334
+ </div>
335
+ </Show>
336
+
337
+ {(() => {
338
+ const [jsonExpanded, setJsonExpanded] = createSignal(false)
339
+ return (
340
+ <div style={{ "margin-top": "1rem" }}>
341
+ <button
342
+ style={{
343
+ cursor: "pointer",
344
+ padding: "0.75rem 1.5rem",
345
+ "font-size": "1rem",
346
+ background: "#f0f0f0",
347
+ border: "1px solid #ccc",
348
+ "border-radius": "4px",
349
+ }}
350
+ onClick={() => setJsonExpanded(!jsonExpanded())}
351
+ >
352
+ <span style={{ "margin-right": "0.5rem" }}>{jsonExpanded() ? "▼" : "▶"}</span>
353
+ Raw JSON
354
+ </button>
355
+ <Show when={jsonExpanded()}>
356
+ <pre>{JSON.stringify(task(), null, 2)}</pre>
357
+ </Show>
358
+ </div>
359
+ )
360
+ })()}
361
+ </Show>
362
+ </div>
363
+ </main>
364
+ )
365
+ }
@@ -0,0 +1,86 @@
1
+ import { Title } from "@solidjs/meta"
2
+ import { A, createAsync, query } from "@solidjs/router"
3
+ import { createMemo, For, Show } from "solid-js"
4
+ import { Database, desc } from "@jonsoc/console-core/drizzle/index.js"
5
+ import { BenchmarkTable } from "@jonsoc/console-core/schema/benchmark.sql.js"
6
+
7
+ interface BenchmarkResult {
8
+ averageScore: number
9
+ tasks: { averageScore: number; task: { id: string } }[]
10
+ }
11
+
12
+ async function getBenchmarks() {
13
+ "use server"
14
+ const rows = await Database.use((tx) =>
15
+ tx.select().from(BenchmarkTable).orderBy(desc(BenchmarkTable.timeCreated)).limit(100),
16
+ )
17
+ return rows.map((row) => {
18
+ const parsed = JSON.parse(row.result) as BenchmarkResult
19
+ const taskScores: Record<string, number> = {}
20
+ for (const t of parsed.tasks) {
21
+ taskScores[t.task.id] = t.averageScore
22
+ }
23
+ return {
24
+ id: row.id,
25
+ agent: row.agent,
26
+ model: row.model,
27
+ averageScore: parsed.averageScore,
28
+ taskScores,
29
+ }
30
+ })
31
+ }
32
+
33
+ const queryBenchmarks = query(getBenchmarks, "benchmarks.list")
34
+
35
+ export default function Bench() {
36
+ const benchmarks = createAsync(() => queryBenchmarks())
37
+
38
+ const taskIds = createMemo(() => {
39
+ const ids = new Set<string>()
40
+ for (const row of benchmarks() ?? []) {
41
+ for (const id of Object.keys(row.taskScores)) {
42
+ ids.add(id)
43
+ }
44
+ }
45
+ return [...ids].sort()
46
+ })
47
+
48
+ return (
49
+ <main data-page="bench" style={{ padding: "2rem" }}>
50
+ <Title>Benchmark</Title>
51
+ <h1 style={{ "margin-bottom": "1.5rem" }}>Benchmarks</h1>
52
+ <table style={{ "border-collapse": "collapse", width: "100%" }}>
53
+ <thead>
54
+ <tr>
55
+ <th style={{ "text-align": "left", padding: "0.75rem" }}>Agent</th>
56
+ <th style={{ "text-align": "left", padding: "0.75rem" }}>Model</th>
57
+ <th style={{ "text-align": "left", padding: "0.75rem" }}>Score</th>
58
+ <For each={taskIds()}>{(id) => <th style={{ "text-align": "left", padding: "0.75rem" }}>{id}</th>}</For>
59
+ </tr>
60
+ </thead>
61
+ <tbody>
62
+ <For each={benchmarks()}>
63
+ {(row) => (
64
+ <tr>
65
+ <td style={{ padding: "0.75rem" }}>{row.agent}</td>
66
+ <td style={{ padding: "0.75rem" }}>{row.model}</td>
67
+ <td style={{ padding: "0.75rem" }}>{row.averageScore.toFixed(3)}</td>
68
+ <For each={taskIds()}>
69
+ {(id) => (
70
+ <td style={{ padding: "0.75rem" }}>
71
+ <Show when={row.taskScores[id] !== undefined} fallback="">
72
+ <A href={`/bench/${row.id}:${id}`} style={{ color: "#0066cc" }}>
73
+ {row.taskScores[id]?.toFixed(3)}
74
+ </A>
75
+ </Show>
76
+ </td>
77
+ )}
78
+ </For>
79
+ </tr>
80
+ )}
81
+ </For>
82
+ </tbody>
83
+ </table>
84
+ </main>
85
+ )
86
+ }
@@ -0,0 +1,29 @@
1
+ import type { APIEvent } from "@solidjs/start/server"
2
+ import { Database } from "@jonsoc/console-core/drizzle/index.js"
3
+ import { BenchmarkTable } from "@jonsoc/console-core/schema/benchmark.sql.js"
4
+ import { Identifier } from "@jonsoc/console-core/identifier.js"
5
+
6
+ interface SubmissionBody {
7
+ model: string
8
+ agent: string
9
+ result: string
10
+ }
11
+
12
+ export async function POST(event: APIEvent) {
13
+ const body = (await event.request.json()) as SubmissionBody
14
+
15
+ if (!body.model || !body.agent || !body.result) {
16
+ return Response.json({ error: "All fields are required" }, { status: 400 })
17
+ }
18
+
19
+ await Database.use((tx) =>
20
+ tx.insert(BenchmarkTable).values({
21
+ id: Identifier.create("benchmark"),
22
+ model: body.model,
23
+ agent: body.agent,
24
+ result: body.result,
25
+ }),
26
+ )
27
+
28
+ return Response.json({ success: true }, { status: 200 })
29
+ }
@@ -0,0 +1,62 @@
1
+ import { Match, Switch } from "solid-js"
2
+
3
+ export const plans = [
4
+ { id: "20", multiplier: null },
5
+ { id: "100", multiplier: "5x more usage than Black 20" },
6
+ { id: "200", multiplier: "20x more usage than Black 20" },
7
+ ] as const
8
+
9
+ export type PlanID = (typeof plans)[number]["id"]
10
+ export type Plan = (typeof plans)[number]
11
+
12
+ export function PlanIcon(props: { plan: string }) {
13
+ return (
14
+ <Switch>
15
+ <Match when={props.plan === "20"}>
16
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
17
+ <title>Black 20 plan</title>
18
+ <rect x="0.5" y="0.5" width="23" height="23" stroke="currentColor" />
19
+ </svg>
20
+ </Match>
21
+ <Match when={props.plan === "100"}>
22
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
23
+ <title>Black 100 plan</title>
24
+ <rect x="0.5" y="0.5" width="9" height="9" stroke="currentColor" />
25
+ <rect x="0.5" y="14.5" width="9" height="9" stroke="currentColor" />
26
+ <rect x="14.5" y="0.5" width="9" height="9" stroke="currentColor" />
27
+ <rect x="14.5" y="14.5" width="9" height="9" stroke="currentColor" />
28
+ </svg>
29
+ </Match>
30
+ <Match when={props.plan === "200"}>
31
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
32
+ <title>Black 200 plan</title>
33
+ <rect x="0.5" y="0.5" width="3" height="3" stroke="currentColor" />
34
+ <rect x="0.5" y="5.5" width="3" height="3" stroke="currentColor" />
35
+ <rect x="0.5" y="10.5" width="3" height="3" stroke="currentColor" />
36
+ <rect x="0.5" y="15.5" width="3" height="3" stroke="currentColor" />
37
+ <rect x="0.5" y="20.5" width="3" height="3" stroke="currentColor" />
38
+ <rect x="5.5" y="0.5" width="3" height="3" stroke="currentColor" />
39
+ <rect x="5.5" y="5.5" width="3" height="3" stroke="currentColor" />
40
+ <rect x="5.5" y="10.5" width="3" height="3" stroke="currentColor" />
41
+ <rect x="5.5" y="15.5" width="3" height="3" stroke="currentColor" />
42
+ <rect x="5.5" y="20.5" width="3" height="3" stroke="currentColor" />
43
+ <rect x="10.5" y="0.5" width="3" height="3" stroke="currentColor" />
44
+ <rect x="10.5" y="5.5" width="3" height="3" stroke="currentColor" />
45
+ <rect x="10.5" y="10.5" width="3" height="3" stroke="currentColor" />
46
+ <rect x="10.5" y="15.5" width="3" height="3" stroke="currentColor" />
47
+ <rect x="10.5" y="20.5" width="3" height="3" stroke="currentColor" />
48
+ <rect x="15.5" y="0.5" width="3" height="3" stroke="currentColor" />
49
+ <rect x="15.5" y="5.5" width="3" height="3" stroke="currentColor" />
50
+ <rect x="15.5" y="10.5" width="3" height="3" stroke="currentColor" />
51
+ <rect x="15.5" y="15.5" width="3" height="3" stroke="currentColor" />
52
+ <rect x="15.5" y="20.5" width="3" height="3" stroke="currentColor" />
53
+ <rect x="20.5" y="0.5" width="3" height="3" stroke="currentColor" />
54
+ <rect x="20.5" y="5.5" width="3" height="3" stroke="currentColor" />
55
+ <rect x="20.5" y="10.5" width="3" height="3" stroke="currentColor" />
56
+ <rect x="20.5" y="15.5" width="3" height="3" stroke="currentColor" />
57
+ <rect x="20.5" y="20.5" width="3" height="3" stroke="currentColor" />
58
+ </svg>
59
+ </Match>
60
+ </Switch>
61
+ )
62
+ }
@@ -0,0 +1,108 @@
1
+ import { A, useSearchParams } from "@solidjs/router"
2
+ import { Title } from "@solidjs/meta"
3
+ import { createMemo, createSignal, For, Match, onMount, Show, Switch } from "solid-js"
4
+ import { PlanIcon, plans } from "./common"
5
+
6
+ export default function Black() {
7
+ const [params] = useSearchParams()
8
+ const [selected, setSelected] = createSignal<string | null>((params.plan as string) || null)
9
+ const [mounted, setMounted] = createSignal(false)
10
+ const selectedPlan = createMemo(() => plans.find((p) => p.id === selected()))
11
+
12
+ onMount(() => {
13
+ requestAnimationFrame(() => setMounted(true))
14
+ })
15
+
16
+ const transition = (action: () => void) => {
17
+ if (mounted() && "startViewTransition" in document) {
18
+ ;(document as any).startViewTransition(action)
19
+ return
20
+ }
21
+
22
+ action()
23
+ }
24
+
25
+ const select = (planId: string) => {
26
+ if (selected() === planId) {
27
+ return
28
+ }
29
+
30
+ transition(() => setSelected(planId))
31
+ }
32
+
33
+ const cancel = () => {
34
+ transition(() => setSelected(null))
35
+ }
36
+
37
+ return (
38
+ <>
39
+ <Title>jonsoc</Title>
40
+ <section data-slot="cta">
41
+ <Switch>
42
+ <Match when={!selected()}>
43
+ <div data-slot="pricing">
44
+ <For each={plans}>
45
+ {(plan) => (
46
+ <button
47
+ type="button"
48
+ onClick={() => select(plan.id)}
49
+ data-slot="pricing-card"
50
+ style={{ "view-transition-name": `card-${plan.id}` }}
51
+ >
52
+ <div data-slot="icon">
53
+ <PlanIcon plan={plan.id} />
54
+ </div>
55
+ <p data-slot="price">
56
+ <span data-slot="amount">${plan.id}</span> <span data-slot="period">per month</span>
57
+ <Show when={plan.multiplier}>
58
+ <span data-slot="multiplier">{plan.multiplier}</span>
59
+ </Show>
60
+ </p>
61
+ </button>
62
+ )}
63
+ </For>
64
+ </div>
65
+ </Match>
66
+ <Match when={selectedPlan()}>
67
+ {(plan) => (
68
+ <div data-slot="selected-plan">
69
+ <div data-slot="selected-card" style={{ "view-transition-name": `card-${plan().id}` }}>
70
+ <div data-slot="icon">
71
+ <PlanIcon plan={plan().id} />
72
+ </div>
73
+ <p data-slot="price">
74
+ <span data-slot="amount">${plan().id}</span>{" "}
75
+ <span data-slot="period">per person billed monthly</span>
76
+ <Show when={plan().multiplier}>
77
+ <span data-slot="multiplier">{plan().multiplier}</span>
78
+ </Show>
79
+ </p>
80
+ <ul data-slot="terms" style={{ "view-transition-name": `terms-${plan().id}` }}>
81
+ <li>Your subscription will not start immediately</li>
82
+ <li>You will be added to the waitlist and activated soon</li>
83
+ <li>Your card will be only charged when your subscription is activated</li>
84
+ <li>Usage limits apply, heavily automated use may reach limits sooner</li>
85
+ <li>Subscriptions for individuals, contact Enterprise for teams</li>
86
+ <li>Limits may be adjusted and plans may be discontinued in the future</li>
87
+ <li>Cancel your subscription at anytime</li>
88
+ </ul>
89
+ <div data-slot="actions" style={{ "view-transition-name": `actions-${plan().id}` }}>
90
+ <button type="button" onClick={() => cancel()} data-slot="cancel">
91
+ Cancel
92
+ </button>
93
+ <a href={`/black/subscribe/${plan().id}`} data-slot="continue">
94
+ Continue
95
+ </a>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ )}
100
+ </Match>
101
+ </Switch>
102
+ <p data-slot="fine-print" style={{ "view-transition-name": "fine-print" }}>
103
+ Prices shown don't include applicable tax · <A href="/legal/terms-of-service">Terms of Service</A>
104
+ </p>
105
+ </section>
106
+ </>
107
+ )
108
+ }