create-nevex-agent 0.1.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 ADDED
@@ -0,0 +1,22 @@
1
+ # create-nevex-agent
2
+
3
+ Genera un proyecto independiente a partir del template de Nevex:
4
+
5
+ ```bash
6
+ npx create-nevex-agent coagua --display-name "Coagua"
7
+ ```
8
+
9
+ El CLI se publica en npm. El repositorio template que clona permanece privado
10
+ y requiere acceso SSH a `nevex-labs/nevex-agent-template`.
11
+
12
+ La publicación automática se dispara desde el repositorio con un tag de la
13
+ forma `create-nevex-agent-v<version>` y el secreto `NPM_TOKEN`.
14
+
15
+ Para probar el CLI contra un checkout local:
16
+
17
+ ```bash
18
+ node bin/create-nevex-agent.mjs demo --template-path ../../
19
+ ```
20
+
21
+ El generador no crea bases, cuentas ni recursos cloud. Solo copia el template
22
+ y personaliza la identidad inicial del nuevo proyecto.
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ cpSync,
5
+ existsSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ readdirSync,
9
+ rmSync,
10
+ writeFileSync,
11
+ } from "node:fs";
12
+ import { join, resolve } from "node:path";
13
+ import { randomUUID } from "node:crypto";
14
+ import { execFileSync } from "node:child_process";
15
+ import { tmpdir } from "node:os";
16
+
17
+ const args = parseArgs(process.argv.slice(2));
18
+ const projectName = args._[0];
19
+
20
+ if (!projectName || args.help) {
21
+ printHelp();
22
+ process.exit(projectName ? 0 : 1);
23
+ }
24
+
25
+ const slug = normalizeSlug(projectName);
26
+ const target = resolve(args.output ?? projectName);
27
+ assertTargetIsSafe(target);
28
+
29
+ const source = args["template-path"]
30
+ ? resolve(args["template-path"])
31
+ : await materializeTemplate(args["template-repo"] ?? process.env.NEVEX_AGENT_TEMPLATE_REPO);
32
+
33
+ mkdirSync(target, { recursive: true });
34
+ cpSync(source, target, {
35
+ recursive: true,
36
+ filter: (entry) => !shouldSkip(entry, source),
37
+ });
38
+
39
+ customizeProject(target, {
40
+ slug,
41
+ displayName: args["display-name"] ?? titleCase(slug),
42
+ });
43
+
44
+ if (!args["template-path"]) rmSync(source, { recursive: true, force: true });
45
+
46
+ console.log(`\nProyecto creado en ${target}`);
47
+ console.log("\nSiguientes pasos:\n");
48
+ console.log(` cd ${JSON.stringify(target)}`);
49
+ console.log(" pnpm install");
50
+ console.log(" cp .env.example .env.local");
51
+ console.log(" pnpm dev\n");
52
+
53
+ async function materializeTemplate(repository) {
54
+ const repo = repository ?? "git@github.com:nevex-labs/nevex-agent-template.git";
55
+ const directory = join(tmpdir(), `nevex-agent-template-${randomUUID()}`);
56
+ execFileSync("git", ["clone", "--depth", "1", repo, directory], { stdio: "inherit" });
57
+ return directory;
58
+ }
59
+
60
+ function customizeProject(directory, values) {
61
+ const packagePath = join(directory, "package.json");
62
+ const packageJson = JSON.parse(readFileSync(packagePath, "utf8"));
63
+ packageJson.name = values.slug;
64
+ packageJson.private = true;
65
+ writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
66
+
67
+ replaceInFile(
68
+ join(directory, "src/lib/config.ts"),
69
+ /APP_NAME = "[^"]*"/,
70
+ `APP_NAME = ${JSON.stringify(values.displayName)}`,
71
+ );
72
+ replaceInFile(
73
+ join(directory, "wrangler.jsonc"),
74
+ /"name": "[^"]*"/,
75
+ `"name": ${JSON.stringify(values.slug)}`,
76
+ );
77
+ replaceInFile(
78
+ join(directory, "wrangler.jsonc"),
79
+ /"database_name": "[^"]*"/,
80
+ `"database_name": ${JSON.stringify(values.slug)}`,
81
+ );
82
+ replaceInFile(
83
+ join(directory, "README.md"),
84
+ /^# Nevex Agent Template/m,
85
+ `# ${values.displayName}`,
86
+ );
87
+ }
88
+
89
+ function replaceInFile(path, pattern, replacement) {
90
+ const content = readFileSync(path, "utf8");
91
+ writeFileSync(path, content.replace(pattern, replacement));
92
+ }
93
+
94
+ function shouldSkip(entry, source) {
95
+ const relative = entry.slice(source.length + 1);
96
+ return (
97
+ relative === ".git" ||
98
+ relative.startsWith(".git/") ||
99
+ relative === "node_modules" ||
100
+ relative.startsWith("node_modules/") ||
101
+ relative === "dist" ||
102
+ relative.startsWith("dist/") ||
103
+ relative === ".wrangler" ||
104
+ relative.startsWith(".wrangler/") ||
105
+ relative === ".github/workflows/publish-create-nevex-agent.yml" ||
106
+ relative === "packages/create-nevex-agent" ||
107
+ relative.startsWith("packages/create-nevex-agent/")
108
+ );
109
+ }
110
+
111
+ function assertTargetIsSafe(directory) {
112
+ if (!existsSync(directory)) return;
113
+ if (readdirSync(directory).length > 0) {
114
+ throw new Error(`El directorio ${directory} ya existe y no está vacío.`);
115
+ }
116
+ }
117
+
118
+ function normalizeSlug(value) {
119
+ const slug = value
120
+ .toLowerCase()
121
+ .replace(/[^a-z0-9]+/g, "-")
122
+ .replace(/^-|-$/g, "");
123
+ if (!slug) throw new Error("El nombre del proyecto no es válido.");
124
+ return slug;
125
+ }
126
+
127
+ function titleCase(value) {
128
+ return value.replace(/-/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
129
+ }
130
+
131
+ function parseArgs(argv) {
132
+ const result = { _: [] };
133
+ for (let index = 0; index < argv.length; index += 1) {
134
+ const value = argv[index];
135
+ if (value === "--help" || value === "-h") result.help = true;
136
+ else if (value.startsWith("--")) result[value.slice(2)] = argv[++index];
137
+ else result._.push(value);
138
+ }
139
+ return result;
140
+ }
141
+
142
+ function printHelp() {
143
+ console.log(`Uso: npx @nevex/create-nevex-agent <nombre> [opciones]
144
+
145
+ Opciones:
146
+ --display-name <nombre> Nombre visible del agente
147
+ --output <directorio> Directorio de destino
148
+ --template-path <path> Usar un template local, útil para desarrollo
149
+ --template-repo <repo> URL o ruta del repositorio template
150
+ -h, --help Mostrar esta ayuda`);
151
+ }
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "create-nevex-agent",
3
+ "version": "0.1.0",
4
+ "description": "Create a custom Nevex WhatsApp agent project",
5
+ "bin": {
6
+ "create-nevex-agent": "bin/create-nevex-agent.mjs"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "README.md"
11
+ ],
12
+ "type": "module",
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "engines": {
17
+ "node": ">=20"
18
+ }
19
+ }