@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.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,26 +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/
|
|
159
|
+
"@voltx/core": "^0.3.0",
|
|
160
|
+
"@voltx/ai": "^0.3.0",
|
|
161
|
+
"@voltx/server": "^0.3.0",
|
|
162
|
+
"@voltx/memory": "^0.3.0"
|
|
160
163
|
},
|
|
161
164
|
"rag-app": {
|
|
162
|
-
"@voltx/core": "^0.
|
|
163
|
-
"@voltx/
|
|
164
|
-
"@voltx/
|
|
165
|
-
"@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"
|
|
166
170
|
},
|
|
167
171
|
"agent-app": {
|
|
168
|
-
"@voltx/core": "^0.
|
|
169
|
-
"@voltx/
|
|
170
|
-
"@voltx/
|
|
171
|
-
"@voltx/
|
|
172
|
-
"@voltx/
|
|
173
|
-
"@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"
|
|
174
177
|
}
|
|
175
178
|
};
|
|
176
179
|
const packageJson = {
|
|
@@ -182,36 +185,335 @@ async function createProject(options) {
|
|
|
182
185
|
build: "voltx build",
|
|
183
186
|
start: "voltx start"
|
|
184
187
|
},
|
|
185
|
-
dependencies:
|
|
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" } : {}
|
|
193
|
+
},
|
|
194
|
+
devDependencies: {
|
|
195
|
+
typescript: "^5.7.0",
|
|
196
|
+
tsx: "^4.21.0",
|
|
197
|
+
tsup: "^8.0.0",
|
|
198
|
+
"@types/node": "^22.0.0"
|
|
199
|
+
}
|
|
186
200
|
};
|
|
187
201
|
fs.writeFileSync(
|
|
188
202
|
path.join(targetDir, "package.json"),
|
|
189
203
|
JSON.stringify(packageJson, null, 2)
|
|
190
204
|
);
|
|
191
|
-
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";
|
|
192
209
|
|
|
193
210
|
export default defineConfig({
|
|
194
211
|
name: "${name}",
|
|
195
212
|
port: 3000,
|
|
196
213
|
ai: {
|
|
197
|
-
provider: "
|
|
198
|
-
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,
|
|
199
234
|
},
|
|
200
235
|
});
|
|
201
236
|
`;
|
|
202
237
|
fs.writeFileSync(path.join(targetDir, "voltx.config.ts"), configContent);
|
|
203
|
-
fs.mkdirSync(path.join(targetDir, "src", "routes"), { recursive: true });
|
|
204
|
-
fs.mkdirSync(path.join(targetDir, "
|
|
205
|
-
|
|
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";
|
|
206
243
|
import config from "../voltx.config";
|
|
207
244
|
|
|
208
245
|
const app = createApp(config);
|
|
209
246
|
app.start();
|
|
210
|
-
|
|
211
|
-
|
|
247
|
+
`
|
|
248
|
+
);
|
|
249
|
+
fs.writeFileSync(
|
|
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
|
+
`
|
|
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);
|
|
494
|
+
fs.writeFileSync(
|
|
495
|
+
path.join(targetDir, ".gitignore"),
|
|
496
|
+
"node_modules\ndist\n.env\n"
|
|
497
|
+
);
|
|
212
498
|
fs.writeFileSync(
|
|
213
|
-
path.join(targetDir, ".
|
|
214
|
-
|
|
499
|
+
path.join(targetDir, "tsconfig.json"),
|
|
500
|
+
JSON.stringify(
|
|
501
|
+
{
|
|
502
|
+
compilerOptions: {
|
|
503
|
+
target: "ES2022",
|
|
504
|
+
module: "ESNext",
|
|
505
|
+
moduleResolution: "bundler",
|
|
506
|
+
strict: true,
|
|
507
|
+
esModuleInterop: true,
|
|
508
|
+
skipLibCheck: true,
|
|
509
|
+
outDir: "dist",
|
|
510
|
+
rootDir: "src"
|
|
511
|
+
},
|
|
512
|
+
include: ["src"]
|
|
513
|
+
},
|
|
514
|
+
null,
|
|
515
|
+
2
|
|
516
|
+
)
|
|
215
517
|
);
|
|
216
518
|
printWelcomeBanner(name);
|
|
217
519
|
}
|
|
@@ -219,12 +521,14 @@ var isDirectRun = typeof require !== "undefined" && require.main === module && p
|
|
|
219
521
|
if (isDirectRun) {
|
|
220
522
|
const projectName = process.argv[2];
|
|
221
523
|
if (!projectName) {
|
|
222
|
-
console.log("Usage: create-voltx-app <project-name> [--template chatbot]");
|
|
524
|
+
console.log("Usage: create-voltx-app <project-name> [--template chatbot] [--auth jwt]");
|
|
223
525
|
process.exit(1);
|
|
224
526
|
}
|
|
225
527
|
const templateFlag = process.argv.indexOf("--template");
|
|
226
528
|
const template = templateFlag !== -1 ? process.argv[templateFlag + 1] : "blank";
|
|
227
|
-
|
|
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) => {
|
|
228
532
|
console.error("[voltx] Error:", err);
|
|
229
533
|
process.exit(1);
|
|
230
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 };
|