@voltx/cli 0.1.1 → 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.
- package/README.md +65 -0
- package/dist/build.d.mts +19 -0
- package/dist/build.d.ts +19 -0
- package/dist/build.js +145 -0
- package/dist/build.mjs +7 -0
- package/dist/chunk-AONHLE42.mjs +141 -0
- package/dist/chunk-BIE3F5AW.mjs +261 -0
- package/dist/chunk-BLBAHJXR.mjs +239 -0
- package/dist/chunk-H2SJO7GX.mjs +102 -0
- package/dist/chunk-IGBR4TFT.mjs +351 -0
- package/dist/chunk-IV352HZA.mjs +107 -0
- package/dist/chunk-KFHPTRKZ.mjs +405 -0
- package/dist/chunk-L3247M3A.mjs +81 -0
- package/dist/chunk-RN7BUALR.mjs +120 -0
- package/dist/chunk-SGL7RBD5.mjs +355 -0
- package/dist/chunk-TUZ3MHD4.mjs +355 -0
- package/dist/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/chunk-YID3JCVU.mjs +131 -0
- package/dist/chunk-ZA7EJWJI.mjs +221 -0
- package/dist/chunk-ZB2F3WTS.mjs +121 -0
- package/dist/chunk-ZLIPYI22.mjs +350 -0
- package/dist/cli.js +971 -53
- package/dist/cli.mjs +123 -23
- package/dist/create.d.mts +1 -0
- package/dist/create.d.ts +1 -0
- package/dist/create.js +334 -30
- package/dist/create.mjs +3 -2
- package/dist/dev.d.mts +19 -0
- package/dist/dev.d.ts +19 -0
- package/dist/dev.js +144 -0
- package/dist/dev.mjs +7 -0
- package/dist/generate.d.mts +13 -0
- package/dist/generate.d.ts +13 -0
- package/dist/generate.js +165 -0
- package/dist/generate.mjs +7 -0
- package/dist/index.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +796 -33
- package/dist/index.mjs +21 -4
- package/dist/start.d.mts +16 -0
- package/dist/start.d.ts +16 -0
- package/dist/start.js +105 -0
- package/dist/start.mjs +7 -0
- package/dist/welcome.js +1 -1
- package/dist/welcome.mjs +2 -1
- package/package.json +24 -22
- 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,26 +154,29 @@ async function createProject(options) {
|
|
|
154
154
|
}
|
|
155
155
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
156
156
|
const templateDeps = {
|
|
157
|
-
blank: {
|
|
157
|
+
blank: {
|
|
158
|
+
"@voltx/core": "^0.3.0",
|
|
159
|
+
"@voltx/server": "^0.3.0"
|
|
160
|
+
},
|
|
158
161
|
chatbot: {
|
|
159
|
-
"@voltx/core": "^0.
|
|
160
|
-
"@voltx/
|
|
161
|
-
"@voltx/
|
|
162
|
-
"@voltx/
|
|
162
|
+
"@voltx/core": "^0.3.0",
|
|
163
|
+
"@voltx/ai": "^0.3.0",
|
|
164
|
+
"@voltx/server": "^0.3.0",
|
|
165
|
+
"@voltx/memory": "^0.3.0"
|
|
163
166
|
},
|
|
164
167
|
"rag-app": {
|
|
165
|
-
"@voltx/core": "^0.
|
|
166
|
-
"@voltx/
|
|
167
|
-
"@voltx/
|
|
168
|
-
"@voltx/
|
|
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"
|
|
169
173
|
},
|
|
170
174
|
"agent-app": {
|
|
171
|
-
"@voltx/core": "^0.
|
|
172
|
-
"@voltx/
|
|
173
|
-
"@voltx/
|
|
174
|
-
"@voltx/
|
|
175
|
-
"@voltx/
|
|
176
|
-
"@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"
|
|
177
180
|
}
|
|
178
181
|
};
|
|
179
182
|
const packageJson = {
|
|
@@ -185,36 +188,335 @@ async function createProject(options) {
|
|
|
185
188
|
build: "voltx build",
|
|
186
189
|
start: "voltx start"
|
|
187
190
|
},
|
|
188
|
-
dependencies:
|
|
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" } : {}
|
|
196
|
+
},
|
|
197
|
+
devDependencies: {
|
|
198
|
+
typescript: "^5.7.0",
|
|
199
|
+
tsx: "^4.21.0",
|
|
200
|
+
tsup: "^8.0.0",
|
|
201
|
+
"@types/node": "^22.0.0"
|
|
202
|
+
}
|
|
189
203
|
};
|
|
190
204
|
fs.writeFileSync(
|
|
191
205
|
path.join(targetDir, "package.json"),
|
|
192
206
|
JSON.stringify(packageJson, null, 2)
|
|
193
207
|
);
|
|
194
|
-
const
|
|
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";
|
|
195
212
|
|
|
196
213
|
export default defineConfig({
|
|
197
214
|
name: "${name}",
|
|
198
215
|
port: 3000,
|
|
199
216
|
ai: {
|
|
200
|
-
provider: "
|
|
201
|
-
model: "
|
|
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,
|
|
202
237
|
},
|
|
203
238
|
});
|
|
204
239
|
`;
|
|
205
240
|
fs.writeFileSync(path.join(targetDir, "voltx.config.ts"), configContent);
|
|
206
|
-
fs.mkdirSync(path.join(targetDir, "src", "routes"), { recursive: true });
|
|
207
|
-
fs.mkdirSync(path.join(targetDir, "
|
|
208
|
-
|
|
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";
|
|
209
246
|
import config from "../voltx.config";
|
|
210
247
|
|
|
211
248
|
const app = createApp(config);
|
|
212
249
|
app.start();
|
|
213
|
-
|
|
214
|
-
|
|
250
|
+
`
|
|
251
|
+
);
|
|
215
252
|
fs.writeFileSync(
|
|
216
|
-
path.join(targetDir, ".
|
|
217
|
-
|
|
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
|
+
`
|
|
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);
|
|
497
|
+
fs.writeFileSync(
|
|
498
|
+
path.join(targetDir, ".gitignore"),
|
|
499
|
+
"node_modules\ndist\n.env\n"
|
|
500
|
+
);
|
|
501
|
+
fs.writeFileSync(
|
|
502
|
+
path.join(targetDir, "tsconfig.json"),
|
|
503
|
+
JSON.stringify(
|
|
504
|
+
{
|
|
505
|
+
compilerOptions: {
|
|
506
|
+
target: "ES2022",
|
|
507
|
+
module: "ESNext",
|
|
508
|
+
moduleResolution: "bundler",
|
|
509
|
+
strict: true,
|
|
510
|
+
esModuleInterop: true,
|
|
511
|
+
skipLibCheck: true,
|
|
512
|
+
outDir: "dist",
|
|
513
|
+
rootDir: "src"
|
|
514
|
+
},
|
|
515
|
+
include: ["src"]
|
|
516
|
+
},
|
|
517
|
+
null,
|
|
518
|
+
2
|
|
519
|
+
)
|
|
218
520
|
);
|
|
219
521
|
printWelcomeBanner(name);
|
|
220
522
|
}
|
|
@@ -229,12 +531,14 @@ var init_create = __esm({
|
|
|
229
531
|
if (isDirectRun) {
|
|
230
532
|
const projectName = process.argv[2];
|
|
231
533
|
if (!projectName) {
|
|
232
|
-
console.log("Usage: create-voltx-app <project-name> [--template chatbot]");
|
|
534
|
+
console.log("Usage: create-voltx-app <project-name> [--template chatbot] [--auth jwt]");
|
|
233
535
|
process.exit(1);
|
|
234
536
|
}
|
|
235
537
|
const templateFlag = process.argv.indexOf("--template");
|
|
236
538
|
const template = templateFlag !== -1 ? process.argv[templateFlag + 1] : "blank";
|
|
237
|
-
|
|
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) => {
|
|
238
542
|
console.error("[voltx] Error:", err);
|
|
239
543
|
process.exit(1);
|
|
240
544
|
});
|
|
@@ -242,56 +546,670 @@ var init_create = __esm({
|
|
|
242
546
|
}
|
|
243
547
|
});
|
|
244
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
|
+
|
|
245
1063
|
// src/cli.ts
|
|
246
1064
|
var args = process.argv.slice(2);
|
|
247
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
|
+
}
|
|
248
1077
|
async function main() {
|
|
249
1078
|
switch (command) {
|
|
1079
|
+
// ─── voltx create ──────────────────────────────────────────────────
|
|
250
1080
|
case "create": {
|
|
251
1081
|
const projectName = args[1];
|
|
252
|
-
if (!projectName) {
|
|
253
|
-
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]");
|
|
254
1084
|
process.exit(1);
|
|
255
1085
|
}
|
|
256
|
-
const
|
|
257
|
-
const
|
|
1086
|
+
const template = parseFlag("--template");
|
|
1087
|
+
const auth = parseFlag("--auth");
|
|
258
1088
|
const { createProject: createProject2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
259
|
-
await createProject2({ name: projectName, template });
|
|
1089
|
+
await createProject2({ name: projectName, template: template ?? "blank", auth: auth ?? "none" });
|
|
260
1090
|
break;
|
|
261
1091
|
}
|
|
1092
|
+
// ─── voltx dev ─────────────────────────────────────────────────────
|
|
262
1093
|
case "dev": {
|
|
263
|
-
|
|
264
|
-
|
|
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
|
+
});
|
|
265
1100
|
break;
|
|
266
1101
|
}
|
|
1102
|
+
// ─── voltx build ───────────────────────────────────────────────────
|
|
267
1103
|
case "build": {
|
|
268
|
-
|
|
269
|
-
|
|
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
|
+
});
|
|
270
1111
|
break;
|
|
271
1112
|
}
|
|
1113
|
+
// ─── voltx start ──────────────────────────────────────────────────
|
|
272
1114
|
case "start": {
|
|
273
|
-
|
|
274
|
-
|
|
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
|
+
});
|
|
275
1121
|
break;
|
|
276
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":
|
|
277
1160
|
default: {
|
|
278
|
-
|
|
1161
|
+
printHelp();
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
function printHelp() {
|
|
1166
|
+
console.log(`
|
|
279
1167
|
\u26A1 voltx \u2014 The AI-first full-stack framework
|
|
280
1168
|
|
|
281
1169
|
Usage:
|
|
282
|
-
voltx
|
|
283
|
-
voltx dev Start the development server
|
|
284
|
-
voltx build Build for production
|
|
285
|
-
voltx start Start the production server
|
|
1170
|
+
voltx <command> [options]
|
|
286
1171
|
|
|
287
|
-
|
|
288
|
-
|
|
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
|
|
289
1178
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
+
`);
|
|
295
1213
|
}
|
|
296
1214
|
main().catch((err) => {
|
|
297
1215
|
console.error("[voltx] Fatal error:", err);
|