@voltx/cli 0.3.1 → 0.3.3

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.
@@ -0,0 +1,94 @@
1
+ // src/start.ts
2
+ import { spawn } from "child_process";
3
+ import { resolve, join } from "path";
4
+ import { existsSync, readFileSync } from "fs";
5
+ async function runStart(options = {}) {
6
+ const cwd = process.cwd();
7
+ const { port, outDir = "dist" } = options;
8
+ const distDir = resolve(cwd, outDir);
9
+ if (!existsSync(distDir)) {
10
+ console.error(`[voltx] Build output not found at ${outDir}/`);
11
+ console.error("[voltx] Run `voltx build` first to create a production build.");
12
+ process.exit(1);
13
+ }
14
+ const entry = options.entry ?? findDistEntry(distDir);
15
+ if (!entry) {
16
+ console.error(`[voltx] No entry file found in ${outDir}/`);
17
+ console.error("[voltx] Expected index.js, index.mjs, or main.js");
18
+ process.exit(1);
19
+ }
20
+ const entryPath = resolve(distDir, entry);
21
+ if (!existsSync(entryPath)) {
22
+ console.error(`[voltx] Entry file not found: ${outDir}/${entry}`);
23
+ process.exit(1);
24
+ }
25
+ const env = {
26
+ ...process.env,
27
+ NODE_ENV: "production"
28
+ };
29
+ const envFile = resolve(cwd, ".env");
30
+ if (existsSync(envFile)) {
31
+ const envContent = readFileSync(envFile, "utf-8");
32
+ for (const line of envContent.split("\n")) {
33
+ const trimmed = line.trim();
34
+ if (!trimmed || trimmed.startsWith("#")) continue;
35
+ const eqIdx = trimmed.indexOf("=");
36
+ if (eqIdx === -1) continue;
37
+ const key = trimmed.slice(0, eqIdx).trim();
38
+ const value = trimmed.slice(eqIdx + 1).trim();
39
+ env[key] = value;
40
+ }
41
+ }
42
+ if (port) {
43
+ env.PORT = String(port);
44
+ }
45
+ console.log("");
46
+ console.log(" \u26A1 VoltX Production Server");
47
+ 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");
48
+ console.log(` Entry: ${outDir}/${entry}`);
49
+ if (port) {
50
+ console.log(` Port: ${port}`);
51
+ }
52
+ console.log(` Mode: production`);
53
+ 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");
54
+ console.log("");
55
+ const child = spawn("node", [entryPath], {
56
+ cwd,
57
+ env,
58
+ stdio: "inherit"
59
+ });
60
+ const signals = ["SIGINT", "SIGTERM"];
61
+ for (const signal of signals) {
62
+ process.on(signal, () => {
63
+ child.kill(signal);
64
+ });
65
+ }
66
+ child.on("error", (err) => {
67
+ console.error("[voltx] Failed to start production server:", err.message);
68
+ process.exit(1);
69
+ });
70
+ child.on("exit", (code) => {
71
+ process.exit(code ?? 0);
72
+ });
73
+ }
74
+ function findDistEntry(distDir) {
75
+ const candidates = [
76
+ "index.mjs",
77
+ "index.js",
78
+ "index.cjs",
79
+ "main.mjs",
80
+ "main.js",
81
+ "src/index.mjs",
82
+ "src/index.js"
83
+ ];
84
+ for (const candidate of candidates) {
85
+ if (existsSync(join(distDir, candidate))) {
86
+ return candidate;
87
+ }
88
+ }
89
+ return null;
90
+ }
91
+
92
+ export {
93
+ runStart
94
+ };
@@ -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:llama3.1-8b",
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,405 @@
1
+ import {
2
+ printWelcomeBanner
3
+ } from "./chunk-IV352HZA.mjs";
4
+ import {
5
+ __require
6
+ } from "./chunk-Y6FXYEAI.mjs";
7
+
8
+ // src/create.ts
9
+ import * as fs from "fs";
10
+ import * as path from "path";
11
+ async function createProject(options) {
12
+ const { name, template = "blank", auth = "none" } = options;
13
+ const targetDir = path.resolve(process.cwd(), name);
14
+ if (fs.existsSync(targetDir)) {
15
+ console.error(`[voltx] Directory "${name}" already exists.`);
16
+ process.exit(1);
17
+ }
18
+ fs.mkdirSync(targetDir, { recursive: true });
19
+ const templateDeps = {
20
+ blank: {
21
+ "@voltx/core": "^0.3.0",
22
+ "@voltx/server": "^0.3.0"
23
+ },
24
+ chatbot: {
25
+ "@voltx/core": "^0.3.0",
26
+ "@voltx/ai": "^0.3.0",
27
+ "@voltx/server": "^0.3.0",
28
+ "@voltx/memory": "^0.3.0"
29
+ },
30
+ "rag-app": {
31
+ "@voltx/core": "^0.3.0",
32
+ "@voltx/ai": "^0.3.0",
33
+ "@voltx/server": "^0.3.0",
34
+ "@voltx/rag": "^0.3.0",
35
+ "@voltx/db": "^0.3.0"
36
+ },
37
+ "agent-app": {
38
+ "@voltx/core": "^0.3.0",
39
+ "@voltx/ai": "^0.3.0",
40
+ "@voltx/server": "^0.3.0",
41
+ "@voltx/agents": "^0.3.0",
42
+ "@voltx/memory": "^0.3.0"
43
+ }
44
+ };
45
+ const packageJson = {
46
+ name,
47
+ version: "0.1.0",
48
+ private: true,
49
+ scripts: {
50
+ dev: "voltx dev",
51
+ build: "voltx build",
52
+ start: "voltx start"
53
+ },
54
+ dependencies: {
55
+ ...templateDeps[template] ?? templateDeps["blank"],
56
+ "@voltx/cli": "^0.3.0",
57
+ ...auth === "better-auth" ? { "@voltx/auth": "^0.3.0", "better-auth": "^1.5.0" } : {},
58
+ ...auth === "jwt" ? { "@voltx/auth": "^0.3.0", "jose": "^6.0.0" } : {}
59
+ },
60
+ devDependencies: {
61
+ typescript: "^5.7.0",
62
+ tsx: "^4.21.0",
63
+ tsup: "^8.0.0",
64
+ "@types/node": "^22.0.0"
65
+ }
66
+ };
67
+ fs.writeFileSync(
68
+ path.join(targetDir, "package.json"),
69
+ JSON.stringify(packageJson, null, 2)
70
+ );
71
+ const hasDb = template === "rag-app" || template === "agent-app" || auth === "better-auth";
72
+ const provider = template === "rag-app" ? "openai" : "cerebras";
73
+ const model = template === "rag-app" ? "gpt-4o" : "llama3.1-8b";
74
+ let configContent = `import { defineConfig } from "@voltx/core";
75
+
76
+ export default defineConfig({
77
+ name: "${name}",
78
+ port: 3000,
79
+ ai: {
80
+ provider: "${provider}",
81
+ model: "${model}",
82
+ },`;
83
+ if (hasDb) {
84
+ configContent += `
85
+ db: {
86
+ url: process.env.DATABASE_URL,
87
+ },`;
88
+ }
89
+ if (auth !== "none") {
90
+ configContent += `
91
+ auth: {
92
+ provider: "${auth}",
93
+ },`;
94
+ }
95
+ configContent += `
96
+ server: {
97
+ routesDir: "src/routes",
98
+ staticDir: "public",
99
+ cors: true,
100
+ },
101
+ });
102
+ `;
103
+ fs.writeFileSync(path.join(targetDir, "voltx.config.ts"), configContent);
104
+ fs.mkdirSync(path.join(targetDir, "src", "routes", "api"), { recursive: true });
105
+ fs.mkdirSync(path.join(targetDir, "public"), { recursive: true });
106
+ fs.writeFileSync(
107
+ path.join(targetDir, "src", "index.ts"),
108
+ `import { createApp } from "@voltx/core";
109
+ import config from "../voltx.config";
110
+
111
+ const app = createApp(config);
112
+ app.start();
113
+ `
114
+ );
115
+ fs.writeFileSync(
116
+ path.join(targetDir, "src", "routes", "index.ts"),
117
+ `// GET / \u2014 Health check
118
+ import type { Context } from "@voltx/server";
119
+
120
+ export function GET(c: Context) {
121
+ return c.json({ name: "${name}", status: "ok" });
122
+ }
123
+ `
124
+ );
125
+ if (template === "chatbot" || template === "agent-app") {
126
+ fs.writeFileSync(
127
+ path.join(targetDir, "src", "routes", "api", "chat.ts"),
128
+ `// POST /api/chat \u2014 Streaming chat with conversation memory
129
+ import type { Context } from "@voltx/server";
130
+ import { streamText } from "@voltx/ai";
131
+ import { createMemory } from "@voltx/memory";
132
+
133
+ // In-memory for dev; swap to createMemory("postgres", { url }) for production
134
+ const memory = createMemory({ maxMessages: 50 });
135
+
136
+ export async function POST(c: Context) {
137
+ const { messages, conversationId = "default" } = await c.req.json();
138
+
139
+ // Store the latest user message
140
+ const lastMessage = messages[messages.length - 1];
141
+ if (lastMessage?.role === "user") {
142
+ await memory.add(conversationId, { role: "user", content: lastMessage.content });
143
+ }
144
+
145
+ // Get conversation history from memory
146
+ const history = await memory.get(conversationId);
147
+
148
+ const result = await streamText({
149
+ model: "${provider}:${model}",
150
+ system: "You are a helpful AI assistant.",
151
+ messages: history.map((m) => ({ role: m.role, content: m.content })),
152
+ });
153
+
154
+ // Store assistant response after stream completes
155
+ result.text.then(async (text) => {
156
+ await memory.add(conversationId, { role: "assistant", content: text });
157
+ });
158
+
159
+ return result.toSSEResponse();
160
+ }
161
+ `
162
+ );
163
+ }
164
+ if (template === "agent-app") {
165
+ fs.mkdirSync(path.join(targetDir, "src", "agents"), { recursive: true });
166
+ fs.mkdirSync(path.join(targetDir, "src", "tools"), { recursive: true });
167
+ fs.writeFileSync(
168
+ path.join(targetDir, "src", "agents", "assistant.ts"),
169
+ `import { createAgent } from "@voltx/agents";
170
+ import { searchTool } from "../tools/search";
171
+
172
+ export const assistant = createAgent({
173
+ name: "assistant",
174
+ model: "${provider}:${model}",
175
+ instructions: "You are a helpful AI assistant. Use your tools when needed.",
176
+ tools: [searchTool],
177
+ maxIterations: 5,
178
+ });
179
+ `
180
+ );
181
+ fs.writeFileSync(
182
+ path.join(targetDir, "src", "tools", "search.ts"),
183
+ `import type { Tool } from "@voltx/agents";
184
+
185
+ export const searchTool: Tool = {
186
+ name: "search",
187
+ description: "Search for information on a topic.",
188
+ parameters: {
189
+ type: "object",
190
+ properties: { query: { type: "string", description: "The search query" } },
191
+ required: ["query"],
192
+ },
193
+ async execute(args: { query: string }) {
194
+ return \`Search results for "\${args.query}": Placeholder \u2014 connect a real search API.\`;
195
+ },
196
+ };
197
+ `
198
+ );
199
+ fs.writeFileSync(
200
+ path.join(targetDir, "src", "routes", "api", "agent.ts"),
201
+ `import type { Context } from "@voltx/server";
202
+ import { assistant } from "../../agents/assistant";
203
+
204
+ export async function POST(c: Context) {
205
+ const { input } = await c.req.json();
206
+ if (!input) return c.json({ error: "Missing 'input' field" }, 400);
207
+ const result = await assistant.run(input);
208
+ return c.json({ content: result.content, steps: result.steps });
209
+ }
210
+ `
211
+ );
212
+ }
213
+ if (template === "rag-app") {
214
+ const embedModel = "openai:text-embedding-3-small";
215
+ fs.mkdirSync(path.join(targetDir, "src", "routes", "api", "rag"), { recursive: true });
216
+ fs.writeFileSync(
217
+ path.join(targetDir, "src", "routes", "api", "rag", "query.ts"),
218
+ `// POST /api/rag/query \u2014 Query documents with RAG
219
+ import type { Context } from "@voltx/server";
220
+ import { streamText } from "@voltx/ai";
221
+ import { createRAGPipeline, createEmbedder } from "@voltx/rag";
222
+ import { createVectorStore } from "@voltx/db";
223
+
224
+ const vectorStore = createVectorStore(); // swap to "pinecone" or "pgvector" for production
225
+ const embedder = createEmbedder({ model: "${embedModel}" });
226
+ const rag = createRAGPipeline({ embedder, vectorStore });
227
+
228
+ export async function POST(c: Context) {
229
+ const { question } = await c.req.json();
230
+
231
+ const context = await rag.getContext(question, { topK: 5 });
232
+
233
+ const result = await streamText({
234
+ model: "${provider}:${model}",
235
+ 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}\`,
236
+ messages: [{ role: "user", content: question }],
237
+ });
238
+
239
+ return result.toSSEResponse();
240
+ }
241
+ `
242
+ );
243
+ fs.writeFileSync(
244
+ path.join(targetDir, "src", "routes", "api", "rag", "ingest.ts"),
245
+ `// POST /api/rag/ingest \u2014 Ingest documents into the vector store
246
+ import type { Context } from "@voltx/server";
247
+ import { createRAGPipeline, createEmbedder } from "@voltx/rag";
248
+ import { createVectorStore } from "@voltx/db";
249
+
250
+ const vectorStore = createVectorStore();
251
+ const embedder = createEmbedder({ model: "${embedModel}" });
252
+ const rag = createRAGPipeline({ embedder, vectorStore });
253
+
254
+ export async function POST(c: Context) {
255
+ const { text, idPrefix } = await c.req.json();
256
+
257
+ if (!text || typeof text !== "string") {
258
+ return c.json({ error: "Missing 'text' field" }, 400);
259
+ }
260
+
261
+ const result = await rag.ingest(text, idPrefix ?? "doc");
262
+ return c.json({ status: "ok", chunks: result.chunks, ids: result.ids });
263
+ }
264
+ `
265
+ );
266
+ }
267
+ if (auth === "better-auth") {
268
+ fs.mkdirSync(path.join(targetDir, "src", "routes", "api", "auth"), { recursive: true });
269
+ fs.writeFileSync(
270
+ path.join(targetDir, "src", "routes", "api", "auth", "[...path].ts"),
271
+ `// ALL /api/auth/* \u2014 Better Auth handler
272
+ import type { Context } from "@voltx/server";
273
+ import { auth } from "../../../lib/auth";
274
+ import { createAuthHandler } from "@voltx/auth";
275
+
276
+ const handler = createAuthHandler(auth);
277
+
278
+ export const GET = (c: Context) => handler(c);
279
+ export const POST = (c: Context) => handler(c);
280
+ `
281
+ );
282
+ fs.mkdirSync(path.join(targetDir, "src", "lib"), { recursive: true });
283
+ fs.writeFileSync(
284
+ path.join(targetDir, "src", "lib", "auth.ts"),
285
+ `// Auth configuration \u2014 Better Auth with DB-backed sessions
286
+ import { createAuth, createAuthMiddleware } from "@voltx/auth";
287
+
288
+ export const auth = createAuth("better-auth", {
289
+ database: process.env.DATABASE_URL!,
290
+ emailAndPassword: true,
291
+ });
292
+
293
+ export const authMiddleware = createAuthMiddleware({
294
+ provider: auth,
295
+ publicPaths: ["/api/auth", "/api/health", "/"],
296
+ });
297
+ `
298
+ );
299
+ } else if (auth === "jwt") {
300
+ fs.mkdirSync(path.join(targetDir, "src", "lib"), { recursive: true });
301
+ fs.writeFileSync(
302
+ path.join(targetDir, "src", "lib", "auth.ts"),
303
+ `// Auth configuration \u2014 JWT (stateless)
304
+ import { createAuth, createAuthMiddleware } from "@voltx/auth";
305
+
306
+ export const jwt = createAuth("jwt", {
307
+ secret: process.env.JWT_SECRET!,
308
+ expiresIn: "7d",
309
+ });
310
+
311
+ export const authMiddleware = createAuthMiddleware({
312
+ provider: jwt,
313
+ publicPaths: ["/api/auth", "/api/health", "/"],
314
+ });
315
+ `
316
+ );
317
+ fs.writeFileSync(
318
+ path.join(targetDir, "src", "routes", "api", "auth.ts"),
319
+ `// POST /api/auth/login \u2014 Example JWT login route
320
+ import type { Context } from "@voltx/server";
321
+ import { jwt } from "../../lib/auth";
322
+
323
+ export async function POST(c: Context) {
324
+ const { email, password } = await c.req.json();
325
+
326
+ if (!email || !password) {
327
+ return c.json({ error: "Email and password are required" }, 400);
328
+ }
329
+
330
+ const token = await jwt.sign({ sub: email, email });
331
+ return c.json({ token });
332
+ }
333
+ `
334
+ );
335
+ }
336
+ let envContent = "";
337
+ if (template === "rag-app") {
338
+ 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";
339
+ 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";
340
+ 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";
341
+ } else if (template === "chatbot" || template === "agent-app") {
342
+ 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";
343
+ if (template === "agent-app") {
344
+ envContent += "# \u2500\u2500\u2500 Database (Neon Postgres \u2014 optional) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nDATABASE_URL=\n\n";
345
+ }
346
+ } else {
347
+ 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";
348
+ }
349
+ if (auth === "better-auth") {
350
+ envContent += "# \u2500\u2500\u2500 Auth (Better Auth) \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\nBETTER_AUTH_SECRET=your-secret-key-min-32-chars-here\nBETTER_AUTH_URL=http://localhost:3000\n";
351
+ if (template !== "rag-app" && template !== "agent-app") {
352
+ envContent += "DATABASE_URL=postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/dbname?sslmode=require\n";
353
+ }
354
+ envContent += "# GITHUB_CLIENT_ID=\n# GITHUB_CLIENT_SECRET=\n\n";
355
+ } else if (auth === "jwt") {
356
+ envContent += "# \u2500\u2500\u2500 Auth (JWT) \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\nJWT_SECRET=your-jwt-secret-key\n\n";
357
+ }
358
+ 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";
359
+ fs.writeFileSync(path.join(targetDir, ".env.example"), envContent);
360
+ fs.writeFileSync(
361
+ path.join(targetDir, ".gitignore"),
362
+ "node_modules\ndist\n.env\n"
363
+ );
364
+ fs.writeFileSync(
365
+ path.join(targetDir, "tsconfig.json"),
366
+ JSON.stringify(
367
+ {
368
+ compilerOptions: {
369
+ target: "ES2022",
370
+ module: "ESNext",
371
+ moduleResolution: "bundler",
372
+ strict: true,
373
+ esModuleInterop: true,
374
+ skipLibCheck: true,
375
+ outDir: "dist",
376
+ rootDir: "src"
377
+ },
378
+ include: ["src"]
379
+ },
380
+ null,
381
+ 2
382
+ )
383
+ );
384
+ printWelcomeBanner(name);
385
+ }
386
+ var isDirectRun = typeof __require !== "undefined" && __require.main === module && process.argv[1]?.includes("create");
387
+ if (isDirectRun) {
388
+ const projectName = process.argv[2];
389
+ if (!projectName) {
390
+ console.log("Usage: create-voltx-app <project-name> [--template chatbot] [--auth jwt]");
391
+ process.exit(1);
392
+ }
393
+ const templateFlag = process.argv.indexOf("--template");
394
+ const template = templateFlag !== -1 ? process.argv[templateFlag + 1] : "blank";
395
+ const authFlag = process.argv.indexOf("--auth");
396
+ const auth = authFlag !== -1 ? process.argv[authFlag + 1] : "none";
397
+ createProject({ name: projectName, template, auth }).catch((err) => {
398
+ console.error("[voltx] Error:", err);
399
+ process.exit(1);
400
+ });
401
+ }
402
+
403
+ export {
404
+ createProject
405
+ };
@@ -0,0 +1,132 @@
1
+ // src/dev.ts
2
+ import { spawn } from "child_process";
3
+ import { resolve, join } from "path";
4
+ import { existsSync, readFileSync } from "fs";
5
+ async function runDev(options = {}) {
6
+ const cwd = process.cwd();
7
+ const {
8
+ port,
9
+ entry = findEntryPoint(cwd),
10
+ clearScreen = true
11
+ } = options;
12
+ if (!entry) {
13
+ console.error("[voltx] Could not find entry point. Expected src/index.ts or src/index.js");
14
+ process.exit(1);
15
+ }
16
+ const entryPath = resolve(cwd, entry);
17
+ if (!existsSync(entryPath)) {
18
+ console.error(`[voltx] Entry file not found: ${entry}`);
19
+ process.exit(1);
20
+ }
21
+ const envFile = resolve(cwd, ".env");
22
+ const env = {
23
+ ...process.env,
24
+ NODE_ENV: "development"
25
+ };
26
+ if (existsSync(envFile)) {
27
+ const envContent = readFileSync(envFile, "utf-8");
28
+ for (const line of envContent.split("\n")) {
29
+ const trimmed = line.trim();
30
+ if (!trimmed || trimmed.startsWith("#")) continue;
31
+ const eqIdx = trimmed.indexOf("=");
32
+ if (eqIdx === -1) continue;
33
+ const key = trimmed.slice(0, eqIdx).trim();
34
+ const value = trimmed.slice(eqIdx + 1).trim();
35
+ env[key] = value;
36
+ }
37
+ }
38
+ if (port) {
39
+ env.PORT = String(port);
40
+ }
41
+ printDevBanner(entry, port);
42
+ const tsxArgs = ["watch"];
43
+ if (clearScreen) {
44
+ tsxArgs.push("--clear-screen=false");
45
+ }
46
+ const watchDirs = [
47
+ "src/routes",
48
+ "src/agents",
49
+ "src/tools",
50
+ "src/jobs",
51
+ "src/lib",
52
+ "voltx.config.ts",
53
+ ...options.watch ?? []
54
+ ];
55
+ tsxArgs.push("--ignore=node_modules", "--ignore=dist", "--ignore=.turbo");
56
+ tsxArgs.push(entry);
57
+ const tsxBin = findTsxBin(cwd);
58
+ let child;
59
+ if (tsxBin) {
60
+ child = spawn(tsxBin, tsxArgs, {
61
+ cwd,
62
+ env,
63
+ stdio: "inherit"
64
+ });
65
+ } else {
66
+ child = spawn("npx", ["tsx", ...tsxArgs], {
67
+ cwd,
68
+ env,
69
+ stdio: "inherit"
70
+ });
71
+ }
72
+ const signals = ["SIGINT", "SIGTERM"];
73
+ for (const signal of signals) {
74
+ process.on(signal, () => {
75
+ child.kill(signal);
76
+ });
77
+ }
78
+ child.on("error", (err) => {
79
+ if (err.code === "ENOENT") {
80
+ console.error("[voltx] tsx not found. Install it with: npm install -D tsx");
81
+ console.error("[voltx] Or run your app directly: npx tsx watch src/index.ts");
82
+ } else {
83
+ console.error("[voltx] Dev server error:", err.message);
84
+ }
85
+ process.exit(1);
86
+ });
87
+ child.on("exit", (code) => {
88
+ process.exit(code ?? 0);
89
+ });
90
+ }
91
+ function findEntryPoint(cwd) {
92
+ const candidates = [
93
+ "src/index.ts",
94
+ "src/index.js",
95
+ "src/index.mts",
96
+ "src/main.ts",
97
+ "src/main.js",
98
+ "index.ts",
99
+ "index.js"
100
+ ];
101
+ for (const candidate of candidates) {
102
+ if (existsSync(join(cwd, candidate))) {
103
+ return candidate;
104
+ }
105
+ }
106
+ return null;
107
+ }
108
+ function findTsxBin(cwd) {
109
+ const localBin = join(cwd, "node_modules", ".bin", "tsx");
110
+ if (existsSync(localBin)) return localBin;
111
+ const parentBin = join(cwd, "..", "node_modules", ".bin", "tsx");
112
+ if (existsSync(parentBin)) return parentBin;
113
+ const rootBin = join(cwd, "..", "..", "node_modules", ".bin", "tsx");
114
+ if (existsSync(rootBin)) return rootBin;
115
+ return null;
116
+ }
117
+ function printDevBanner(entry, port) {
118
+ console.log("");
119
+ console.log(" \u26A1 VoltX Dev Server");
120
+ 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");
121
+ console.log(` Entry: ${entry}`);
122
+ if (port) {
123
+ console.log(` Port: ${port}`);
124
+ }
125
+ console.log(` Mode: development (hot reload)`);
126
+ 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");
127
+ console.log("");
128
+ }
129
+
130
+ export {
131
+ runDev
132
+ };
package/dist/cli.js CHANGED
@@ -207,7 +207,7 @@ async function createProject(options) {
207
207
  );
