@codespar/cli 0.2.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 +116 -0
- package/dist/api.js +62 -0
- package/dist/api.js.map +1 -0
- package/dist/commands/connect.js +71 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/execute.js +62 -0
- package/dist/commands/execute.js.map +1 -0
- package/dist/commands/init.js +130 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.js +55 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logs.js +74 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/servers.js +48 -0
- package/dist/commands/servers.js.map +1 -0
- package/dist/commands/sessions.js +62 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/tools.js +43 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/index.js +210 -0
- package/dist/index.js.map +1 -0
- package/dist/output.js +54 -0
- package/dist/output.js.map +1 -0
- package/package.json +55 -0
- package/templates/ecommerce-checkout/.env.example +2 -0
- package/templates/ecommerce-checkout/README.md +22 -0
- package/templates/ecommerce-checkout/package.json +24 -0
- package/templates/ecommerce-checkout/src/index.ts +73 -0
- package/templates/ecommerce-checkout/tsconfig.json +15 -0
- package/templates/multi-tenant/.env.example +2 -0
- package/templates/multi-tenant/README.md +31 -0
- package/templates/multi-tenant/app/api/commerce/[tenantId]/route.ts +55 -0
- package/templates/multi-tenant/lib/commerce.ts +24 -0
- package/templates/multi-tenant/package.json +26 -0
- package/templates/multi-tenant/tsconfig.json +21 -0
- package/templates/pix-agent/.env.example +2 -0
- package/templates/pix-agent/README.md +22 -0
- package/templates/pix-agent/package.json +24 -0
- package/templates/pix-agent/src/index.ts +66 -0
- package/templates/pix-agent/tsconfig.json +15 -0
- package/templates/streaming-chat/.env.example +2 -0
- package/templates/streaming-chat/README.md +23 -0
- package/templates/streaming-chat/app/api/chat/route.ts +29 -0
- package/templates/streaming-chat/app/layout.tsx +12 -0
- package/templates/streaming-chat/app/page.tsx +66 -0
- package/templates/streaming-chat/package.json +29 -0
- package/templates/streaming-chat/tsconfig.json +21 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{name}} — End-to-end e-commerce checkout agent.
|
|
3
|
+
*
|
|
4
|
+
* Full Complete Loop: checkout → invoice → ship → notify, in one
|
|
5
|
+
* conversation, powered by Claude.
|
|
6
|
+
*/
|
|
7
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
8
|
+
import { CodeSpar } from "@codespar/sdk";
|
|
9
|
+
import { getTools, handleToolUse, toToolResultBlock } from "@codespar/claude";
|
|
10
|
+
|
|
11
|
+
const claude = new Anthropic();
|
|
12
|
+
const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });
|
|
13
|
+
|
|
14
|
+
export async function checkoutAgent(userMessage: string): Promise<string> {
|
|
15
|
+
const session = await codespar.create("cli-user", {
|
|
16
|
+
servers: ["stripe", "nuvem-fiscal", "correios"],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const tools = await getTools(session);
|
|
21
|
+
|
|
22
|
+
const messages: Anthropic.MessageParam[] = [{ role: "user", content: userMessage }];
|
|
23
|
+
|
|
24
|
+
let response = await claude.messages.create({
|
|
25
|
+
model: "claude-opus-4-6",
|
|
26
|
+
max_tokens: 4096,
|
|
27
|
+
system: `You are a commerce assistant for a Brazilian e-commerce store.
|
|
28
|
+
|
|
29
|
+
Products:
|
|
30
|
+
- Pro Plan: R$49.90/month
|
|
31
|
+
- Enterprise Plan: R$199.90/month
|
|
32
|
+
- Starter Kit: R$149.00 (one-time)
|
|
33
|
+
|
|
34
|
+
Flow: confirm → checkout → invoice → ship → notify. Respond in Portuguese.`,
|
|
35
|
+
tools,
|
|
36
|
+
messages,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let iterations = 0;
|
|
40
|
+
while (response.stop_reason === "tool_use" && iterations < 10) {
|
|
41
|
+
const toolBlocks = response.content.filter(
|
|
42
|
+
(b): b is Anthropic.ToolUseBlock => b.type === "tool_use",
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const resultBlocks = [];
|
|
46
|
+
for (const block of toolBlocks) {
|
|
47
|
+
const result = await handleToolUse(session, block);
|
|
48
|
+
resultBlocks.push(toToolResultBlock(block.id, result));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
messages.push({ role: "assistant", content: response.content });
|
|
52
|
+
messages.push({ role: "user", content: resultBlocks });
|
|
53
|
+
|
|
54
|
+
response = await claude.messages.create({
|
|
55
|
+
model: "claude-opus-4-6",
|
|
56
|
+
max_tokens: 4096,
|
|
57
|
+
tools,
|
|
58
|
+
messages,
|
|
59
|
+
});
|
|
60
|
+
iterations += 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const text = response.content.find((b): b is Anthropic.TextBlock => b.type === "text");
|
|
64
|
+
return text?.text ?? "";
|
|
65
|
+
} finally {
|
|
66
|
+
await session.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
71
|
+
const reply = await checkoutAgent("Quero comprar o Starter Kit. Meu CEP é 01310-100.");
|
|
72
|
+
console.log(reply);
|
|
73
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
Multi-tenant SaaS commerce agent scaffolded by `codespar init`.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cp .env.example .env
|
|
9
|
+
# Fill in CODESPAR_API_KEY and OPENAI_API_KEY
|
|
10
|
+
|
|
11
|
+
npm install
|
|
12
|
+
npm run dev
|
|
13
|
+
# → http://localhost:3000
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
One codespar API key, N tenants. Each tenant has its own session, servers, and credentials. Every tool call is tagged with `metadata.tenant_id` for per-tenant billing.
|
|
19
|
+
|
|
20
|
+
Try it:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
curl -X POST http://localhost:3000/api/commerce/tenant_acme \
|
|
24
|
+
-H "Content-Type: application/json" \
|
|
25
|
+
-d '{"message":"Create a Pix charge of R$50"}'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Learn more
|
|
29
|
+
|
|
30
|
+
- [Multi-Tenant Agent cookbook](https://codespar.dev/docs/cookbooks/multi-tenant)
|
|
31
|
+
- [Sessions reference](https://codespar.dev/docs/concepts/sessions)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
import { getTools, handleToolCall } from "@codespar/openai";
|
|
4
|
+
import { createTenantSession } from "@/lib/commerce";
|
|
5
|
+
|
|
6
|
+
const openai = new OpenAI();
|
|
7
|
+
|
|
8
|
+
// In production, fetch tenant config from your database instead.
|
|
9
|
+
const TENANTS: Record<string, { servers: string[] }> = {
|
|
10
|
+
tenant_acme: { servers: ["stripe", "correios", "nuvem-fiscal"] },
|
|
11
|
+
tenant_loja: { servers: ["asaas", "melhor-envio", "z-api"] },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export async function POST(
|
|
15
|
+
req: Request,
|
|
16
|
+
{ params }: { params: Promise<{ tenantId: string }> },
|
|
17
|
+
) {
|
|
18
|
+
const { tenantId } = await params;
|
|
19
|
+
const tenant = TENANTS[tenantId];
|
|
20
|
+
if (!tenant) return NextResponse.json({ error: "Tenant not found" }, { status: 404 });
|
|
21
|
+
|
|
22
|
+
const { message } = await req.json();
|
|
23
|
+
|
|
24
|
+
const session = await createTenantSession({
|
|
25
|
+
id: tenantId,
|
|
26
|
+
servers: tenant.servers,
|
|
27
|
+
metadata: { source: "api" },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const tools = await getTools(session);
|
|
32
|
+
const messages: OpenAI.ChatCompletionMessageParam[] = [
|
|
33
|
+
{ role: "system", content: `You are a commerce assistant for tenant ${tenantId}.` },
|
|
34
|
+
{ role: "user", content: message },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
let response = await openai.chat.completions.create({ model: "gpt-4o", tools, messages });
|
|
38
|
+
let iterations = 0;
|
|
39
|
+
|
|
40
|
+
while (response.choices[0].message.tool_calls?.length && iterations < 10) {
|
|
41
|
+
const msg = response.choices[0].message;
|
|
42
|
+
messages.push(msg);
|
|
43
|
+
for (const toolCall of msg.tool_calls) {
|
|
44
|
+
const result = await handleToolCall(session, toolCall);
|
|
45
|
+
messages.push({ role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(result) });
|
|
46
|
+
}
|
|
47
|
+
response = await openai.chat.completions.create({ model: "gpt-4o", tools, messages });
|
|
48
|
+
iterations += 1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return NextResponse.json({ reply: response.choices[0].message.content });
|
|
52
|
+
} finally {
|
|
53
|
+
await session.close();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CodeSpar } from "@codespar/sdk";
|
|
2
|
+
|
|
3
|
+
const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY! });
|
|
4
|
+
|
|
5
|
+
export interface TenantConfig {
|
|
6
|
+
id: string;
|
|
7
|
+
servers: string[];
|
|
8
|
+
metadata?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Wraps sessions.create() to inject tenant_id metadata on every session,
|
|
13
|
+
* so tool-call logs are automatically attributable per tenant — no extra
|
|
14
|
+
* code needed at call sites.
|
|
15
|
+
*/
|
|
16
|
+
export async function createTenantSession(tenant: TenantConfig) {
|
|
17
|
+
return codespar.create(tenant.id, {
|
|
18
|
+
servers: tenant.servers,
|
|
19
|
+
metadata: {
|
|
20
|
+
tenant_id: tenant.id,
|
|
21
|
+
...(tenant.metadata ?? {}),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@codespar/sdk": "^0.2.0",
|
|
12
|
+
"@codespar/openai": "^0.2.0",
|
|
13
|
+
"openai": "^4.0.0",
|
|
14
|
+
"next": "^15.0.0",
|
|
15
|
+
"react": "^19.0.0",
|
|
16
|
+
"react-dom": "^19.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^22.0.0",
|
|
20
|
+
"@types/react": "^19.0.0",
|
|
21
|
+
"typescript": "^5.0.0"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"paths": { "@/*": ["./*"] },
|
|
17
|
+
"plugins": [{ "name": "next" }]
|
|
18
|
+
},
|
|
19
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
|
20
|
+
"exclude": ["node_modules"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
Pix payment agent scaffolded by `codespar init`.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cp .env.example .env
|
|
9
|
+
# Fill in CODESPAR_API_KEY and OPENAI_API_KEY
|
|
10
|
+
|
|
11
|
+
npm install
|
|
12
|
+
npm run dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
Creates a Pix charge on Asaas and sends the QR code to a customer's WhatsApp via Twilio. See `src/index.ts` for the agent loop.
|
|
18
|
+
|
|
19
|
+
## Learn more
|
|
20
|
+
|
|
21
|
+
- [Pix Payment Agent cookbook](https://codespar.dev/docs/cookbooks/pix-payment-agent)
|
|
22
|
+
- [OpenAI provider reference](https://codespar.dev/docs/providers/openai)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx src/index.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@codespar/sdk": "^0.2.0",
|
|
13
|
+
"@codespar/openai": "^0.2.0",
|
|
14
|
+
"openai": "^4.0.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^22.0.0",
|
|
18
|
+
"tsx": "^4.0.0",
|
|
19
|
+
"typescript": "^5.0.0"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{name}} — Pix payment agent.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* pnpm dev (or: npm run dev)
|
|
6
|
+
* pixAgent("+5511999887766", 15000, "Pro Plan")
|
|
7
|
+
*/
|
|
8
|
+
import OpenAI from "openai";
|
|
9
|
+
import { CodeSpar } from "@codespar/sdk";
|
|
10
|
+
import { getTools, handleToolCall } from "@codespar/openai";
|
|
11
|
+
|
|
12
|
+
const openai = new OpenAI();
|
|
13
|
+
const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY });
|
|
14
|
+
|
|
15
|
+
export async function pixAgent(
|
|
16
|
+
customerPhone: string,
|
|
17
|
+
amount: number,
|
|
18
|
+
description: string,
|
|
19
|
+
): Promise<string | null> {
|
|
20
|
+
const session = await codespar.create("cli-user", { servers: ["asaas", "twilio"] });
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const tools = await getTools(session);
|
|
24
|
+
|
|
25
|
+
const messages: OpenAI.ChatCompletionMessageParam[] = [
|
|
26
|
+
{
|
|
27
|
+
role: "system",
|
|
28
|
+
content: `You are a payment assistant for a Brazilian e-commerce store.
|
|
29
|
+
When asked to create a Pix payment:
|
|
30
|
+
1. Use codespar_pay to create the Pix charge on asaas
|
|
31
|
+
2. Use codespar_notify to send the QR code link via WhatsApp on twilio
|
|
32
|
+
Always confirm amount and phone before processing. Respond in Portuguese.`,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
role: "user",
|
|
36
|
+
content: `Create Pix of R$${(amount / 100).toFixed(2)} for "${description}", send to ${customerPhone}`,
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
let response = await openai.chat.completions.create({ model: "gpt-4o", tools, messages });
|
|
41
|
+
let iterations = 0;
|
|
42
|
+
|
|
43
|
+
while (response.choices[0].message.tool_calls?.length && iterations < 10) {
|
|
44
|
+
const msg = response.choices[0].message;
|
|
45
|
+
messages.push(msg);
|
|
46
|
+
|
|
47
|
+
for (const toolCall of msg.tool_calls) {
|
|
48
|
+
const result = await handleToolCall(session, toolCall);
|
|
49
|
+
messages.push({ role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(result) });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
response = await openai.chat.completions.create({ model: "gpt-4o", tools, messages });
|
|
53
|
+
iterations += 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return response.choices[0].message.content;
|
|
57
|
+
} finally {
|
|
58
|
+
await session.close();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Demo run when executed directly
|
|
63
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
64
|
+
const reply = await pixAgent("+5511999887766", 15000, "Pro Plan");
|
|
65
|
+
console.log(reply);
|
|
66
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
Real-time streaming commerce chat scaffolded by `codespar init`.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cp .env.example .env
|
|
9
|
+
# Fill in CODESPAR_API_KEY and OPENAI_API_KEY
|
|
10
|
+
|
|
11
|
+
npm install
|
|
12
|
+
npm run dev
|
|
13
|
+
# → http://localhost:3000
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
Next.js 15 App Router + Vercel AI SDK + CodeSpar. Token-by-token streaming chat where tool calls happen automatically and appear live in the UI.
|
|
19
|
+
|
|
20
|
+
## Learn more
|
|
21
|
+
|
|
22
|
+
- [Streaming Chat cookbook](https://codespar.dev/docs/cookbooks/streaming-chat)
|
|
23
|
+
- [Vercel AI SDK provider reference](https://codespar.dev/docs/providers/vercel)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { streamText } from "ai";
|
|
2
|
+
import { openai } from "@ai-sdk/openai";
|
|
3
|
+
import { CodeSpar } from "@codespar/sdk";
|
|
4
|
+
import { getTools } from "@codespar/vercel";
|
|
5
|
+
|
|
6
|
+
const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY! });
|
|
7
|
+
|
|
8
|
+
export async function POST(req: Request) {
|
|
9
|
+
const { messages } = await req.json();
|
|
10
|
+
|
|
11
|
+
const session = await codespar.create("cli-user", {
|
|
12
|
+
servers: ["stripe", "asaas", "correios"],
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const tools = await getTools(session);
|
|
16
|
+
|
|
17
|
+
const result = streamText({
|
|
18
|
+
model: openai("gpt-4o"),
|
|
19
|
+
tools,
|
|
20
|
+
maxSteps: 10,
|
|
21
|
+
system: "Commerce assistant for a Brazilian store. Be concise and respond in Portuguese when asked.",
|
|
22
|
+
messages,
|
|
23
|
+
onFinish: async () => {
|
|
24
|
+
await session.close();
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return result.toDataStreamResponse();
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const metadata = {
|
|
2
|
+
title: "{{name}}",
|
|
3
|
+
description: "Streaming commerce chat powered by CodeSpar + Vercel AI SDK",
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
7
|
+
return (
|
|
8
|
+
<html lang="en">
|
|
9
|
+
<body>{children}</body>
|
|
10
|
+
</html>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useChat } from "@ai-sdk/react";
|
|
4
|
+
|
|
5
|
+
export default function Chat() {
|
|
6
|
+
const { messages, input, handleInputChange, handleSubmit } = useChat({ api: "/api/chat" });
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<main style={{ maxWidth: 720, margin: "40px auto", fontFamily: "system-ui", padding: "0 20px" }}>
|
|
10
|
+
<h1 style={{ fontSize: 24, marginBottom: 24 }}>{{name}}</h1>
|
|
11
|
+
|
|
12
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 12, minHeight: 400 }}>
|
|
13
|
+
{messages.map((m) => (
|
|
14
|
+
<div
|
|
15
|
+
key={m.id}
|
|
16
|
+
style={{
|
|
17
|
+
padding: "10px 14px",
|
|
18
|
+
borderRadius: 8,
|
|
19
|
+
background: m.role === "user" ? "#EFF6FF" : "#F4F4F5",
|
|
20
|
+
alignSelf: m.role === "user" ? "flex-end" : "flex-start",
|
|
21
|
+
maxWidth: "80%",
|
|
22
|
+
}}
|
|
23
|
+
>
|
|
24
|
+
{m.content}
|
|
25
|
+
{m.toolInvocations?.map((tool) => (
|
|
26
|
+
<div key={tool.toolCallId} style={{ marginTop: 6, fontSize: 12, color: "#6B7280" }}>
|
|
27
|
+
{tool.state === "result"
|
|
28
|
+
? `✓ ${tool.toolName} completed`
|
|
29
|
+
: `⏳ Calling ${tool.toolName}...`}
|
|
30
|
+
</div>
|
|
31
|
+
))}
|
|
32
|
+
</div>
|
|
33
|
+
))}
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<form onSubmit={handleSubmit} style={{ marginTop: 24, display: "flex", gap: 8 }}>
|
|
37
|
+
<input
|
|
38
|
+
value={input}
|
|
39
|
+
onChange={handleInputChange}
|
|
40
|
+
placeholder="Ask about payments, shipping, invoices..."
|
|
41
|
+
style={{
|
|
42
|
+
flex: 1,
|
|
43
|
+
padding: "10px 14px",
|
|
44
|
+
borderRadius: 8,
|
|
45
|
+
border: "1px solid #E5E7EB",
|
|
46
|
+
fontSize: 14,
|
|
47
|
+
}}
|
|
48
|
+
/>
|
|
49
|
+
<button
|
|
50
|
+
type="submit"
|
|
51
|
+
style={{
|
|
52
|
+
padding: "10px 18px",
|
|
53
|
+
borderRadius: 8,
|
|
54
|
+
background: "#3B82F6",
|
|
55
|
+
color: "white",
|
|
56
|
+
border: "none",
|
|
57
|
+
fontWeight: 600,
|
|
58
|
+
cursor: "pointer",
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
Send
|
|
62
|
+
</button>
|
|
63
|
+
</form>
|
|
64
|
+
</main>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@codespar/sdk": "^0.2.0",
|
|
12
|
+
"@codespar/vercel": "^0.2.0",
|
|
13
|
+
"@ai-sdk/openai": "^1.0.0",
|
|
14
|
+
"@ai-sdk/react": "^1.0.0",
|
|
15
|
+
"ai": "^4.0.0",
|
|
16
|
+
"next": "^15.0.0",
|
|
17
|
+
"react": "^19.0.0",
|
|
18
|
+
"react-dom": "^19.0.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^22.0.0",
|
|
22
|
+
"@types/react": "^19.0.0",
|
|
23
|
+
"@types/react-dom": "^19.0.0",
|
|
24
|
+
"typescript": "^5.0.0"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"paths": { "@/*": ["./*"] },
|
|
17
|
+
"plugins": [{ "name": "next" }]
|
|
18
|
+
},
|
|
19
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
|
20
|
+
"exclude": ["node_modules"]
|
|
21
|
+
}
|