@tellet/create 0.8.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/README.md +195 -0
- package/dist/ai/generate.d.ts +33 -0
- package/dist/ai/generate.js +108 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +337 -0
- package/dist/scaffold/project.d.ts +44 -0
- package/dist/scaffold/project.js +318 -0
- package/package.json +48 -0
- package/template/Dockerfile +35 -0
- package/template/app/(dashboard)/agents/page.tsx +14 -0
- package/template/app/(dashboard)/conversations/[id]/page.tsx +103 -0
- package/template/app/(dashboard)/conversations/page.tsx +50 -0
- package/template/app/(dashboard)/dashboard/page.tsx +102 -0
- package/template/app/(dashboard)/layout.tsx +15 -0
- package/template/app/(dashboard)/settings/page.tsx +46 -0
- package/template/app/(site)/layout.tsx +3 -0
- package/template/app/(site)/page.tsx +25 -0
- package/template/app/api/chat/route.ts +129 -0
- package/template/app/api/cron/route.ts +29 -0
- package/template/app/api/orchestrator/route.ts +139 -0
- package/template/app/globals.css +30 -0
- package/template/app/layout.tsx +18 -0
- package/template/components/chat/ChatWidget.tsx +109 -0
- package/template/components/chat/Markdown.tsx +136 -0
- package/template/components/dashboard/AgentChat.tsx +192 -0
- package/template/components/dashboard/AgentsListClient.tsx +86 -0
- package/template/components/dashboard/DashboardAgentGrid.tsx +73 -0
- package/template/components/dashboard/OrchestratorChat.tsx +251 -0
- package/template/components/dashboard/Sidebar.tsx +44 -0
- package/template/components/dashboard/StatsCards.tsx +40 -0
- package/template/components/dashboard/Welcome.tsx +139 -0
- package/template/components/sections/Agents.tsx +67 -0
- package/template/components/sections/CTA.tsx +46 -0
- package/template/components/sections/FAQ.tsx +81 -0
- package/template/components/sections/Features.tsx +51 -0
- package/template/components/sections/Footer.tsx +22 -0
- package/template/components/sections/Hero.tsx +86 -0
- package/template/components/sections/Icons.tsx +29 -0
- package/template/components/ui/Button.tsx +26 -0
- package/template/docker-compose.yml +32 -0
- package/template/infra/bin/app.ts +16 -0
- package/template/infra/cdk.json +6 -0
- package/template/infra/lib/tellet-stack.ts +216 -0
- package/template/infra/package.json +20 -0
- package/template/infra/tsconfig.json +16 -0
- package/template/lib/db.ts +37 -0
- package/template/lib/engine/default.ts +227 -0
- package/template/lib/engine/index.ts +17 -0
- package/template/lib/mcp/client.ts +97 -0
- package/template/lib/mcp/knowledge.ts +84 -0
- package/template/lib/mcp/registry.ts +106 -0
- package/template/lib/orchestrator/executor.ts +202 -0
- package/template/lib/orchestrator/tools.ts +245 -0
- package/template/lib/providers/anthropic.ts +41 -0
- package/template/lib/providers/index.ts +36 -0
- package/template/lib/providers/openai.ts +46 -0
- package/template/lib/scheduler.ts +115 -0
- package/template/lib/supabase.ts +30 -0
- package/template/lib/tellet.ts +45 -0
- package/template/lib/utils.ts +6 -0
- package/template/next.config.ts +7 -0
- package/template/public/widget.js +172 -0
- package/template/railway.toml +9 -0
- package/template/tsconfig.json +21 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
interface AgentConfig {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
role: string;
|
|
5
|
+
description: string;
|
|
6
|
+
systemPrompt: string;
|
|
7
|
+
model: string;
|
|
8
|
+
}
|
|
9
|
+
interface SiteContent {
|
|
10
|
+
tagline: string;
|
|
11
|
+
subtitle: string;
|
|
12
|
+
features: {
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
icon: string;
|
|
16
|
+
}[];
|
|
17
|
+
faq: {
|
|
18
|
+
question: string;
|
|
19
|
+
answer: string;
|
|
20
|
+
}[];
|
|
21
|
+
cta: string;
|
|
22
|
+
}
|
|
23
|
+
export type DeployTier = "quickstart" | "cloud" | "enterprise";
|
|
24
|
+
interface ScaffoldOptions {
|
|
25
|
+
company: {
|
|
26
|
+
name: string;
|
|
27
|
+
description: string;
|
|
28
|
+
industry: string;
|
|
29
|
+
};
|
|
30
|
+
agents: AgentConfig[];
|
|
31
|
+
site: SiteContent;
|
|
32
|
+
provider: "anthropic" | "openai";
|
|
33
|
+
tier: DeployTier;
|
|
34
|
+
mode: "new" | "connect";
|
|
35
|
+
websiteUrl: string;
|
|
36
|
+
infra: {
|
|
37
|
+
anthropicKey: string;
|
|
38
|
+
openaiKey?: string;
|
|
39
|
+
supabaseUrl: string;
|
|
40
|
+
supabaseKey: string;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export declare function scaffoldProject(options: ScaffoldOptions): Promise<string>;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
export async function scaffoldProject(options) {
|
|
5
|
+
const { company, agents, site, provider, tier, mode, websiteUrl, infra } = options;
|
|
6
|
+
const slug = company.name
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
9
|
+
.replace(/^-|-$/g, "");
|
|
10
|
+
const projectDir = path.resolve(process.cwd(), slug);
|
|
11
|
+
if (await fs.pathExists(projectDir)) {
|
|
12
|
+
throw new Error(`Directory "${slug}" already exists`);
|
|
13
|
+
}
|
|
14
|
+
// Copy template
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const templateDir = path.resolve(__dirname, "..", "..", "template");
|
|
17
|
+
if (await fs.pathExists(templateDir)) {
|
|
18
|
+
await fs.copy(templateDir, projectDir);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
await fs.mkdirp(projectDir);
|
|
22
|
+
}
|
|
23
|
+
// Remove infra/ if not enterprise tier
|
|
24
|
+
if (tier !== "enterprise") {
|
|
25
|
+
await fs.remove(path.join(projectDir, "infra"));
|
|
26
|
+
}
|
|
27
|
+
// Remove Docker files if quickstart tier
|
|
28
|
+
if (tier === "quickstart") {
|
|
29
|
+
await fs.remove(path.join(projectDir, "Dockerfile"));
|
|
30
|
+
await fs.remove(path.join(projectDir, "docker-compose.yml"));
|
|
31
|
+
await fs.remove(path.join(projectDir, "railway.toml"));
|
|
32
|
+
}
|
|
33
|
+
// Connect mode: remove public site, keep dashboard + API + widget
|
|
34
|
+
if (mode === "connect") {
|
|
35
|
+
await fs.remove(path.join(projectDir, "app", "(site)"));
|
|
36
|
+
await fs.remove(path.join(projectDir, "components", "sections"));
|
|
37
|
+
}
|
|
38
|
+
// Ensure directory structure
|
|
39
|
+
const dirs = [
|
|
40
|
+
"app/(site)",
|
|
41
|
+
"app/(dashboard)/dashboard",
|
|
42
|
+
"app/(dashboard)/agents",
|
|
43
|
+
"app/(dashboard)/conversations",
|
|
44
|
+
"app/(dashboard)/settings",
|
|
45
|
+
"app/api/chat",
|
|
46
|
+
"app/api/agents",
|
|
47
|
+
"app/login",
|
|
48
|
+
"agents",
|
|
49
|
+
"channels",
|
|
50
|
+
"components/chat",
|
|
51
|
+
"components/dashboard",
|
|
52
|
+
"components/ui",
|
|
53
|
+
"lib/engine",
|
|
54
|
+
"lib/providers",
|
|
55
|
+
"lib/storage",
|
|
56
|
+
"supabase/migrations",
|
|
57
|
+
];
|
|
58
|
+
for (const dir of dirs) {
|
|
59
|
+
await fs.mkdirp(path.join(projectDir, dir));
|
|
60
|
+
}
|
|
61
|
+
// Write tellet.json
|
|
62
|
+
const telletConfig = {
|
|
63
|
+
$schema: "https://tellet.com/schema/v1.json",
|
|
64
|
+
version: "1.0.0",
|
|
65
|
+
company: {
|
|
66
|
+
name: company.name,
|
|
67
|
+
description: company.description,
|
|
68
|
+
industry: company.industry,
|
|
69
|
+
},
|
|
70
|
+
engine: "default",
|
|
71
|
+
llm: {
|
|
72
|
+
provider,
|
|
73
|
+
defaultModel: provider === "openai" ? "gpt-4.1" : "claude-sonnet-4-6",
|
|
74
|
+
fallback: null,
|
|
75
|
+
},
|
|
76
|
+
agents: agents.map((a) => ({
|
|
77
|
+
id: a.id,
|
|
78
|
+
name: a.name,
|
|
79
|
+
role: a.role,
|
|
80
|
+
model: a.model,
|
|
81
|
+
channels: ["web_chat"],
|
|
82
|
+
tools: a.role === "customer_support" ? ["search_knowledge"] : [],
|
|
83
|
+
})),
|
|
84
|
+
tools: {
|
|
85
|
+
search_knowledge: {
|
|
86
|
+
type: "builtin",
|
|
87
|
+
description: "Search company knowledge base",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
channels: {
|
|
91
|
+
web_chat: { enabled: true },
|
|
92
|
+
slack: { enabled: false },
|
|
93
|
+
email: { enabled: false },
|
|
94
|
+
},
|
|
95
|
+
mode,
|
|
96
|
+
storage: "supabase",
|
|
97
|
+
integrations: [],
|
|
98
|
+
...(websiteUrl ? { websiteUrl } : {}),
|
|
99
|
+
...(mode === "new" ? { site } : {}),
|
|
100
|
+
};
|
|
101
|
+
await fs.writeJSON(path.join(projectDir, "tellet.json"), telletConfig, {
|
|
102
|
+
spaces: 2,
|
|
103
|
+
});
|
|
104
|
+
// Write environment file
|
|
105
|
+
const envLines = [
|
|
106
|
+
`# Orchestrator (always Anthropic)`,
|
|
107
|
+
`ANTHROPIC_API_KEY=${infra.anthropicKey}`,
|
|
108
|
+
];
|
|
109
|
+
if (infra.openaiKey) {
|
|
110
|
+
envLines.push(`OPENAI_API_KEY=${infra.openaiKey}`);
|
|
111
|
+
}
|
|
112
|
+
if (tier === "quickstart") {
|
|
113
|
+
envLines.push(`NEXT_PUBLIC_SUPABASE_URL=${infra.supabaseUrl}`, `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=${infra.supabaseKey}`);
|
|
114
|
+
}
|
|
115
|
+
else if (tier === "cloud") {
|
|
116
|
+
envLines.push(`DATABASE_URL=postgresql://tellet:tellet@localhost:5432/tellet`, `# For Railway: use the DATABASE_URL from Railway dashboard`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
envLines.push(`DATABASE_URL=postgresql://tellet:tellet@localhost:5432/tellet`, `# AWS: CDK sets this automatically via Lambda environment`);
|
|
120
|
+
}
|
|
121
|
+
const envFileName = tier === "cloud" ? ".env" : ".env.local";
|
|
122
|
+
await fs.writeFile(path.join(projectDir, envFileName), envLines.join("\n") + "\n");
|
|
123
|
+
// Write agent files
|
|
124
|
+
for (const agent of agents) {
|
|
125
|
+
const agentFile = `import { defineAgent } from "@/lib/engine";
|
|
126
|
+
|
|
127
|
+
export default defineAgent({
|
|
128
|
+
id: "${agent.id}",
|
|
129
|
+
name: "${agent.name}",
|
|
130
|
+
role: "${agent.role}",
|
|
131
|
+
model: "${agent.model}",
|
|
132
|
+
systemPrompt: ${JSON.stringify(agent.systemPrompt)},
|
|
133
|
+
channels: ["web_chat"],
|
|
134
|
+
tools: [],
|
|
135
|
+
});
|
|
136
|
+
`;
|
|
137
|
+
await fs.writeFile(path.join(projectDir, "agents", `${agent.id}.ts`), agentFile);
|
|
138
|
+
}
|
|
139
|
+
// Write agents/index.ts (registry)
|
|
140
|
+
const imports = agents
|
|
141
|
+
.map((a) => `import ${a.id} from "./${a.id}.js";`)
|
|
142
|
+
.join("\n");
|
|
143
|
+
const exports = agents.map((a) => ` ${a.id}`).join(",\n");
|
|
144
|
+
await fs.writeFile(path.join(projectDir, "agents", "index.ts"), `${imports}\n\nexport const agents = {\n${exports},\n};\n`);
|
|
145
|
+
// Write package.json
|
|
146
|
+
await fs.writeJSON(path.join(projectDir, "package.json"), {
|
|
147
|
+
name: slug,
|
|
148
|
+
version: "0.1.0",
|
|
149
|
+
private: true,
|
|
150
|
+
scripts: {
|
|
151
|
+
dev: "next dev",
|
|
152
|
+
build: "next build",
|
|
153
|
+
start: "next start",
|
|
154
|
+
},
|
|
155
|
+
dependencies: {
|
|
156
|
+
next: "^16.2.0",
|
|
157
|
+
react: "^19.2.0",
|
|
158
|
+
"react-dom": "^19.2.0",
|
|
159
|
+
"@anthropic-ai/sdk": "^0.80.0",
|
|
160
|
+
openai: "^6.32.0",
|
|
161
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
162
|
+
"@supabase/supabase-js": "^2.99.0",
|
|
163
|
+
"@supabase/ssr": "^0.9.0",
|
|
164
|
+
tailwindcss: "^4.0.0",
|
|
165
|
+
"@tailwindcss/postcss": "^4.0.0",
|
|
166
|
+
"framer-motion": "^12.0.0",
|
|
167
|
+
clsx: "^2.1.0",
|
|
168
|
+
"tailwind-merge": "^3.0.0",
|
|
169
|
+
},
|
|
170
|
+
devDependencies: {
|
|
171
|
+
typescript: "^5.0.0",
|
|
172
|
+
"@types/node": "^22.0.0",
|
|
173
|
+
"@types/react": "^19.0.0",
|
|
174
|
+
},
|
|
175
|
+
}, { spaces: 2 });
|
|
176
|
+
// Write postcss.config.mjs
|
|
177
|
+
await fs.writeFile(path.join(projectDir, "postcss.config.mjs"), `/** @type {import('postcss-load-config').Config} */\nconst config = {\n plugins: {\n "@tailwindcss/postcss": {},\n },\n};\n\nexport default config;\n`);
|
|
178
|
+
// Write DB migration
|
|
179
|
+
await fs.writeFile(path.join(projectDir, "supabase", "migrations", "001_initial.sql"), generateMigration(agents, company));
|
|
180
|
+
return projectDir;
|
|
181
|
+
}
|
|
182
|
+
function generateMigration(agents, company) {
|
|
183
|
+
const seedValues = agents
|
|
184
|
+
.map((a) => `('${a.id}', '${a.name}', '${a.role}', ${pgEscape(a.systemPrompt)}, '${a.model}', 'active', '{}')`)
|
|
185
|
+
.join(",\n");
|
|
186
|
+
return `-- tellet schema
|
|
187
|
+
|
|
188
|
+
-- Companies (multi-company support)
|
|
189
|
+
create table companies (
|
|
190
|
+
id uuid primary key default gen_random_uuid(),
|
|
191
|
+
name text not null,
|
|
192
|
+
description text,
|
|
193
|
+
industry text,
|
|
194
|
+
config jsonb default '{}',
|
|
195
|
+
created_at timestamptz default now()
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
insert into companies (id, name, description, industry) values
|
|
199
|
+
('00000000-0000-0000-0000-000000000001', ${pgEscape(company.name)}, ${pgEscape(company.description)}, ${pgEscape(company.industry)});
|
|
200
|
+
|
|
201
|
+
-- Knowledge Base (pgvector)
|
|
202
|
+
create extension if not exists vector;
|
|
203
|
+
|
|
204
|
+
create table documents (
|
|
205
|
+
id uuid primary key default gen_random_uuid(),
|
|
206
|
+
company_id uuid references companies(id) default '00000000-0000-0000-0000-000000000001',
|
|
207
|
+
title text not null,
|
|
208
|
+
content text not null,
|
|
209
|
+
embedding vector(1536),
|
|
210
|
+
category text default 'general',
|
|
211
|
+
metadata jsonb default '{}',
|
|
212
|
+
created_at timestamptz default now()
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
create index on documents using ivfflat (embedding vector_cosine_ops) with (lists = 50);
|
|
216
|
+
|
|
217
|
+
create or replace function match_documents(
|
|
218
|
+
query_embedding vector(1536),
|
|
219
|
+
match_count int default 3,
|
|
220
|
+
match_threshold float default 0.5
|
|
221
|
+
)
|
|
222
|
+
returns table (id uuid, title text, content text, category text, similarity float)
|
|
223
|
+
language sql stable
|
|
224
|
+
as $$
|
|
225
|
+
select
|
|
226
|
+
d.id, d.title, d.content, d.category,
|
|
227
|
+
1 - (d.embedding <=> query_embedding) as similarity
|
|
228
|
+
from documents d
|
|
229
|
+
where 1 - (d.embedding <=> query_embedding) > match_threshold
|
|
230
|
+
order by d.embedding <=> query_embedding
|
|
231
|
+
limit match_count;
|
|
232
|
+
$$;
|
|
233
|
+
|
|
234
|
+
-- Core tables
|
|
235
|
+
create table agents (
|
|
236
|
+
id text primary key,
|
|
237
|
+
company_id uuid references companies(id) default '00000000-0000-0000-0000-000000000001',
|
|
238
|
+
name text not null,
|
|
239
|
+
role text not null,
|
|
240
|
+
system_prompt text not null,
|
|
241
|
+
model text default 'claude-sonnet-4-6',
|
|
242
|
+
status text default 'active',
|
|
243
|
+
config jsonb default '{}',
|
|
244
|
+
created_at timestamptz default now()
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
create table conversations (
|
|
248
|
+
id uuid primary key default gen_random_uuid(),
|
|
249
|
+
agent_id text references agents(id),
|
|
250
|
+
channel text not null,
|
|
251
|
+
visitor_id text,
|
|
252
|
+
metadata jsonb default '{}',
|
|
253
|
+
created_at timestamptz default now()
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
create table messages (
|
|
257
|
+
id uuid primary key default gen_random_uuid(),
|
|
258
|
+
conversation_id uuid references conversations(id) on delete cascade,
|
|
259
|
+
role text not null check (role in ('user', 'assistant')),
|
|
260
|
+
content text not null,
|
|
261
|
+
tokens_used integer default 0,
|
|
262
|
+
created_at timestamptz default now()
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
create table activity_log (
|
|
266
|
+
id uuid primary key default gen_random_uuid(),
|
|
267
|
+
agent_id text references agents(id),
|
|
268
|
+
action text not null,
|
|
269
|
+
summary text,
|
|
270
|
+
cost_usd numeric(10,4) default 0,
|
|
271
|
+
metadata jsonb default '{}',
|
|
272
|
+
created_at timestamptz default now()
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
-- Indexes
|
|
276
|
+
create index idx_conv_agent on conversations(agent_id);
|
|
277
|
+
create index idx_msg_conv on messages(conversation_id);
|
|
278
|
+
create index idx_activity_agent on activity_log(agent_id);
|
|
279
|
+
create index idx_activity_time on activity_log(created_at desc);
|
|
280
|
+
|
|
281
|
+
-- RLS
|
|
282
|
+
alter table companies enable row level security;
|
|
283
|
+
alter table documents enable row level security;
|
|
284
|
+
alter table agents enable row level security;
|
|
285
|
+
|
|
286
|
+
create policy "auth_all" on companies for all to authenticated using (true) with check (true);
|
|
287
|
+
create policy "anon_read_companies" on companies for select to anon using (true);
|
|
288
|
+
alter table conversations enable row level security;
|
|
289
|
+
alter table messages enable row level security;
|
|
290
|
+
alter table activity_log enable row level security;
|
|
291
|
+
|
|
292
|
+
create policy "auth_all" on documents for all to authenticated using (true) with check (true);
|
|
293
|
+
create policy "anon_read_docs" on documents for select to anon using (true);
|
|
294
|
+
|
|
295
|
+
create policy "auth_all" on agents for all to authenticated using (true) with check (true);
|
|
296
|
+
create policy "auth_all" on conversations for all to authenticated using (true) with check (true);
|
|
297
|
+
create policy "auth_all" on messages for all to authenticated using (true) with check (true);
|
|
298
|
+
create policy "auth_all" on activity_log for all to authenticated using (true) with check (true);
|
|
299
|
+
|
|
300
|
+
create policy "anon_read_agents" on agents for select to anon using (status = 'active');
|
|
301
|
+
create policy "anon_insert_conv" on conversations for insert to anon with check (channel = 'web_chat');
|
|
302
|
+
create policy "anon_insert_msg" on messages for insert to anon with check (true);
|
|
303
|
+
create policy "anon_read_msg" on messages for select to anon using (true);
|
|
304
|
+
create policy "anon_read_conv" on conversations for select to anon using (true);
|
|
305
|
+
create policy "anon_read_activity" on activity_log for select to anon using (true);
|
|
306
|
+
|
|
307
|
+
-- Realtime
|
|
308
|
+
alter publication supabase_realtime add table activity_log;
|
|
309
|
+
alter publication supabase_realtime add table messages;
|
|
310
|
+
|
|
311
|
+
-- Seed agents
|
|
312
|
+
insert into agents (id, name, role, system_prompt, model, status, config) values
|
|
313
|
+
${seedValues};
|
|
314
|
+
`;
|
|
315
|
+
}
|
|
316
|
+
function pgEscape(str) {
|
|
317
|
+
return "'" + str.replace(/'/g, "''") + "'";
|
|
318
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tellet/create",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "The open-source platform for running an Agentic Company",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ai",
|
|
20
|
+
"agent",
|
|
21
|
+
"agentic-company",
|
|
22
|
+
"tellet",
|
|
23
|
+
"cli",
|
|
24
|
+
"orchestrator",
|
|
25
|
+
"mcp",
|
|
26
|
+
"knowledge-base",
|
|
27
|
+
"ai-agents",
|
|
28
|
+
"no-code"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/agentic-company/create-tellet"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@anthropic-ai/sdk": "^0.80.0",
|
|
37
|
+
"@clack/prompts": "^1.1.0",
|
|
38
|
+
"chalk": "^5.6.2",
|
|
39
|
+
"fs-extra": "^11.3.4",
|
|
40
|
+
"openai": "^6.32.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/fs-extra": "^11.0.4",
|
|
44
|
+
"@types/node": "^25.5.0",
|
|
45
|
+
"tsx": "^4.21.0",
|
|
46
|
+
"typescript": "^5.9.3"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
FROM node:22-alpine AS base
|
|
2
|
+
|
|
3
|
+
# Stage 1: Install dependencies
|
|
4
|
+
FROM base AS deps
|
|
5
|
+
WORKDIR /app
|
|
6
|
+
COPY package.json package-lock.json* ./
|
|
7
|
+
RUN npm ci --omit=dev
|
|
8
|
+
|
|
9
|
+
# Stage 2: Build
|
|
10
|
+
FROM base AS builder
|
|
11
|
+
WORKDIR /app
|
|
12
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
13
|
+
COPY . .
|
|
14
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
15
|
+
RUN npm run build
|
|
16
|
+
|
|
17
|
+
# Stage 3: Production
|
|
18
|
+
FROM base AS runner
|
|
19
|
+
WORKDIR /app
|
|
20
|
+
ENV NODE_ENV=production
|
|
21
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
22
|
+
|
|
23
|
+
RUN addgroup --system --gid 1001 nodejs
|
|
24
|
+
RUN adduser --system --uid 1001 nextjs
|
|
25
|
+
|
|
26
|
+
COPY --from=builder /app/public ./public
|
|
27
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
28
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
29
|
+
|
|
30
|
+
USER nextjs
|
|
31
|
+
EXPOSE 3000
|
|
32
|
+
ENV PORT=3000
|
|
33
|
+
ENV HOSTNAME="0.0.0.0"
|
|
34
|
+
|
|
35
|
+
CMD ["node", "server.js"]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createServerSupabase } from "@/lib/supabase";
|
|
2
|
+
import { AgentsListClient } from "@/components/dashboard/AgentsListClient";
|
|
3
|
+
|
|
4
|
+
export default async function AgentsPage() {
|
|
5
|
+
const supabase = await createServerSupabase();
|
|
6
|
+
const { data: agents } = await supabase.from("agents").select("*").order("created_at");
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div className="space-y-6">
|
|
10
|
+
<h1 className="text-2xl font-semibold tracking-tight">Agents</h1>
|
|
11
|
+
<AgentsListClient agents={agents || []} />
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { createServerSupabase } from "@/lib/supabase";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
export default async function ConversationDetailPage({
|
|
6
|
+
params,
|
|
7
|
+
}: {
|
|
8
|
+
params: Promise<{ id: string }>;
|
|
9
|
+
}) {
|
|
10
|
+
const { id } = await params;
|
|
11
|
+
const supabase = await createServerSupabase();
|
|
12
|
+
|
|
13
|
+
const { data: conversation } = await supabase
|
|
14
|
+
.from("conversations")
|
|
15
|
+
.select("*, agents(name, role)")
|
|
16
|
+
.eq("id", id)
|
|
17
|
+
.single();
|
|
18
|
+
|
|
19
|
+
if (!conversation) {
|
|
20
|
+
return (
|
|
21
|
+
<div className="space-y-6">
|
|
22
|
+
<Link
|
|
23
|
+
href="/conversations"
|
|
24
|
+
className="text-sm text-text-tertiary hover:text-text-secondary transition-colors"
|
|
25
|
+
>
|
|
26
|
+
← Back to conversations
|
|
27
|
+
</Link>
|
|
28
|
+
<div className="rounded-lg border border-dashed border-border bg-bg-secondary/20 p-8 text-center">
|
|
29
|
+
<p className="text-text-secondary text-sm">Conversation not found.</p>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { data: messages } = await supabase
|
|
36
|
+
.from("messages")
|
|
37
|
+
.select("*")
|
|
38
|
+
.eq("conversation_id", id)
|
|
39
|
+
.order("created_at");
|
|
40
|
+
|
|
41
|
+
const agent = conversation.agents as { name: string; role: string };
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="space-y-6">
|
|
45
|
+
<Link
|
|
46
|
+
href="/conversations"
|
|
47
|
+
className="text-sm text-text-tertiary hover:text-text-secondary transition-colors"
|
|
48
|
+
>
|
|
49
|
+
← Back to conversations
|
|
50
|
+
</Link>
|
|
51
|
+
|
|
52
|
+
<div className="flex items-center gap-3">
|
|
53
|
+
<div className="w-10 h-10 rounded-full bg-accent/10 text-accent text-sm font-bold flex items-center justify-center">
|
|
54
|
+
{agent.name[0]}
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
<h1 className="text-xl font-semibold">{agent.name}</h1>
|
|
58
|
+
<p className="text-xs text-text-tertiary capitalize">
|
|
59
|
+
{agent.role.replace("_", " ")} · {conversation.channel} ·{" "}
|
|
60
|
+
{new Date(conversation.created_at).toLocaleString()}
|
|
61
|
+
</p>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div className="space-y-3 max-w-3xl">
|
|
66
|
+
{messages && messages.length > 0 ? (
|
|
67
|
+
messages.map((m) => (
|
|
68
|
+
<div
|
|
69
|
+
key={m.id}
|
|
70
|
+
className={cn(
|
|
71
|
+
"flex",
|
|
72
|
+
m.role === "user" ? "justify-end" : "justify-start"
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
<div
|
|
76
|
+
className={cn(
|
|
77
|
+
"rounded-xl px-4 py-3 max-w-[80%] text-sm leading-relaxed",
|
|
78
|
+
m.role === "user"
|
|
79
|
+
? "bg-accent text-white"
|
|
80
|
+
: "bg-bg-secondary text-text-primary border border-border"
|
|
81
|
+
)}
|
|
82
|
+
>
|
|
83
|
+
<p className="whitespace-pre-wrap">{m.content}</p>
|
|
84
|
+
<p
|
|
85
|
+
className={cn(
|
|
86
|
+
"text-[11px] mt-1",
|
|
87
|
+
m.role === "user" ? "text-white/60" : "text-text-tertiary"
|
|
88
|
+
)}
|
|
89
|
+
>
|
|
90
|
+
{new Date(m.created_at).toLocaleTimeString()}
|
|
91
|
+
</p>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
))
|
|
95
|
+
) : (
|
|
96
|
+
<div className="rounded-lg border border-dashed border-border bg-bg-secondary/20 p-8 text-center">
|
|
97
|
+
<p className="text-text-secondary text-sm">No messages in this conversation.</p>
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
2
|
+
import { createServerSupabase } from "@/lib/supabase";
|
|
3
|
+
|
|
4
|
+
export default async function ConversationsPage() {
|
|
5
|
+
const supabase = await createServerSupabase();
|
|
6
|
+
const { data: conversations } = await supabase
|
|
7
|
+
.from("conversations")
|
|
8
|
+
.select("*, agents(name), messages(count)")
|
|
9
|
+
.order("created_at", { ascending: false })
|
|
10
|
+
.limit(50);
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="space-y-6">
|
|
14
|
+
<h1 className="text-2xl font-semibold tracking-tight">Conversations</h1>
|
|
15
|
+
{conversations && conversations.length > 0 ? (
|
|
16
|
+
<div className="space-y-2">
|
|
17
|
+
{conversations.map((c) => (
|
|
18
|
+
<Link
|
|
19
|
+
key={c.id}
|
|
20
|
+
href={`/conversations/${c.id}`}
|
|
21
|
+
className="flex items-center gap-4 rounded-xl border border-border bg-bg-secondary/50 p-4 hover:border-border-hover transition-colors"
|
|
22
|
+
>
|
|
23
|
+
<div className="flex-1">
|
|
24
|
+
<span className="font-medium text-sm">
|
|
25
|
+
{(c.agents as { name: string })?.name}
|
|
26
|
+
</span>
|
|
27
|
+
<span className="text-xs text-text-tertiary ml-2">
|
|
28
|
+
via {c.channel}
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
<span className="text-xs text-text-secondary">
|
|
32
|
+
{(c.messages as { count: number }[])?.[0]?.count || 0} messages
|
|
33
|
+
</span>
|
|
34
|
+
<span className="text-xs text-text-tertiary">
|
|
35
|
+
{new Date(c.created_at).toLocaleString()}
|
|
36
|
+
</span>
|
|
37
|
+
</Link>
|
|
38
|
+
))}
|
|
39
|
+
</div>
|
|
40
|
+
) : (
|
|
41
|
+
<div className="rounded-lg border border-dashed border-border bg-bg-secondary/20 p-8 text-center">
|
|
42
|
+
<p className="text-text-secondary text-sm">No conversations yet.</p>
|
|
43
|
+
<p className="text-text-tertiary text-xs mt-2">
|
|
44
|
+
Start chatting with an agent from the Dashboard or your public site.
|
|
45
|
+
</p>
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|