208
208
  const hasDb = template === "rag-app" || template === "agent-app" || auth === "better-auth";
209
209
  const provider = template === "rag-app" ? "openai" : "cerebras";
210
- const model = template === "rag-app" ? "gpt-4o" : "llama-4-scout-17b-16e";
210
+ const model = template === "rag-app" ? "gpt-4o" : "llama3.1-8b";
211
211
  let configContent = `import { defineConfig } from "@voltx/core";
212
212
 
213
213
  export default defineConfig({
@@ -572,6 +572,18 @@ async function runDev(options = {}) {
572
572
  ...process.env,
573
573
  NODE_ENV: "development"
574
574
  };
575
+ if ((0, import_node_fs.existsSync)(envFile)) {
576
+ const envContent = (0, import_node_fs.readFileSync)(envFile, "utf-8");
577
+ for (const line of envContent.split("\n")) {
578
+ const trimmed = line.trim();
579
+ if (!trimmed || trimmed.startsWith("#")) continue;
580
+ const eqIdx = trimmed.indexOf("=");
581
+ if (eqIdx === -1) continue;
582
+ const key = trimmed.slice(0, eqIdx).trim();
583
+ const value = trimmed.slice(eqIdx + 1).trim();
584
+ env[key] = value;
585
+ }
586
+ }
575
587
  if (port) {
576
588
  env.PORT = String(port);
577
589
  }
@@ -830,6 +842,19 @@ async function runStart(options = {}) {
830
842
  ...process.env,
831
843
  NODE_ENV: "production"
832
844
  };
845
+ const envFile = (0, import_node_path3.resolve)(cwd, ".env");
846
+ if ((0, import_node_fs3.existsSync)(envFile)) {
847
+ const envContent = (0, import_node_fs3.readFileSync)(envFile, "utf-8");
848
+ for (const line of envContent.split("\n")) {
849
+ const trimmed = line.trim();
850
+ if (!trimmed || trimmed.startsWith("#")) continue;
851
+ const eqIdx = trimmed.indexOf("=");
852
+ if (eqIdx === -1) continue;
853
+ const key = trimmed.slice(0, eqIdx).trim();
854
+ const value = trimmed.slice(eqIdx + 1).trim();
855
+ env[key] = value;
856
+ }
857
+ }
833
858
  if (port) {
834
859
  env.PORT = String(port);
835
860
  }
@@ -947,7 +972,7 @@ import { createAgent } from "@voltx/agents";
947
972
 
948
973
  export const ${toCamelCase(name)} = createAgent({
949
974
  name: "${name}",
950
- model: "cerebras:llama-4-scout-17b-16e",
975
+ model: "cerebras:llama3.1-8b",
951
976
  instructions: "You are a helpful AI assistant named ${name}.",
952
977
  tools: [
953
978
  // Add tools here:
@@ -1056,7 +1081,7 @@ var init_index = __esm({
1056
1081
  init_build();
1057
1082
  init_start();
1058
1083
  init_generate();
1059
- CLI_VERSION = "0.2.0";
1084
+ CLI_VERSION = "0.3.3";
1060
1085
  }
1061
1086
  });
1062
1087
 
package/dist/create.js CHANGED
@@ -204,7 +204,7 @@ async function createProject(options) {
204
204
  );
205
205
  const hasDb = template === "rag-app" || template === "agent-app" || auth === "better-auth";
206
206
  const provider = template === "rag-app" ? "openai" : "cerebras";
207
- const model = template === "rag-app" ? "gpt-4o" : "llama-4-scout-17b-16e";
207
+ const model = template === "rag-app" ? "gpt-4o" : "llama3.1-8b";
208
208
  let configContent = `import { defineConfig } from "@voltx/core";
