@voltx/cli 0.2.0 → 0.3.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 (45) hide show
  1. package/README.md +65 -0
  2. package/dist/build.d.mts +19 -0
  3. package/dist/build.d.ts +19 -0
  4. package/dist/build.js +145 -0
  5. package/dist/build.mjs +7 -0
  6. package/dist/chunk-AONHLE42.mjs +141 -0
  7. package/dist/chunk-BIE3F5AW.mjs +261 -0
  8. package/dist/chunk-BLBAHJXR.mjs +239 -0
  9. package/dist/chunk-IGBR4TFT.mjs +351 -0
  10. package/dist/chunk-IV352HZA.mjs +107 -0
  11. package/dist/chunk-KFHPTRKZ.mjs +405 -0
  12. package/dist/chunk-L3247M3A.mjs +81 -0
  13. package/dist/chunk-RN7BUALR.mjs +120 -0
  14. package/dist/chunk-SGL7RBD5.mjs +355 -0
  15. package/dist/chunk-TUZ3MHD4.mjs +355 -0
  16. package/dist/chunk-Y6FXYEAI.mjs +10 -0
  17. package/dist/chunk-ZA7EJWJI.mjs +221 -0
  18. package/dist/chunk-ZB2F3WTS.mjs +121 -0
  19. package/dist/chunk-ZLIPYI22.mjs +350 -0
  20. package/dist/cli.js +945 -59
  21. package/dist/cli.mjs +123 -23
  22. package/dist/create.d.mts +1 -0
  23. package/dist/create.d.ts +1 -0
  24. package/dist/create.js +308 -36
  25. package/dist/create.mjs +3 -2
  26. package/dist/dev.d.mts +19 -0
  27. package/dist/dev.d.ts +19 -0
  28. package/dist/dev.js +144 -0
  29. package/dist/dev.mjs +7 -0
  30. package/dist/generate.d.mts +13 -0
  31. package/dist/generate.d.ts +13 -0
  32. package/dist/generate.js +165 -0
  33. package/dist/generate.mjs +7 -0
  34. package/dist/index.d.mts +5 -1
  35. package/dist/index.d.ts +5 -1
  36. package/dist/index.js +770 -39
  37. package/dist/index.mjs +21 -4
  38. package/dist/start.d.mts +16 -0
  39. package/dist/start.d.ts +16 -0
  40. package/dist/start.js +105 -0
  41. package/dist/start.mjs +7 -0
  42. package/dist/welcome.js +1 -1
  43. package/dist/welcome.mjs +2 -1
  44. package/package.json +24 -22
  45. package/LICENSE +0 -21
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ <p align="center">
2
+ <strong>@voltx/cli</strong><br/>
3
+ <em>CLI tools for the VoltX framework</em>
4
+ </p>
5
+
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/@voltx/cli"><img src="https://img.shields.io/npm/v/@voltx/cli?color=blue" alt="npm" /></a>
8
+ <a href="https://www.npmjs.com/package/@voltx/cli"><img src="https://img.shields.io/npm/dm/@voltx/cli" alt="downloads" /></a>
9
+ <a href="https://github.com/codewithshail/voltx/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@voltx/cli" alt="license" /></a>
10
+ </p>
11
+
12
+ ---
13
+
14
+ Command-line tools for the [VoltX](https://github.com/codewithshail/voltx) framework. Scaffold new projects, run the dev server, and build for production.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install -g @voltx/cli
20
+ ```
21
+
22
+ Or use directly with npx:
23
+
24
+ ```bash
25
+ npx @voltx/cli create my-app
26
+ ```
27
+
28
+ ## Commands
29
+
30
+ ### Create a New Project
31
+
32
+ ```bash
33
+ voltx create my-app
34
+ voltx create my-app --template chatbot
35
+ voltx create my-app --template rag-app
36
+ voltx create my-app --template agent-app
37
+ ```
38
+
39
+ ### Available Templates
40
+
41
+ | Template | Description |
42
+ |----------|-------------|
43
+ | `blank` | Minimal server with file-based routing |
44
+ | `chatbot` | Streaming chat with AI + conversation memory |
45
+ | `rag-app` | Document Q&A with embeddings + vector search |
46
+ | `agent-app` | Autonomous agent with tools + memory |
47
+
48
+ ## Programmatic Usage
49
+
50
+ ```ts
51
+ import { createProject } from "@voltx/cli";
52
+
53
+ await createProject({
54
+ name: "my-app",
55
+ template: "chatbot",
56
+ });
57
+ ```
58
+
59
+ ## Part of VoltX
60
+
61
+ This package is part of the [VoltX](https://github.com/codewithshail/voltx) framework. See the [monorepo](https://github.com/codewithshail/voltx) for full documentation.
62
+
63
+ ## License
64
+
65
+ [MIT](https://github.com/codewithshail/voltx/blob/main/LICENSE) — Made by the [Promptly AI Team](https://buymeacoffee.com/promptlyai)
@@ -0,0 +1,19 @@
1
+ interface BuildOptions {
2
+ /** Entry file (default: src/index.ts) */
3
+ entry?: string;
4
+ /** Output directory (default: dist) */
5
+ outDir?: string;
6
+ /** Minify output (default: true) */
7
+ minify?: boolean;
8
+ /** Generate sourcemaps (default: false) */
9
+ sourcemap?: boolean;
10
+ }
11
+ /**
12
+ * Build the VoltX app for production.
13
+ *
14
+ * Uses tsup to bundle the server-side TypeScript into a single optimized JS file.
15
+ * If a frontend directory exists (src/frontend), also builds it with Vite.
16
+ */
17
+ declare function runBuild(options?: BuildOptions): Promise<void>;
18
+
19
+ export { type BuildOptions, runBuild };
@@ -0,0 +1,19 @@
1
+ interface BuildOptions {
2
+ /** Entry file (default: src/index.ts) */
3
+ entry?: string;
4
+ /** Output directory (default: dist) */
5
+ outDir?: string;
6
+ /** Minify output (default: true) */
7
+ minify?: boolean;
8
+ /** Generate sourcemaps (default: false) */
9
+ sourcemap?: boolean;
10
+ }
11
+ /**
12
+ * Build the VoltX app for production.
13
+ *
14
+ * Uses tsup to bundle the server-side TypeScript into a single optimized JS file.
15
+ * If a frontend directory exists (src/frontend), also builds it with Vite.
16
+ */
17
+ declare function runBuild(options?: BuildOptions): Promise<void>;
18
+
19
+ export { type BuildOptions, runBuild };
package/dist/build.js ADDED
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/build.ts
21
+ var build_exports = {};
22
+ __export(build_exports, {
23
+ runBuild: () => runBuild
24
+ });
25
+ module.exports = __toCommonJS(build_exports);
26
+ var import_node_child_process = require("child_process");
27
+ var import_node_path = require("path");
28
+ var import_node_fs = require("fs");
29
+ async function runBuild(options = {}) {
30
+ const cwd = process.cwd();
31
+ const {
32
+ entry = findEntryPoint(cwd),
33
+ outDir = "dist",
34
+ minify = true,
35
+ sourcemap = false
36
+ } = options;
37
+ if (!entry) {
38
+ console.error("[voltx] Could not find entry point. Expected src/index.ts");
39
+ process.exit(1);
40
+ }
41
+ const entryPath = (0, import_node_path.resolve)(cwd, entry);
42
+ if (!(0, import_node_fs.existsSync)(entryPath)) {
43
+ console.error(`[voltx] Entry file not found: ${entry}`);
44
+ process.exit(1);
45
+ }
46
+ console.log("");
47
+ console.log(" \u26A1 VoltX Build");
48
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
49
+ console.log(` Entry: ${entry}`);
50
+ console.log(` Output: ${outDir}/`);
51
+ console.log(` Minify: ${minify}`);
52
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
53
+ console.log("");
54
+ (0, import_node_fs.mkdirSync)((0, import_node_path.resolve)(cwd, outDir), { recursive: true });
55
+ console.log(" [1/2] Building server...");
56
+ const tsupArgs = [
57
+ entry,
58
+ "--format",
59
+ "esm",
60
+ "--out-dir",
61
+ outDir,
62
+ "--clean",
63
+ "--target",
64
+ "node20"
65
+ ];
66
+ if (minify) tsupArgs.push("--minify");
67
+ if (sourcemap) tsupArgs.push("--sourcemap");
68
+ tsupArgs.push("--no-splitting");
69
+ const tsupBin = findBin(cwd, "tsup");
70
+ await runCommand(
71
+ tsupBin ?? "npx",
72
+ tsupBin ? tsupArgs : ["tsup", ...tsupArgs],
73
+ cwd
74
+ );
75
+ console.log(" \u2713 Server built successfully");
76
+ const frontendDir = (0, import_node_path.resolve)(cwd, "src", "frontend");
77
+ const viteConfig = (0, import_node_path.resolve)(cwd, "vite.config.ts");
78
+ const hasFrontend = (0, import_node_fs.existsSync)(frontendDir) || (0, import_node_fs.existsSync)(viteConfig);
79
+ if (hasFrontend) {
80
+ console.log(" [2/2] Building frontend...");
81
+ const viteBin = findBin(cwd, "vite");
82
+ const viteArgs = ["build", "--outDir", (0, import_node_path.join)(outDir, "public")];
83
+ await runCommand(
84
+ viteBin ?? "npx",
85
+ viteBin ? viteArgs : ["vite", ...viteArgs],
86
+ cwd
87
+ );
88
+ console.log(" \u2713 Frontend built successfully");
89
+ } else {
90
+ console.log(" [2/2] No frontend found, skipping...");
91
+ }
92
+ console.log("");
93
+ console.log(" \u26A1 Build complete!");
94
+ console.log(` Run \`voltx start\` to start the production server.`);
95
+ console.log("");
96
+ }
97
+ function findEntryPoint(cwd) {
98
+ const candidates = [
99
+ "src/index.ts",
100
+ "src/index.js",
101
+ "src/index.mts",
102
+ "src/main.ts",
103
+ "src/main.js"
104
+ ];
105
+ for (const candidate of candidates) {
106
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, candidate))) {
107
+ return candidate;
108
+ }
109
+ }
110
+ return null;
111
+ }
112
+ function findBin(cwd, name) {
113
+ const paths = [
114
+ (0, import_node_path.join)(cwd, "node_modules", ".bin", name),
115
+ (0, import_node_path.join)(cwd, "..", "node_modules", ".bin", name),
116
+ (0, import_node_path.join)(cwd, "..", "..", "node_modules", ".bin", name)
117
+ ];
118
+ for (const p of paths) {
119
+ if ((0, import_node_fs.existsSync)(p)) return p;
120
+ }
121
+ return null;
122
+ }
123
+ function runCommand(cmd, args, cwd) {
124
+ return new Promise((resolve2, reject) => {
125
+ const child = (0, import_node_child_process.spawn)(cmd, args, {
126
+ cwd,
127
+ stdio: "inherit",
128
+ env: { ...process.env, NODE_ENV: "production" }
129
+ });
130
+ child.on("error", (err) => {
131
+ if (err.code === "ENOENT") {
132
+ console.error(`[voltx] ${cmd} not found. Install it with: npm install -D ${cmd}`);
133
+ }
134
+ reject(err);
135
+ });
136
+ child.on("exit", (code) => {
137
+ if (code === 0) resolve2();
138
+ else reject(new Error(`${cmd} exited with code ${code}`));
139
+ });
140
+ });
141
+ }
142
+ // Annotate the CommonJS export names for ESM import in node:
143
+ 0 && (module.exports = {
144
+ runBuild
145
+ });
package/dist/build.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ runBuild
3
+ } from "./chunk-ZB2F3WTS.mjs";
4
+ import "./chunk-Y6FXYEAI.mjs";
5
+ export {
6
+ runBuild
7
+ };
@@ -0,0 +1,141 @@
1
+ // src/generate.ts
2
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ async function runGenerate(options) {
5
+ const cwd = process.cwd();
6
+ const { type, name } = options;
7
+ switch (type) {
8
+ case "route":
9
+ generateRoute(cwd, name, options.method);
10
+ break;
11
+ case "agent":
12
+ generateAgent(cwd, name);
13
+ break;
14
+ case "tool":
15
+ generateTool(cwd, name);
16
+ break;
17
+ case "job":
18
+ generateJob(cwd, name);
19
+ break;
20
+ default:
21
+ console.error(`[voltx] Unknown generator type: ${type}`);
22
+ console.error("[voltx] Available: route, agent, tool, job");
23
+ process.exit(1);
24
+ }
25
+ }
26
+ function generateRoute(cwd, name, method = "POST") {
27
+ const routePath = name.startsWith("/") ? name.slice(1) : name;
28
+ const filePath = join(cwd, "src", "routes", `${routePath}.ts`);
29
+ if (existsSync(filePath)) {
30
+ console.error(`[voltx] Route already exists: src/routes/${routePath}.ts`);
31
+ process.exit(1);
32
+ }
33
+ const upperMethod = method.toUpperCase();
34
+ const urlPath = "/" + routePath;
35
+ const content = `// ${upperMethod} ${urlPath}
36
+ import type { Context } from "@voltx/server";
37
+
38
+ export async function ${upperMethod}(c: Context) {
39
+ return c.json({ message: "Hello from ${urlPath}" });
40
+ }
41
+ `;
42
+ writeFileSafe(filePath, content);
43
+ console.log(` \u2713 Created route: src/routes/${routePath}.ts`);
44
+ console.log(` ${upperMethod} ${urlPath}`);
45
+ }
46
+ function generateAgent(cwd, name) {
47
+ const filePath = join(cwd, "src", "agents", `${name}.ts`);
48
+ if (existsSync(filePath)) {
49
+ console.error(`[voltx] Agent already exists: src/agents/${name}.ts`);
50
+ process.exit(1);
51
+ }
52
+ const content = `// Agent: ${name}
53
+ import { createAgent } from "@voltx/agents";
54
+
55
+ export const ${toCamelCase(name)} = createAgent({
56
+ name: "${name}",
57
+ model: "cerebras:llama-4-scout-17b-16e",
58
+ instructions: "You are a helpful AI assistant named ${name}.",
59
+ tools: [
60
+ // Add tools here:
61
+ // {
62
+ // name: "example",
63
+ // description: "An example tool",
64
+ // parameters: { type: "object", properties: { input: { type: "string" } }, required: ["input"] },
65
+ // execute: async (params) => \`Processed: \${params.input}\`,
66
+ // },
67
+ ],
68
+ });
69
+ `;
70
+ writeFileSafe(filePath, content);
71
+ console.log(` \u2713 Created agent: src/agents/${name}.ts`);
72
+ }
73
+ function generateTool(cwd, name) {
74
+ const filePath = join(cwd, "src", "tools", `${name}.ts`);
75
+ if (existsSync(filePath)) {
76
+ console.error(`[voltx] Tool already exists: src/tools/${name}.ts`);
77
+ process.exit(1);
78
+ }
79
+ const content = `// Tool: ${name}
80
+
81
+ export const ${toCamelCase(name)}Tool = {
82
+ name: "${name}",
83
+ description: "TODO: Describe what this tool does",
84
+ parameters: {
85
+ type: "object" as const,
86
+ properties: {
87
+ input: { type: "string", description: "The input to process" },
88
+ },
89
+ required: ["input"],
90
+ },
91
+ execute: async (params: { input: string }): Promise<string> => {
92
+ // TODO: Implement tool logic
93
+ return \`${name} result for: \${params.input}\`;
94
+ },
95
+ };
96
+ `;
97
+ writeFileSafe(filePath, content);
98
+ console.log(` \u2713 Created tool: src/tools/${name}.ts`);
99
+ }
100
+ function generateJob(cwd, name) {
101
+ const filePath = join(cwd, "src", "jobs", `${name}.ts`);
102
+ if (existsSync(filePath)) {
103
+ console.error(`[voltx] Job already exists: src/jobs/${name}.ts`);
104
+ process.exit(1);
105
+ }
106
+ const content = `// Job: ${name}
107
+ // Runs on a schedule or triggered via ctx.jobs.enqueue("${name}", data)
108
+
109
+ export const config = {
110
+ // Cron schedule (uncomment to enable):
111
+ // schedule: "0 */6 * * *", // every 6 hours
112
+ //
113
+ // Or make it a queue job (triggered on-demand):
114
+ queue: true,
115
+ retries: 3,
116
+ timeout: "5m",
117
+ };
118
+
119
+ export async function run(ctx: any, data?: Record<string, unknown>) {
120
+ console.log("[job:${name}] Running...", data);
121
+
122
+ // TODO: Implement job logic
123
+
124
+ console.log("[job:${name}] Done.");
125
+ }
126
+ `;
127
+ writeFileSafe(filePath, content);
128
+ console.log(` \u2713 Created job: src/jobs/${name}.ts`);
129
+ }
130
+ function writeFileSafe(filePath, content) {
131
+ const dir = dirname(filePath);
132
+ mkdirSync(dir, { recursive: true });
133
+ writeFileSync(filePath, content, "utf-8");
134
+ }
135
+ function toCamelCase(str) {
136
+ return str.replace(/[-_](.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toLowerCase());
137
+ }
138
+
139
+ export {
140
+ runGenerate
141
+ };
@@ -0,0 +1,261 @@
1
+ import {
2
+ __require,
3
+ printWelcomeBanner
4
+ } from "./chunk-RH5Q7I3S.mjs";
5
+
6
+ // src/create.ts
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ async function createProject(options) {
10
+ const { name, template = "blank" } = options;
11
+ const targetDir = path.resolve(process.cwd(), name);
12
+ if (fs.existsSync(targetDir)) {
13
+ console.error(`[voltx] Directory "${name}" already exists.`);
14
+ process.exit(1);
15
+ }
16
+ fs.mkdirSync(targetDir, { recursive: true });
17
+ const templateDeps = {
18
+ blank: {
19
+ "@voltx/core": "^0.2.0",
20
+ "@voltx/server": "^0.2.0"
21
+ },
22
+ chatbot: {
23
+ "@voltx/core": "^0.2.0",
24
+ "@voltx/ai": "^0.2.0",
25
+ "@voltx/server": "^0.2.0",
26
+ "@voltx/memory": "^0.2.0"
27
+ },
28
+ "rag-app": {
29
+ "@voltx/core": "^0.2.0",
30
+ "@voltx/ai": "^0.2.0",
31
+ "@voltx/server": "^0.2.0",
32
+ "@voltx/rag": "^0.2.0",
33
+ "@voltx/db": "^0.2.0"
34
+ },
35
+ "agent-app": {
36
+ "@voltx/core": "^0.2.0",
37
+ "@voltx/ai": "^0.2.0",
38
+ "@voltx/server": "^0.2.0",
39
+ "@voltx/agents": "^0.2.0",
40
+ "@voltx/memory": "^0.2.0"
41
+ }
42
+ };
43
+ const packageJson = {
44
+ name,
45
+ version: "0.1.0",
46
+ private: true,
47
+ scripts: {
48
+ dev: "tsx src/index.ts",
49
+ build: "tsc",
50
+ start: "node dist/index.js"
51
+ },
52
+ dependencies: templateDeps[template] ?? templateDeps["blank"],
53
+ devDependencies: {
54
+ typescript: "^5.7.0",
55
+ tsx: "^4.21.0",
56
+ "@types/node": "^22.0.0"
57
+ }
58
+ };
59
+ fs.writeFileSync(
60
+ path.join(targetDir, "package.json"),
61
+ JSON.stringify(packageJson, null, 2)
62
+ );
63
+ const hasDb = template === "rag-app" || template === "agent-app";
64
+ const provider = template === "rag-app" ? "openai" : "cerebras";
65
+ const model = template === "rag-app" ? "gpt-4o" : "llama-4-scout-17b-16e";
66
+ let configContent = `import { defineConfig } from "@voltx/core";
67
+
68
+ export default defineConfig({
69
+ name: "${name}",
70
+ port: 3000,
71
+ ai: {
72
+ provider: "${provider}",
73
+ model: "${model}",
74
+ },`;
75
+ if (hasDb) {
76
+ configContent += `
77
+ db: {
78
+ url: process.env.DATABASE_URL,
79
+ },`;
80
+ }
81
+ configContent += `
82
+ server: {
83
+ routesDir: "src/routes",
84
+ staticDir: "public",
85
+ cors: true,
86
+ },
87
+ });
88
+ `;
89
+ fs.writeFileSync(path.join(targetDir, "voltx.config.ts"), configContent);
90
+ fs.mkdirSync(path.join(targetDir, "src", "routes", "api"), { recursive: true });
91
+ fs.mkdirSync(path.join(targetDir, "public"), { recursive: true });
92
+ fs.writeFileSync(
93
+ path.join(targetDir, "src", "index.ts"),
94
+ `import { createApp } from "@voltx/core";
95
+ import config from "../voltx.config";
96
+
97
+ const app = createApp(config);
98
+ app.start();
99
+ `
100
+ );
101
+ fs.writeFileSync(
102
+ path.join(targetDir, "src", "routes", "index.ts"),
103
+ `// GET / \u2014 Health check
104
+ import type { Context } from "@voltx/server";
105
+
106
+ export function GET(c: Context) {
107
+ return c.json({ name: "${name}", status: "ok" });
108
+ }
109
+ `
110
+ );
111
+ if (template === "chatbot" || template === "agent-app") {
112
+ fs.writeFileSync(
113
+ path.join(targetDir, "src", "routes", "api", "chat.ts"),
114
+ `// POST /api/chat \u2014 Streaming chat with conversation memory
115
+ import type { Context } from "@voltx/server";
116
+ import { streamText } from "@voltx/ai";
117
+ import { createMemory } from "@voltx/memory";
118
+
119
+ // In-memory for dev; swap to createMemory("postgres", { url }) for production
120
+ const memory = createMemory({ maxMessages: 50 });
121
+
122
+ export async function POST(c: Context) {
123
+ const { messages, conversationId = "default" } = await c.req.json();
124
+
125
+ // Store the latest user message
126
+ const lastMessage = messages[messages.length - 1];
127
+ if (lastMessage?.role === "user") {
128
+ await memory.add(conversationId, { role: "user", content: lastMessage.content });
129
+ }
130
+
131
+ // Get conversation history from memory
132
+ const history = await memory.get(conversationId);
133
+
134
+ const result = await streamText({
135
+ model: "cerebras:llama-4-scout-17b-16e",
136
+ system: "You are a helpful AI assistant.",
137
+ messages: history.map((m) => ({ role: m.role, content: m.content })),
138
+ });
139
+
140
+ // Store assistant response after stream completes
141
+ result.text.then(async (text) => {
142
+ await memory.add(conversationId, { role: "assistant", content: text });
143
+ });
144
+
145
+ return result.toSSEResponse();
146
+ }
147
+ `
148
+ );
149
+ }
150
+ if (template === "rag-app") {
151
+ fs.mkdirSync(path.join(targetDir, "src", "routes", "api", "rag"), { recursive: true });
152
+ fs.writeFileSync(
153
+ path.join(targetDir, "src", "routes", "api", "rag", "query.ts"),
154
+ `// POST /api/rag/query \u2014 Query documents with RAG
155
+ import type { Context } from "@voltx/server";
156
+ import { streamText } from "@voltx/ai";
157
+ import { createRAGPipeline, createEmbedder } from "@voltx/rag";
158
+ import { createVectorStore } from "@voltx/db";
159
+
160
+ const vectorStore = createVectorStore(); // swap to "pinecone" or "pgvector" for production
161
+ const embedder = createEmbedder({ model: "openai:text-embedding-3-small" });
162
+ const rag = createRAGPipeline({ embedder, vectorStore });
163
+
164
+ export async function POST(c: Context) {
165
+ const { question } = await c.req.json();
166
+
167
+ const context = await rag.getContext(question, { topK: 5 });
168
+
169
+ const result = await streamText({
170
+ model: "openai:gpt-4o",
171
+ system: \`Answer the user's question based on the following context. If the context doesn't contain relevant information, say so.\\n\\nContext:\\n\${context}\`,
172
+ messages: [{ role: "user", content: question }],
173
+ });
174
+
175
+ return result.toSSEResponse();
176
+ }
177
+ `
178
+ );
179
+ fs.writeFileSync(
180
+ path.join(targetDir, "src", "routes", "api", "rag", "ingest.ts"),
181
+ `// POST /api/rag/ingest \u2014 Ingest documents into the vector store
182
+ import type { Context } from "@voltx/server";
183
+ import { createRAGPipeline, createEmbedder } from "@voltx/rag";
184
+ import { createVectorStore } from "@voltx/db";
185
+
186
+ const vectorStore = createVectorStore();
187
+ const embedder = createEmbedder({ model: "openai:text-embedding-3-small" });
188
+ const rag = createRAGPipeline({ embedder, vectorStore });
189
+
190
+ export async function POST(c: Context) {
191
+ const { text, idPrefix } = await c.req.json();
192
+
193
+ if (!text || typeof text !== "string") {
194
+ return c.json({ error: "Missing 'text' field" }, 400);
195
+ }
196
+
197
+ const result = await rag.ingest(text, idPrefix ?? "doc");
198
+ return c.json({ status: "ok", chunks: result.chunks, ids: result.ids });
199
+ }
200
+ `
201
+ );
202
+ }
203
+ let envContent = "";
204
+ if (template === "rag-app") {
205
+ envContent += "# \u2500\u2500\u2500 LLM Provider \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nOPENAI_API_KEY=sk-...\n\n";
206
+ envContent += "# \u2500\u2500\u2500 Database (Neon Postgres) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nDATABASE_URL=postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/dbname?sslmode=require\n\n";
207
+ envContent += "# \u2500\u2500\u2500 Vector Database (Pinecone) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nPINECONE_API_KEY=pc-...\nPINECONE_INDEX=voltx-embeddings\n\n";
208
+ } else if (template === "chatbot" || template === "agent-app") {
209
+ envContent += "# \u2500\u2500\u2500 LLM Provider \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nCEREBRAS_API_KEY=csk-...\n\n";
210
+ if (template === "agent-app") {
211
+ envContent += "# \u2500\u2500\u2500 Database (Neon Postgres \u2014 optional) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nDATABASE_URL=\n\n";
212
+ }
213
+ } else {
214
+ envContent += "# \u2500\u2500\u2500 LLM Provider (add your key) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# OPENAI_API_KEY=sk-...\n# CEREBRAS_API_KEY=csk-...\n\n";
215
+ }
216
+ envContent += "# \u2500\u2500\u2500 App \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nPORT=3000\nNODE_ENV=development\n";
217
+ fs.writeFileSync(path.join(targetDir, ".env.example"), envContent);
218
+ fs.writeFileSync(
219
+ path.join(targetDir, ".gitignore"),
220
+ "node_modules\ndist\n.env\n"
221
+ );
222
+ fs.writeFileSync(
223
+ path.join(targetDir, "tsconfig.json"),
224
+ JSON.stringify(
225
+ {
226
+ compilerOptions: {
227
+ target: "ES2022",
228
+ module: "ESNext",
229
+ moduleResolution: "bundler",
230
+ strict: true,
231
+ esModuleInterop: true,
232
+ skipLibCheck: true,
233
+ outDir: "dist",
234
+ rootDir: "src"
235
+ },
236
+ include: ["src"]
237
+ },
238
+ null,
239
+ 2
240
+ )
241
+ );
242
+ printWelcomeBanner(name);
243
+ }
244
+ var isDirectRun = typeof __require !== "undefined" && __require.main === module && process.argv[1]?.includes("create");
245
+ if (isDirectRun) {
246
+ const projectName = process.argv[2];
247
+ if (!projectName) {
248
+ console.log("Usage: create-voltx-app <project-name> [--template chatbot]");
249
+ process.exit(1);
250
+ }
251
+ const templateFlag = process.argv.indexOf("--template");
252
+ const template = templateFlag !== -1 ? process.argv[templateFlag + 1] : "blank";
253
+ createProject({ name: projectName, template }).catch((err) => {
254
+ console.error("[voltx] Error:", err);
255
+ process.exit(1);
256
+ });
257
+ }
258
+
259
+ export {
260
+ createProject
261
+ };