@wenathlan/cobaia 0.0.0-stage → 2.0.155
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/App.tsx +52 -0
- package/Dockerfile +45 -0
- package/Sol/Sol.tsx +91 -0
- package/Sol/apps/apps.tsx +66 -0
- package/Sol/blog/blog.tsx +86 -0
- package/Sol/home/home.tsx +140 -0
- package/Sol/intro/intro.tsx +187 -0
- package/Sol/legal/legal.tsx +76 -0
- package/Sol/notfound/notfound.tsx +32 -0
- package/Sol/pricing/pricing.tsx +98 -0
- package/Sol/shell/Shell.tsx +161 -0
- package/Sol/sol.css +1944 -0
- package/Sol/support/support.tsx +102 -0
- package/Sol/wingets/IntroFooter.tsx +69 -0
- package/Sol/wingets/IntroPlans.tsx +93 -0
- package/Sol/wingets/IntroSplash.tsx +105 -0
- package/Sol/wingets/ShellAdsslot.tsx +47 -0
- package/Sol/wingets/ShellBrand.tsx +86 -0
- package/Sol/wingets/index.ts +18 -0
- package/_headers +4 -0
- package/_redirects +1 -0
- package/adsconfig.ts +59 -0
- package/biome.jsonc +134 -0
- package/bun.lock +2331 -0
- package/capacitor.config.ts +51 -0
- package/checkoutlinks.ts +80 -0
- package/content.ts +425 -0
- package/db.ts +219 -0
- package/devthink.toml +5 -0
- package/docs/.gitkeep +0 -0
- package/familyurl.ts +22 -0
- package/favicon.svg +1 -0
- package/icon.svg +1 -0
- package/index.html +26 -0
- package/labels.ts +291 -0
- package/manifest.webmanifest +56 -0
- package/mime.types +12 -0
- package/netlify.toml +2 -0
- package/package.json +200 -4
- package/pnpm-workspace.yaml +82 -0
- package/posts.ts +54 -0
- package/robots.txt +90 -0
- package/roster.ts +29 -0
- package/sandboxmodality.ts +138 -0
- package/schema.prisma +31 -0
- package/sitemap.xml +42 -0
- package/tests/adsconfig.test.ts +51 -0
- package/tests/checkoutlinks.test.ts +73 -0
- package/tests/familyurl.test.ts +40 -0
- package/tests/posts.test.ts +76 -0
- package/tests/sandboxmodality.test.ts +77 -0
- package/tsconfig.json +22 -0
- package/tsconfig.node.json +22 -0
- package/vercel.json +3 -0
- package/vite.config.ts +44 -0
- package/vitest.config.ts +42 -0
- package/wrangler.toml +7 -0
- package/README.md +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,202 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenathlan/cobaia",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"packageManager": "pnpm@12.3.4",
|
|
4
|
+
"version": "2.0.155",
|
|
5
|
+
"description": "cobaia is the monetization surface of the wenathlan family: the content site that carries the plans, the support rails and the honest advertisement slots while the family apps stay clean; the name is provisional until the owner picks the official one",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/wenathlan/devthink"
|
|
10
|
+
},
|
|
11
|
+
"author": "Wenathlan",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "vite --host",
|
|
15
|
+
"build": "vite build",
|
|
16
|
+
"start": "vite preview --host",
|
|
17
|
+
"preview": "vite preview --host",
|
|
18
|
+
"check": "tsc --noEmit",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"test": "vitest run"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@fluentui/react-icons": "^2.0.345",
|
|
24
|
+
"@fontsource-variable/bricolage-grotesque": "^5.3.0",
|
|
25
|
+
"@fontsource-variable/geist-mono": "^5.3.0",
|
|
26
|
+
"@fontsource-variable/inter": "^5.3.0",
|
|
27
|
+
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
28
|
+
"@fontsource-variable/outfit": "^5.3.0",
|
|
29
|
+
"@hono/node-server": "^2.1.5",
|
|
30
|
+
"@libsql/client": "^0.18.0",
|
|
31
|
+
"@prisma/adapter-libsql": "^7.10.1",
|
|
32
|
+
"@prisma/client": "^7.10.1",
|
|
33
|
+
"@wenathlan/cadria": "^2.0.154",
|
|
34
|
+
"@wenathlan/debonair": "^2.0.154",
|
|
35
|
+
"@wenathlan/devthink": "^2.0.154",
|
|
36
|
+
"@wenathlan/getry": "^2.0.154",
|
|
37
|
+
"@wenathlan/saddle": "^2.0.154",
|
|
38
|
+
"@wenathlan/vault": "^2.0.154",
|
|
39
|
+
"better-sqlite3": "^13.0.3",
|
|
40
|
+
"drizzle-orm": "^0.45.4",
|
|
41
|
+
"gsap": "^3.15.0",
|
|
42
|
+
"hono": "^4.13.16",
|
|
43
|
+
"ink": "^7.1.1",
|
|
44
|
+
"lucide-react": "^1.67.0",
|
|
45
|
+
"motion": "^12.43.0",
|
|
46
|
+
"react": "^19.3.1",
|
|
47
|
+
"react-devtools-core": "^8.0.0",
|
|
48
|
+
"react-dom": "^19.3.1",
|
|
49
|
+
"three": "^0.187.0",
|
|
50
|
+
"wouter": "^3.13.0",
|
|
51
|
+
"z-ai-web-dev-sdk": "^0.0.18"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@biomejs/biome": "^2.6.4",
|
|
55
|
+
"@capacitor/android": "^8.6.0",
|
|
56
|
+
"@capacitor/cli": "^8.6.0",
|
|
57
|
+
"@capacitor/core": "^8.6.0",
|
|
58
|
+
"@capacitor/ios": "^8.6.0",
|
|
59
|
+
"@fluentui/react-icons": "^2.0.345",
|
|
60
|
+
"@hookform/resolvers": "^5.9.2",
|
|
61
|
+
"@libsql/client": "^0.18.0",
|
|
62
|
+
"@opencode-ai/sdk": "^1.19.5",
|
|
63
|
+
"@prisma/client": "^7.10.1",
|
|
64
|
+
"@radix-ui/react-accordion": "^1.3.1",
|
|
65
|
+
"@radix-ui/react-alert-dialog": "^1.2.1",
|
|
66
|
+
"@radix-ui/react-aspect-ratio": "^1.2.0",
|
|
67
|
+
"@radix-ui/react-avatar": "^1.3.1",
|
|
68
|
+
"@radix-ui/react-checkbox": "^1.4.1",
|
|
69
|
+
"@radix-ui/react-collapsible": "^1.2.1",
|
|
70
|
+
"@radix-ui/react-context-menu": "^2.4.1",
|
|
71
|
+
"@radix-ui/react-dialog": "^1.3.1",
|
|
72
|
+
"@radix-ui/react-dropdown-menu": "^2.2.1",
|
|
73
|
+
"@radix-ui/react-hover-card": "^1.2.1",
|
|
74
|
+
"@radix-ui/react-label": "^2.1.17",
|
|
75
|
+
"@radix-ui/react-menubar": "^1.2.1",
|
|
76
|
+
"@radix-ui/react-navigation-menu": "^1.4.1",
|
|
77
|
+
"@radix-ui/react-popover": "^1.3.1",
|
|
78
|
+
"@radix-ui/react-progress": "^1.2.1",
|
|
79
|
+
"@radix-ui/react-radio-group": "^1.5.1",
|
|
80
|
+
"@radix-ui/react-scroll-area": "^1.4.0",
|
|
81
|
+
"@radix-ui/react-select": "^2.4.1",
|
|
82
|
+
"@radix-ui/react-separator": "^1.2.0",
|
|
83
|
+
"@radix-ui/react-slider": "^1.6.1",
|
|
84
|
+
"@radix-ui/react-slot": "^1.5.0",
|
|
85
|
+
"@radix-ui/react-switch": "^1.4.1",
|
|
86
|
+
"@radix-ui/react-tabs": "^1.2.1",
|
|
87
|
+
"@radix-ui/react-toggle": "^1.2.1",
|
|
88
|
+
"@radix-ui/react-toggle-group": "^1.2.1",
|
|
89
|
+
"@radix-ui/react-tooltip": "^1.4.1",
|
|
90
|
+
"@tailwindcss/postcss": "^4.3.4",
|
|
91
|
+
"@tailwindcss/typography": "^0.5.20",
|
|
92
|
+
"@tailwindcss/vite": "^4.3.4",
|
|
93
|
+
"@tsconfig/node26": "^26.0.1",
|
|
94
|
+
"@types/better-sqlite3": "^9.6.0",
|
|
95
|
+
"@types/chrome": "^0.3.7",
|
|
96
|
+
"@types/express": "^5.0.6",
|
|
97
|
+
"@types/google.maps": "^3.66.9",
|
|
98
|
+
"@types/node": "^26.6.6",
|
|
99
|
+
"@types/react": "^19.3.0",
|
|
100
|
+
"@types/react-dom": "^19.3.0",
|
|
101
|
+
"@types/three": "^0.187.0",
|
|
102
|
+
"@vitejs/plugin-react": "^6.2.1",
|
|
103
|
+
"add": "^2.0.9",
|
|
104
|
+
"autoprefixer": "^10.6.2",
|
|
105
|
+
"axios": "^1.21.0",
|
|
106
|
+
"better-sqlite3": "^13.0.3",
|
|
107
|
+
"bun-types": "^1.4.4",
|
|
108
|
+
"cheerio": "^1.2.0",
|
|
109
|
+
"class-variance-authority": "^0.7.1",
|
|
110
|
+
"clsx": "^2.1.1",
|
|
111
|
+
"cmdk": "^1.1.1",
|
|
112
|
+
"drizzle-kit": "^0.31.11",
|
|
113
|
+
"drizzle-orm": "^0.45.4",
|
|
114
|
+
"embla-carousel-react": "^8.6.0",
|
|
115
|
+
"emittery": "^2.2.0",
|
|
116
|
+
"esbuild": "^0.28.2",
|
|
117
|
+
"express": "^5.3.0",
|
|
118
|
+
"fast-check": "^4.10.2",
|
|
119
|
+
"framer-motion": "^13.5.1",
|
|
120
|
+
"hono": "^4.13.16",
|
|
121
|
+
"input-otp": "^1.5.0",
|
|
122
|
+
"jsdom": "26.1.0",
|
|
123
|
+
"lucide-react": "^1.67.0",
|
|
124
|
+
"mysql2": "^3.24.6",
|
|
125
|
+
"nanoid": "^6.0.2",
|
|
126
|
+
"next-themes": "^0.4.6",
|
|
127
|
+
"p-limit": "^7.3.3",
|
|
128
|
+
"p-retry": "^8.0.1",
|
|
129
|
+
"playwright": "^1.64.1",
|
|
130
|
+
"pnpm": "^12.16.1",
|
|
131
|
+
"postcss": "^8.5.32",
|
|
132
|
+
"prettier": "^3.9.10",
|
|
133
|
+
"prisma": "^7.10.1",
|
|
134
|
+
"react": "^19.3.1",
|
|
135
|
+
"react-day-picker": "^10.0.2",
|
|
136
|
+
"react-dom": "^19.3.1",
|
|
137
|
+
"react-hook-form": "^7.91.0",
|
|
138
|
+
"react-resizable-panels": "^4.18.0",
|
|
139
|
+
"recharts": "^3.10.1",
|
|
140
|
+
"sonner": "^2.0.8",
|
|
141
|
+
"streamdown": "^2.7.1",
|
|
142
|
+
"tailwind-merge": "^3.7.0",
|
|
143
|
+
"tailwindcss": "^4.3.4",
|
|
144
|
+
"tailwindcss-animate": "^1.0.7",
|
|
145
|
+
"tsup": "^8.5.1",
|
|
146
|
+
"tsx": "^4.23.15",
|
|
147
|
+
"tw-animate-css": "^1.4.0",
|
|
148
|
+
"typescript": "^7.0.2",
|
|
149
|
+
"vaul": "^1.1.2",
|
|
150
|
+
"vite": "^8.3.5",
|
|
151
|
+
"vite-plugin-manus-runtime": "^0.0.59",
|
|
152
|
+
"vitest": "^5.1.0",
|
|
153
|
+
"webpack": "^5.112.2",
|
|
154
|
+
"webpack-cli": "^6.0.1",
|
|
155
|
+
"wouter": "^3.13.0",
|
|
156
|
+
"yaml": "^2.9.1",
|
|
157
|
+
"zod": "^4.6.7"
|
|
158
|
+
},
|
|
159
|
+
"engines": {
|
|
160
|
+
"bare": "^1.18.0",
|
|
161
|
+
"bun": "^1.4.2",
|
|
162
|
+
"cargo": ">=1.85.0",
|
|
163
|
+
"chrome": "^16.0.912",
|
|
164
|
+
"cnpm": "^9.4.0",
|
|
165
|
+
"corepack": "^0.35.0",
|
|
166
|
+
"deno": "^2.9.5",
|
|
167
|
+
"dotnet": ">=8.0.0",
|
|
168
|
+
"ecmascript": "^5.0.0",
|
|
169
|
+
"firefox": ">=0.8.0",
|
|
170
|
+
"go": ">=1.24.0",
|
|
171
|
+
"iojs": "^1.0.0",
|
|
172
|
+
"java": ">=17",
|
|
173
|
+
"node": ">=26.13.0 <27",
|
|
174
|
+
"npm": "^12.1.0",
|
|
175
|
+
"pnpm": "^12.6.0",
|
|
176
|
+
"python": ">=3.11.0",
|
|
177
|
+
"rust": ">=1.85.0",
|
|
178
|
+
"teleport": ">=0.2.0",
|
|
179
|
+
"typescript": "^5.8.0",
|
|
180
|
+
"v": "^0.5.2",
|
|
181
|
+
"vscode": "^1.118.0",
|
|
182
|
+
"yarn": "^4.17.1"
|
|
183
|
+
},
|
|
184
|
+
"overrides": {
|
|
185
|
+
"body-parser": "^2.4.0",
|
|
186
|
+
"fast-uri": "^3.1.9",
|
|
187
|
+
"follow-redirects": "^1.16.2",
|
|
188
|
+
"minimatch": "^11.0.3",
|
|
189
|
+
"glob": "^14.0.3",
|
|
190
|
+
"zod": "^4.6.7",
|
|
191
|
+
"esbuild": "^0.28.2",
|
|
192
|
+
"undici": "^7.30.1",
|
|
193
|
+
"uuid": "^11.1.1",
|
|
194
|
+
"postcss-selector-parser": "^7.1.6",
|
|
195
|
+
"deepmerge-ts": "^8.0.0",
|
|
196
|
+
"katex": "^0.18.2",
|
|
197
|
+
"mysql2": "^3.24.6",
|
|
198
|
+
"proxy-addr": "2.0.8",
|
|
199
|
+
"shell-quote": "^1.11.0",
|
|
200
|
+
"source-map-js": "1.2.2"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# the vulnerability overrides of the family security gate (the devthink
|
|
2
|
+
# override contract): pnpm 12 reads overrides here, the lockfile regenerates
|
|
3
|
+
# against these pins and the OSV scan of the committed lockfile answers clean.
|
|
4
|
+
overrides:
|
|
5
|
+
undici: ^7.30.1
|
|
6
|
+
zod: ^4.6.7
|
|
7
|
+
follow-redirects: ^1.16.2
|
|
8
|
+
glob: ^14.0.2
|
|
9
|
+
minimatch: ^11.0.1
|
|
10
|
+
fast-uri: ^3.1.9
|
|
11
|
+
body-parser: ^2.4.0
|
|
12
|
+
esbuild: ^0.28.2
|
|
13
|
+
deepmerge-ts: ^8.0.0
|
|
14
|
+
mysql2: ^3.23.1
|
|
15
|
+
|
|
16
|
+
# the freshly published security fixes install before they age: pnpm 12
|
|
17
|
+
# verifies the supply-chain age by default and these four corrections ride
|
|
18
|
+
# the exclusion so the family heals now, not after the waiting window.
|
|
19
|
+
minimumReleaseAgeExclude:
|
|
20
|
+
- body-parser@2.4.0
|
|
21
|
+
- fast-uri@3.1.9
|
|
22
|
+
- follow-redirects@1.16.1
|
|
23
|
+
- zod@4.6.6
|
|
24
|
+
- '@biomejs/biome@2.6.3 || 2.6.4'
|
|
25
|
+
- '@biomejs/cli-android-arm64@2.6.3 || 2.6.4'
|
|
26
|
+
- '@biomejs/cli-android-x64@2.6.3 || 2.6.4'
|
|
27
|
+
- '@biomejs/cli-darwin-arm64@2.6.3 || 2.6.4'
|
|
28
|
+
- '@biomejs/cli-darwin-x64@2.6.3 || 2.6.4'
|
|
29
|
+
- '@biomejs/cli-linux-arm64-musl@2.6.3 || 2.6.4'
|
|
30
|
+
- '@biomejs/cli-linux-arm64@2.6.3 || 2.6.4'
|
|
31
|
+
- '@biomejs/cli-linux-x64-musl@2.6.3 || 2.6.4'
|
|
32
|
+
- '@biomejs/cli-linux-x64@2.6.3 || 2.6.4'
|
|
33
|
+
- '@biomejs/cli-win32-arm64@2.6.3 || 2.6.4'
|
|
34
|
+
- '@biomejs/cli-win32-x64@2.6.3 || 2.6.4'
|
|
35
|
+
- '@types/node@26.6.6'
|
|
36
|
+
- '@capacitor/android@8.6.0'
|
|
37
|
+
- '@capacitor/cli@8.6.0'
|
|
38
|
+
- '@capacitor/core@8.6.0'
|
|
39
|
+
- '@capacitor/ios@8.6.0'
|
|
40
|
+
- '@opencode-ai/sdk@1.19.5'
|
|
41
|
+
- react-resizable-panels@4.15.1 || 4.15.2 || 4.15.3 || 4.16.0 || 4.16.1 || 4.17.0 || 4.18.0
|
|
42
|
+
- '@types/google.maps@3.66.9'
|
|
43
|
+
- lucide-react@1.64.0 || 1.65.0 || 1.66.0 || 1.67.0
|
|
44
|
+
- '@fluentui/react-icons@2.0.345'
|
|
45
|
+
- '@wenathlan/vault@2.0.152 || 2.0.153 || 2.0.154 || 2.0.155 || 2.1.8'
|
|
46
|
+
- hono@4.13.16
|
|
47
|
+
- '@wenathlan/cadria@2.0.154 || 2.0.155 || 2.1.8'
|
|
48
|
+
- '@wenathlan/debonair@2.0.154 || 2.0.155 || 2.1.8'
|
|
49
|
+
- '@wenathlan/getry@2.0.154 || 2.0.155'
|
|
50
|
+
- '@tailwindcss/node@4.3.4'
|
|
51
|
+
- '@tailwindcss/oxide-android-arm64@4.3.4'
|
|
52
|
+
- '@tailwindcss/oxide-darwin-arm64@4.3.4'
|
|
53
|
+
- '@tailwindcss/oxide-darwin-x64@4.3.4'
|
|
54
|
+
- '@tailwindcss/oxide-freebsd-x64@4.3.4'
|
|
55
|
+
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.4'
|
|
56
|
+
- '@tailwindcss/oxide-linux-arm64-gnu@4.3.4'
|
|
57
|
+
- '@tailwindcss/oxide-linux-arm64-musl@4.3.4'
|
|
58
|
+
- '@tailwindcss/oxide-linux-x64-gnu@4.3.4'
|
|
59
|
+
- '@tailwindcss/oxide-linux-x64-musl@4.3.4'
|
|
60
|
+
- '@tailwindcss/oxide-wasm32-wasi@4.3.4'
|
|
61
|
+
- '@tailwindcss/oxide-win32-arm64-msvc@4.3.4'
|
|
62
|
+
- '@tailwindcss/oxide-win32-x64-msvc@4.3.4'
|
|
63
|
+
- '@tailwindcss/oxide@4.3.4'
|
|
64
|
+
- '@tailwindcss/postcss@4.3.4'
|
|
65
|
+
- '@tailwindcss/vite@4.3.4'
|
|
66
|
+
- '@vitejs/plugin-react@6.2.1'
|
|
67
|
+
- '@vitest/mocker@5.1.0'
|
|
68
|
+
- '@vitest/spy@5.1.0'
|
|
69
|
+
- tailwindcss@4.3.4
|
|
70
|
+
- vitest@5.1.0
|
|
71
|
+
- '@wenathlan/saddle@2.0.154 || 2.0.155 || 2.1.8'
|
|
72
|
+
- '@wenathlan/devthink@2.0.154 || 2.0.155'
|
|
73
|
+
# the pnpm settings live here since pnpm 12 (the package.json pnpm field is
|
|
74
|
+
# no longer read, and onlyBuiltDependencies died in pnpm 11 — the map below
|
|
75
|
+
# is allowBuilds). the honest-site surface builds exactly the engines its db
|
|
76
|
+
# layer and toolchain ride on; nothing else is granted a build script.
|
|
77
|
+
allowBuilds:
|
|
78
|
+
'@prisma/engines': true
|
|
79
|
+
better-sqlite3: true
|
|
80
|
+
esbuild: true
|
|
81
|
+
prisma: true
|
|
82
|
+
dangerouslyAllowAllBuilds: false
|
package/posts.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* posts.ts — the typed accessor of the seed blog: the thin door the blog
|
|
3
|
+
* page and the tests import, answering the posts of the content lane
|
|
4
|
+
* (content.ts — the manifest; db.ts — the content_rows table; the platform
|
|
5
|
+
* gateway — GET /api/v1/ui/content). The data never lives here anymore:
|
|
6
|
+
* posts() answers the live rows the platform served, the manifest seed
|
|
7
|
+
* otherwise (the uistore chain), the index sort and the honest word count
|
|
8
|
+
* stay exactly where they rode, and the blog index (/blog) renders the
|
|
9
|
+
* sorted index and the full bodies through this door — real, written
|
|
10
|
+
* content about how the family is built, so the site is useful on its own
|
|
11
|
+
* merits (the ad networks approve content, not containers). New posts
|
|
12
|
+
* append to the manifest; the index sorts by date, so the manifest order
|
|
13
|
+
* never matters.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { type blogpost, contenttable, seedPosts } from "./content.ts";
|
|
17
|
+
|
|
18
|
+
export type { blogpost };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The typed accessor of the blog: the live db rows when the platform
|
|
22
|
+
* answered, the manifest seed otherwise.
|
|
23
|
+
*
|
|
24
|
+
* @returns the posts of the blog.
|
|
25
|
+
*/
|
|
26
|
+
export function posts(): readonly blogpost[] {
|
|
27
|
+
return contenttable("posts", seedPosts);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Builds the blog index: the posts sorted by date, newest first. The ISO
|
|
32
|
+
* date sort is lexicographic and stable, so the index order never depends
|
|
33
|
+
* on the row order the store answers.
|
|
34
|
+
*
|
|
35
|
+
* @returns the posts of the blog, newest first.
|
|
36
|
+
*/
|
|
37
|
+
export function postindex(): readonly blogpost[] {
|
|
38
|
+
return [...posts()].sort((a, b) => {
|
|
39
|
+
if (a.date < b.date) return 1;
|
|
40
|
+
if (a.date > b.date) return -1;
|
|
41
|
+
return 0;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Counts the words of one post's body — the honest length measure the tests
|
|
47
|
+
* and the page footer can both read.
|
|
48
|
+
*
|
|
49
|
+
* @param post the post to measure.
|
|
50
|
+
* @returns the total word count across the body paragraphs.
|
|
51
|
+
*/
|
|
52
|
+
export function postwords(post: blogpost): number {
|
|
53
|
+
return post.body.join(" ").split(/\s+/).filter(Boolean).length;
|
|
54
|
+
}
|
package/robots.txt
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# robots.txt — cobaia, the front door of the DevThink family
|
|
2
|
+
# Universal deployable template: no domain is hardcoded here. The canonical
|
|
3
|
+
# domain binds on the deploy platform (netlify, vercel, sandbox) and the
|
|
4
|
+
# relative Sitemap line resolves against whichever origin serves this file.
|
|
5
|
+
# Public surface: home, pricing, support, the app directory, the update blog
|
|
6
|
+
# and the legal pages. This site exists to be indexed — content pages stay
|
|
7
|
+
# open to the search and ads crawlers.
|
|
8
|
+
|
|
9
|
+
User-agent: Googlebot
|
|
10
|
+
Allow: /
|
|
11
|
+
Disallow: /node_modules/
|
|
12
|
+
Disallow: /*.js.map$
|
|
13
|
+
Disallow: /*.json$
|
|
14
|
+
Disallow: /*.ts$
|
|
15
|
+
Disallow: /*.tsx$
|
|
16
|
+
Disallow: /*.css.map$
|
|
17
|
+
Crawl-delay: 5
|
|
18
|
+
|
|
19
|
+
User-agent: Bingbot
|
|
20
|
+
Allow: /
|
|
21
|
+
Disallow: /node_modules/
|
|
22
|
+
Disallow: /*.js.map$
|
|
23
|
+
Disallow: /*.json$
|
|
24
|
+
Disallow: /*.ts$
|
|
25
|
+
Disallow: /*.tsx$
|
|
26
|
+
Disallow: /*.css.map$
|
|
27
|
+
Crawl-delay: 10
|
|
28
|
+
|
|
29
|
+
User-agent: *
|
|
30
|
+
Allow: /
|
|
31
|
+
Disallow: /node_modules/
|
|
32
|
+
Disallow: /*.js.map$
|
|
33
|
+
Disallow: /*.json$
|
|
34
|
+
Disallow: /*.ts$
|
|
35
|
+
Disallow: /*.tsx$
|
|
36
|
+
Disallow: /*.css.map$
|
|
37
|
+
Crawl-delay: 10
|
|
38
|
+
|
|
39
|
+
# SEO and scraper bots blocked outright
|
|
40
|
+
User-agent: AhrefsBot
|
|
41
|
+
Disallow: /
|
|
42
|
+
|
|
43
|
+
User-agent: SemrushBot
|
|
44
|
+
Disallow: /
|
|
45
|
+
|
|
46
|
+
User-agent: DotBot
|
|
47
|
+
Disallow: /
|
|
48
|
+
|
|
49
|
+
User-agent: MJ12bot
|
|
50
|
+
Disallow: /
|
|
51
|
+
|
|
52
|
+
User-agent: PetalBot
|
|
53
|
+
Disallow: /
|
|
54
|
+
|
|
55
|
+
User-agent: BLEXBot
|
|
56
|
+
Disallow: /
|
|
57
|
+
|
|
58
|
+
User-agent: DataForSeoBot
|
|
59
|
+
Disallow: /
|
|
60
|
+
|
|
61
|
+
# AI training crawlers blocked outright
|
|
62
|
+
User-agent: GPTBot
|
|
63
|
+
Disallow: /
|
|
64
|
+
|
|
65
|
+
User-agent: CCBot
|
|
66
|
+
Disallow: /
|
|
67
|
+
|
|
68
|
+
User-agent: anthropic-ai
|
|
69
|
+
Disallow: /
|
|
70
|
+
|
|
71
|
+
User-agent: Claude-Web
|
|
72
|
+
Disallow: /
|
|
73
|
+
|
|
74
|
+
User-agent: ChatGPT-User
|
|
75
|
+
Disallow: /
|
|
76
|
+
|
|
77
|
+
User-agent: Google-Extended
|
|
78
|
+
Disallow: /
|
|
79
|
+
|
|
80
|
+
User-agent: Bytespider
|
|
81
|
+
Disallow: /
|
|
82
|
+
|
|
83
|
+
# ads relevance crawlers keep full access — the site runs honest ad slots
|
|
84
|
+
User-agent: Mediapartners-Google
|
|
85
|
+
Allow: /
|
|
86
|
+
|
|
87
|
+
User-agent: AdsBot-Google
|
|
88
|
+
Allow: /
|
|
89
|
+
|
|
90
|
+
Sitemap: /sitemap.xml
|
package/roster.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* roster.ts — the typed accessor of the family directory data: the thin
|
|
3
|
+
* door the /apps page, the shell footer, the home preview and the intro
|
|
4
|
+
* footer import, answering the roster of the content lane (content.ts —
|
|
5
|
+
* the manifest; db.ts — the content_rows table; the platform gateway — GET
|
|
6
|
+
* /api/v1/ui/content). The data never lives here anymore: roster() answers
|
|
7
|
+
* the live rows the platform served, the manifest seed otherwise (the
|
|
8
|
+
* uistore chain). Every deploy unit beside the cobaia, with the one accent
|
|
9
|
+
* each carries (the spec §12 table, mirrored from the argan contract; the
|
|
10
|
+
* devthink OS rides the family sun) and the honest role each app declares
|
|
11
|
+
* in its own devthink.toml — condensed to one line, nothing embellished:
|
|
12
|
+
* the page and the tests read the same table through this door, so the
|
|
13
|
+
* directory can never drift from the family.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { contenttable, type familymember, seedRoster } from "./content.ts";
|
|
17
|
+
|
|
18
|
+
export type { familymember };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The typed accessor of the directory: the live db rows when the platform
|
|
22
|
+
* answered, the manifest seed otherwise — rail order, the OS first, then
|
|
23
|
+
* the applications.
|
|
24
|
+
*
|
|
25
|
+
* @returns the directory roster.
|
|
26
|
+
*/
|
|
27
|
+
export function roster(): readonly familymember[] {
|
|
28
|
+
return contenttable("roster", seedRoster);
|
|
29
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// # sandboxmodality — the workload lane declaration of the cobaia
|
|
2
|
+
// application (root layer, wave WL1): the lanes this app owns in the
|
|
3
|
+
// family workload catalog, mirrored from the engine of record
|
|
4
|
+
// (saddle/workloadlaw.ts — the FAMILYLANES table) as the string contract
|
|
5
|
+
// the deploy and the platform speak. The mirror law (the same discipline
|
|
6
|
+
// the devthink modalities vendor rides): the rows are mirrored, not
|
|
7
|
+
// imported, because the published saddle envelope the container build
|
|
8
|
+
// installs does not carry the workload law yet and the family law bars a
|
|
9
|
+
// relative path across apps — the day the envelope publishes, the ids
|
|
10
|
+
// answer the same strings (the lockstep is the string contract, not the
|
|
11
|
+
// import). The app battery pins the mirror against the engine source
|
|
12
|
+
// through the library bridge so the vendor can never drift in silence.
|
|
13
|
+
//
|
|
14
|
+
// The seat law (the deploy's opt-in, the family's shared shape): the
|
|
15
|
+
// deploy pins COBAIA_SANDBOX_LANE — "local" (the default, the deploy's own process),
|
|
16
|
+
// "join" (the platform resolves every lane through the engine's join
|
|
17
|
+
// resolver — the nuclear modality law: the workload never names a
|
|
18
|
+
// modality, the join answers), or a modality id the deploy pins for the
|
|
19
|
+
// app's lanes (named here, validated by the platform's join at dispatch —
|
|
20
|
+
// this module never judges engine vocabulary, so the mirror cannot drift
|
|
21
|
+
// when the registry grows). The module never spawns anything and resolves
|
|
22
|
+
// no join: it names the lanes' truth; the platform resolves the sandbox.
|
|
23
|
+
// Zero consumer data, zero imports.
|
|
24
|
+
|
|
25
|
+
/** one mirrored workload lane row — the same five keys the engine law pins
|
|
26
|
+
* (app, lane, role, demand, today). */
|
|
27
|
+
export type AppWorkloadLane = {
|
|
28
|
+
/** the official app slug every row carries. */
|
|
29
|
+
readonly app: string;
|
|
30
|
+
/** the lane id inside the app (kebab-case, the engine catalog id). */
|
|
31
|
+
readonly lane: string;
|
|
32
|
+
/** what the lane does, in one sentence (the engine row's sentence). */
|
|
33
|
+
readonly role: string;
|
|
34
|
+
/** the demand the lane puts on the sandbox (gpu, network, isolation,
|
|
35
|
+
* boot budget — never a modality name: the nuclear law). */
|
|
36
|
+
readonly demand: {
|
|
37
|
+
readonly gpu: boolean;
|
|
38
|
+
readonly network: boolean;
|
|
39
|
+
readonly isolation: "process" | "namespace" | "vm" | "hardware";
|
|
40
|
+
readonly bootbudget?: number;
|
|
41
|
+
};
|
|
42
|
+
/** where the heavy pass runs today — the honest truth the census reads. */
|
|
43
|
+
readonly today: "engine-lane" | "app-lane";
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** the app slug every mirrored row carries. */
|
|
47
|
+
export const APPSLUG = "cobaia";
|
|
48
|
+
|
|
49
|
+
/** the workload lanes this app owns — the mirror of the engine catalog
|
|
50
|
+
* rows (saddle/workloadlaw.ts, the FAMILYLANES table; the battery pins
|
|
51
|
+
* the two surfaces equal). */
|
|
52
|
+
export const WORKLOADLANES: readonly AppWorkloadLane[] = Object.freeze([
|
|
53
|
+
{
|
|
54
|
+
app: "cobaia",
|
|
55
|
+
lane: "plan-checkout",
|
|
56
|
+
role: "the plan checkout surface: mrr plan rows, link validation and the billing webhook walk when it lands, secrets server-side",
|
|
57
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 25 },
|
|
58
|
+
today: "app-lane",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
app: "cobaia",
|
|
62
|
+
lane: "ads-slot",
|
|
63
|
+
role: "the honest ad slot lane: the reserved slots and the rotation decision the deploy answers before the render",
|
|
64
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 25 },
|
|
65
|
+
today: "app-lane",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
app: "cobaia",
|
|
69
|
+
lane: "label-seed",
|
|
70
|
+
role: "the label manifest seed of the monetization face: the store walk the sqlite vault carries",
|
|
71
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
72
|
+
today: "app-lane",
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
/** the environment name the deploy pins the sandbox lane seat through
|
|
77
|
+
* (the opt-in seat — documented in the env example, zero hardcode). */
|
|
78
|
+
export const SANDBOXLANEENV = "COBAIA_SANDBOX_LANE";
|
|
79
|
+
|
|
80
|
+
/** the typed answer of the seat: the declaration the deploy reads —
|
|
81
|
+
* local (the lanes run in the deploy's own process), join (the platform
|
|
82
|
+
* resolves every lane through the engine's join resolver), or the pinned
|
|
83
|
+
* modality id the deploy named (validated by the platform's join at
|
|
84
|
+
* dispatch, never judged here). every answer is honest: an absent or
|
|
85
|
+
* blank choice answers the declared default, never a silent switch. */
|
|
86
|
+
export type LaneSeatResult =
|
|
87
|
+
| { ok: true; seat: "local"; sandboxed: false }
|
|
88
|
+
| { ok: true; seat: "join"; sandboxed: true; pinned: false }
|
|
89
|
+
| { ok: true; seat: string; sandboxed: true; pinned: true };
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Resolves the sandbox lane seat of this application — the deploy's
|
|
93
|
+
* opt-in: an absent or blank choice answers the declared default ("local"
|
|
94
|
+
* — the lanes run in the deploy's own process); "join" opts every lane
|
|
95
|
+
* onto the platform's join resolver (the nuclear modality law); any other
|
|
96
|
+
* non-empty choice is the modality id the deploy pins for the lanes
|
|
97
|
+
* (named by the deploy, validated by the platform's join at dispatch —
|
|
98
|
+
* this module never judges engine vocabulary, so the mirror cannot drift
|
|
99
|
+
* when the registry grows).
|
|
100
|
+
*
|
|
101
|
+
* @param choice the deploy's choice (the env value or the config row).
|
|
102
|
+
* @returns the seat declaration.
|
|
103
|
+
*/
|
|
104
|
+
export function lanesandbox(choice: string | undefined): LaneSeatResult {
|
|
105
|
+
const name = choice?.trim() ?? "";
|
|
106
|
+
if (name.length === 0 || name === "local") {
|
|
107
|
+
return { ok: true, seat: "local", sandboxed: false };
|
|
108
|
+
}
|
|
109
|
+
if (name === "join") {
|
|
110
|
+
return { ok: true, seat: "join", sandboxed: true, pinned: false };
|
|
111
|
+
}
|
|
112
|
+
return { ok: true, seat: name, sandboxed: true, pinned: true };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Reads the lane seat off one environment lookup — the deploy pins
|
|
117
|
+
* COBAIA_SANDBOX_LANE (opt-in per deploy); the lookup is injected so the server lane
|
|
118
|
+
* hands process.env and any other lane hands its own seat. this module
|
|
119
|
+
* never spawns anything: it names the truth the platform resolves.
|
|
120
|
+
*
|
|
121
|
+
* @param lookup the environment lookup function (the env carrier).
|
|
122
|
+
* @returns the seat declaration.
|
|
123
|
+
*/
|
|
124
|
+
export function lanesandboxfromenv(lookup: (key: string) => string | undefined): LaneSeatResult {
|
|
125
|
+
return lanesandbox(lookup(SANDBOXLANEENV));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Answers one mirrored lane row by lane id — the lookup the app surfaces
|
|
130
|
+
* and the batteries read.
|
|
131
|
+
*
|
|
132
|
+
* @param lane the lane id inside the app.
|
|
133
|
+
* @returns the mirrored row, or undefined when the app carries no such
|
|
134
|
+
* lane.
|
|
135
|
+
*/
|
|
136
|
+
export function laneof(lane: string): AppWorkloadLane | undefined {
|
|
137
|
+
return WORKLOADLANES.find((row) => row.lane === lane);
|
|
138
|
+
}
|
package/schema.prisma
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// cobaia prisma schema — the self-hosted database of the family site. cobaia
|
|
2
|
+
// is the monetization surface of the family: the content site that carries
|
|
3
|
+
// the plans, the support rails and the honest advertisement slots while the
|
|
4
|
+
// family apps stay clean.
|
|
5
|
+
//
|
|
6
|
+
// family sync contract (devthink master): a table of the same kind carries
|
|
7
|
+
// the same columns and the same pointers in every application that holds
|
|
8
|
+
// the kind — identity pointers are text, counts are 32-bit ints, time is
|
|
9
|
+
// datetime or iso text, and byte-scale pointers (sizes, lengths, offsets)
|
|
10
|
+
// are 64-bit BigInt, the pointer that holds terabytes. The sqlite
|
|
11
|
+
// autoincrement rowid is 64-bit by the engine contract. This file is the
|
|
12
|
+
// documentation of record: the runtime runs better-sqlite3 beside it and
|
|
13
|
+
// mirrors the same columns in its boot DDL (db.ts).
|
|
14
|
+
generator client {
|
|
15
|
+
provider = "prisma-client-js"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
datasource db {
|
|
19
|
+
provider = "sqlite"
|
|
20
|
+
url = env("DATABASE_URL")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// the first-run state of one visitor (the platform contract the gateway
|
|
24
|
+
/// serves over /api/v1/onboarding)
|
|
25
|
+
model OnboardingState {
|
|
26
|
+
id Int @id @default(autoincrement())
|
|
27
|
+
visitor String @unique
|
|
28
|
+
introseen Boolean @default(false)
|
|
29
|
+
onboardingseen Boolean @default(false)
|
|
30
|
+
updatedAt DateTime @updatedAt
|
|
31
|
+
}
|
package/sitemap.xml
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!-- cobaia — the family site. No domain is hardcoded. The loc entries are
|
|
3
|
+
relative and resolve against whichever origin serves this sitemap; the
|
|
4
|
+
canonical domain binds on the deploy platform. -->
|
|
5
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
6
|
+
<url>
|
|
7
|
+
<loc>/</loc>
|
|
8
|
+
<lastmod>2026-10-10</lastmod>
|
|
9
|
+
<changefreq>weekly</changefreq>
|
|
10
|
+
<priority>1.0</priority>
|
|
11
|
+
</url>
|
|
12
|
+
<url>
|
|
13
|
+
<loc>/apps</loc>
|
|
14
|
+
<lastmod>2026-10-10</lastmod>
|
|
15
|
+
<changefreq>weekly</changefreq>
|
|
16
|
+
<priority>0.9</priority>
|
|
17
|
+
</url>
|
|
18
|
+
<url>
|
|
19
|
+
<loc>/pricing</loc>
|
|
20
|
+
<lastmod>2026-10-10</lastmod>
|
|
21
|
+
<changefreq>monthly</changefreq>
|
|
22
|
+
<priority>0.9</priority>
|
|
23
|
+
</url>
|
|
24
|
+
<url>
|
|
25
|
+
<loc>/blog</loc>
|
|
26
|
+
<lastmod>2026-10-06</lastmod>
|
|
27
|
+
<changefreq>weekly</changefreq>
|
|
28
|
+
<priority>0.8</priority>
|
|
29
|
+
</url>
|
|
30
|
+
<url>
|
|
31
|
+
<loc>/support</loc>
|
|
32
|
+
<lastmod>2026-10-10</lastmod>
|
|
33
|
+
<changefreq>monthly</changefreq>
|
|
34
|
+
<priority>0.6</priority>
|
|
35
|
+
</url>
|
|
36
|
+
<url>
|
|
37
|
+
<loc>/legal</loc>
|
|
38
|
+
<lastmod>2026-10-10</lastmod>
|
|
39
|
+
<changefreq>monthly</changefreq>
|
|
40
|
+
<priority>0.5</priority>
|
|
41
|
+
</url>
|
|
42
|
+
</urlset>
|