209
209
 
210
210
  export default defineConfig({
package/dist/create.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createProject
4
- } from "./chunk-KFHPTRKZ.mjs";
4
+ } from "./chunk-HM7F67C2.mjs";
5
5
  import "./chunk-IV352HZA.mjs";
6
6
  import "./chunk-Y6FXYEAI.mjs";
7
7
  export {
package/dist/dev.js CHANGED
@@ -47,6 +47,18 @@ async function runDev(options = {}) {
47
47
  ...process.env,
48
48
  NODE_ENV: "development"
49
49
  };
50
+ if ((0, import_node_fs.existsSync)(envFile)) {
51
+ const envContent = (0, import_node_fs.readFileSync)(envFile, "utf-8");
52
+ for (const line of envContent.split("\n")) {
53
+ const trimmed = line.trim();
54
+ if (!trimmed || trimmed.startsWith("#")) continue;
55
+ const eqIdx = trimmed.indexOf("=");
56
+ if (eqIdx === -1) continue;
57
+ const key = trimmed.slice(0, eqIdx).trim();
58
+ const value = trimmed.slice(eqIdx + 1).trim();
59
+ env[key] = value;
60
+ }
61
+ }
50
62
  if (port) {
51
63
  env.PORT = String(port);
52
64
  }
package/dist/dev.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runDev
3
- } from "./chunk-RN7BUALR.mjs";
3
+ } from "./chunk-QND74HUW.mjs";
4
4
  import "./chunk-Y6FXYEAI.mjs";
