@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/dist/cli.js CHANGED
@@ -90,7 +90,7 @@ function printWelcomeBanner(projectName) {
90
90
  console.log(` ${rgb(100, 180, 255, "Next steps:")}`);
91
91
  console.log(` ${rgb(200, 200, 220, ` cd ${projectName}`)}`);
92
92
  console.log(` ${rgb(200, 200, 220, " pnpm install")}`);
93
- console.log(` ${rgb(200, 200, 220, " pnpm dev")}`);
93
+ console.log(` ${rgb(200, 200, 220, " pnpm dev # or: npx voltx dev")}`);
94
94
  console.log("");
95
95
  console.log(dimRule());
96
96
  console.log("");
@@ -146,7 +146,7 @@ __export(create_exports, {
146
146
  createProject: () => createProject
147
147
  });
148
148
  async function createProject(options) {
149
- const { name, template = "blank" } = options;
149
+ const { name, template = "blank", auth = "none" } = options;
150
150
  const targetDir = path.resolve(process.cwd(), name);
151
151
  if (fs.existsSync(targetDir)) {
152
152
  console.error(`[voltx] Directory "${name}" already exists.`);
@@ -154,29 +154,29 @@ async function createProject(options) {
154
154
  }
155
155
  fs.mkdirSync(targetDir, { recursive: true });
156
156
  const templateDeps = {
157
- blank: { "@voltx/core": "^0.1.0" },
157
+ blank: {
158
+ "@voltx/core": "^0.3.0",
159
+ "@voltx/server": "^0.3.0"
160
+ },
158
161
  chatbot: {
159
- "@voltx/core": "^0.1.0",
160
- "@voltx/agents": "^0.1.0",
161
- "@voltx/memory": "^0.1.0",
162
- "@voltx/auth": "^0.1.0",
163
- "@voltx/ui": "^0.1.0"
162
+ "@voltx/core": "^0.3.0",
163
+ "@voltx/ai": "^0.3.0",
164
+ "@voltx/server": "^0.3.0",
165
+ "@voltx/memory": "^0.3.0"
164
166
  },
165
167
  "rag-app": {
166
- "@voltx/core": "^0.1.0",
167
- "@voltx/rag": "^0.1.0",
168
- "@voltx/db": "^0.1.0",
169
- "@voltx/auth": "^0.1.0",
170
- "@voltx/ui": "^0.1.0"
168
+ "@voltx/core": "^0.3.0",
169
+ "@voltx/ai": "^0.3.0",
170
+ "@voltx/server": "^0.3.0",
171
+ "@voltx/rag": "^0.3.0",
172
+ "@voltx/db": "^0.3.0"
171
173
  },
172
174
  "agent-app": {
173
- "@voltx/core": "^0.1.0",
174
- "@voltx/agents": "^0.1.0",
175
- "@voltx/memory": "^0.1.0",
176
- "@voltx/rag": "^0.1.0",
177
- "@voltx/db": "^0.1.0",
178
- "@voltx/auth": "^0.1.0",
179
- "@voltx/ui": "^0.1.0"
175
+ "@voltx/core": "^0.3.0",
176
+ "@voltx/ai": "^0.3.0",
177
+ "@voltx/server": "^0.3.0",
178
+ "@voltx/agents": "^0.3.0",
179
+ "@voltx/memory": "^0.3.0"
180
180
  }
181
181
  };
182
182
  const packageJson = {
@@ -184,14 +184,20 @@ async function createProject(options) {
184
184
  version: "0.1.0",
185
185
  private: true,
186
186
  scripts: {
187
- dev: "tsx src/index.ts",
188
- build: "tsc",
189
- start: "node dist/index.js"
187
+ dev: "voltx dev",
188
+ build: "voltx build",
189
+ start: "voltx start"
190
+ },
191
+ dependencies: {
192
+ ...templateDeps[template] ?? templateDeps["blank"],
193
+ "@voltx/cli": "^0.3.0",
194
+ ...auth === "better-auth" ? { "@voltx/auth": "^0.3.0", "better-auth": "^1.5.0" } : {},
195
+ ...auth === "jwt" ? { "@voltx/auth": "^0.3.0", "jose": "^6.0.0" } : {}
190
196
  },
191
- dependencies: templateDeps[template] ?? templateDeps["blank"],
192
197
  devDependencies: {
193
198
  typescript: "^5.7.0",
194
199
  tsx: "^4.21.0",
200
+ tsup: "^8.0.0",
195
201
  "@types/node": "^22.0.0"
196
202
  }
197
203
  };
@@ -199,31 +205,295 @@ async function createProject(options) {
199
205
  path.join(targetDir, "package.json"),
200
206
  JSON.stringify(packageJson, null, 2)
201
207
  );
202
- const configContent = `import { defineConfig } from "@voltx/core";
208
+ const hasDb = template === "rag-app" || template === "agent-app" || auth === "better-auth";
209
+ const provider = template === "rag-app" ? "openai" : "cerebras";
210
+ const model = template === "rag-app" ? "gpt-4o" : "llama-4-scout-17b-16e";
211
+ let configContent = `import { defineConfig } from "@voltx/core";
203
212
 
204
213
  export default defineConfig({
205
214
  name: "${name}",
206
215
  port: 3000,
207
216
  ai: {
208
- provider: "openai",
209
- model: "gpt-4o",
217
+ provider: "${provider}",
218
+ model: "${model}",
219
+ },`;
220
+ if (hasDb) {
221
+ configContent += `
222
+ db: {
223
+ url: process.env.DATABASE_URL,
224
+ },`;
225
+ }
226
+ if (auth !== "none") {
227
+ configContent += `
228
+ auth: {
229
+ provider: "${auth}",
230
+ },`;
231
+ }
232
+ configContent += `
233
+ server: {
234
+ routesDir: "src/routes",
235
+ staticDir: "public",
236
+ cors: true,
210
237
  },
211
238
  });
212
239
  `;
213
240
  fs.writeFileSync(path.join(targetDir, "voltx.config.ts"), configContent);
214
- fs.mkdirSync(path.join(targetDir, "src", "routes"), { recursive: true });
215
- fs.mkdirSync(path.join(targetDir, "src", "agents"), { recursive: true });
216
- const indexContent = `import { createApp } from "@voltx/core";
241
+ fs.mkdirSync(path.join(targetDir, "src", "routes", "api"), { recursive: true });
242
+ fs.mkdirSync(path.join(targetDir, "public"), { recursive: true });
243
+ fs.writeFileSync(
244
+ path.join(targetDir, "src", "index.ts"),
245
+ `import { createApp } from "@voltx/core";
217
246
  import config from "../voltx.config";
218
247
 
219
248
  const app = createApp(config);
220
249
  app.start();
221
- `;
222
- fs.writeFileSync(path.join(targetDir, "src", "index.ts"), indexContent);
250
+ `
251
+ );
223
252
  fs.writeFileSync(
224
- path.join(targetDir, ".env.example"),
225
- "OPENAI_API_KEY=your-key-here\nDATABASE_URL=\n"
253
+ path.join(targetDir, "src", "routes", "index.ts"),
254
+ `// GET / \u2014 Health check
255
+ import type { Context } from "@voltx/server";
256
+
257
+ export function GET(c: Context) {
258
+ return c.json({ name: "${name}", status: "ok" });
259
+ }
260
+ `
226
261
  );
262
+ if (template === "chatbot" || template === "agent-app") {
263
+ fs.writeFileSync(
264
+ path.join(targetDir, "src", "routes", "api", "chat.ts"),
265
+ `// POST /api/chat \u2014 Streaming chat with conversation memory
266
+ import type { Context } from "@voltx/server";
267
+ import { streamText } from "@voltx/ai";
268
+ import { createMemory } from "@voltx/memory";
269
+
270
+ // In-memory for dev; swap to createMemory("postgres", { url }) for production
271
+ const memory = createMemory({ maxMessages: 50 });
272
+
273
+ export async function POST(c: Context) {
274
+ const { messages, conversationId = "default" } = await c.req.json();
275
+
276
+ // Store the latest user message
277
+ const lastMessage = messages[messages.length - 1];
278
+ if (lastMessage?.role === "user") {
279
+ await memory.add(conversationId, { role: "user", content: lastMessage.content });
280
+ }
281
+
282
+ // Get conversation history from memory
283
+ const history = await memory.get(conversationId);
284
+
285
+ const result = await streamText({
286
+ model: "${provider}:${model}",
287
+ system: "You are a helpful AI assistant.",
288
+ messages: history.map((m) => ({ role: m.role, content: m.content })),
289
+ });
290
+
291
+ // Store assistant response after stream completes
292
+ result.text.then(async (text) => {
293
+ await memory.add(conversationId, { role: "assistant", content: text });
294
+ });
295
+
296
+ return result.toSSEResponse();
297
+ }
298
+ `
299
+ );
300
+ }
301
+ if (template === "agent-app") {
302
+ fs.mkdirSync(path.join(targetDir, "src", "agents"), { recursive: true });
303
+ fs.mkdirSync(path.join(targetDir, "src", "tools"), { recursive: true });
304
+ fs.writeFileSync(
305
+ path.join(targetDir, "src", "agents", "assistant.ts"),
306
+ `import { createAgent } from "@voltx/agents";
307
+ import { searchTool } from "../tools/search";
308
+
309
+ export const assistant = createAgent({
310
+ name: "assistant",
311
+ model: "${provider}:${model}",
312
+ instructions: "You are a helpful AI assistant. Use your tools when needed.",
313
+ tools: [searchTool],
314
+ maxIterations: 5,
315
+ });
316
+ `
317
+ );
318
+ fs.writeFileSync(
319
+ path.join(targetDir, "src", "tools", "search.ts"),
320
+ `import type { Tool } from "@voltx/agents";
321
+
322
+ export const searchTool: Tool = {
323
+ name: "search",
324
+ description: "Search for information on a topic.",
325
+ parameters: {
326
+ type: "object",
327
+ properties: { query: { type: "string", description: "The search query" } },
328
+ required: ["query"],
329
+ },
330
+ async execute(args: { query: string }) {
331
+ return \`Search results for "\${args.query}": Placeholder \u2014 connect a real search API.\`;
332
+ },
333
+ };
334
+ `
335
+ );
336
+ fs.writeFileSync(
337
+ path.join(targetDir, "src", "routes", "api", "agent.ts"),
338
+ `import type { Context } from "@voltx/server";
339
+ import { assistant } from "../../agents/assistant";
340
+
341
+ export async function POST(c: Context) {
342
+ const { input } = await c.req.json();
343
+ if (!input) return c.json({ error: "Missing 'input' field" }, 400);
344
+ const result = await assistant.run(input);
345
+ return c.json({ content: result.content, steps: result.steps });
346
+ }
347
+ `
348
+ );
349
+ }
350
+ if (template === "rag-app") {
351
+ const embedModel = "openai:text-embedding-3-small";
352
+ fs.mkdirSync(path.join(targetDir, "src", "routes", "api", "rag"), { recursive: true });
353
+ fs.writeFileSync(
354
+ path.join(targetDir, "src", "routes", "api", "rag", "query.ts"),
355
+ `// POST /api/rag/query \u2014 Query documents with RAG
356
+ import type { Context } from "@voltx/server";
357
+ import { streamText } from "@voltx/ai";
358
+ import { createRAGPipeline, createEmbedder } from "@voltx/rag";
359
+ import { createVectorStore } from "@voltx/db";
360
+
361
+ const vectorStore = createVectorStore(); // swap to "pinecone" or "pgvector" for production
362
+ const embedder = createEmbedder({ model: "${embedModel}" });
363
+ const rag = createRAGPipeline({ embedder, vectorStore });
364
+
365
+ export async function POST(c: Context) {
366
+ const { question } = await c.req.json();
367
+
368
+ const context = await rag.getContext(question, { topK: 5 });
369
+
370
+ const result = await streamText({
371
+ model: "${provider}:${model}",
372
+ 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}\`,
373
+ messages: [{ role: "user", content: question }],
374
+ });
375
+
376
+ return result.toSSEResponse();
377
+ }
378
+ `
379
+ );
380
+ fs.writeFileSync(
381
+ path.join(targetDir, "src", "routes", "api", "rag", "ingest.ts"),
382
+ `// POST /api/rag/ingest \u2014 Ingest documents into the vector store
383
+ import type { Context } from "@voltx/server";
384
+ import { createRAGPipeline, createEmbedder } from "@voltx/rag";
385
+ import { createVectorStore } from "@voltx/db";
386
+
387
+ const vectorStore = createVectorStore();
388
+ const embedder = createEmbedder({ model: "${embedModel}" });
389
+ const rag = createRAGPipeline({ embedder, vectorStore });
390
+
391
+ export async function POST(c: Context) {
392
+ const { text, idPrefix } = await c.req.json();
393
+
394
+ if (!text || typeof text !== "string") {
395
+ return c.json({ error: "Missing 'text' field" }, 400);
396
+ }
397
+
398
+ const result = await rag.ingest(text, idPrefix ?? "doc");
399
+ return c.json({ status: "ok", chunks: result.chunks, ids: result.ids });
400
+ }
401
+ `
402
+ );
403
+ }
404
+ if (auth === "better-auth") {
405
+ fs.mkdirSync(path.join(targetDir, "src", "routes", "api", "auth"), { recursive: true });
406
+ fs.writeFileSync(
407
+ path.join(targetDir, "src", "routes", "api", "auth", "[...path].ts"),
408
+ `// ALL /api/auth/* \u2014 Better Auth handler
409
+ import type { Context } from "@voltx/server";
410
+ import { auth } from "../../../lib/auth";
411
+ import { createAuthHandler } from "@voltx/auth";
412
+
413
+ const handler = createAuthHandler(auth);
414
+
415
+ export const GET = (c: Context) => handler(c);
416
+ export const POST = (c: Context) => handler(c);
417
+ `
418
+ );
419
+ fs.mkdirSync(path.join(targetDir, "src", "lib"), { recursive: true });
420
+ fs.writeFileSync(
421
+ path.join(targetDir, "src", "lib", "auth.ts"),
422
+ `// Auth configuration \u2014 Better Auth with DB-backed sessions
423
+ import { createAuth, createAuthMiddleware } from "@voltx/auth";
424
+
425
+ export const auth = createAuth("better-auth", {
426
+ database: process.env.DATABASE_URL!,
427
+ emailAndPassword: true,
428
+ });
429
+
430
+ export const authMiddleware = createAuthMiddleware({
431
+ provider: auth,
432
+ publicPaths: ["/api/auth", "/api/health", "/"],
433
+ });
434
+ `
435
+ );
436
+ } else if (auth === "jwt") {
437
+ fs.mkdirSync(path.join(targetDir, "src", "lib"), { recursive: true });
438
+ fs.writeFileSync(
439
+ path.join(targetDir, "src", "lib", "auth.ts"),
440
+ `// Auth configuration \u2014 JWT (stateless)
441
+ import { createAuth, createAuthMiddleware } from "@voltx/auth";
442
+
443
+ export const jwt = createAuth("jwt", {
444
+ secret: process.env.JWT_SECRET!,
445
+ expiresIn: "7d",
446
+ });
447
+
448
+ export const authMiddleware = createAuthMiddleware({
449
+ provider: jwt,
450
+ publicPaths: ["/api/auth", "/api/health", "/"],
451
+ });
452
+ `
453
+ );
454
+ fs.writeFileSync(
455
+ path.join(targetDir, "src", "routes", "api", "auth.ts"),
456
+ `// POST /api/auth/login \u2014 Example JWT login route
457
+ import type { Context } from "@voltx/server";
458
+ import { jwt } from "../../lib/auth";
459
+
460
+ export async function POST(c: Context) {
461
+ const { email, password } = await c.req.json();
462
+
463
+ if (!email || !password) {
464
+ return c.json({ error: "Email and password are required" }, 400);
465
+ }
466
+
467
+ const token = await jwt.sign({ sub: email, email });
468
+ return c.json({ token });
469
+ }
470
+ `
471
+ );
472
+ }
473
+ let envContent = "";
474
+ if (template === "rag-app") {
475
+ 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";
476
+ 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";
477
+ 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";
478
+ } else if (template === "chatbot" || template === "agent-app") {
479
+ 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";
480
+ if (template === "agent-app") {
481
+ envContent += "# \u2500\u2500\u2500 Database (Neon Postgres \u2014 optional) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nDATABASE_URL=\n\n";
482
+ }
483
+ } else {
484
+ 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";
485
+ }
486
+ if (auth === "better-auth") {
487
+ 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";
488
+ if (template !== "rag-app" && template !== "agent-app") {
489
+ envContent += "DATABASE_URL=postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/dbname?sslmode=require\n";
490
+ }
491
+ envContent += "# GITHUB_CLIENT_ID=\n# GITHUB_CLIENT_SECRET=\n\n";
492
+ } else if (auth === "jwt") {
493
+ 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";
494
+ }
495
+ 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";
496
+ fs.writeFileSync(path.join(targetDir, ".env.example"), envContent);
227
497
  fs.writeFileSync(
228
498
  path.join(targetDir, ".gitignore"),
229
499
  "node_modules\ndist\n.env\n"
@@ -261,12 +531,14 @@ var init_create = __esm({
261
531
  if (isDirectRun) {
262
532
  const projectName = process.argv[2];
263
533
  if (!projectName) {
264
- console.log("Usage: create-voltx-app <project-name> [--template chatbot]");
534
+ console.log("Usage: create-voltx-app <project-name> [--template chatbot] [--auth jwt]");
265
535
  process.exit(1);
266
536
  }
267
537
  const templateFlag = process.argv.indexOf("--template");
268
538
  const template = templateFlag !== -1 ? process.argv[templateFlag + 1] : "blank";
269
- createProject({ name: projectName, template }).catch((err) => {
539
+ const authFlag = process.argv.indexOf("--auth");
540
+ const auth = authFlag !== -1 ? process.argv[authFlag + 1] : "none";
541
+ createProject({ name: projectName, template, auth }).catch((err) => {
270
542
  console.error("[voltx] Error:", err);
271
543
  process.exit(1);
272
544
  });
@@ -274,56 +546,670 @@ var init_create = __esm({
274
546
  }
275
547
  });
276
548
 
549
+ // src/dev.ts
550
+ var dev_exports = {};
551
+ __export(dev_exports, {
552
+ runDev: () => runDev
553
+ });
554
+ async function runDev(options = {}) {
555
+ const cwd = process.cwd();
556
+ const {
557
+ port,
558
+ entry = findEntryPoint(cwd),
559
+ clearScreen = true
560
+ } = options;
561
+ if (!entry) {
562
+ console.error("[voltx] Could not find entry point. Expected src/index.ts or src/index.js");
563
+ process.exit(1);
564
+ }
565
+ const entryPath = (0, import_node_path.resolve)(cwd, entry);
566
+ if (!(0, import_node_fs.existsSync)(entryPath)) {
567
+ console.error(`[voltx] Entry file not found: ${entry}`);
568
+ process.exit(1);
569
+ }
570
+ const envFile = (0, import_node_path.resolve)(cwd, ".env");
571
+ const env = {
572
+ ...process.env,
573
+ NODE_ENV: "development"
574
+ };
575
+ if (port) {
576
+ env.PORT = String(port);
577
+ }
578
+ printDevBanner(entry, port);
579
+ const tsxArgs = ["watch"];
580
+ if (clearScreen) {
581
+ tsxArgs.push("--clear-screen=false");
582
+ }
583
+ const watchDirs = [
584
+ "src/routes",
585
+ "src/agents",
586
+ "src/tools",
587
+ "src/jobs",
588
+ "src/lib",
589
+ "voltx.config.ts",
590
+ ...options.watch ?? []
591
+ ];
592
+ tsxArgs.push("--ignore=node_modules", "--ignore=dist", "--ignore=.turbo");
593
+ tsxArgs.push(entry);
594
+ const tsxBin = findTsxBin(cwd);
595
+ let child;
596
+ if (tsxBin) {
597
+ child = (0, import_node_child_process.spawn)(tsxBin, tsxArgs, {
598
+ cwd,
599
+ env,
600
+ stdio: "inherit"
601
+ });
602
+ } else {
603
+ child = (0, import_node_child_process.spawn)("npx", ["tsx", ...tsxArgs], {
604
+ cwd,
605
+ env,
606
+ stdio: "inherit"
607
+ });
608
+ }
609
+ const signals = ["SIGINT", "SIGTERM"];
610
+ for (const signal of signals) {
611
+ process.on(signal, () => {
612
+ child.kill(signal);
613
+ });
614
+ }
615
+ child.on("error", (err) => {
616
+ if (err.code === "ENOENT") {
617
+ console.error("[voltx] tsx not found. Install it with: npm install -D tsx");
618
+ console.error("[voltx] Or run your app directly: npx tsx watch src/index.ts");
619
+ } else {
620
+ console.error("[voltx] Dev server error:", err.message);
621
+ }
622
+ process.exit(1);
623
+ });
624
+ child.on("exit", (code) => {
625
+ process.exit(code ?? 0);
626
+ });
627
+ }
628
+ function findEntryPoint(cwd) {
629
+ const candidates = [
630
+ "src/index.ts",
631
+ "src/index.js",
632
+ "src/index.mts",
633
+ "src/main.ts",
634
+ "src/main.js",
635
+ "index.ts",
636
+ "index.js"
637
+ ];
638
+ for (const candidate of candidates) {
639
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, candidate))) {
640
+ return candidate;
641
+ }
642
+ }
643
+ return null;
644
+ }
645
+ function findTsxBin(cwd) {
646
+ const localBin = (0, import_node_path.join)(cwd, "node_modules", ".bin", "tsx");
647
+ if ((0, import_node_fs.existsSync)(localBin)) return localBin;
648
+ const parentBin = (0, import_node_path.join)(cwd, "..", "node_modules", ".bin", "tsx");
649
+ if ((0, import_node_fs.existsSync)(parentBin)) return parentBin;
650
+ const rootBin = (0, import_node_path.join)(cwd, "..", "..", "node_modules", ".bin", "tsx");
651
+ if ((0, import_node_fs.existsSync)(rootBin)) return rootBin;
652
+ return null;
653
+ }
654
+ function printDevBanner(entry, port) {
655
+ console.log("");
656
+ console.log(" \u26A1 VoltX Dev Server");
657
+ 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");
658
+ console.log(` Entry: ${entry}`);
659
+ if (port) {
660
+ console.log(` Port: ${port}`);
661
+ }
662
+ console.log(` Mode: development (hot reload)`);
663
+ 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");
664
+ console.log("");
665
+ }
666
+ var import_node_child_process, import_node_path, import_node_fs;
667
+ var init_dev = __esm({
668
+ "src/dev.ts"() {
669
+ "use strict";
670
+ import_node_child_process = require("child_process");
671
+ import_node_path = require("path");
672
+ import_node_fs = require("fs");
673
+ }
674
+ });
675
+
676
+ // src/build.ts
677
+ var build_exports = {};
678
+ __export(build_exports, {
679
+ runBuild: () => runBuild
680
+ });
681
+ async function runBuild(options = {}) {
682
+ const cwd = process.cwd();
683
+ const {
684
+ entry = findEntryPoint2(cwd),
685
+ outDir = "dist",
686
+ minify = true,
687
+ sourcemap = false
688
+ } = options;
689
+ if (!entry) {
690
+ console.error("[voltx] Could not find entry point. Expected src/index.ts");
691
+ process.exit(1);
692
+ }
693
+ const entryPath = (0, import_node_path2.resolve)(cwd, entry);
694
+ if (!(0, import_node_fs2.existsSync)(entryPath)) {
695
+ console.error(`[voltx] Entry file not found: ${entry}`);
696
+ process.exit(1);
697
+ }
698
+ console.log("");
699
+ console.log(" \u26A1 VoltX Build");
700
+ 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");
701
+ console.log(` Entry: ${entry}`);
702
+ console.log(` Output: ${outDir}/`);
703
+ console.log(` Minify: ${minify}`);
704
+ 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");
705
+ console.log("");
706
+ (0, import_node_fs2.mkdirSync)((0, import_node_path2.resolve)(cwd, outDir), { recursive: true });
707
+ console.log(" [1/2] Building server...");
708
+ const tsupArgs = [
709
+ entry,
710
+ "--format",
711
+ "esm",
712
+ "--out-dir",
713
+ outDir,
714
+ "--clean",
715
+ "--target",
716
+ "node20"
717
+ ];
718
+ if (minify) tsupArgs.push("--minify");
719
+ if (sourcemap) tsupArgs.push("--sourcemap");
720
+ tsupArgs.push("--no-splitting");
721
+ const tsupBin = findBin(cwd, "tsup");
722
+ await runCommand(
723
+ tsupBin ?? "npx",
724
+ tsupBin ? tsupArgs : ["tsup", ...tsupArgs],
725
+ cwd
726
+ );
727
+ console.log(" \u2713 Server built successfully");
728
+ const frontendDir = (0, import_node_path2.resolve)(cwd, "src", "frontend");
729
+ const viteConfig = (0, import_node_path2.resolve)(cwd, "vite.config.ts");
730
+ const hasFrontend = (0, import_node_fs2.existsSync)(frontendDir) || (0, import_node_fs2.existsSync)(viteConfig);
731
+ if (hasFrontend) {
732
+ console.log(" [2/2] Building frontend...");
733
+ const viteBin = findBin(cwd, "vite");
734
+ const viteArgs = ["build", "--outDir", (0, import_node_path2.join)(outDir, "public")];
735
+ await runCommand(
736
+ viteBin ?? "npx",
737
+ viteBin ? viteArgs : ["vite", ...viteArgs],
738
+ cwd
739
+ );
740
+ console.log(" \u2713 Frontend built successfully");
741
+ } else {
742
+ console.log(" [2/2] No frontend found, skipping...");
743
+ }
744
+ console.log("");
745
+ console.log(" \u26A1 Build complete!");
746
+ console.log(` Run \`voltx start\` to start the production server.`);
747
+ console.log("");
748
+ }
749
+ function findEntryPoint2(cwd) {
750
+ const candidates = [
751
+ "src/index.ts",
752
+ "src/index.js",
753
+ "src/index.mts",
754
+ "src/main.ts",
755
+ "src/main.js"
756
+ ];
757
+ for (const candidate of candidates) {
758
+ if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(cwd, candidate))) {
759
+ return candidate;
760
+ }
761
+ }
762
+ return null;
763
+ }
764
+ function findBin(cwd, name) {
765
+ const paths = [
766
+ (0, import_node_path2.join)(cwd, "node_modules", ".bin", name),
767
+ (0, import_node_path2.join)(cwd, "..", "node_modules", ".bin", name),
768
+ (0, import_node_path2.join)(cwd, "..", "..", "node_modules", ".bin", name)
769
+ ];
770
+ for (const p of paths) {
771
+ if ((0, import_node_fs2.existsSync)(p)) return p;
772
+ }
773
+ return null;
774
+ }
775
+ function runCommand(cmd, args2, cwd) {
776
+ return new Promise((resolve6, reject) => {
777
+ const child = (0, import_node_child_process2.spawn)(cmd, args2, {
778
+ cwd,
779
+ stdio: "inherit",
780
+ env: { ...process.env, NODE_ENV: "production" }
781
+ });
782
+ child.on("error", (err) => {
783
+ if (err.code === "ENOENT") {
784
+ console.error(`[voltx] ${cmd} not found. Install it with: npm install -D ${cmd}`);
785
+ }
786
+ reject(err);
787
+ });
788
+ child.on("exit", (code) => {
789
+ if (code === 0) resolve6();
790
+ else reject(new Error(`${cmd} exited with code ${code}`));
791
+ });
792
+ });
793
+ }
794
+ var import_node_child_process2, import_node_path2, import_node_fs2;
795
+ var init_build = __esm({
796
+ "src/build.ts"() {
797
+ "use strict";
798
+ import_node_child_process2 = require("child_process");
799
+ import_node_path2 = require("path");
800
+ import_node_fs2 = require("fs");
801
+ }
802
+ });
803
+
804
+ // src/start.ts
805
+ var start_exports = {};
806
+ __export(start_exports, {
807
+ runStart: () => runStart
808
+ });
809
+ async function runStart(options = {}) {
810
+ const cwd = process.cwd();
811
+ const { port, outDir = "dist" } = options;
812
+ const distDir = (0, import_node_path3.resolve)(cwd, outDir);
813
+ if (!(0, import_node_fs3.existsSync)(distDir)) {
814
+ console.error(`[voltx] Build output not found at ${outDir}/`);
815
+ console.error("[voltx] Run `voltx build` first to create a production build.");
816
+ process.exit(1);
817
+ }
818
+ const entry = options.entry ?? findDistEntry(distDir);
819
+ if (!entry) {
820
+ console.error(`[voltx] No entry file found in ${outDir}/`);
821
+ console.error("[voltx] Expected index.js, index.mjs, or main.js");
822
+ process.exit(1);
823
+ }
824
+ const entryPath = (0, import_node_path3.resolve)(distDir, entry);
825
+ if (!(0, import_node_fs3.existsSync)(entryPath)) {
826
+ console.error(`[voltx] Entry file not found: ${outDir}/${entry}`);
827
+ process.exit(1);
828
+ }
829
+ const env = {
830
+ ...process.env,
831
+ NODE_ENV: "production"
832
+ };
833
+ if (port) {
834
+ env.PORT = String(port);
835
+ }
836
+ console.log("");
837
+ console.log(" \u26A1 VoltX Production Server");
838
+ 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");
839
+ console.log(` Entry: ${outDir}/${entry}`);
840
+ if (port) {
841
+ console.log(` Port: ${port}`);
842
+ }
843
+ console.log(` Mode: production`);
844
+ 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");
845
+ console.log("");
846
+ const child = (0, import_node_child_process3.spawn)("node", [entryPath], {
847
+ cwd,
848
+ env,
849
+ stdio: "inherit"
850
+ });
851
+ const signals = ["SIGINT", "SIGTERM"];
852
+ for (const signal of signals) {
853
+ process.on(signal, () => {
854
+ child.kill(signal);
855
+ });
856
+ }
857
+ child.on("error", (err) => {
858
+ console.error("[voltx] Failed to start production server:", err.message);
859
+ process.exit(1);
860
+ });
861
+ child.on("exit", (code) => {
862
+ process.exit(code ?? 0);
863
+ });
864
+ }
865
+ function findDistEntry(distDir) {
866
+ const candidates = [
867
+ "index.mjs",
868
+ "index.js",
869
+ "index.cjs",
870
+ "main.mjs",
871
+ "main.js",
872
+ "src/index.mjs",
873
+ "src/index.js"
874
+ ];
875
+ for (const candidate of candidates) {
876
+ if ((0, import_node_fs3.existsSync)((0, import_node_path3.join)(distDir, candidate))) {
877
+ return candidate;
878
+ }
879
+ }
880
+ return null;
881
+ }
882
+ var import_node_child_process3, import_node_path3, import_node_fs3;
883
+ var init_start = __esm({
884
+ "src/start.ts"() {
885
+ "use strict";
886
+ import_node_child_process3 = require("child_process");
887
+ import_node_path3 = require("path");
888
+ import_node_fs3 = require("fs");
889
+ }
890
+ });
891
+
892
+ // src/generate.ts
893
+ var generate_exports = {};
894
+ __export(generate_exports, {
895
+ runGenerate: () => runGenerate
896
+ });
897
+ async function runGenerate(options) {
898
+ const cwd = process.cwd();
899
+ const { type, name } = options;
900
+ switch (type) {
901
+ case "route":
902
+ generateRoute(cwd, name, options.method);
903
+ break;
904
+ case "agent":
905
+ generateAgent(cwd, name);
906
+ break;
907
+ case "tool":
908
+ generateTool(cwd, name);
909
+ break;
910
+ case "job":
911
+ generateJob(cwd, name);
912
+ break;
913
+ default:
914
+ console.error(`[voltx] Unknown generator type: ${type}`);
915
+ console.error("[voltx] Available: route, agent, tool, job");
916
+ process.exit(1);
917
+ }
918
+ }
919
+ function generateRoute(cwd, name, method = "POST") {
920
+ const routePath = name.startsWith("/") ? name.slice(1) : name;
921
+ const filePath = (0, import_node_path4.join)(cwd, "src", "routes", `${routePath}.ts`);
922
+ if ((0, import_node_fs4.existsSync)(filePath)) {
923
+ console.error(`[voltx] Route already exists: src/routes/${routePath}.ts`);
924
+ process.exit(1);
925
+ }
926
+ const upperMethod = method.toUpperCase();
927
+ const urlPath = "/" + routePath;
928
+ const content = `// ${upperMethod} ${urlPath}
929
+ import type { Context } from "@voltx/server";
930
+
931
+ export async function ${upperMethod}(c: Context) {
932
+ return c.json({ message: "Hello from ${urlPath}" });
933
+ }
934
+ `;
935
+ writeFileSafe(filePath, content);
936
+ console.log(` \u2713 Created route: src/routes/${routePath}.ts`);
937
+ console.log(` ${upperMethod} ${urlPath}`);
938
+ }
939
+ function generateAgent(cwd, name) {
940
+ const filePath = (0, import_node_path4.join)(cwd, "src", "agents", `${name}.ts`);
941
+ if ((0, import_node_fs4.existsSync)(filePath)) {
942
+ console.error(`[voltx] Agent already exists: src/agents/${name}.ts`);
943
+ process.exit(1);
944
+ }
945
+ const content = `// Agent: ${name}
946
+ import { createAgent } from "@voltx/agents";
947
+
948
+ export const ${toCamelCase(name)} = createAgent({
949
+ name: "${name}",
950
+ model: "cerebras:llama-4-scout-17b-16e",
951
+ instructions: "You are a helpful AI assistant named ${name}.",
952
+ tools: [
953
+ // Add tools here:
954
+ // {
955
+ // name: "example",
956
+ // description: "An example tool",
957
+ // parameters: { type: "object", properties: { input: { type: "string" } }, required: ["input"] },
958
+ // execute: async (params) => \`Processed: \${params.input}\`,
959
+ // },
960
+ ],
961
+ });
962
+ `;
963
+ writeFileSafe(filePath, content);
964
+ console.log(` \u2713 Created agent: src/agents/${name}.ts`);
965
+ }
966
+ function generateTool(cwd, name) {
967
+ const filePath = (0, import_node_path4.join)(cwd, "src", "tools", `${name}.ts`);
968
+ if ((0, import_node_fs4.existsSync)(filePath)) {
969
+ console.error(`[voltx] Tool already exists: src/tools/${name}.ts`);
970
+ process.exit(1);
971
+ }
972
+ const content = `// Tool: ${name}
973
+
974
+ export const ${toCamelCase(name)}Tool = {
975
+ name: "${name}",
976
+ description: "TODO: Describe what this tool does",
977
+ parameters: {
978
+ type: "object" as const,
979
+ properties: {
980
+ input: { type: "string", description: "The input to process" },
981
+ },
982
+ required: ["input"],
983
+ },
984
+ execute: async (params: { input: string }): Promise<string> => {
985
+ // TODO: Implement tool logic
986
+ return \`${name} result for: \${params.input}\`;
987
+ },
988
+ };
989
+ `;
990
+ writeFileSafe(filePath, content);
991
+ console.log(` \u2713 Created tool: src/tools/${name}.ts`);
992
+ }
993
+ function generateJob(cwd, name) {
994
+ const filePath = (0, import_node_path4.join)(cwd, "src", "jobs", `${name}.ts`);
995
+ if ((0, import_node_fs4.existsSync)(filePath)) {
996
+ console.error(`[voltx] Job already exists: src/jobs/${name}.ts`);
997
+ process.exit(1);
998
+ }
999
+ const content = `// Job: ${name}
1000
+ // Runs on a schedule or triggered via ctx.jobs.enqueue("${name}", data)
1001
+
1002
+ export const config = {
1003
+ // Cron schedule (uncomment to enable):
1004
+ // schedule: "0 */6 * * *", // every 6 hours
1005
+ //
1006
+ // Or make it a queue job (triggered on-demand):
1007
+ queue: true,
1008
+ retries: 3,
1009
+ timeout: "5m",
1010
+ };
1011
+
1012
+ export async function run(ctx: any, data?: Record<string, unknown>) {
1013
+ console.log("[job:${name}] Running...", data);
1014
+
1015
+ // TODO: Implement job logic
1016
+
1017
+ console.log("[job:${name}] Done.");
1018
+ }
1019
+ `;
1020
+ writeFileSafe(filePath, content);
1021
+ console.log(` \u2713 Created job: src/jobs/${name}.ts`);
1022
+ }
1023
+ function writeFileSafe(filePath, content) {
1024
+ const dir = (0, import_node_path4.dirname)(filePath);
1025
+ (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
1026
+ (0, import_node_fs4.writeFileSync)(filePath, content, "utf-8");
1027
+ }
1028
+ function toCamelCase(str) {
1029
+ return str.replace(/[-_](.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toLowerCase());
1030
+ }
1031
+ var import_node_fs4, import_node_path4;
1032
+ var init_generate = __esm({
1033
+ "src/generate.ts"() {
1034
+ "use strict";
1035
+ import_node_fs4 = require("fs");
1036
+ import_node_path4 = require("path");
1037
+ }
1038
+ });
1039
+
1040
+ // src/index.ts
1041
+ var index_exports = {};
1042
+ __export(index_exports, {
1043
+ CLI_VERSION: () => CLI_VERSION,
1044
+ createProject: () => createProject,
1045
+ runBuild: () => runBuild,
1046
+ runDev: () => runDev,
1047
+ runGenerate: () => runGenerate,
1048
+ runStart: () => runStart
1049
+ });
1050
+ var CLI_VERSION;
1051
+ var init_index = __esm({
1052
+ "src/index.ts"() {
1053
+ "use strict";
1054
+ init_create();
1055
+ init_dev();
1056
+ init_build();
1057
+ init_start();
1058
+ init_generate();
1059
+ CLI_VERSION = "0.2.0";
1060
+ }
1061
+ });
1062
+
277
1063
  // src/cli.ts
278
1064
  var args = process.argv.slice(2);
279
1065
  var command = args[0];
1066
+ function parseFlag(flag) {
1067
+ const idx = args.indexOf(flag);
1068
+ return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
1069
+ }
1070
+ function hasFlag(flag) {
1071
+ return args.includes(flag);
1072
+ }
1073
+ function parsePort() {
1074
+ const portStr = parseFlag("--port") ?? parseFlag("-p");
1075
+ return portStr ? Number(portStr) : void 0;
1076
+ }
280
1077
  async function main() {
281
1078
  switch (command) {
1079
+ // ─── voltx create ──────────────────────────────────────────────────
282
1080
  case "create": {
283
1081
  const projectName = args[1];
284
- if (!projectName) {
285
- console.error("[voltx] Usage: voltx create <project-name> [--template chatbot|rag-app|agent-app]");
1082
+ if (!projectName || projectName.startsWith("-")) {
1083
+ console.error("[voltx] Usage: voltx create <project-name> [--template chatbot|rag-app|agent-app] [--auth better-auth|jwt|none]");
286
1084
  process.exit(1);
287
1085
  }
288
- const templateFlag = args.indexOf("--template");
289
- const template = templateFlag !== -1 ? args[templateFlag + 1] : "blank";
1086
+ const template = parseFlag("--template");
1087
+ const auth = parseFlag("--auth");
290
1088
  const { createProject: createProject2 } = await Promise.resolve().then(() => (init_create(), create_exports));
291
- await createProject2({ name: projectName, template });
1089
+ await createProject2({ name: projectName, template: template ?? "blank", auth: auth ?? "none" });
292
1090
  break;
293
1091
  }
1092
+ // ─── voltx dev ─────────────────────────────────────────────────────
294
1093
  case "dev": {
295
- console.log("[voltx] Starting dev server...");
296
- console.log("[voltx] Dev server not yet implemented \u2014 coming in Phase 2");
1094
+ const { runDev: runDev2 } = await Promise.resolve().then(() => (init_dev(), dev_exports));
1095
+ await runDev2({
1096
+ port: parsePort(),
1097
+ entry: parseFlag("--entry"),
1098
+ clearScreen: !hasFlag("--no-clear")
1099
+ });
297
1100
  break;
298
1101
  }
1102
+ // ─── voltx build ───────────────────────────────────────────────────
299
1103
  case "build": {
300
- console.log("[voltx] Building for production...");
301
- console.log("[voltx] Build not yet implemented \u2014 coming in Phase 2");
1104
+ const { runBuild: runBuild2 } = await Promise.resolve().then(() => (init_build(), build_exports));
1105
+ await runBuild2({
1106
+ entry: parseFlag("--entry"),
1107
+ outDir: parseFlag("--out-dir") ?? parseFlag("-o"),
1108
+ minify: !hasFlag("--no-minify"),
1109
+ sourcemap: hasFlag("--sourcemap")
1110
+ });
302
1111
  break;
303
1112
  }
1113
+ // ─── voltx start ──────────────────────────────────────────────────
304
1114
  case "start": {
305
- console.log("[voltx] Starting production server...");
306
- console.log("[voltx] Start not yet implemented \u2014 coming in Phase 2");
1115
+ const { runStart: runStart2 } = await Promise.resolve().then(() => (init_start(), start_exports));
1116
+ await runStart2({
1117
+ port: parsePort(),
1118
+ outDir: parseFlag("--out-dir") ?? parseFlag("-o"),
1119
+ entry: parseFlag("--entry")
1120
+ });
307
1121
  break;
308
1122
  }
1123
+ // ─── voltx generate ───────────────────────────────────────────────
1124
+ case "generate":
1125
+ case "g": {
1126
+ const type = args[1];
1127
+ const name = args[2];
1128
+ if (!type || !name) {
1129
+ console.error("[voltx] Usage: voltx generate <type> <name>");
1130
+ console.error("");
1131
+ console.error(" Types:");
1132
+ console.error(" route <path> Generate a new API route (e.g., api/users)");
1133
+ console.error(" agent <name> Generate a new agent (e.g., assistant)");
1134
+ console.error(" tool <name> Generate a new tool (e.g., search)");
1135
+ console.error(" job <name> Generate a new background job (e.g., cleanup)");
1136
+ console.error("");
1137
+ console.error(" Options:");
1138
+ console.error(" --method <GET|POST|PUT|DELETE> HTTP method for routes (default: POST)");
1139
+ process.exit(1);
1140
+ }
1141
+ const { runGenerate: runGenerate2 } = await Promise.resolve().then(() => (init_generate(), generate_exports));
1142
+ await runGenerate2({
1143
+ type,
1144
+ name,
1145
+ method: parseFlag("--method")
1146
+ });
1147
+ break;
1148
+ }
1149
+ // ─── voltx --version ──────────────────────────────────────────────
1150
+ case "--version":
1151
+ case "-v": {
1152
+ const { CLI_VERSION: CLI_VERSION2 } = await Promise.resolve().then(() => (init_index(), index_exports));
1153
+ console.log(`voltx v${CLI_VERSION2}`);
1154
+ break;
1155
+ }
1156
+ // ─── voltx help / default ─────────────────────────────────────────
1157
+ case "help":
1158
+ case "--help":
1159
+ case "-h":
309
1160
  default: {
310
- console.log(`
1161
+ printHelp();
1162
+ }
1163
+ }
1164
+ }
1165
+ function printHelp() {
1166
+ console.log(`
311
1167
  \u26A1 voltx \u2014 The AI-first full-stack framework
312
1168
 
313
1169
  Usage:
314
- voltx create <name> Create a new VoltX project
315
- voltx dev Start the development server
316
- voltx build Build for production
317
- voltx start Start the production server
1170
+ voltx <command> [options]
318
1171
 
319
- Options:
320
- --template <type> Template: chatbot, rag-app, agent-app, blank (default)
1172
+ Commands:
1173
+ create <name> Create a new VoltX project
1174
+ dev Start the development server (hot reload)
1175
+ build Build for production
1176
+ start Start the production server
1177
+ generate <type> <name> Generate routes, agents, tools, or jobs
321
1178
 
322
- Example:
323
- voltx create my-app --template chatbot
324
- `);
325
- }
326
- }
1179
+ Create Options:
1180
+ --template <type> Template: chatbot, rag-app, agent-app, blank (default)
1181
+ --auth <provider> Auth: better-auth, jwt, none (default)
1182
+
1183
+ Dev Options:
1184
+ --port, -p <number> Port override
1185
+ --entry <file> Custom entry file (default: src/index.ts)
1186
+ --no-clear Don't clear screen on restart
1187
+
1188
+ Build Options:
1189
+ --entry <file> Custom entry file
1190
+ --out-dir, -o <dir> Output directory (default: dist)
1191
+ --no-minify Skip minification
1192
+ --sourcemap Generate source maps
1193
+
1194
+ Start Options:
1195
+ --port, -p <number> Port override
1196
+ --out-dir, -o <dir> Build output directory (default: dist)
1197
+ --entry <file> Entry file within output dir
1198
+
1199
+ Generate Types:
1200
+ route <path> API route (e.g., voltx generate route api/users)
1201
+ agent <name> Agent (e.g., voltx generate agent assistant)
1202
+ tool <name> Tool (e.g., voltx generate tool search)
1203
+ job <name> Background job (e.g., voltx generate job cleanup)
1204
+
1205
+ Examples:
1206
+ voltx create my-app --template chatbot --auth jwt
1207
+ voltx dev --port 4000
1208
+ voltx build --sourcemap
1209
+ voltx start
1210
+ voltx generate route api/users --method GET
1211
+ voltx generate agent assistant
1212
+ `);
327
1213
  }
328
1214
  main().catch((err) => {
329
1215
  console.error("[voltx] Fatal error:", err);