@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.
Files changed (49) hide show
  1. package/README.md +116 -0
  2. package/dist/api.js +62 -0
  3. package/dist/api.js.map +1 -0
  4. package/dist/commands/connect.js +71 -0
  5. package/dist/commands/connect.js.map +1 -0
  6. package/dist/commands/execute.js +62 -0
  7. package/dist/commands/execute.js.map +1 -0
  8. package/dist/commands/init.js +130 -0
  9. package/dist/commands/init.js.map +1 -0
  10. package/dist/commands/login.js +55 -0
  11. package/dist/commands/login.js.map +1 -0
  12. package/dist/commands/logs.js +74 -0
  13. package/dist/commands/logs.js.map +1 -0
  14. package/dist/commands/servers.js +48 -0
  15. package/dist/commands/servers.js.map +1 -0
  16. package/dist/commands/sessions.js +62 -0
  17. package/dist/commands/sessions.js.map +1 -0
  18. package/dist/commands/tools.js +43 -0
  19. package/dist/commands/tools.js.map +1 -0
  20. package/dist/config.js +57 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/index.js +210 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/output.js +54 -0
  25. package/dist/output.js.map +1 -0
  26. package/package.json +55 -0
  27. package/templates/ecommerce-checkout/.env.example +2 -0
  28. package/templates/ecommerce-checkout/README.md +22 -0
  29. package/templates/ecommerce-checkout/package.json +24 -0
  30. package/templates/ecommerce-checkout/src/index.ts +73 -0
  31. package/templates/ecommerce-checkout/tsconfig.json +15 -0
  32. package/templates/multi-tenant/.env.example +2 -0
  33. package/templates/multi-tenant/README.md +31 -0
  34. package/templates/multi-tenant/app/api/commerce/[tenantId]/route.ts +55 -0
  35. package/templates/multi-tenant/lib/commerce.ts +24 -0
  36. package/templates/multi-tenant/package.json +26 -0
  37. package/templates/multi-tenant/tsconfig.json +21 -0
  38. package/templates/pix-agent/.env.example +2 -0
  39. package/templates/pix-agent/README.md +22 -0
  40. package/templates/pix-agent/package.json +24 -0
  41. package/templates/pix-agent/src/index.ts +66 -0
  42. package/templates/pix-agent/tsconfig.json +15 -0
  43. package/templates/streaming-chat/.env.example +2 -0
  44. package/templates/streaming-chat/README.md +23 -0
  45. package/templates/streaming-chat/app/api/chat/route.ts +29 -0
  46. package/templates/streaming-chat/app/layout.tsx +12 -0
  47. package/templates/streaming-chat/app/page.tsx +66 -0
  48. package/templates/streaming-chat/package.json +29 -0
  49. 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,2 @@
1
+ CODESPAR_API_KEY=csk_live_...
2
+ OPENAI_API_KEY=sk-...
@@ -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,2 @@
1
+ CODESPAR_API_KEY=csk_live_...
2
+ OPENAI_API_KEY=sk-...
@@ -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,2 @@
1
+ CODESPAR_API_KEY=csk_live_...
2
+ OPENAI_API_KEY=sk-...
@@ -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
+ }