@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/index.js
CHANGED
|
@@ -31,7 +31,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
CLI_VERSION: () => CLI_VERSION,
|
|
34
|
-
createProject: () => createProject
|
|
34
|
+
createProject: () => createProject,
|
|
35
|
+
runBuild: () => runBuild,
|
|
36
|
+
runDev: () => runDev,
|
|
37
|
+
runGenerate: () => runGenerate,
|
|
38
|
+
runStart: () => runStart
|
|
35
39
|
});
|
|
36
40
|
module.exports = __toCommonJS(index_exports);
|
|
37
41
|
|
|
@@ -120,7 +124,7 @@ function printWelcomeBanner(projectName) {
|
|
|
120
124
|
console.log(` ${rgb(100, 180, 255, "Next steps:")}`);
|
|
121
125
|
console.log(` ${rgb(200, 200, 220, ` cd ${projectName}`)}`);
|
|
122
126
|
console.log(` ${rgb(200, 200, 220, " pnpm install")}`);
|
|
123
|
-
console.log(` ${rgb(200, 200, 220, " pnpm dev")}`);
|
|
127
|
+
console.log(` ${rgb(200, 200, 220, " pnpm dev # or: npx voltx dev")}`);
|
|
124
128
|
console.log("");
|
|
125
129
|
console.log(dimRule());
|
|
126
130
|
console.log("");
|
|
@@ -145,7 +149,7 @@ function printWelcomeBanner(projectName) {
|
|
|
145
149
|
|
|
146
150
|
// src/create.ts
|
|
147
151
|
async function createProject(options) {
|
|
148
|
-
const { name, template = "blank" } = options;
|
|
152
|
+
const { name, template = "blank", auth = "none" } = options;
|
|
149
153
|
const targetDir = path.resolve(process.cwd(), name);
|
|
150
154
|
if (fs.existsSync(targetDir)) {
|
|
151
155
|
console.error(`[voltx] Directory "${name}" already exists.`);
|
|
@@ -153,26 +157,29 @@ async function createProject(options) {
|
|
|
153
157
|
}
|
|
154
158
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
155
159
|
const templateDeps = {
|
|
156
|
-
blank: {
|
|
160
|
+
blank: {
|
|
161
|
+
"@voltx/core": "^0.3.0",
|
|
162
|
+
"@voltx/server": "^0.3.0"
|
|
163
|
+
},
|
|
157
164
|
chatbot: {
|
|
158
|
-
"@voltx/core": "^0.
|
|
159
|
-
"@voltx/
|
|
160
|
-
"@voltx/
|
|
161
|
-
"@voltx/
|
|
165
|
+
"@voltx/core": "^0.3.0",
|
|
166
|
+
"@voltx/ai": "^0.3.0",
|
|
167
|
+
"@voltx/server": "^0.3.0",
|
|
168
|
+
"@voltx/memory": "^0.3.0"
|
|
162
169
|
},
|
|
163
170
|
"rag-app": {
|
|
164
|
-
"@voltx/core": "^0.
|
|
165
|
-
"@voltx/
|
|
166
|
-
"@voltx/
|
|
167
|
-
"@voltx/
|
|
171
|
+
"@voltx/core": "^0.3.0",
|
|
172
|
+
"@voltx/ai": "^0.3.0",
|
|
173
|
+
"@voltx/server": "^0.3.0",
|
|
174
|
+
"@voltx/rag": "^0.3.0",
|
|
175
|
+
"@voltx/db": "^0.3.0"
|
|
168
176
|
},
|
|
169
177
|
"agent-app": {
|
|
170
|
-
"@voltx/core": "^0.
|
|
171
|
-
"@voltx/
|
|
172
|
-
"@voltx/
|
|
173
|
-
"@voltx/
|
|
174
|
-
"@voltx/
|
|
175
|
-
"@voltx/ui": "^0.1.0"
|
|
178
|
+
"@voltx/core": "^0.3.0",
|
|
179
|
+
"@voltx/ai": "^0.3.0",
|
|
180
|
+
"@voltx/server": "^0.3.0",
|
|
181
|
+
"@voltx/agents": "^0.3.0",
|
|
182
|
+
"@voltx/memory": "^0.3.0"
|
|
176
183
|
}
|
|
177
184
|
};
|
|
178
185
|
const packageJson = {
|
|
@@ -184,36 +191,335 @@ async function createProject(options) {
|
|
|
184
191
|
build: "voltx build",
|
|
185
192
|
start: "voltx start"
|
|
186
193
|
},
|
|
187
|
-
dependencies:
|
|
194
|
+
dependencies: {
|
|
195
|
+
...templateDeps[template] ?? templateDeps["blank"],
|
|
196
|
+
"@voltx/cli": "^0.3.0",
|
|
197
|
+
...auth === "better-auth" ? { "@voltx/auth": "^0.3.0", "better-auth": "^1.5.0" } : {},
|
|
198
|
+
...auth === "jwt" ? { "@voltx/auth": "^0.3.0", "jose": "^6.0.0" } : {}
|
|
199
|
+
},
|
|
200
|
+
devDependencies: {
|
|
201
|
+
typescript: "^5.7.0",
|
|
202
|
+
tsx: "^4.21.0",
|
|
203
|
+
tsup: "^8.0.0",
|
|
204
|
+
"@types/node": "^22.0.0"
|
|
205
|
+
}
|
|
188
206
|
};
|
|
189
207
|
fs.writeFileSync(
|
|
190
208
|
path.join(targetDir, "package.json"),
|
|
191
209
|
JSON.stringify(packageJson, null, 2)
|
|
192
210
|
);
|
|
193
|
-
const
|
|
211
|
+
const hasDb = template === "rag-app" || template === "agent-app" || auth === "better-auth";
|
|
212
|
+
const provider = template === "rag-app" ? "openai" : "cerebras";
|
|
213
|
+
const model = template === "rag-app" ? "gpt-4o" : "llama-4-scout-17b-16e";
|
|
214
|
+
let configContent = `import { defineConfig } from "@voltx/core";
|
|
194
215
|
|
|
195
216
|
export default defineConfig({
|
|
196
217
|
name: "${name}",
|
|
197
218
|
port: 3000,
|
|
198
219
|
ai: {
|
|
199
|
-
provider: "
|
|
200
|
-
model: "
|
|
220
|
+
provider: "${provider}",
|
|
221
|
+
model: "${model}",
|
|
222
|
+
},`;
|
|
223
|
+
if (hasDb) {
|
|
224
|
+
configContent += `
|
|
225
|
+
db: {
|
|
226
|
+
url: process.env.DATABASE_URL,
|
|
227
|
+
},`;
|
|
228
|
+
}
|
|
229
|
+
if (auth !== "none") {
|
|
230
|
+
configContent += `
|
|
231
|
+
auth: {
|
|
232
|
+
provider: "${auth}",
|
|
233
|
+
},`;
|
|
234
|
+
}
|
|
235
|
+
configContent += `
|
|
236
|
+
server: {
|
|
237
|
+
routesDir: "src/routes",
|
|
238
|
+
staticDir: "public",
|
|
239
|
+
cors: true,
|
|
201
240
|
},
|
|
202
241
|
});
|
|
203
242
|
`;
|
|
204
243
|
fs.writeFileSync(path.join(targetDir, "voltx.config.ts"), configContent);
|
|
205
|
-
fs.mkdirSync(path.join(targetDir, "src", "routes"), { recursive: true });
|
|
206
|
-
fs.mkdirSync(path.join(targetDir, "
|
|
207
|
-
|
|
244
|
+
fs.mkdirSync(path.join(targetDir, "src", "routes", "api"), { recursive: true });
|
|
245
|
+
fs.mkdirSync(path.join(targetDir, "public"), { recursive: true });
|
|
246
|
+
fs.writeFileSync(
|
|
247
|
+
path.join(targetDir, "src", "index.ts"),
|
|
248
|
+
`import { createApp } from "@voltx/core";
|
|
208
249
|
import config from "../voltx.config";
|
|
209
250
|
|
|
210
251
|
const app = createApp(config);
|
|
211
252
|
app.start();
|
|
212
|
-
|
|
213
|
-
|
|
253
|
+
`
|
|
254
|
+
);
|
|
214
255
|
fs.writeFileSync(
|
|
215
|
-
path.join(targetDir, ".
|
|
216
|
-
|
|
256
|
+
path.join(targetDir, "src", "routes", "index.ts"),
|
|
257
|
+
`// GET / \u2014 Health check
|
|
258
|
+
import type { Context } from "@voltx/server";
|
|
259
|
+
|
|
260
|
+
export function GET(c: Context) {
|
|
261
|
+
return c.json({ name: "${name}", status: "ok" });
|
|
262
|
+
}
|
|
263
|
+
`
|
|
264
|
+
);
|
|
265
|
+
if (template === "chatbot" || template === "agent-app") {
|
|
266
|
+
fs.writeFileSync(
|
|
267
|
+
path.join(targetDir, "src", "routes", "api", "chat.ts"),
|
|
268
|
+
`// POST /api/chat \u2014 Streaming chat with conversation memory
|
|
269
|
+
import type { Context } from "@voltx/server";
|
|
270
|
+
import { streamText } from "@voltx/ai";
|
|
271
|
+
import { createMemory } from "@voltx/memory";
|
|
272
|
+
|
|
273
|
+
// In-memory for dev; swap to createMemory("postgres", { url }) for production
|
|
274
|
+
const memory = createMemory({ maxMessages: 50 });
|
|
275
|
+
|
|
276
|
+
export async function POST(c: Context) {
|
|
277
|
+
const { messages, conversationId = "default" } = await c.req.json();
|
|
278
|
+
|
|
279
|
+
// Store the latest user message
|
|
280
|
+
const lastMessage = messages[messages.length - 1];
|
|
281
|
+
if (lastMessage?.role === "user") {
|
|
282
|
+
await memory.add(conversationId, { role: "user", content: lastMessage.content });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Get conversation history from memory
|
|
286
|
+
const history = await memory.get(conversationId);
|
|
287
|
+
|
|
288
|
+
const result = await streamText({
|
|
289
|
+
model: "${provider}:${model}",
|
|
290
|
+
system: "You are a helpful AI assistant.",
|
|
291
|
+
messages: history.map((m) => ({ role: m.role, content: m.content })),
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Store assistant response after stream completes
|
|
295
|
+
result.text.then(async (text) => {
|
|
296
|
+
await memory.add(conversationId, { role: "assistant", content: text });
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
return result.toSSEResponse();
|
|
300
|
+
}
|
|
301
|
+
`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
if (template === "agent-app") {
|
|
305
|
+
fs.mkdirSync(path.join(targetDir, "src", "agents"), { recursive: true });
|
|
306
|
+
fs.mkdirSync(path.join(targetDir, "src", "tools"), { recursive: true });
|
|
307
|
+
fs.writeFileSync(
|
|
308
|
+
path.join(targetDir, "src", "agents", "assistant.ts"),
|
|
309
|
+
`import { createAgent } from "@voltx/agents";
|
|
310
|
+
import { searchTool } from "../tools/search";
|
|
311
|
+
|
|
312
|
+
export const assistant = createAgent({
|
|
313
|
+
name: "assistant",
|
|
314
|
+
model: "${provider}:${model}",
|
|
315
|
+
instructions: "You are a helpful AI assistant. Use your tools when needed.",
|
|
316
|
+
tools: [searchTool],
|
|
317
|
+
maxIterations: 5,
|
|
318
|
+
});
|
|
319
|
+
`
|
|
320
|
+
);
|
|
321
|
+
fs.writeFileSync(
|
|
322
|
+
path.join(targetDir, "src", "tools", "search.ts"),
|
|
323
|
+
`import type { Tool } from "@voltx/agents";
|
|
324
|
+
|
|
325
|
+
export const searchTool: Tool = {
|
|
326
|
+
name: "search",
|
|
327
|
+
description: "Search for information on a topic.",
|
|
328
|
+
parameters: {
|
|
329
|
+
type: "object",
|
|
330
|
+
properties: { query: { type: "string", description: "The search query" } },
|
|
331
|
+
required: ["query"],
|
|
332
|
+
},
|
|
333
|
+
async execute(args: { query: string }) {
|
|
334
|
+
return \`Search results for "\${args.query}": Placeholder \u2014 connect a real search API.\`;
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
`
|
|
338
|
+
);
|
|
339
|
+
fs.writeFileSync(
|
|
340
|
+
path.join(targetDir, "src", "routes", "api", "agent.ts"),
|
|
341
|
+
`import type { Context } from "@voltx/server";
|
|
342
|
+
import { assistant } from "../../agents/assistant";
|
|
343
|
+
|
|
344
|
+
export async function POST(c: Context) {
|
|
345
|
+
const { input } = await c.req.json();
|
|
346
|
+
if (!input) return c.json({ error: "Missing 'input' field" }, 400);
|
|
347
|
+
const result = await assistant.run(input);
|
|
348
|
+
return c.json({ content: result.content, steps: result.steps });
|
|
349
|
+
}
|
|
350
|
+
`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
if (template === "rag-app") {
|
|
354
|
+
const embedModel = "openai:text-embedding-3-small";
|
|
355
|
+
fs.mkdirSync(path.join(targetDir, "src", "routes", "api", "rag"), { recursive: true });
|
|
356
|
+
fs.writeFileSync(
|
|
357
|
+
path.join(targetDir, "src", "routes", "api", "rag", "query.ts"),
|
|
358
|
+
`// POST /api/rag/query \u2014 Query documents with RAG
|
|
359
|
+
import type { Context } from "@voltx/server";
|
|
360
|
+
import { streamText } from "@voltx/ai";
|
|
361
|
+
import { createRAGPipeline, createEmbedder } from "@voltx/rag";
|
|
362
|
+
import { createVectorStore } from "@voltx/db";
|
|
363
|
+
|
|
364
|
+
const vectorStore = createVectorStore(); // swap to "pinecone" or "pgvector" for production
|
|
365
|
+
const embedder = createEmbedder({ model: "${embedModel}" });
|
|
366
|
+
const rag = createRAGPipeline({ embedder, vectorStore });
|
|
367
|
+
|
|
368
|
+
export async function POST(c: Context) {
|
|
369
|
+
const { question } = await c.req.json();
|
|
370
|
+
|
|
371
|
+
const context = await rag.getContext(question, { topK: 5 });
|
|
372
|
+
|
|
373
|
+
const result = await streamText({
|
|
374
|
+
model: "${provider}:${model}",
|
|
375
|
+
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}\`,
|
|
376
|
+
messages: [{ role: "user", content: question }],
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
return result.toSSEResponse();
|
|
380
|
+
}
|
|
381
|
+
`
|
|
382
|
+
);
|
|
383
|
+
fs.writeFileSync(
|
|
384
|
+
path.join(targetDir, "src", "routes", "api", "rag", "ingest.ts"),
|
|
385
|
+
`// POST /api/rag/ingest \u2014 Ingest documents into the vector store
|
|
386
|
+
import type { Context } from "@voltx/server";
|
|
387
|
+
import { createRAGPipeline, createEmbedder } from "@voltx/rag";
|
|
388
|
+
import { createVectorStore } from "@voltx/db";
|
|
389
|
+
|
|
390
|
+
const vectorStore = createVectorStore();
|
|
391
|
+
const embedder = createEmbedder({ model: "${embedModel}" });
|
|
392
|
+
const rag = createRAGPipeline({ embedder, vectorStore });
|
|
393
|
+
|
|
394
|
+
export async function POST(c: Context) {
|
|
395
|
+
const { text, idPrefix } = await c.req.json();
|
|
396
|
+
|
|
397
|
+
if (!text || typeof text !== "string") {
|
|
398
|
+
return c.json({ error: "Missing 'text' field" }, 400);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const result = await rag.ingest(text, idPrefix ?? "doc");
|
|
402
|
+
return c.json({ status: "ok", chunks: result.chunks, ids: result.ids });
|
|
403
|
+
}
|
|
404
|
+
`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
if (auth === "better-auth") {
|
|
408
|
+
fs.mkdirSync(path.join(targetDir, "src", "routes", "api", "auth"), { recursive: true });
|
|
409
|
+
fs.writeFileSync(
|
|
410
|
+
path.join(targetDir, "src", "routes", "api", "auth", "[...path].ts"),
|
|
411
|
+
`// ALL /api/auth/* \u2014 Better Auth handler
|
|
412
|
+
import type { Context } from "@voltx/server";
|
|
413
|
+
import { auth } from "../../../lib/auth";
|
|
414
|
+
import { createAuthHandler } from "@voltx/auth";
|
|
415
|
+
|
|
416
|
+
const handler = createAuthHandler(auth);
|
|
417
|
+
|
|
418
|
+
export const GET = (c: Context) => handler(c);
|
|
419
|
+
export const POST = (c: Context) => handler(c);
|
|
420
|
+
`
|
|
421
|
+
);
|
|
422
|
+
fs.mkdirSync(path.join(targetDir, "src", "lib"), { recursive: true });
|
|
423
|
+
fs.writeFileSync(
|
|
424
|
+
path.join(targetDir, "src", "lib", "auth.ts"),
|
|
425
|
+
`// Auth configuration \u2014 Better Auth with DB-backed sessions
|
|
426
|
+
import { createAuth, createAuthMiddleware } from "@voltx/auth";
|
|
427
|
+
|
|
428
|
+
export const auth = createAuth("better-auth", {
|
|
429
|
+
database: process.env.DATABASE_URL!,
|
|
430
|
+
emailAndPassword: true,
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
export const authMiddleware = createAuthMiddleware({
|
|
434
|
+
provider: auth,
|
|
435
|
+
publicPaths: ["/api/auth", "/api/health", "/"],
|
|
436
|
+
});
|
|
437
|
+
`
|
|
438
|
+
);
|
|
439
|
+
} else if (auth === "jwt") {
|
|
440
|
+
fs.mkdirSync(path.join(targetDir, "src", "lib"), { recursive: true });
|
|
441
|
+
fs.writeFileSync(
|
|
442
|
+
path.join(targetDir, "src", "lib", "auth.ts"),
|
|
443
|
+
`// Auth configuration \u2014 JWT (stateless)
|
|
444
|
+
import { createAuth, createAuthMiddleware } from "@voltx/auth";
|
|
445
|
+
|
|
446
|
+
export const jwt = createAuth("jwt", {
|
|
447
|
+
secret: process.env.JWT_SECRET!,
|
|
448
|
+
expiresIn: "7d",
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
export const authMiddleware = createAuthMiddleware({
|
|
452
|
+
provider: jwt,
|
|
453
|
+
publicPaths: ["/api/auth", "/api/health", "/"],
|
|
454
|
+
});
|
|
455
|
+
`
|
|
456
|
+
);
|
|
457
|
+
fs.writeFileSync(
|
|
458
|
+
path.join(targetDir, "src", "routes", "api", "auth.ts"),
|
|
459
|
+
`// POST /api/auth/login \u2014 Example JWT login route
|
|
460
|
+
import type { Context } from "@voltx/server";
|
|
461
|
+
import { jwt } from "../../lib/auth";
|
|
462
|
+
|
|
463
|
+
export async function POST(c: Context) {
|
|
464
|
+
const { email, password } = await c.req.json();
|
|
465
|
+
|
|
466
|
+
if (!email || !password) {
|
|
467
|
+
return c.json({ error: "Email and password are required" }, 400);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const token = await jwt.sign({ sub: email, email });
|
|
471
|
+
return c.json({ token });
|
|
472
|
+
}
|
|
473
|
+
`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
let envContent = "";
|
|
477
|
+
if (template === "rag-app") {
|
|
478
|
+
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";
|
|
479
|
+
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";
|
|
480
|
+
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";
|
|
481
|
+
} else if (template === "chatbot" || template === "agent-app") {
|
|
482
|
+
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";
|
|
483
|
+
if (template === "agent-app") {
|
|
484
|
+
envContent += "# \u2500\u2500\u2500 Database (Neon Postgres \u2014 optional) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nDATABASE_URL=\n\n";
|
|
485
|
+
}
|
|
486
|
+
} else {
|
|
487
|
+
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";
|
|
488
|
+
}
|
|
489
|
+
if (auth === "better-auth") {
|
|
490
|
+
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";
|
|
491
|
+
if (template !== "rag-app" && template !== "agent-app") {
|
|
492
|
+
envContent += "DATABASE_URL=postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/dbname?sslmode=require\n";
|
|
493
|
+
}
|
|
494
|
+
envContent += "# GITHUB_CLIENT_ID=\n# GITHUB_CLIENT_SECRET=\n\n";
|
|
495
|
+
} else if (auth === "jwt") {
|
|
496
|
+
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";
|
|
497
|
+
}
|
|
498
|
+
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";
|
|
499
|
+
fs.writeFileSync(path.join(targetDir, ".env.example"), envContent);
|
|
500
|
+
fs.writeFileSync(
|
|
501
|
+
path.join(targetDir, ".gitignore"),
|
|
502
|
+
"node_modules\ndist\n.env\n"
|
|
503
|
+
);
|
|
504
|
+
fs.writeFileSync(
|
|
505
|
+
path.join(targetDir, "tsconfig.json"),
|
|
506
|
+
JSON.stringify(
|
|
507
|
+
{
|
|
508
|
+
compilerOptions: {
|
|
509
|
+
target: "ES2022",
|
|
510
|
+
module: "ESNext",
|
|
511
|
+
moduleResolution: "bundler",
|
|
512
|
+
strict: true,
|
|
513
|
+
esModuleInterop: true,
|
|
514
|
+
skipLibCheck: true,
|
|
515
|
+
outDir: "dist",
|
|
516
|
+
rootDir: "src"
|
|
517
|
+
},
|
|
518
|
+
include: ["src"]
|
|
519
|
+
},
|
|
520
|
+
null,
|
|
521
|
+
2
|
|
522
|
+
)
|
|
217
523
|
);
|
|
218
524
|
printWelcomeBanner(name);
|
|
219
525
|
}
|
|
@@ -221,21 +527,478 @@ var isDirectRun = typeof require !== "undefined" && require.main === module && p
|
|
|
221
527
|
if (isDirectRun) {
|
|
222
528
|
const projectName = process.argv[2];
|
|
223
529
|
if (!projectName) {
|
|
224
|
-
console.log("Usage: create-voltx-app <project-name> [--template chatbot]");
|
|
530
|
+
console.log("Usage: create-voltx-app <project-name> [--template chatbot] [--auth jwt]");
|
|
225
531
|
process.exit(1);
|
|
226
532
|
}
|
|
227
533
|
const templateFlag = process.argv.indexOf("--template");
|
|
228
534
|
const template = templateFlag !== -1 ? process.argv[templateFlag + 1] : "blank";
|
|
229
|
-
|
|
535
|
+
const authFlag = process.argv.indexOf("--auth");
|
|
536
|
+
const auth = authFlag !== -1 ? process.argv[authFlag + 1] : "none";
|
|
537
|
+
createProject({ name: projectName, template, auth }).catch((err) => {
|
|
230
538
|
console.error("[voltx] Error:", err);
|
|
231
539
|
process.exit(1);
|
|
232
540
|
});
|
|
233
541
|
}
|
|
234
542
|
|
|
543
|
+
// src/dev.ts
|
|
544
|
+
var import_node_child_process = require("child_process");
|
|
545
|
+
var import_node_path = require("path");
|
|
546
|
+
var import_node_fs = require("fs");
|
|
547
|
+
async function runDev(options = {}) {
|
|
548
|
+
const cwd = process.cwd();
|
|
549
|
+
const {
|
|
550
|
+
port,
|
|
551
|
+
entry = findEntryPoint(cwd),
|
|
552
|
+
clearScreen = true
|
|
553
|
+
} = options;
|
|
554
|
+
if (!entry) {
|
|
555
|
+
console.error("[voltx] Could not find entry point. Expected src/index.ts or src/index.js");
|
|
556
|
+
process.exit(1);
|
|
557
|
+
}
|
|
558
|
+
const entryPath = (0, import_node_path.resolve)(cwd, entry);
|
|
559
|
+
if (!(0, import_node_fs.existsSync)(entryPath)) {
|
|
560
|
+
console.error(`[voltx] Entry file not found: ${entry}`);
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
const envFile = (0, import_node_path.resolve)(cwd, ".env");
|
|
564
|
+
const env = {
|
|
565
|
+
...process.env,
|
|
566
|
+
NODE_ENV: "development"
|
|
567
|
+
};
|
|
568
|
+
if (port) {
|
|
569
|
+
env.PORT = String(port);
|
|
570
|
+
}
|
|
571
|
+
printDevBanner(entry, port);
|
|
572
|
+
const tsxArgs = ["watch"];
|
|
573
|
+
if (clearScreen) {
|
|
574
|
+
tsxArgs.push("--clear-screen=false");
|
|
575
|
+
}
|
|
576
|
+
const watchDirs = [
|
|
577
|
+
"src/routes",
|
|
578
|
+
"src/agents",
|
|
579
|
+
"src/tools",
|
|
580
|
+
"src/jobs",
|
|
581
|
+
"src/lib",
|
|
582
|
+
"voltx.config.ts",
|
|
583
|
+
...options.watch ?? []
|
|
584
|
+
];
|
|
585
|
+
tsxArgs.push("--ignore=node_modules", "--ignore=dist", "--ignore=.turbo");
|
|
586
|
+
tsxArgs.push(entry);
|
|
587
|
+
const tsxBin = findTsxBin(cwd);
|
|
588
|
+
let child;
|
|
589
|
+
if (tsxBin) {
|
|
590
|
+
child = (0, import_node_child_process.spawn)(tsxBin, tsxArgs, {
|
|
591
|
+
cwd,
|
|
592
|
+
env,
|
|
593
|
+
stdio: "inherit"
|
|
594
|
+
});
|
|
595
|
+
} else {
|
|
596
|
+
child = (0, import_node_child_process.spawn)("npx", ["tsx", ...tsxArgs], {
|
|
597
|
+
cwd,
|
|
598
|
+
env,
|
|
599
|
+
stdio: "inherit"
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
603
|
+
for (const signal of signals) {
|
|
604
|
+
process.on(signal, () => {
|
|
605
|
+
child.kill(signal);
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
child.on("error", (err) => {
|
|
609
|
+
if (err.code === "ENOENT") {
|
|
610
|
+
console.error("[voltx] tsx not found. Install it with: npm install -D tsx");
|
|
611
|
+
console.error("[voltx] Or run your app directly: npx tsx watch src/index.ts");
|
|
612
|
+
} else {
|
|
613
|
+
console.error("[voltx] Dev server error:", err.message);
|
|
614
|
+
}
|
|
615
|
+
process.exit(1);
|
|
616
|
+
});
|
|
617
|
+
child.on("exit", (code) => {
|
|
618
|
+
process.exit(code ?? 0);
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
function findEntryPoint(cwd) {
|
|
622
|
+
const candidates = [
|
|
623
|
+
"src/index.ts",
|
|
624
|
+
"src/index.js",
|
|
625
|
+
"src/index.mts",
|
|
626
|
+
"src/main.ts",
|
|
627
|
+
"src/main.js",
|
|
628
|
+
"index.ts",
|
|
629
|
+
"index.js"
|
|
630
|
+
];
|
|
631
|
+
for (const candidate of candidates) {
|
|
632
|
+
if ((0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, candidate))) {
|
|
633
|
+
return candidate;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
function findTsxBin(cwd) {
|
|
639
|
+
const localBin = (0, import_node_path.join)(cwd, "node_modules", ".bin", "tsx");
|
|
640
|
+
if ((0, import_node_fs.existsSync)(localBin)) return localBin;
|
|
641
|
+
const parentBin = (0, import_node_path.join)(cwd, "..", "node_modules", ".bin", "tsx");
|
|
642
|
+
if ((0, import_node_fs.existsSync)(parentBin)) return parentBin;
|
|
643
|
+
const rootBin = (0, import_node_path.join)(cwd, "..", "..", "node_modules", ".bin", "tsx");
|
|
644
|
+
if ((0, import_node_fs.existsSync)(rootBin)) return rootBin;
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
function printDevBanner(entry, port) {
|
|
648
|
+
console.log("");
|
|
649
|
+
console.log(" \u26A1 VoltX Dev Server");
|
|
650
|
+
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");
|
|
651
|
+
console.log(` Entry: ${entry}`);
|
|
652
|
+
if (port) {
|
|
653
|
+
console.log(` Port: ${port}`);
|
|
654
|
+
}
|
|
655
|
+
console.log(` Mode: development (hot reload)`);
|
|
656
|
+
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");
|
|
657
|
+
console.log("");
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// src/build.ts
|
|
661
|
+
var import_node_child_process2 = require("child_process");
|
|
662
|
+
var import_node_path2 = require("path");
|
|
663
|
+
var import_node_fs2 = require("fs");
|
|
664
|
+
async function runBuild(options = {}) {
|
|
665
|
+
const cwd = process.cwd();
|
|
666
|
+
const {
|
|
667
|
+
entry = findEntryPoint2(cwd),
|
|
668
|
+
outDir = "dist",
|
|
669
|
+
minify = true,
|
|
670
|
+
sourcemap = false
|
|
671
|
+
} = options;
|
|
672
|
+
if (!entry) {
|
|
673
|
+
console.error("[voltx] Could not find entry point. Expected src/index.ts");
|
|
674
|
+
process.exit(1);
|
|
675
|
+
}
|
|
676
|
+
const entryPath = (0, import_node_path2.resolve)(cwd, entry);
|
|
677
|
+
if (!(0, import_node_fs2.existsSync)(entryPath)) {
|
|
678
|
+
console.error(`[voltx] Entry file not found: ${entry}`);
|
|
679
|
+
process.exit(1);
|
|
680
|
+
}
|
|
681
|
+
console.log("");
|
|
682
|
+
console.log(" \u26A1 VoltX Build");
|
|
683
|
+
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");
|
|
684
|
+
console.log(` Entry: ${entry}`);
|
|
685
|
+
console.log(` Output: ${outDir}/`);
|
|
686
|
+
console.log(` Minify: ${minify}`);
|
|
687
|
+
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");
|
|
688
|
+
console.log("");
|
|
689
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path2.resolve)(cwd, outDir), { recursive: true });
|
|
690
|
+
console.log(" [1/2] Building server...");
|
|
691
|
+
const tsupArgs = [
|
|
692
|
+
entry,
|
|
693
|
+
"--format",
|
|
694
|
+
"esm",
|
|
695
|
+
"--out-dir",
|
|
696
|
+
outDir,
|
|
697
|
+
"--clean",
|
|
698
|
+
"--target",
|
|
699
|
+
"node20"
|
|
700
|
+
];
|
|
701
|
+
if (minify) tsupArgs.push("--minify");
|
|
702
|
+
if (sourcemap) tsupArgs.push("--sourcemap");
|
|
703
|
+
tsupArgs.push("--no-splitting");
|
|
704
|
+
const tsupBin = findBin(cwd, "tsup");
|
|
705
|
+
await runCommand(
|
|
706
|
+
tsupBin ?? "npx",
|
|
707
|
+
tsupBin ? tsupArgs : ["tsup", ...tsupArgs],
|
|
708
|
+
cwd
|
|
709
|
+
);
|
|
710
|
+
console.log(" \u2713 Server built successfully");
|
|
711
|
+
const frontendDir = (0, import_node_path2.resolve)(cwd, "src", "frontend");
|
|
712
|
+
const viteConfig = (0, import_node_path2.resolve)(cwd, "vite.config.ts");
|
|
713
|
+
const hasFrontend = (0, import_node_fs2.existsSync)(frontendDir) || (0, import_node_fs2.existsSync)(viteConfig);
|
|
714
|
+
if (hasFrontend) {
|
|
715
|
+
console.log(" [2/2] Building frontend...");
|
|
716
|
+
const viteBin = findBin(cwd, "vite");
|
|
717
|
+
const viteArgs = ["build", "--outDir", (0, import_node_path2.join)(outDir, "public")];
|
|
718
|
+
await runCommand(
|
|
719
|
+
viteBin ?? "npx",
|
|
720
|
+
viteBin ? viteArgs : ["vite", ...viteArgs],
|
|
721
|
+
cwd
|
|
722
|
+
);
|
|
723
|
+
console.log(" \u2713 Frontend built successfully");
|
|
724
|
+
} else {
|
|
725
|
+
console.log(" [2/2] No frontend found, skipping...");
|
|
726
|
+
}
|
|
727
|
+
console.log("");
|
|
728
|
+
console.log(" \u26A1 Build complete!");
|
|
729
|
+
console.log(` Run \`voltx start\` to start the production server.`);
|
|
730
|
+
console.log("");
|
|
731
|
+
}
|
|
732
|
+
function findEntryPoint2(cwd) {
|
|
733
|
+
const candidates = [
|
|
734
|
+
"src/index.ts",
|
|
735
|
+
"src/index.js",
|
|
736
|
+
"src/index.mts",
|
|
737
|
+
"src/main.ts",
|
|
738
|
+
"src/main.js"
|
|
739
|
+
];
|
|
740
|
+
for (const candidate of candidates) {
|
|
741
|
+
if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(cwd, candidate))) {
|
|
742
|
+
return candidate;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
function findBin(cwd, name) {
|
|
748
|
+
const paths = [
|
|
749
|
+
(0, import_node_path2.join)(cwd, "node_modules", ".bin", name),
|
|
750
|
+
(0, import_node_path2.join)(cwd, "..", "node_modules", ".bin", name),
|
|
751
|
+
(0, import_node_path2.join)(cwd, "..", "..", "node_modules", ".bin", name)
|
|
752
|
+
];
|
|
753
|
+
for (const p of paths) {
|
|
754
|
+
if ((0, import_node_fs2.existsSync)(p)) return p;
|
|
755
|
+
}
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
function runCommand(cmd, args, cwd) {
|
|
759
|
+
return new Promise((resolve6, reject) => {
|
|
760
|
+
const child = (0, import_node_child_process2.spawn)(cmd, args, {
|
|
761
|
+
cwd,
|
|
762
|
+
stdio: "inherit",
|
|
763
|
+
env: { ...process.env, NODE_ENV: "production" }
|
|
764
|
+
});
|
|
765
|
+
child.on("error", (err) => {
|
|
766
|
+
if (err.code === "ENOENT") {
|
|
767
|
+
console.error(`[voltx] ${cmd} not found. Install it with: npm install -D ${cmd}`);
|
|
768
|
+
}
|
|
769
|
+
reject(err);
|
|
770
|
+
});
|
|
771
|
+
child.on("exit", (code) => {
|
|
772
|
+
if (code === 0) resolve6();
|
|
773
|
+
else reject(new Error(`${cmd} exited with code ${code}`));
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/start.ts
|
|
779
|
+
var import_node_child_process3 = require("child_process");
|
|
780
|
+
var import_node_path3 = require("path");
|
|
781
|
+
var import_node_fs3 = require("fs");
|
|
782
|
+
async function runStart(options = {}) {
|
|
783
|
+
const cwd = process.cwd();
|
|
784
|
+
const { port, outDir = "dist" } = options;
|
|
785
|
+
const distDir = (0, import_node_path3.resolve)(cwd, outDir);
|
|
786
|
+
if (!(0, import_node_fs3.existsSync)(distDir)) {
|
|
787
|
+
console.error(`[voltx] Build output not found at ${outDir}/`);
|
|
788
|
+
console.error("[voltx] Run `voltx build` first to create a production build.");
|
|
789
|
+
process.exit(1);
|
|
790
|
+
}
|
|
791
|
+
const entry = options.entry ?? findDistEntry(distDir);
|
|
792
|
+
if (!entry) {
|
|
793
|
+
console.error(`[voltx] No entry file found in ${outDir}/`);
|
|
794
|
+
console.error("[voltx] Expected index.js, index.mjs, or main.js");
|
|
795
|
+
process.exit(1);
|
|
796
|
+
}
|
|
797
|
+
const entryPath = (0, import_node_path3.resolve)(distDir, entry);
|
|
798
|
+
if (!(0, import_node_fs3.existsSync)(entryPath)) {
|
|
799
|
+
console.error(`[voltx] Entry file not found: ${outDir}/${entry}`);
|
|
800
|
+
process.exit(1);
|
|
801
|
+
}
|
|
802
|
+
const env = {
|
|
803
|
+
...process.env,
|
|
804
|
+
NODE_ENV: "production"
|
|
805
|
+
};
|
|
806
|
+
if (port) {
|
|
807
|
+
env.PORT = String(port);
|
|
808
|
+
}
|
|
809
|
+
console.log("");
|
|
810
|
+
console.log(" \u26A1 VoltX Production Server");
|
|
811
|
+
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");
|
|
812
|
+
console.log(` Entry: ${outDir}/${entry}`);
|
|
813
|
+
if (port) {
|
|
814
|
+
console.log(` Port: ${port}`);
|
|
815
|
+
}
|
|
816
|
+
console.log(` Mode: production`);
|
|
817
|
+
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");
|
|
818
|
+
console.log("");
|
|
819
|
+
const child = (0, import_node_child_process3.spawn)("node", [entryPath], {
|
|
820
|
+
cwd,
|
|
821
|
+
env,
|
|
822
|
+
stdio: "inherit"
|
|
823
|
+
});
|
|
824
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
825
|
+
for (const signal of signals) {
|
|
826
|
+
process.on(signal, () => {
|
|
827
|
+
child.kill(signal);
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
child.on("error", (err) => {
|
|
831
|
+
console.error("[voltx] Failed to start production server:", err.message);
|
|
832
|
+
process.exit(1);
|
|
833
|
+
});
|
|
834
|
+
child.on("exit", (code) => {
|
|
835
|
+
process.exit(code ?? 0);
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
function findDistEntry(distDir) {
|
|
839
|
+
const candidates = [
|
|
840
|
+
"index.mjs",
|
|
841
|
+
"index.js",
|
|
842
|
+
"index.cjs",
|
|
843
|
+
"main.mjs",
|
|
844
|
+
"main.js",
|
|
845
|
+
"src/index.mjs",
|
|
846
|
+
"src/index.js"
|
|
847
|
+
];
|
|
848
|
+
for (const candidate of candidates) {
|
|
849
|
+
if ((0, import_node_fs3.existsSync)((0, import_node_path3.join)(distDir, candidate))) {
|
|
850
|
+
return candidate;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// src/generate.ts
|
|
857
|
+
var import_node_fs4 = require("fs");
|
|
858
|
+
var import_node_path4 = require("path");
|
|
859
|
+
async function runGenerate(options) {
|
|
860
|
+
const cwd = process.cwd();
|
|
861
|
+
const { type, name } = options;
|
|
862
|
+
switch (type) {
|
|
863
|
+
case "route":
|
|
864
|
+
generateRoute(cwd, name, options.method);
|
|
865
|
+
break;
|
|
866
|
+
case "agent":
|
|
867
|
+
generateAgent(cwd, name);
|
|
868
|
+
break;
|
|
869
|
+
case "tool":
|
|
870
|
+
generateTool(cwd, name);
|
|
871
|
+
break;
|
|
872
|
+
case "job":
|
|
873
|
+
generateJob(cwd, name);
|
|
874
|
+
break;
|
|
875
|
+
default:
|
|
876
|
+
console.error(`[voltx] Unknown generator type: ${type}`);
|
|
877
|
+
console.error("[voltx] Available: route, agent, tool, job");
|
|
878
|
+
process.exit(1);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function generateRoute(cwd, name, method = "POST") {
|
|
882
|
+
const routePath = name.startsWith("/") ? name.slice(1) : name;
|
|
883
|
+
const filePath = (0, import_node_path4.join)(cwd, "src", "routes", `${routePath}.ts`);
|
|
884
|
+
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
885
|
+
console.error(`[voltx] Route already exists: src/routes/${routePath}.ts`);
|
|
886
|
+
process.exit(1);
|
|
887
|
+
}
|
|
888
|
+
const upperMethod = method.toUpperCase();
|
|
889
|
+
const urlPath = "/" + routePath;
|
|
890
|
+
const content = `// ${upperMethod} ${urlPath}
|
|
891
|
+
import type { Context } from "@voltx/server";
|
|
892
|
+
|
|
893
|
+
export async function ${upperMethod}(c: Context) {
|
|
894
|
+
return c.json({ message: "Hello from ${urlPath}" });
|
|
895
|
+
}
|
|
896
|
+
`;
|
|
897
|
+
writeFileSafe(filePath, content);
|
|
898
|
+
console.log(` \u2713 Created route: src/routes/${routePath}.ts`);
|
|
899
|
+
console.log(` ${upperMethod} ${urlPath}`);
|
|
900
|
+
}
|
|
901
|
+
function generateAgent(cwd, name) {
|
|
902
|
+
const filePath = (0, import_node_path4.join)(cwd, "src", "agents", `${name}.ts`);
|
|
903
|
+
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
904
|
+
console.error(`[voltx] Agent already exists: src/agents/${name}.ts`);
|
|
905
|
+
process.exit(1);
|
|
906
|
+
}
|
|
907
|
+
const content = `// Agent: ${name}
|
|
908
|
+
import { createAgent } from "@voltx/agents";
|
|
909
|
+
|
|
910
|
+
export const ${toCamelCase(name)} = createAgent({
|
|
911
|
+
name: "${name}",
|
|
912
|
+
model: "cerebras:llama-4-scout-17b-16e",
|
|
913
|
+
instructions: "You are a helpful AI assistant named ${name}.",
|
|
914
|
+
tools: [
|
|
915
|
+
// Add tools here:
|
|
916
|
+
// {
|
|
917
|
+
// name: "example",
|
|
918
|
+
// description: "An example tool",
|
|
919
|
+
// parameters: { type: "object", properties: { input: { type: "string" } }, required: ["input"] },
|
|
920
|
+
// execute: async (params) => \`Processed: \${params.input}\`,
|
|
921
|
+
// },
|
|
922
|
+
],
|
|
923
|
+
});
|
|
924
|
+
`;
|
|
925
|
+
writeFileSafe(filePath, content);
|
|
926
|
+
console.log(` \u2713 Created agent: src/agents/${name}.ts`);
|
|
927
|
+
}
|
|
928
|
+
function generateTool(cwd, name) {
|
|
929
|
+
const filePath = (0, import_node_path4.join)(cwd, "src", "tools", `${name}.ts`);
|
|
930
|
+
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
931
|
+
console.error(`[voltx] Tool already exists: src/tools/${name}.ts`);
|
|
932
|
+
process.exit(1);
|
|
933
|
+
}
|
|
934
|
+
const content = `// Tool: ${name}
|
|
935
|
+
|
|
936
|
+
export const ${toCamelCase(name)}Tool = {
|
|
937
|
+
name: "${name}",
|
|
938
|
+
description: "TODO: Describe what this tool does",
|
|
939
|
+
parameters: {
|
|
940
|
+
type: "object" as const,
|
|
941
|
+
properties: {
|
|
942
|
+
input: { type: "string", description: "The input to process" },
|
|
943
|
+
},
|
|
944
|
+
required: ["input"],
|
|
945
|
+
},
|
|
946
|
+
execute: async (params: { input: string }): Promise<string> => {
|
|
947
|
+
// TODO: Implement tool logic
|
|
948
|
+
return \`${name} result for: \${params.input}\`;
|
|
949
|
+
},
|
|
950
|
+
};
|
|
951
|
+
`;
|
|
952
|
+
writeFileSafe(filePath, content);
|
|
953
|
+
console.log(` \u2713 Created tool: src/tools/${name}.ts`);
|
|
954
|
+
}
|
|
955
|
+
function generateJob(cwd, name) {
|
|
956
|
+
const filePath = (0, import_node_path4.join)(cwd, "src", "jobs", `${name}.ts`);
|
|
957
|
+
if ((0, import_node_fs4.existsSync)(filePath)) {
|
|
958
|
+
console.error(`[voltx] Job already exists: src/jobs/${name}.ts`);
|
|
959
|
+
process.exit(1);
|
|
960
|
+
}
|
|
961
|
+
const content = `// Job: ${name}
|
|
962
|
+
// Runs on a schedule or triggered via ctx.jobs.enqueue("${name}", data)
|
|
963
|
+
|
|
964
|
+
export const config = {
|
|
965
|
+
// Cron schedule (uncomment to enable):
|
|
966
|
+
// schedule: "0 */6 * * *", // every 6 hours
|
|
967
|
+
//
|
|
968
|
+
// Or make it a queue job (triggered on-demand):
|
|
969
|
+
queue: true,
|
|
970
|
+
retries: 3,
|
|
971
|
+
timeout: "5m",
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
export async function run(ctx: any, data?: Record<string, unknown>) {
|
|
975
|
+
console.log("[job:${name}] Running...", data);
|
|
976
|
+
|
|
977
|
+
// TODO: Implement job logic
|
|
978
|
+
|
|
979
|
+
console.log("[job:${name}] Done.");
|
|
980
|
+
}
|
|
981
|
+
`;
|
|
982
|
+
writeFileSafe(filePath, content);
|
|
983
|
+
console.log(` \u2713 Created job: src/jobs/${name}.ts`);
|
|
984
|
+
}
|
|
985
|
+
function writeFileSafe(filePath, content) {
|
|
986
|
+
const dir = (0, import_node_path4.dirname)(filePath);
|
|
987
|
+
(0, import_node_fs4.mkdirSync)(dir, { recursive: true });
|
|
988
|
+
(0, import_node_fs4.writeFileSync)(filePath, content, "utf-8");
|
|
989
|
+
}
|
|
990
|
+
function toCamelCase(str) {
|
|
991
|
+
return str.replace(/[-_](.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toLowerCase());
|
|
992
|
+
}
|
|
993
|
+
|
|
235
994
|
// src/index.ts
|
|
236
|
-
var CLI_VERSION = "0.
|
|
995
|
+
var CLI_VERSION = "0.2.0";
|
|
237
996
|
// Annotate the CommonJS export names for ESM import in node:
|
|
238
997
|
0 && (module.exports = {
|
|
239
998
|
CLI_VERSION,
|
|
240
|
-
createProject
|
|
999
|
+
createProject,
|
|
1000
|
+
runBuild,
|
|
1001
|
+
runDev,
|
|
1002
|
+
runGenerate,
|
|
1003
|
+
runStart
|
|
241
1004
|
});
|