create-theokit 0.2.3 → 0.5.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.
- package/package.json +5 -1
- package/templates/default/.env.example +5 -0
- package/templates/default/README.md.tmpl +71 -57
- package/templates/default/_gitignore +2 -3
- package/templates/default/app/layout.tsx +51 -82
- package/templates/default/app/page.tsx +169 -271
- package/templates/default/app.ts +23 -0
- package/templates/default/package.json.tmpl +10 -23
- package/templates/default/server/agents/assistant.agent.ts +46 -0
- package/templates/default/server/controllers/tasks.controller.ts +70 -0
- package/templates/default/server/filters/http-error.filter.ts +20 -0
- package/templates/default/server/guards/auth.guard.ts +47 -0
- package/templates/default/server/interceptors/timing.interceptor.ts +14 -0
- package/templates/default/server/middleware/logger.middleware.ts +12 -0
- package/templates/default/server/store.ts +46 -0
- package/templates/default/server/toolboxes/task.tools.ts +58 -0
- package/templates/default/tsconfig.json +7 -7
- package/templates/api-only/.nvmrc +0 -1
- package/templates/api-only/README.md.tmpl +0 -78
- package/templates/api-only/_gitignore +0 -5
- package/templates/api-only/app/page.tsx +0 -3
- package/templates/api-only/index.html +0 -12
- package/templates/api-only/package.json.tmpl +0 -28
- package/templates/api-only/public/.gitkeep +0 -0
- package/templates/api-only/public/favicon.ico +0 -0
- package/templates/api-only/server/routes/health.ts +0 -5
- package/templates/api-only/server/routes/users.ts +0 -27
- package/templates/api-only/server/routes/webhooks/echo.ts +0 -34
- package/templates/api-only/theo.config.ts +0 -3
- package/templates/api-only/tsconfig.json +0 -15
- package/templates/dashboard/.nvmrc +0 -1
- package/templates/dashboard/README.md.tmpl +0 -76
- package/templates/dashboard/_gitignore +0 -5
- package/templates/dashboard/app/about/page.tsx +0 -3
- package/templates/dashboard/app/dashboard/layout.tsx +0 -10
- package/templates/dashboard/app/dashboard/page.tsx +0 -3
- package/templates/dashboard/app/layout.tsx +0 -14
- package/templates/dashboard/app/page.tsx +0 -8
- package/templates/dashboard/index.html +0 -12
- package/templates/dashboard/package.json.tmpl +0 -28
- package/templates/dashboard/public/.gitkeep +0 -0
- package/templates/dashboard/public/favicon.ico +0 -0
- package/templates/dashboard/server/crons/cleanup-conversations.ts +0 -59
- package/templates/dashboard/server/routes/health.ts +0 -5
- package/templates/dashboard/theo.config.ts +0 -3
- package/templates/dashboard/tsconfig.json +0 -15
- package/templates/default/.nvmrc +0 -1
- package/templates/default/index.html +0 -12
- package/templates/default/public/.gitkeep +0 -0
- package/templates/default/public/favicon.ico +0 -0
- package/templates/default/server/crons/cleanup-conversations.ts +0 -59
- package/templates/default/server/routes/chat.ts +0 -69
- package/templates/default/server/routes/health.ts +0 -5
- package/templates/default/theo.config.ts +0 -3
- package/templates/default/types/jobs.d.ts +0 -25
- package/templates/postgres/.env.example +0 -5
- package/templates/postgres/.nvmrc +0 -1
- package/templates/postgres/README.md.tmpl +0 -83
- package/templates/postgres/_gitignore +0 -5
- package/templates/postgres/app/layout.tsx +0 -14
- package/templates/postgres/app/page.tsx +0 -8
- package/templates/postgres/db/index.ts +0 -7
- package/templates/postgres/db/schema.ts +0 -8
- package/templates/postgres/drizzle.config.ts +0 -10
- package/templates/postgres/index.html +0 -12
- package/templates/postgres/package.json.tmpl +0 -36
- package/templates/postgres/public/.gitkeep +0 -0
- package/templates/postgres/public/favicon.ico +0 -0
- package/templates/postgres/server/context.ts +0 -5
- package/templates/postgres/server/jobs/log-message.ts +0 -26
- package/templates/postgres/server/routes/health.ts +0 -5
- package/templates/postgres/server/routes/users.ts +0 -22
- package/templates/postgres/theo.config.ts +0 -3
- package/templates/postgres/tsconfig.json +0 -15
- package/templates/saas/.env.example +0 -7
- package/templates/saas/.nvmrc +0 -1
- package/templates/saas/README.md.tmpl +0 -103
- package/templates/saas/_gitignore +0 -5
- package/templates/saas/app/layout.tsx +0 -5
- package/templates/saas/app/page.tsx +0 -104
- package/templates/saas/db/index.ts +0 -6
- package/templates/saas/db/schema.ts +0 -20
- package/templates/saas/drizzle.config.ts +0 -10
- package/templates/saas/index.html +0 -12
- package/templates/saas/package.json.tmpl +0 -38
- package/templates/saas/public/.gitkeep +0 -0
- package/templates/saas/public/favicon.ico +0 -0
- package/templates/saas/server/context.ts +0 -37
- package/templates/saas/server/routes/agent.ts +0 -49
- package/templates/saas/server/routes/billing/stripe-webhook.ts +0 -49
- package/templates/saas/server/routes/login.ts +0 -25
- package/templates/saas/server/routes/logout.ts +0 -10
- package/templates/saas/server/routes/me.ts +0 -10
- package/templates/saas/theo.config.ts +0 -5
- package/templates/saas/tsconfig.json +0 -15
- package/templates/services/agent-node/Dockerfile.tmpl +0 -20
- package/templates/services/agent-node/README.md +0 -38
- package/templates/services/agent-node/package.json.tmpl +0 -18
- package/templates/services/agent-node/src/index.ts +0 -58
- package/templates/services/agent-node/tsconfig.json +0 -13
- package/templates/services/agent-python/Dockerfile.tmpl +0 -20
- package/templates/services/agent-python/README.md +0 -37
- package/templates/services/agent-python/main.py +0 -77
- package/templates/services/agent-python/pyproject.toml.tmpl +0 -16
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RolesGuard — RBAC authorization for controllers AND agents.
|
|
3
|
+
*
|
|
4
|
+
* Same guard, same pipeline, same behavior on both HTTP and AI routes.
|
|
5
|
+
* Uses @Roles([Role.Admin]) on class/method and @IsPublic(true) to skip.
|
|
6
|
+
*/
|
|
7
|
+
import {
|
|
8
|
+
createDecorator, Reflector,
|
|
9
|
+
type CanActivate, type ExecutionContext,
|
|
10
|
+
} from '@theokit/http-decorators'
|
|
11
|
+
|
|
12
|
+
export enum Role {
|
|
13
|
+
User = 'user',
|
|
14
|
+
Admin = 'admin',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Declare required roles for a route or agent. */
|
|
18
|
+
export const Roles = createDecorator<Role[]>()
|
|
19
|
+
|
|
20
|
+
/** Mark a route as public — skips auth entirely. */
|
|
21
|
+
export const IsPublic = createDecorator<boolean>()
|
|
22
|
+
|
|
23
|
+
const reflector = new Reflector()
|
|
24
|
+
|
|
25
|
+
export class RolesGuard implements CanActivate {
|
|
26
|
+
canActivate(context: ExecutionContext): boolean {
|
|
27
|
+
// Public routes skip auth
|
|
28
|
+
const isPublic = reflector.getAllAndOverride(
|
|
29
|
+
IsPublic,
|
|
30
|
+
context.getClass(),
|
|
31
|
+
context.getMethodName(),
|
|
32
|
+
)
|
|
33
|
+
if (isPublic) return true
|
|
34
|
+
|
|
35
|
+
// Check required roles
|
|
36
|
+
const roles = reflector.getAllAndOverride(
|
|
37
|
+
Roles,
|
|
38
|
+
context.getClass(),
|
|
39
|
+
context.getMethodName(),
|
|
40
|
+
)
|
|
41
|
+
if (!roles) return true // no roles required
|
|
42
|
+
|
|
43
|
+
// Read role from request header (replace with JWT/session in production)
|
|
44
|
+
const userRole = context.getRequest().headers.get('x-role') ?? ''
|
|
45
|
+
return roles.some((r) => r === userRole)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TimingInterceptor — logs request duration.
|
|
3
|
+
* Applied to both controllers and agents.
|
|
4
|
+
*/
|
|
5
|
+
import type { Interceptor } from '@theokit/http-decorators'
|
|
6
|
+
|
|
7
|
+
export class TimingInterceptor implements Interceptor {
|
|
8
|
+
async intercept(_request: Request, next: () => Promise<unknown>): Promise<unknown> {
|
|
9
|
+
const start = Date.now()
|
|
10
|
+
const result = await next()
|
|
11
|
+
console.log(` ${Date.now() - start}ms`)
|
|
12
|
+
return result
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoggerMiddleware — logs every incoming request.
|
|
3
|
+
*/
|
|
4
|
+
import type { NestMiddleware } from '@theokit/http-decorators'
|
|
5
|
+
|
|
6
|
+
export class LoggerMiddleware implements NestMiddleware {
|
|
7
|
+
use(request: Request, next: () => Promise<Response | null>): Promise<Response | null> {
|
|
8
|
+
const url = new URL(request.url)
|
|
9
|
+
console.log(` ${new Date().toLocaleTimeString()} ${request.method} ${url.pathname}`)
|
|
10
|
+
return next()
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory data store.
|
|
3
|
+
* Replace with Drizzle + PostgreSQL for production.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface Task {
|
|
7
|
+
id: number
|
|
8
|
+
title: string
|
|
9
|
+
priority: 'low' | 'medium' | 'high'
|
|
10
|
+
done: boolean
|
|
11
|
+
createdAt: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let seq = 0
|
|
15
|
+
|
|
16
|
+
const tasks: Task[] = [
|
|
17
|
+
{ id: ++seq, title: 'Set up TheoKit project', priority: 'high', done: true, createdAt: '2026-01-01' },
|
|
18
|
+
{ id: ++seq, title: 'Create first controller', priority: 'high', done: true, createdAt: '2026-01-02' },
|
|
19
|
+
{ id: ++seq, title: 'Add AI agent', priority: 'medium', done: false, createdAt: '2026-01-03' },
|
|
20
|
+
{ id: ++seq, title: 'Deploy to production', priority: 'low', done: false, createdAt: '2026-01-04' },
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
export const taskStore = {
|
|
24
|
+
list: () => [...tasks],
|
|
25
|
+
get: (id: number) => tasks.find((t) => t.id === id),
|
|
26
|
+
search: (q: string) => tasks.filter((t) => t.title.toLowerCase().includes(q.toLowerCase())),
|
|
27
|
+
create: (data: { title: string; priority?: Task['priority'] }) => {
|
|
28
|
+
const task: Task = { id: ++seq, title: data.title, priority: data.priority ?? 'medium', done: false, createdAt: new Date().toISOString().split('T')[0] }
|
|
29
|
+
tasks.push(task)
|
|
30
|
+
return task
|
|
31
|
+
},
|
|
32
|
+
update: (id: number, data: Partial<Pick<Task, 'title' | 'priority' | 'done'>>) => {
|
|
33
|
+
const t = tasks.find((x) => x.id === id)
|
|
34
|
+
if (!t) return null
|
|
35
|
+
if (data.title !== undefined) t.title = data.title
|
|
36
|
+
if (data.priority !== undefined) t.priority = data.priority
|
|
37
|
+
if (data.done !== undefined) t.done = data.done
|
|
38
|
+
return t
|
|
39
|
+
},
|
|
40
|
+
remove: (id: number) => {
|
|
41
|
+
const idx = tasks.findIndex((t) => t.id === id)
|
|
42
|
+
if (idx === -1) return false
|
|
43
|
+
tasks.splice(idx, 1)
|
|
44
|
+
return true
|
|
45
|
+
},
|
|
46
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TaskTools — agent toolbox for task management.
|
|
3
|
+
*
|
|
4
|
+
* These tools are called by the LLM when the agent decides
|
|
5
|
+
* to interact with the task data. Each @Tool method is
|
|
6
|
+
* compiled to a defineTool() call at startup.
|
|
7
|
+
*/
|
|
8
|
+
import 'reflect-metadata'
|
|
9
|
+
import { z } from 'zod'
|
|
10
|
+
import { Toolbox, Tool, Trace, Audit } from '@theokit/agents'
|
|
11
|
+
import { taskStore } from '../store.js'
|
|
12
|
+
|
|
13
|
+
@Toolbox({ namespace: 'tasks' })
|
|
14
|
+
@Trace(true)
|
|
15
|
+
export class TaskTools {
|
|
16
|
+
@Tool({
|
|
17
|
+
name: 'list',
|
|
18
|
+
description: 'List all tasks with their status and priority',
|
|
19
|
+
input: z.object({}),
|
|
20
|
+
})
|
|
21
|
+
async list() {
|
|
22
|
+
return JSON.stringify(taskStore.list())
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Tool({
|
|
26
|
+
name: 'search',
|
|
27
|
+
description: 'Search tasks by keyword in title',
|
|
28
|
+
input: z.object({ query: z.string() }),
|
|
29
|
+
})
|
|
30
|
+
async search(input: { query: string }) {
|
|
31
|
+
return JSON.stringify(taskStore.search(input.query))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Tool({
|
|
35
|
+
name: 'create',
|
|
36
|
+
description: 'Create a new task',
|
|
37
|
+
input: z.object({
|
|
38
|
+
title: z.string(),
|
|
39
|
+
priority: z.enum(['low', 'medium', 'high']).default('medium'),
|
|
40
|
+
}),
|
|
41
|
+
risk: 'medium',
|
|
42
|
+
})
|
|
43
|
+
@Audit(true)
|
|
44
|
+
async create(input: { title: string; priority?: 'low' | 'medium' | 'high' }) {
|
|
45
|
+
return JSON.stringify(taskStore.create(input))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Tool({
|
|
49
|
+
name: 'complete',
|
|
50
|
+
description: 'Mark a task as done by its ID',
|
|
51
|
+
input: z.object({ taskId: z.number() }),
|
|
52
|
+
})
|
|
53
|
+
@Audit(true)
|
|
54
|
+
async complete(input: { taskId: number }) {
|
|
55
|
+
const task = taskStore.update(input.taskId, { done: true })
|
|
56
|
+
return task ? JSON.stringify(task) : 'Task not found'
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"target": "ES2022",
|
|
4
|
-
"module": "
|
|
5
|
-
"moduleResolution": "
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"experimentalDecorators": true,
|
|
7
|
+
"emitDecoratorMetadata": true,
|
|
6
8
|
"strict": true,
|
|
7
|
-
"noEmit": true,
|
|
8
9
|
"esModuleInterop": true,
|
|
9
10
|
"skipLibCheck": true,
|
|
10
|
-
"
|
|
11
|
-
"isolatedModules": true,
|
|
12
|
-
"resolveJsonModule": true
|
|
11
|
+
"outDir": "dist"
|
|
13
12
|
},
|
|
14
|
-
"include": ["
|
|
13
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
14
|
+
"exclude": ["node_modules", "dist"]
|
|
15
15
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
22.12
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# {{name}}
|
|
2
|
-
|
|
3
|
-
TheoKit API-only project. Backend routes with Zod validation + typed responses — no frontend bundle, no React.
|
|
4
|
-
|
|
5
|
-
> 📚 **Full docs:** https://docs.theokit.dev
|
|
6
|
-
|
|
7
|
-
## Quick start
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# 1. Set your provider key if you wire an agent route later
|
|
11
|
-
echo 'OPENROUTER_API_KEY=sk-or-v1-...' > .env
|
|
12
|
-
|
|
13
|
-
# 2. Boot the dev server
|
|
14
|
-
npx theokit dev
|
|
15
|
-
|
|
16
|
-
# 3. Probe the health route
|
|
17
|
-
curl http://localhost:3000/api/health
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
You should see `{"status":"ok"}`. The server is now serving the routes under `server/routes/`.
|
|
21
|
-
|
|
22
|
-
## Templates
|
|
23
|
-
|
|
24
|
-
- **default** — TheoUI chat composer + agent route.
|
|
25
|
-
- **dashboard** — nested layouts + sidebar.
|
|
26
|
-
- **api-only** (this one) — server routes without React.
|
|
27
|
-
- **postgres** — Drizzle ORM + migrations.
|
|
28
|
-
- **saas** — full app with auth, billing, sessions.
|
|
29
|
-
|
|
30
|
-
## What the framework auto-loads
|
|
31
|
-
|
|
32
|
-
- **`.env` → `process.env`**. Edit `.env`; restart the dev server.
|
|
33
|
-
- **`.theo/` build output cleanup** on every `theokit build`.
|
|
34
|
-
- **Route discovery** — every `server/routes/*.ts` becomes a wired endpoint.
|
|
35
|
-
|
|
36
|
-
## Project structure
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
server/
|
|
40
|
-
├── routes/
|
|
41
|
-
│ ├── health.ts GET /api/health — returns {status:"ok"}
|
|
42
|
-
│ └── users.ts CRUD /api/users — Zod-validated body
|
|
43
|
-
theo.config.ts Framework config
|
|
44
|
-
.env Secrets — never committed (.gitignore)
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Sample requests
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
# Health check
|
|
51
|
-
curl http://localhost:3000/api/health
|
|
52
|
-
|
|
53
|
-
# Create a user (POST with JSON body)
|
|
54
|
-
curl -X POST http://localhost:3000/api/users \
|
|
55
|
-
-H 'Content-Type: application/json' \
|
|
56
|
-
-d '{"name":"Alice","email":"alice@example.com"}'
|
|
57
|
-
|
|
58
|
-
# List users
|
|
59
|
-
curl http://localhost:3000/api/users
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## Common commands
|
|
63
|
-
|
|
64
|
-
| Command | What it does |
|
|
65
|
-
|---|---|
|
|
66
|
-
| `npx theokit dev` | Dev server with HMR + structured logs |
|
|
67
|
-
| `npx theokit build` | Production build → `.theo/` |
|
|
68
|
-
| `npx theokit start` | Serve the production build |
|
|
69
|
-
| `npx theokit routes` | List all routes detected |
|
|
70
|
-
| `npm run typecheck` | TypeScript strict check (no emit) |
|
|
71
|
-
|
|
72
|
-
## Add a new route
|
|
73
|
-
|
|
74
|
-
Drop a `.ts` file in `server/routes/`. Use `defineRoute` from `theokit/server` for Zod-validated handlers, or export `GET`/`POST` directly.
|
|
75
|
-
|
|
76
|
-
## License
|
|
77
|
-
|
|
78
|
-
Apply your own. The TheoKit framework is Apache-2.0.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Theo App</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/@theo/entry-client"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{name}}",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "theokit dev",
|
|
8
|
-
"build": "theokit build",
|
|
9
|
-
"start": "theokit start",
|
|
10
|
-
"typecheck": "tsc --noEmit"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"theokit": "^0.2.2",
|
|
14
|
-
"react": "^19.0.0",
|
|
15
|
-
"react-dom": "^19.0.0"
|
|
16
|
-
},
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"@types/node": "^22.10.0",
|
|
19
|
-
"typescript": "^5.7.0",
|
|
20
|
-
"@types/react": "^19.0.0",
|
|
21
|
-
"@types/react-dom": "^19.0.0"
|
|
22
|
-
},
|
|
23
|
-
"pnpm": {
|
|
24
|
-
"onlyBuiltDependencies": [
|
|
25
|
-
"esbuild"
|
|
26
|
-
]
|
|
27
|
-
}
|
|
28
|
-
}
|
|
File without changes
|
|
Binary file
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { defineRoute } from 'theokit/server'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
|
|
4
|
-
const users = [
|
|
5
|
-
{ id: '1', name: 'Alice', email: 'alice@example.com' },
|
|
6
|
-
{ id: '2', name: 'Bob', email: 'bob@example.com' },
|
|
7
|
-
]
|
|
8
|
-
|
|
9
|
-
export const GET = defineRoute({
|
|
10
|
-
query: z.object({ search: z.string().optional() }),
|
|
11
|
-
handler: ({ query }) => {
|
|
12
|
-
if (query.search) {
|
|
13
|
-
return users.filter((u) => u.name.toLowerCase().includes(query.search!.toLowerCase()))
|
|
14
|
-
}
|
|
15
|
-
return users
|
|
16
|
-
},
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
export const POST = defineRoute({
|
|
20
|
-
body: z.object({ name: z.string().min(1), email: z.string().email() }),
|
|
21
|
-
status: 201,
|
|
22
|
-
handler: ({ body }) => ({
|
|
23
|
-
id: String(users.length + 1),
|
|
24
|
-
name: body.name,
|
|
25
|
-
email: body.email,
|
|
26
|
-
}),
|
|
27
|
-
})
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { defineWebhook } from 'theokit/server'
|
|
2
|
-
import { createHmac, timingSafeEqual } from 'node:crypto'
|
|
3
|
-
import { z } from 'zod'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Echo webhook — demonstrates `defineWebhook` HMAC-SHA256 pattern
|
|
7
|
-
* without depending on an external provider (Stripe, GitHub, etc.).
|
|
8
|
-
*
|
|
9
|
-
* Self-test:
|
|
10
|
-
* SECRET=$(openssl rand -base64 32)
|
|
11
|
-
* echo -n '{"message":"hi"}' | openssl dgst -sha256 -hmac "$SECRET"
|
|
12
|
-
* curl -X POST localhost:3000/api/webhooks/echo \
|
|
13
|
-
* -H "x-echo-signature: <hex from above>" \
|
|
14
|
-
* -H "Content-Type: application/json" \
|
|
15
|
-
* -d '{"message":"hi"}'
|
|
16
|
-
*/
|
|
17
|
-
const ECHO_SECRET = process.env.ECHO_WEBHOOK_SECRET ?? ''
|
|
18
|
-
|
|
19
|
-
export const POST = defineWebhook({
|
|
20
|
-
verify: ({ rawBody, headers }) => {
|
|
21
|
-
if (ECHO_SECRET === '') return false
|
|
22
|
-
const sig = headers.get('x-echo-signature') ?? ''
|
|
23
|
-
const expected = createHmac('sha256', ECHO_SECRET).update(rawBody).digest('hex')
|
|
24
|
-
try {
|
|
25
|
-
return timingSafeEqual(Buffer.from(sig, 'utf-8'), Buffer.from(expected, 'utf-8'))
|
|
26
|
-
} catch {
|
|
27
|
-
return false
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
inputSchema: z.object({ message: z.string() }),
|
|
31
|
-
handler: async ({ input }) => {
|
|
32
|
-
return Response.json({ echoed: input.message, timestamp: new Date().toISOString() })
|
|
33
|
-
},
|
|
34
|
-
})
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"strict": true,
|
|
7
|
-
"noEmit": true,
|
|
8
|
-
"esModuleInterop": true,
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"jsx": "react-jsx",
|
|
11
|
-
"isolatedModules": true,
|
|
12
|
-
"resolveJsonModule": true
|
|
13
|
-
},
|
|
14
|
-
"include": ["app/**/*.ts", "app/**/*.tsx", "server/**/*.ts"]
|
|
15
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
22.12
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# {{name}}
|
|
2
|
-
|
|
3
|
-
TheoKit dashboard project. Build the app your agent lives in — with nested layouts and a sidebar wired from day one.
|
|
4
|
-
|
|
5
|
-
> 📚 **Full docs:** https://docs.theokit.dev
|
|
6
|
-
|
|
7
|
-
## Quick start
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# 1. Set your provider key (OpenRouter recommended — one key, any model)
|
|
11
|
-
echo 'OPENROUTER_API_KEY=sk-or-v1-...' > .env
|
|
12
|
-
|
|
13
|
-
# 2. Boot the dev server
|
|
14
|
-
npx theokit dev
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Open the printed URL. The default surface is a dashboard shell with sidebar nav + content area, ready to host your agent panels.
|
|
18
|
-
|
|
19
|
-
## Templates
|
|
20
|
-
|
|
21
|
-
- **default** — TheoUI chat composer + agent route.
|
|
22
|
-
- **dashboard** (this one) — nested layouts + sidebar nav.
|
|
23
|
-
- **api-only** — server routes without React.
|
|
24
|
-
- **postgres** — Drizzle ORM + migrations.
|
|
25
|
-
- **saas** — full app with auth, billing, sessions.
|
|
26
|
-
|
|
27
|
-
## What the framework auto-loads
|
|
28
|
-
|
|
29
|
-
- **`.env` → `process.env`**. Edit `.env`; restart the dev server.
|
|
30
|
-
- **`.theo/` build output cleanup** on every `theokit build`.
|
|
31
|
-
- **Tailwind + `@theokit/ui` styling** auto-configured for the TheoUI surface.
|
|
32
|
-
|
|
33
|
-
## Project structure
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
app/ Frontend (file-based routing with nested layouts)
|
|
37
|
-
├── layout.tsx root wrapper — TheoUI provider + theme
|
|
38
|
-
├── page.tsx / — dashboard home
|
|
39
|
-
├── dashboard/
|
|
40
|
-
│ ├── layout.tsx /dashboard/* — sidebar shell
|
|
41
|
-
│ └── page.tsx /dashboard — primary panel
|
|
42
|
-
server/ Backend (explicit routes)
|
|
43
|
-
├── routes/
|
|
44
|
-
│ └── health.ts GET /api/health
|
|
45
|
-
theo.config.ts Framework config
|
|
46
|
-
tailwind.config.ts Tailwind theme tokens
|
|
47
|
-
.env Secrets — never committed
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Common commands
|
|
51
|
-
|
|
52
|
-
| Command | What it does |
|
|
53
|
-
|---|---|
|
|
54
|
-
| `npx theokit dev` | Dev server with HMR + devtools overlay |
|
|
55
|
-
| `npx theokit build` | Production build → `.theo/` |
|
|
56
|
-
| `npx theokit start` | Serve the production build |
|
|
57
|
-
| `npx theokit check` | Lint for upgrade-readiness |
|
|
58
|
-
| `npx theokit routes` | List all routes + actions detected |
|
|
59
|
-
| `npm run typecheck` | TypeScript strict check (no emit) |
|
|
60
|
-
|
|
61
|
-
## Add a new panel
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
mkdir -p app/dashboard/billing
|
|
65
|
-
cat > app/dashboard/billing/page.tsx <<'EOF'
|
|
66
|
-
export default function BillingPage() {
|
|
67
|
-
return <h2>Billing</h2>
|
|
68
|
-
}
|
|
69
|
-
EOF
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
The route appears at `/dashboard/billing` after HMR.
|
|
73
|
-
|
|
74
|
-
## License
|
|
75
|
-
|
|
76
|
-
Apply your own. The TheoKit framework is Apache-2.0.
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Outlet } from 'react-router'
|
|
2
|
-
|
|
3
|
-
export default function RootLayout() {
|
|
4
|
-
return (
|
|
5
|
-
<div>
|
|
6
|
-
<nav>
|
|
7
|
-
<a href="/">Home</a> | <a href="/about">About</a> | <a href="/dashboard">Dashboard</a>
|
|
8
|
-
</nav>
|
|
9
|
-
<main>
|
|
10
|
-
<Outlet />
|
|
11
|
-
</main>
|
|
12
|
-
</div>
|
|
13
|
-
)
|
|
14
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Theo App</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/@theo/entry-client"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{name}}",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "theokit dev",
|
|
8
|
-
"build": "theokit build",
|
|
9
|
-
"start": "theokit start",
|
|
10
|
-
"typecheck": "tsc --noEmit"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"theokit": "^0.2.2",
|
|
14
|
-
"react": "^19.0.0",
|
|
15
|
-
"react-dom": "^19.0.0"
|
|
16
|
-
},
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"@types/node": "^22.10.0",
|
|
19
|
-
"typescript": "^5.7.0",
|
|
20
|
-
"@types/react": "^19.0.0",
|
|
21
|
-
"@types/react-dom": "^19.0.0"
|
|
22
|
-
},
|
|
23
|
-
"pnpm": {
|
|
24
|
-
"onlyBuiltDependencies": [
|
|
25
|
-
"esbuild"
|
|
26
|
-
]
|
|
27
|
-
}
|
|
28
|
-
}
|
|
File without changes
|
|
Binary file
|