5
5
  export {
6
6
  runDev
package/dist/generate.js CHANGED
@@ -78,7 +78,7 @@ import { createAgent } from "@voltx/agents";
78
78
 
79
79
  export const ${toCamelCase(name)} = createAgent({
80
80
  name: "${name}",
81
- model: "cerebras:llama-4-scout-17b-16e",
81
+ model: "cerebras:llama3.1-8b",
82
82
  instructions: "You are a helpful AI assistant named ${name}.",
83
83
  tools: [
84
84
  // Add tools here:
package/dist/generate.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runGenerate
3
- } from "./chunk-AONHLE42.mjs";
3
+ } from "./chunk-HAFJKS2O.mjs";
4
4
  import "./chunk-Y6FXYEAI.mjs";
5
5
  export {
6
6
  runGenerate
package/dist/index.d.mts CHANGED
@@ -4,6 +4,6 @@ export { BuildOptions, runBuild } from './build.mjs';
4
4
  export { StartOptions, runStart } from './start.mjs';
5
5
  export { GenerateOptions, GeneratorType, runGenerate } from './generate.mjs';
6
6
 
7
- declare const CLI_VERSION = "0.2.0";
7
+ declare const CLI_VERSION = "0.3.3";
8
8
 
9
9
  export { CLI_VERSION };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,6 @@ export { BuildOptions, runBuild } from './build.js';
4
4
  export { StartOptions, runStart } from './start.js';
5
5
  export { GenerateOptions, GeneratorType, runGenerate } from './generate.js';
6
6
 
7
- declare const CLI_VERSION = "0.2.0";
7
+ declare const CLI_VERSION = "0.3.3";
8
8
 
9
9
  export { CLI_VERSION };
package/dist/index.js CHANGED
@@ -210,7 +210,7 @@ async function createProject(options) {
210
210
  );
211
211
  const hasDb = template === "rag-app" || template === "agent-app" || auth === "better-auth";
212
212
  const provider = template === "rag-app" ? "openai" : "cerebras";
213
- const model = template === "rag-app" ? "gpt-4o" : "llama-4-scout-17b-16e";
213
+ const model = template === "rag-app" ? "gpt-4o" : "llama3.1-8b";
214
214
  let configContent = `import { defineConfig } from "@voltx/core";
215
215
 
216
216
  export default defineConfig({
@@ -565,6 +565,18 @@ async function runDev(options = {}) {
565
565
  ...process.env,
566
566
  NODE_ENV: "development"
567
567
  };
568
+ if ((0, import_node_fs.existsSync)(envFile)) {
569
+ const envContent = (0, import_node_fs.readFileSync)(envFile, "utf-8");
570
+ for (const line of envContent.split("\n")) {
571
+ const trimmed = line.trim();
572
+ if (!trimmed || trimmed.startsWith("#")) continue;
573
+ const eqIdx = trimmed.indexOf("=");
574
+ if (eqIdx === -1) continue;
575
+ const key = trimmed.slice(0, eqIdx).trim();
576
+ const value = trimmed.slice(eqIdx + 1).trim();
577
+ env[key] = value;
578
+ }
579
+ }
568
580
  if (port) {
569
581
  env.PORT = String(port);
570
582
  }
@@ -803,6 +815,19 @@ async function runStart(options = {}) {
803
815
  ...process.env,
804
816
  NODE_ENV: "production"
805
817
  };
818
+ const envFile = (0, import_node_path3.resolve)(cwd, ".env");
819
+ if ((0, import_node_fs3.existsSync)(envFile)) {
820
+ const envContent = (0, import_node_fs3.readFileSync)(envFile, "utf-8");
821
+ for (const line of envContent.split("\n")) {
822
+ const trimmed = line.trim();
823
+ if (!trimmed || trimmed.startsWith("#")) continue;
824
+ const eqIdx = trimmed.indexOf("=");
825
+ if (eqIdx === -1) continue;
826
+ const key = trimmed.slice(0, eqIdx).trim();
827
+ const value = trimmed.slice(eqIdx + 1).trim();
828
+ env[key] = value;
829
+ }
830
+ }
806
831
  if (port) {
807
832
  env.PORT = String(port);
808
833
  }
@@ -909,7 +934,7 @@ import { createAgent } from "@voltx/agents";
909
934
 
910
935
  export const ${toCamelCase(name)} = createAgent({
911
936
  name: "${name}",
912
- model: "cerebras:llama-4-scout-17b-16e",
937
+ model: "cerebras:llama3.1-8b",
913
938
  instructions: "You are a helpful AI assistant named ${name}.",
914
939
  tools: [
915
940
  // Add tools here:
@@ -992,7 +1017,7 @@ function toCamelCase(str) {
992
1017
  }
993
1018
 
994
1019
  // src/index.ts
995
- var CLI_VERSION = "0.2.0";
1020
+ var CLI_VERSION = "0.3.3";
996
1021
  // Annotate the CommonJS export names for ESM import in node:
997
1022
  0 && (module.exports = {
998
1023
  CLI_VERSION,
package/dist/index.mjs CHANGED
@@ -3,21 +3,21 @@ import {
3
3
  } from "./chunk-ZB2F3WTS.mjs";
4
4
  import {
5
5
  createProject
6
- } from "./chunk-KFHPTRKZ.mjs";
6
+ } from "./chunk-HM7F67C2.mjs";
7
7
  import {
8
8
  runDev
9
- } from "./chunk-RN7BUALR.mjs";
9
+ } from "./chunk-QND74HUW.mjs";
10
10
  import {
11
11
  runGenerate
12
- } from "./chunk-AONHLE42.mjs";
12
+ } from "./chunk-HAFJKS2O.mjs";
13
13
  import {
14
14
  runStart
15
- } from "./chunk-L3247M3A.mjs";
15
+ } from "./chunk-BXHQORLN.mjs";
16
16
  import "./chunk-IV352HZA.mjs";
17
17
  import "./chunk-Y6FXYEAI.mjs";
18
18
 
19
19
  // src/index.ts
20
- var CLI_VERSION = "0.2.0";
20
+ var CLI_VERSION = "0.3.3";
21
21
  export {
22
22
  CLI_VERSION,
23
23
  createProject,
package/dist/start.js CHANGED
@@ -50,6 +50,19 @@ async function runStart(options = {}) {
50
50
  ...process.env,
51
51
  NODE_ENV: "production"
52
52
  };
53
+ const envFile = (0, import_node_path.resolve)(cwd, ".env");
54
+ if ((0, import_node_fs.existsSync)(envFile)) {
55
+ const envContent = (0, import_node_fs.readFileSync)(envFile, "utf-8");
56
+ for (const line of envContent.split("\n")) {
57
+ const trimmed = line.trim();
58
+ if (!trimmed || trimmed.startsWith("#")) continue;
59
+ const eqIdx = trimmed.indexOf("=");
60
+ if (eqIdx === -1) continue;
61
+ const key = trimmed.slice(0, eqIdx).trim();
62
+ const value = trimmed.slice(eqIdx + 1).trim();
63
+ env[key] = value;
64
+ }
65
+ }
53
66
  if (port) {
54
67
  env.PORT = String(port);
55
68
  }
package/dist/start.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runStart
3
- } from "./chunk-L3247M3A.mjs";
3
+ } from "./chunk-BXHQORLN.mjs";
4
4
  import "./chunk-Y6FXYEAI.mjs";
5
5
  export {
6
6
  runStart
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltx/cli",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "VoltX CLI — dev server, build, start, generate, and scaffolding",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",