@voltx/cli 0.2.0 → 0.3.1
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-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-ZA7EJWJI.mjs +221 -0
- package/dist/chunk-ZB2F3WTS.mjs +121 -0
- package/dist/chunk-ZLIPYI22.mjs +350 -0
- package/dist/cli.js +945 -59
- package/dist/cli.mjs +123 -23
- package/dist/create.d.mts +1 -0
- package/dist/create.d.ts +1 -0
- package/dist/create.js +308 -36
- 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 +770 -39
- 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.mjs
CHANGED
|
@@ -3,53 +3,153 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
var args = process.argv.slice(2);
|
|
5
5
|
var command = args[0];
|
|
6
|
+
function parseFlag(flag) {
|
|
7
|
+
const idx = args.indexOf(flag);
|
|
8
|
+
return idx !== -1 && args[idx + 1] ? args[idx + 1] : void 0;
|
|
9
|
+
}
|
|
10
|
+
function hasFlag(flag) {
|
|
11
|
+
return args.includes(flag);
|
|
12
|
+
}
|
|
13
|
+
function parsePort() {
|
|
14
|
+
const portStr = parseFlag("--port") ?? parseFlag("-p");
|
|
15
|
+
return portStr ? Number(portStr) : void 0;
|
|
16
|
+
}
|
|
6
17
|
async function main() {
|
|
7
18
|
switch (command) {
|
|
19
|
+
// ─── voltx create ──────────────────────────────────────────────────
|
|
8
20
|
case "create": {
|
|
9
21
|
const projectName = args[1];
|
|
10
|
-
if (!projectName) {
|
|
11
|
-
console.error("[voltx] Usage: voltx create <project-name> [--template chatbot|rag-app|agent-app]");
|
|
22
|
+
if (!projectName || projectName.startsWith("-")) {
|
|
23
|
+
console.error("[voltx] Usage: voltx create <project-name> [--template chatbot|rag-app|agent-app] [--auth better-auth|jwt|none]");
|
|
12
24
|
process.exit(1);
|
|
13
25
|
}
|
|
14
|
-
const
|
|
15
|
-
const
|
|
26
|
+
const template = parseFlag("--template");
|
|
27
|
+
const auth = parseFlag("--auth");
|
|
16
28
|
const { createProject } = await import("./create.mjs");
|
|
17
|
-
await createProject({ name: projectName, template });
|
|
29
|
+
await createProject({ name: projectName, template: template ?? "blank", auth: auth ?? "none" });
|
|
18
30
|
break;
|
|
19
31
|
}
|
|
32
|
+
// ─── voltx dev ─────────────────────────────────────────────────────
|
|
20
33
|
case "dev": {
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
const { runDev } = await import("./dev.mjs");
|
|
35
|
+
await runDev({
|
|
36
|
+
port: parsePort(),
|
|
37
|
+
entry: parseFlag("--entry"),
|
|
38
|
+
clearScreen: !hasFlag("--no-clear")
|
|
39
|
+
});
|
|
23
40
|
break;
|
|
24
41
|
}
|
|
42
|
+
// ─── voltx build ───────────────────────────────────────────────────
|
|
25
43
|
case "build": {
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
const { runBuild } = await import("./build.mjs");
|
|
45
|
+
await runBuild({
|
|
46
|
+
entry: parseFlag("--entry"),
|
|
47
|
+
outDir: parseFlag("--out-dir") ?? parseFlag("-o"),
|
|
48
|
+
minify: !hasFlag("--no-minify"),
|
|
49
|
+
sourcemap: hasFlag("--sourcemap")
|
|
50
|
+
});
|
|
28
51
|
break;
|
|
29
52
|
}
|
|
53
|
+
// ─── voltx start ──────────────────────────────────────────────────
|
|
30
54
|
case "start": {
|
|
31
|
-
|
|
32
|
-
|
|
55
|
+
const { runStart } = await import("./start.mjs");
|
|
56
|
+
await runStart({
|
|
57
|
+
port: parsePort(),
|
|
58
|
+
outDir: parseFlag("--out-dir") ?? parseFlag("-o"),
|
|
59
|
+
entry: parseFlag("--entry")
|
|
60
|
+
});
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
// ─── voltx generate ───────────────────────────────────────────────
|
|
64
|
+
case "generate":
|
|
65
|
+
case "g": {
|
|
66
|
+
const type = args[1];
|
|
67
|
+
const name = args[2];
|
|
68
|
+
if (!type || !name) {
|
|
69
|
+
console.error("[voltx] Usage: voltx generate <type> <name>");
|
|
70
|
+
console.error("");
|
|
71
|
+
console.error(" Types:");
|
|
72
|
+
console.error(" route <path> Generate a new API route (e.g., api/users)");
|
|
73
|
+
console.error(" agent <name> Generate a new agent (e.g., assistant)");
|
|
74
|
+
console.error(" tool <name> Generate a new tool (e.g., search)");
|
|
75
|
+
console.error(" job <name> Generate a new background job (e.g., cleanup)");
|
|
76
|
+
console.error("");
|
|
77
|
+
console.error(" Options:");
|
|
78
|
+
console.error(" --method <GET|POST|PUT|DELETE> HTTP method for routes (default: POST)");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const { runGenerate } = await import("./generate.mjs");
|
|
82
|
+
await runGenerate({
|
|
83
|
+
type,
|
|
84
|
+
name,
|
|
85
|
+
method: parseFlag("--method")
|
|
86
|
+
});
|
|
33
87
|
break;
|
|
34
88
|
}
|
|
89
|
+
// ─── voltx --version ──────────────────────────────────────────────
|
|
90
|
+
case "--version":
|
|
91
|
+
case "-v": {
|
|
92
|
+
const { CLI_VERSION } = await import("./index.mjs");
|
|
93
|
+
console.log(`voltx v${CLI_VERSION}`);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
// ─── voltx help / default ─────────────────────────────────────────
|
|
97
|
+
case "help":
|
|
98
|
+
case "--help":
|
|
99
|
+
case "-h":
|
|
35
100
|
default: {
|
|
36
|
-
|
|
101
|
+
printHelp();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function printHelp() {
|
|
106
|
+
console.log(`
|
|
37
107
|
\u26A1 voltx \u2014 The AI-first full-stack framework
|
|
38
108
|
|
|
39
109
|
Usage:
|
|
40
|
-
voltx
|
|
41
|
-
voltx dev Start the development server
|
|
42
|
-
voltx build Build for production
|
|
43
|
-
voltx start Start the production server
|
|
110
|
+
voltx <command> [options]
|
|
44
111
|
|
|
45
|
-
|
|
46
|
-
|
|
112
|
+
Commands:
|
|
113
|
+
create <name> Create a new VoltX project
|
|
114
|
+
dev Start the development server (hot reload)
|
|
115
|
+
build Build for production
|
|
116
|
+
start Start the production server
|
|
117
|
+
generate <type> <name> Generate routes, agents, tools, or jobs
|
|
47
118
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
119
|
+
Create Options:
|
|
120
|
+
--template <type> Template: chatbot, rag-app, agent-app, blank (default)
|
|
121
|
+
--auth <provider> Auth: better-auth, jwt, none (default)
|
|
122
|
+
|
|
123
|
+
Dev Options:
|
|
124
|
+
--port, -p <number> Port override
|
|
125
|
+
--entry <file> Custom entry file (default: src/index.ts)
|
|
126
|
+
--no-clear Don't clear screen on restart
|
|
127
|
+
|
|
128
|
+
Build Options:
|
|
129
|
+
--entry <file> Custom entry file
|
|
130
|
+
--out-dir, -o <dir> Output directory (default: dist)
|
|
131
|
+
--no-minify Skip minification
|
|
132
|
+
--sourcemap Generate source maps
|
|
133
|
+
|
|
134
|
+
Start Options:
|
|
135
|
+
--port, -p <number> Port override
|
|
136
|
+
--out-dir, -o <dir> Build output directory (default: dist)
|
|
137
|
+
--entry <file> Entry file within output dir
|
|
138
|
+
|
|
139
|
+
Generate Types:
|
|
140
|
+
route <path> API route (e.g., voltx generate route api/users)
|
|
141
|
+
agent <name> Agent (e.g., voltx generate agent assistant)
|
|
142
|
+
tool <name> Tool (e.g., voltx generate tool search)
|
|
143
|
+
job <name> Background job (e.g., voltx generate job cleanup)
|
|
144
|
+
|
|
145
|
+
Examples:
|
|
146
|
+
voltx create my-app --template chatbot --auth jwt
|
|
147
|
+
voltx dev --port 4000
|
|
148
|
+
voltx build --sourcemap
|
|
149
|
+
voltx start
|
|
150
|
+
voltx generate route api/users --method GET
|
|
151
|
+
voltx generate agent assistant
|
|
152
|
+
`);
|
|
53
153
|
}
|
|
54
154
|
main().catch((err) => {
|
|
55
155
|
console.error("[voltx] Fatal error:", err);
|
package/dist/create.d.mts
CHANGED
package/dist/create.d.ts
CHANGED
package/dist/create.js
CHANGED
|
@@ -118,7 +118,7 @@ function printWelcomeBanner(projectName) {
|
|
|
118
118
|
console.log(` ${rgb(100, 180, 255, "Next steps:")}`);
|
|
119
119
|
console.log(` ${rgb(200, 200, 220, ` cd ${projectName}`)}`);
|
|
120
120
|
console.log(` ${rgb(200, 200, 220, " pnpm install")}`);
|
|
121
|
-
console.log(` ${rgb(200, 200, 220, " pnpm dev")}`);
|
|
121
|
+
console.log(` ${rgb(200, 200, 220, " pnpm dev # or: npx voltx dev")}`);
|
|
122
122
|
console.log("");
|
|
123
123
|
console.log(dimRule());
|
|
124
124
|
console.log("");
|
|
@@ -143,7 +143,7 @@ function printWelcomeBanner(projectName) {
|
|
|
143
143
|
|
|
144
144
|
// src/create.ts
|
|
145
145
|
async function createProject(options) {
|
|
146
|
-
const { name, template = "blank" } = options;
|
|
146
|
+
const { name, template = "blank", auth = "none" } = options;
|
|
147
147
|
const targetDir = path.resolve(process.cwd(), name);
|
|
148
148
|
if (fs.existsSync(targetDir)) {
|
|
149
149
|
console.error(`[voltx] Directory "${name}" already exists.`);
|
|
@@ -151,29 +151,29 @@ async function createProject(options) {
|
|
|
151
151
|
}
|
|
152
152
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
153
153
|
const templateDeps = {
|
|
154
|
-
blank: {
|
|
154
|
+
blank: {
|
|
155
|
+
"@voltx/core": "^0.3.0",
|
|
156
|
+
"@voltx/server": "^0.3.0"
|
|
157
|
+
},
|
|
155
158
|
chatbot: {
|
|
156
|
-
"@voltx/core": "^0.
|
|
157
|
-
"@voltx/
|
|
158
|
-
"@voltx/
|
|
159
|
-
"@voltx/
|
|
160
|
-
"@voltx/ui": "^0.1.0"
|
|
159
|
+
"@voltx/core": "^0.3.0",
|
|
160
|
+
"@voltx/ai": "^0.3.0",
|
|
161
|
+
"@voltx/server": "^0.3.0",
|
|
162
|
+
"@voltx/memory": "^0.3.0"
|
|
161
163
|
},
|
|
162
164
|
"rag-app": {
|
|
163
|
-
"@voltx/core": "^0.
|
|
164
|
-
"@voltx/
|
|
165
|
-
"@voltx/
|
|
166
|
-
"@voltx/
|
|
167
|
-
"@voltx/
|
|
165
|
+
"@voltx/core": "^0.3.0",
|
|
166
|
+
"@voltx/ai": "^0.3.0",
|
|
167
|
+
"@voltx/server": "^0.3.0",
|
|
168
|
+
"@voltx/rag": "^0.3.0",
|
|
169
|
+
"@voltx/db": "^0.3.0"
|
|
168
170
|
},
|
|
169
171
|
"agent-app": {
|
|
170
|
-
"@voltx/core": "^0.
|
|
171
|
-
"@voltx/
|
|
172
|
-
"@voltx/
|
|
173
|
-
"@voltx/
|
|
174
|
-
"@voltx/
|
|
175
|
-
"@voltx/auth": "^0.1.0",
|
|
176
|
-
"@voltx/ui": "^0.1.0"
|
|
172
|
+
"@voltx/core": "^0.3.0",
|
|
173
|
+
"@voltx/ai": "^0.3.0",
|
|
174
|
+
"@voltx/server": "^0.3.0",
|
|
175
|
+
"@voltx/agents": "^0.3.0",
|
|
176
|
+
"@voltx/memory": "^0.3.0"
|
|
177
177
|
}
|
|
178
178
|
};
|
|
179
179
|
const packageJson = {
|
|
@@ -181,14 +181,20 @@ async function createProject(options) {
|
|
|
181
181
|
version: "0.1.0",
|
|
182
182
|
private: true,
|
|
183
183
|
scripts: {
|
|
184
|
-
dev: "
|
|
185
|
-
build: "
|
|
186
|
-
start: "
|
|
184
|
+
dev: "voltx dev",
|
|
185
|
+
build: "voltx build",
|
|
186
|
+
start: "voltx start"
|
|
187
|
+
},
|
|
188
|
+
dependencies: {
|
|
189
|
+
...templateDeps[template] ?? templateDeps["blank"],
|
|
190
|
+
"@voltx/cli": "^0.3.0",
|
|
191
|
+
...auth === "better-auth" ? { "@voltx/auth": "^0.3.0", "better-auth": "^1.5.0" } : {},
|
|
192
|
+
...auth === "jwt" ? { "@voltx/auth": "^0.3.0", "jose": "^6.0.0" } : {}
|
|
187
193
|
},
|
|
188
|
-
dependencies: templateDeps[template] ?? templateDeps["blank"],
|
|
189
194
|
devDependencies: {
|
|
190
195
|
typescript: "^5.7.0",
|
|
191
196
|
tsx: "^4.21.0",
|
|
197
|
+
tsup: "^8.0.0",
|
|
192
198
|
"@types/node": "^22.0.0"
|
|
193
199
|
}
|
|
194
200
|
};
|
|
@@ -196,31 +202,295 @@ async function createProject(options) {
|
|
|
196
202
|
path.join(targetDir, "package.json"),
|
|
197
203
|
JSON.stringify(packageJson, null, 2)
|
|
198
204
|
);
|
|
199
|
-
const
|
|
205
|
+
const hasDb = template === "rag-app" || template === "agent-app" || auth === "better-auth";
|
|
206
|
+
const provider = template === "rag-app" ? "openai" : "cerebras";
|
|
207
|
+
const model = template === "rag-app" ? "gpt-4o" : "llama-4-scout-17b-16e";
|
|
208
|
+
let configContent = `import { defineConfig } from "@voltx/core";
|
|
200
209
|
|
|
201
210
|
export default defineConfig({
|
|
202
211
|
name: "${name}",
|
|
203
212
|
port: 3000,
|
|
204
213
|
ai: {
|
|
205
|
-
provider: "
|
|
206
|
-
model: "
|
|
214
|
+
provider: "${provider}",
|
|
215
|
+
model: "${model}",
|
|
216
|
+
},`;
|
|
217
|
+
if (hasDb) {
|
|
218
|
+
configContent += `
|
|
219
|
+
db: {
|
|
220
|
+
url: process.env.DATABASE_URL,
|
|
221
|
+
},`;
|
|
222
|
+
}
|
|
223
|
+
if (auth !== "none") {
|
|
224
|
+
configContent += `
|
|
225
|
+
auth: {
|
|
226
|
+
provider: "${auth}",
|
|
227
|
+
},`;
|
|
228
|
+
}
|
|
229
|
+
configContent += `
|
|
230
|
+
server: {
|
|
231
|
+
routesDir: "src/routes",
|
|
232
|
+
staticDir: "public",
|
|
233
|
+
cors: true,
|
|
207
234
|
},
|
|
208
235
|
});
|
|
209
236
|
`;
|
|
210
237
|
fs.writeFileSync(path.join(targetDir, "voltx.config.ts"), configContent);
|
|
211
|
-
fs.mkdirSync(path.join(targetDir, "src", "routes"), { recursive: true });
|
|
212
|
-
fs.mkdirSync(path.join(targetDir, "
|
|
213
|
-
|
|
238
|
+
fs.mkdirSync(path.join(targetDir, "src", "routes", "api"), { recursive: true });
|
|
239
|
+
fs.mkdirSync(path.join(targetDir, "public"), { recursive: true });
|
|
240
|
+
fs.writeFileSync(
|
|
241
|
+
path.join(targetDir, "src", "index.ts"),
|
|
242
|
+
`import { createApp } from "@voltx/core";
|
|
214
243
|
import config from "../voltx.config";
|
|
215
244
|
|
|
216
245
|
const app = createApp(config);
|
|
217
246
|
app.start();
|
|
218
|
-
|
|
219
|
-
|
|
247
|
+
`
|
|
248
|
+
);
|
|
220
249
|
fs.writeFileSync(
|
|
221
|
-
path.join(targetDir, ".
|
|
222
|
-
|
|
250
|
+
path.join(targetDir, "src", "routes", "index.ts"),
|
|
251
|
+
`// GET / \u2014 Health check
|
|
252
|
+
import type { Context } from "@voltx/server";
|
|
253
|
+
|
|
254
|
+
export function GET(c: Context) {
|
|
255
|
+
return c.json({ name: "${name}", status: "ok" });
|
|
256
|
+
}
|
|
257
|
+
`
|
|
223
258
|
);
|
|
259
|
+
if (template === "chatbot" || template === "agent-app") {
|
|
260
|
+
fs.writeFileSync(
|
|
261
|
+
path.join(targetDir, "src", "routes", "api", "chat.ts"),
|
|
262
|
+
`// POST /api/chat \u2014 Streaming chat with conversation memory
|
|
263
|
+
import type { Context } from "@voltx/server";
|
|
264
|
+
import { streamText } from "@voltx/ai";
|
|
265
|
+
import { createMemory } from "@voltx/memory";
|
|
266
|
+
|
|
267
|
+
// In-memory for dev; swap to createMemory("postgres", { url }) for production
|
|
268
|
+
const memory = createMemory({ maxMessages: 50 });
|
|
269
|
+
|
|
270
|
+
export async function POST(c: Context) {
|
|
271
|
+
const { messages, conversationId = "default" } = await c.req.json();
|
|
272
|
+
|
|
273
|
+
// Store the latest user message
|
|
274
|
+
const lastMessage = messages[messages.length - 1];
|
|
275
|
+
if (lastMessage?.role === "user") {
|
|
276
|
+
await memory.add(conversationId, { role: "user", content: lastMessage.content });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Get conversation history from memory
|
|
280
|
+
const history = await memory.get(conversationId);
|
|
281
|
+
|
|
282
|
+
const result = await streamText({
|
|
283
|
+
model: "${provider}:${model}",
|
|
284
|
+
system: "You are a helpful AI assistant.",
|
|
285
|
+
messages: history.map((m) => ({ role: m.role, content: m.content })),
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Store assistant response after stream completes
|
|
289
|
+
result.text.then(async (text) => {
|
|
290
|
+
await memory.add(conversationId, { role: "assistant", content: text });
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return result.toSSEResponse();
|
|
294
|
+
}
|
|
295
|
+
`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
if (template === "agent-app") {
|
|
299
|
+
fs.mkdirSync(path.join(targetDir, "src", "agents"), { recursive: true });
|
|
300
|
+
fs.mkdirSync(path.join(targetDir, "src", "tools"), { recursive: true });
|
|
301
|
+
fs.writeFileSync(
|
|
302
|
+
path.join(targetDir, "src", "agents", "assistant.ts"),
|
|
303
|
+
`import { createAgent } from "@voltx/agents";
|
|
304
|
+
import { searchTool } from "../tools/search";
|
|
305
|
+
|
|
306
|
+
export const assistant = createAgent({
|
|
307
|
+
name: "assistant",
|
|
308
|
+
model: "${provider}:${model}",
|
|
309
|
+
instructions: "You are a helpful AI assistant. Use your tools when needed.",
|
|
310
|
+
tools: [searchTool],
|
|
311
|
+
maxIterations: 5,
|
|
312
|
+
});
|
|
313
|
+
`
|
|
314
|
+
);
|
|
315
|
+
fs.writeFileSync(
|
|
316
|
+
path.join(targetDir, "src", "tools", "search.ts"),
|
|
317
|
+
`import type { Tool } from "@voltx/agents";
|
|
318
|
+
|
|
319
|
+
export const searchTool: Tool = {
|
|
320
|
+
name: "search",
|
|
321
|
+
description: "Search for information on a topic.",
|
|
322
|
+
parameters: {
|
|
323
|
+
type: "object",
|
|
324
|
+
properties: { query: { type: "string", description: "The search query" } },
|
|
325
|
+
required: ["query"],
|
|
326
|
+
},
|
|
327
|
+
async execute(args: { query: string }) {
|
|
328
|
+
return \`Search results for "\${args.query}": Placeholder \u2014 connect a real search API.\`;
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
`
|
|
332
|
+
);
|
|
333
|
+
fs.writeFileSync(
|
|
334
|
+
path.join(targetDir, "src", "routes", "api", "agent.ts"),
|
|
335
|
+
`import type { Context } from "@voltx/server";
|
|
336
|
+
import { assistant } from "../../agents/assistant";
|
|
337
|
+
|
|
338
|
+
export async function POST(c: Context) {
|
|
339
|
+
const { input } = await c.req.json();
|
|
340
|
+
if (!input) return c.json({ error: "Missing 'input' field" }, 400);
|
|
341
|
+
const result = await assistant.run(input);
|
|
342
|
+
return c.json({ content: result.content, steps: result.steps });
|
|
343
|
+
}
|
|
344
|
+
`
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
if (template === "rag-app") {
|
|
348
|
+
const embedModel = "openai:text-embedding-3-small";
|
|
349
|
+
fs.mkdirSync(path.join(targetDir, "src", "routes", "api", "rag"), { recursive: true });
|
|
350
|
+
fs.writeFileSync(
|
|
351
|
+
path.join(targetDir, "src", "routes", "api", "rag", "query.ts"),
|
|
352
|
+
`// POST /api/rag/query \u2014 Query documents with RAG
|
|
353
|
+
import type { Context } from "@voltx/server";
|
|
354
|
+
import { streamText } from "@voltx/ai";
|
|
355
|
+
import { createRAGPipeline, createEmbedder } from "@voltx/rag";
|
|
356
|
+
import { createVectorStore } from "@voltx/db";
|
|
357
|
+
|
|
358
|
+
const vectorStore = createVectorStore(); // swap to "pinecone" or "pgvector" for production
|
|
359
|
+
const embedder = createEmbedder({ model: "${embedModel}" });
|
|
360
|
+
const rag = createRAGPipeline({ embedder, vectorStore });
|
|
361
|
+
|
|
362
|
+
export async function POST(c: Context) {
|
|
363
|
+
const { question } = await c.req.json();
|
|
364
|
+
|
|
365
|
+
const context = await rag.getContext(question, { topK: 5 });
|
|
366
|
+
|
|
367
|
+
const result = await streamText({
|
|
368
|
+
model: "${provider}:${model}",
|
|
369
|
+
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}\`,
|
|
370
|
+
messages: [{ role: "user", content: question }],
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
return result.toSSEResponse();
|
|
374
|
+
}
|
|
375
|
+
`
|
|
376
|
+
);
|
|
377
|
+
fs.writeFileSync(
|
|
378
|
+
path.join(targetDir, "src", "routes", "api", "rag", "ingest.ts"),
|
|
379
|
+
`// POST /api/rag/ingest \u2014 Ingest documents into the vector store
|
|
380
|
+
import type { Context } from "@voltx/server";
|
|
381
|
+
import { createRAGPipeline, createEmbedder } from "@voltx/rag";
|
|
382
|
+
import { createVectorStore } from "@voltx/db";
|
|
383
|
+
|
|
384
|
+
const vectorStore = createVectorStore();
|
|
385
|
+
const embedder = createEmbedder({ model: "${embedModel}" });
|
|
386
|
+
const rag = createRAGPipeline({ embedder, vectorStore });
|
|
387
|
+
|
|
388
|
+
export async function POST(c: Context) {
|
|
389
|
+
const { text, idPrefix } = await c.req.json();
|
|
390
|
+
|
|
391
|
+
if (!text || typeof text !== "string") {
|
|
392
|
+
return c.json({ error: "Missing 'text' field" }, 400);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const result = await rag.ingest(text, idPrefix ?? "doc");
|
|
396
|
+
return c.json({ status: "ok", chunks: result.chunks, ids: result.ids });
|
|
397
|
+
}
|
|
398
|
+
`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
if (auth === "better-auth") {
|
|
402
|
+
fs.mkdirSync(path.join(targetDir, "src", "routes", "api", "auth"), { recursive: true });
|
|
403
|
+
fs.writeFileSync(
|
|
404
|
+
path.join(targetDir, "src", "routes", "api", "auth", "[...path].ts"),
|
|
405
|
+
`// ALL /api/auth/* \u2014 Better Auth handler
|
|
406
|
+
import type { Context } from "@voltx/server";
|
|
407
|
+
import { auth } from "../../../lib/auth";
|
|
408
|
+
import { createAuthHandler } from "@voltx/auth";
|
|
409
|
+
|
|
410
|
+
const handler = createAuthHandler(auth);
|
|
411
|
+
|
|
412
|
+
export const GET = (c: Context) => handler(c);
|
|
413
|
+
export const POST = (c: Context) => handler(c);
|
|
414
|
+
`
|
|
415
|
+
);
|
|
416
|
+
fs.mkdirSync(path.join(targetDir, "src", "lib"), { recursive: true });
|
|
417
|
+
fs.writeFileSync(
|
|
418
|
+
path.join(targetDir, "src", "lib", "auth.ts"),
|
|
419
|
+
`// Auth configuration \u2014 Better Auth with DB-backed sessions
|
|
420
|
+
import { createAuth, createAuthMiddleware } from "@voltx/auth";
|
|
421
|
+
|
|
422
|
+
export const auth = createAuth("better-auth", {
|
|
423
|
+
database: process.env.DATABASE_URL!,
|
|
424
|
+
emailAndPassword: true,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
export const authMiddleware = createAuthMiddleware({
|
|
428
|
+
provider: auth,
|
|
429
|
+
publicPaths: ["/api/auth", "/api/health", "/"],
|
|
430
|
+
});
|
|
431
|
+
`
|
|
432
|
+
);
|
|
433
|
+
} else if (auth === "jwt") {
|
|
434
|
+
fs.mkdirSync(path.join(targetDir, "src", "lib"), { recursive: true });
|
|
435
|
+
fs.writeFileSync(
|
|
436
|
+
path.join(targetDir, "src", "lib", "auth.ts"),
|
|
437
|
+
`// Auth configuration \u2014 JWT (stateless)
|
|
438
|
+
import { createAuth, createAuthMiddleware } from "@voltx/auth";
|
|
439
|
+
|
|
440
|
+
export const jwt = createAuth("jwt", {
|
|
441
|
+
secret: process.env.JWT_SECRET!,
|
|
442
|
+
expiresIn: "7d",
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
export const authMiddleware = createAuthMiddleware({
|
|
446
|
+
provider: jwt,
|
|
447
|
+
publicPaths: ["/api/auth", "/api/health", "/"],
|
|
448
|
+
});
|
|
449
|
+
`
|
|
450
|
+
);
|
|
451
|
+
fs.writeFileSync(
|
|
452
|
+
path.join(targetDir, "src", "routes", "api", "auth.ts"),
|
|
453
|
+
`// POST /api/auth/login \u2014 Example JWT login route
|
|
454
|
+
import type { Context } from "@voltx/server";
|
|
455
|
+
import { jwt } from "../../lib/auth";
|
|
456
|
+
|
|
457
|
+
export async function POST(c: Context) {
|
|
458
|
+
const { email, password } = await c.req.json();
|
|
459
|
+
|
|
460
|
+
if (!email || !password) {
|
|
461
|
+
return c.json({ error: "Email and password are required" }, 400);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const token = await jwt.sign({ sub: email, email });
|
|
465
|
+
return c.json({ token });
|
|
466
|
+
}
|
|
467
|
+
`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
let envContent = "";
|
|
471
|
+
if (template === "rag-app") {
|
|
472
|
+
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";
|
|
473
|
+
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";
|
|
474
|
+
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";
|
|
475
|
+
} else if (template === "chatbot" || template === "agent-app") {
|
|
476
|
+
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";
|
|
477
|
+
if (template === "agent-app") {
|
|
478
|
+
envContent += "# \u2500\u2500\u2500 Database (Neon Postgres \u2014 optional) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nDATABASE_URL=\n\n";
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
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";
|
|
482
|
+
}
|
|
483
|
+
if (auth === "better-auth") {
|
|
484
|
+
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";
|
|
485
|
+
if (template !== "rag-app" && template !== "agent-app") {
|
|
486
|
+
envContent += "DATABASE_URL=postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/dbname?sslmode=require\n";
|
|
487
|
+
}
|
|
488
|
+
envContent += "# GITHUB_CLIENT_ID=\n# GITHUB_CLIENT_SECRET=\n\n";
|
|
489
|
+
} else if (auth === "jwt") {
|
|
490
|
+
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";
|
|
491
|
+
}
|
|
492
|
+
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";
|
|
493
|
+
fs.writeFileSync(path.join(targetDir, ".env.example"), envContent);
|
|
224
494
|
fs.writeFileSync(
|
|
225
495
|
path.join(targetDir, ".gitignore"),
|
|
226
496
|
"node_modules\ndist\n.env\n"
|
|
@@ -251,12 +521,14 @@ var isDirectRun = typeof require !== "undefined" && require.main === module && p
|
|
|
251
521
|
if (isDirectRun) {
|
|
252
522
|
const projectName = process.argv[2];
|
|
253
523
|
if (!projectName) {
|
|
254
|
-
console.log("Usage: create-voltx-app <project-name> [--template chatbot]");
|
|
524
|
+
console.log("Usage: create-voltx-app <project-name> [--template chatbot] [--auth jwt]");
|
|
255
525
|
process.exit(1);
|
|
256
526
|
}
|
|
257
527
|
const templateFlag = process.argv.indexOf("--template");
|
|
258
528
|
const template = templateFlag !== -1 ? process.argv[templateFlag + 1] : "blank";
|
|
259
|
-
|
|
529
|
+
const authFlag = process.argv.indexOf("--auth");
|
|
530
|
+
const auth = authFlag !== -1 ? process.argv[authFlag + 1] : "none";
|
|
531
|
+
createProject({ name: projectName, template, auth }).catch((err) => {
|
|
260
532
|
console.error("[voltx] Error:", err);
|
|
261
533
|
process.exit(1);
|
|
262
534
|
});
|
package/dist/create.mjs
CHANGED
package/dist/dev.d.mts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface DevOptions {
|
|
2
|
+
/** Port override (reads from voltx.config.ts or env if not set) */
|
|
3
|
+
port?: number;
|
|
4
|
+
/** Entry file (default: src/index.ts) */
|
|
5
|
+
entry?: string;
|
|
6
|
+
/** Extra directories to watch */
|
|
7
|
+
watch?: string[];
|
|
8
|
+
/** Clear console on restart */
|
|
9
|
+
clearScreen?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Start the VoltX dev server with hot reload.
|
|
13
|
+
*
|
|
14
|
+
* Spawns `tsx watch` on the app entry point. tsx handles TypeScript
|
|
15
|
+
* compilation and automatic restarts when files change.
|
|
16
|
+
*/
|
|
17
|
+
declare function runDev(options?: DevOptions): Promise<void>;
|
|
18
|
+
|
|
19
|
+
export { type DevOptions, runDev };
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface DevOptions {
|
|
2
|
+
/** Port override (reads from voltx.config.ts or env if not set) */
|
|
3
|
+
port?: number;
|
|
4
|
+
/** Entry file (default: src/index.ts) */
|
|
5
|
+
entry?: string;
|
|
6
|
+
/** Extra directories to watch */
|
|
7
|
+
watch?: string[];
|
|
8
|
+
/** Clear console on restart */
|
|
9
|
+
clearScreen?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Start the VoltX dev server with hot reload.
|
|
13
|
+
*
|
|
14
|
+
* Spawns `tsx watch` on the app entry point. tsx handles TypeScript
|
|
15
|
+
* compilation and automatic restarts when files change.
|
|
16
|
+
*/
|
|
17
|
+
declare function runDev(options?: DevOptions): Promise<void>;
|
|
18
|
+
|
|
19
|
+
export { type DevOptions, runDev };
|