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 +22 -0
- package/bin/create-nevex-agent.mjs +151 -0
- package/package.json +19 -0
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
|
+
}
|