kooni-bot 0.2.1 → 0.2.2
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/bin/kooni.js +31 -23
- package/package.json +1 -1
package/bin/kooni.js
CHANGED
|
@@ -22,7 +22,7 @@ import { execFileSync, spawnSync } from "node:child_process";
|
|
|
22
22
|
import { randomUUID } from "node:crypto";
|
|
23
23
|
import { fileURLToPath } from "node:url";
|
|
24
24
|
|
|
25
|
-
const CLI_VERSION = "0.2.
|
|
25
|
+
const CLI_VERSION = "0.2.2";
|
|
26
26
|
|
|
27
27
|
const REPO = process.env.KOONI_REPO || "iamnocodeveloper/kooni-bot";
|
|
28
28
|
const BRANCH = process.env.KOONI_BRANCH || "main";
|
|
@@ -67,9 +67,9 @@ const DICT = {
|
|
|
67
67
|
qBusiness: "¿Cómo se llama tu negocio?",
|
|
68
68
|
qBotName: "¿Cómo se llama tu asistente?",
|
|
69
69
|
qLang: "¿En qué idioma debe hablar tu bot?",
|
|
70
|
-
qTier: "¿Qué plan quieres? (free | pro)",
|
|
71
70
|
brainQ: "¿Con qué cerebro (modelo de IA) quieres que piense tu bot?",
|
|
72
71
|
brainDesc: "recomendado",
|
|
72
|
+
qApiKey: (label) => `Pega tu API key de ${label} (no se mostrará):`,
|
|
73
73
|
qBaseUrl: "URL base del gateway (ej. https://api.aisa.one/v1)",
|
|
74
74
|
qWhat: "En una frase, ¿a qué se dedica?",
|
|
75
75
|
qOffer: "¿Qué ofreces? (tus servicios o productos principales, con precios si quieres)",
|
|
@@ -140,9 +140,9 @@ const DICT = {
|
|
|
140
140
|
qBusiness: "What's your business called?",
|
|
141
141
|
qBotName: "What's your assistant's name?",
|
|
142
142
|
qLang: "What language should your bot speak?",
|
|
143
|
-
qTier: "Which plan do you want? (free | pro)",
|
|
144
143
|
brainQ: "Which brain (AI model) should your bot think with?",
|
|
145
144
|
brainDesc: "recommended",
|
|
145
|
+
qApiKey: (label) => `Paste your ${label} API key (hidden):`,
|
|
146
146
|
qBaseUrl: "Gateway base URL (e.g. https://api.aisa.one/v1)",
|
|
147
147
|
qWhat: "In one line, what does it do?",
|
|
148
148
|
qOffer: "What do you offer? (main services or products, with prices if you like)",
|
|
@@ -494,24 +494,27 @@ function resolveBotDir(arg) {
|
|
|
494
494
|
// ── exec cross-platform ──────────────────────────────────────────────────────
|
|
495
495
|
const isWin = process.platform === "win32";
|
|
496
496
|
|
|
497
|
-
// Ejecuta un binario de forma segura en Windows/macOS/Linux
|
|
498
|
-
// -
|
|
499
|
-
//
|
|
500
|
-
//
|
|
501
|
-
//
|
|
497
|
+
// Ejecuta un binario de forma segura en Windows/macOS/Linux:
|
|
498
|
+
// - Unix/mac: `spawnSync(file, args)` directo.
|
|
499
|
+
// - Windows: `npx`/`pnpm` son shims `.cmd`, que NO se pueden spawnear directo
|
|
500
|
+
// (EINVAL). Se lanzan con `cmd.exe /c <file> <args...>` pasando los argumentos
|
|
501
|
+
// como lista separada (nada de `shell: true` ni concatenación → sin DEP0190).
|
|
502
|
+
// - `stdio` hereda por defecto (progreso en vivo); `capture` captura para parsear.
|
|
502
503
|
function run(file, args = [], opts = {}) {
|
|
503
504
|
const cwd = opts.cwd;
|
|
504
505
|
const input = opts.input != null ? String(opts.input) : undefined;
|
|
505
506
|
const stdio = opts.stdio || (opts.capture ? ["pipe", "pipe", "pipe"] : "inherit");
|
|
506
507
|
|
|
507
508
|
let cmd = file;
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
509
|
+
let argv = args;
|
|
510
|
+
|
|
511
|
+
if (isWin) {
|
|
512
|
+
const shim = { npx: "npx.cmd", pnpm: "pnpm.cmd" }[file] || file;
|
|
513
|
+
cmd = "cmd.exe";
|
|
514
|
+
argv = ["/d", "/c", shim, ...args];
|
|
512
515
|
}
|
|
513
516
|
|
|
514
|
-
const r = spawnSync(cmd,
|
|
517
|
+
const r = spawnSync(cmd, argv, { cwd, input, stdio, encoding: "utf8" });
|
|
515
518
|
if (r.error) throw r.error;
|
|
516
519
|
if (r.status !== 0) {
|
|
517
520
|
const err = new Error(`Command failed: ${[cmd, ...args].join(" ")}`);
|
|
@@ -692,6 +695,7 @@ function collectAnswers(flags) {
|
|
|
692
695
|
brainKey,
|
|
693
696
|
baseUrl: String(flags["base-url"] || "").trim() || undefined,
|
|
694
697
|
secret: brainKey ? BRAINS[brainKey].secret : undefined,
|
|
698
|
+
apiKey: String(flags["api-key"] || "").trim() || undefined,
|
|
695
699
|
what: String(flags.que || "").trim() || undefined,
|
|
696
700
|
offer: String(flags.ofrece || "").trim() || undefined,
|
|
697
701
|
hours: String(flags.horario || "").trim() || undefined,
|
|
@@ -723,11 +727,9 @@ async function onboarding(rl, answers, defaultDir) {
|
|
|
723
727
|
const langIdx = await select(rl, t().qLang, langKeys.map((k) => ({ key: k, label: k })), { value: answers.lang, default: 0 });
|
|
724
728
|
answers.lang = langKeys[langIdx] || "es-MX";
|
|
725
729
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
], { value: answers.tier, default: 0 });
|
|
730
|
-
answers.tier = tierIdx === 1 ? "pro" : "free";
|
|
730
|
+
// El plan NO se pregunta: se instala en free y la activación de plan/clave se
|
|
731
|
+
// hace después desde el dashboard. Aquí solo fijamos el default.
|
|
732
|
+
answers.tier = answers.tier || "free";
|
|
731
733
|
|
|
732
734
|
const brainKeys = ["claude", "chatgpt", "grok", "gateway"];
|
|
733
735
|
const brainIdx = await select(rl, t().brainQ, brainKeys.map((k) => ({
|
|
@@ -741,6 +743,12 @@ async function onboarding(rl, answers, defaultDir) {
|
|
|
741
743
|
answers.baseUrl = await ask(rl, t().qBaseUrl, undefined) || "https://api.aisa.one/v1";
|
|
742
744
|
}
|
|
743
745
|
|
|
746
|
+
// La API key se pide aquí, apenas se elige el cerebro — y se conserva para el
|
|
747
|
+
// deploy (se guarda como secret sin mostrarla). En no-interactivo se salta.
|
|
748
|
+
if (!answers.apiKey) {
|
|
749
|
+
answers.apiKey = await promptSecret(t().qApiKey(BRAINS[answers.brainKey].label));
|
|
750
|
+
}
|
|
751
|
+
|
|
744
752
|
answers.what = answers.what ?? (await ask(rl, t().qWhat, undefined) || "");
|
|
745
753
|
answers.offer = answers.offer ?? (await ask(rl, t().qOffer, undefined) || "");
|
|
746
754
|
answers.hours = answers.hours ?? (await ask(rl, t().qHours, undefined) || "");
|
|
@@ -850,11 +858,9 @@ async function deployBot(dir, { flags = {}, rl } = {}) {
|
|
|
850
858
|
// secrets (sin mostrarlos)
|
|
851
859
|
console.log("\n " + C.dim(t().secretsTitle));
|
|
852
860
|
|
|
853
|
-
// API key:
|
|
861
|
+
// API key: viene del onboarding (o de --api-key). Si no está y no-interactivo,
|
|
862
|
+
// se delega al agente (wrangler secret put). No se vuelve a preguntar aquí.
|
|
854
863
|
let apiKey = flags["api-key"] || "";
|
|
855
|
-
if (!apiKey && interactive()) {
|
|
856
|
-
apiKey = await promptSecret(m(`Pega tu API key de ${BRAINS[flags.brainKey || "claude"].label} (no se mostrará):`, `Paste your ${BRAINS[flags.brainKey || "claude"].label} API key (hidden):`));
|
|
857
|
-
}
|
|
858
864
|
if (apiKey) {
|
|
859
865
|
wrangler(dir, ["secret", "put", BRAINS[flags.brainKey || "claude"].secret], { input: apiKey, capture: true });
|
|
860
866
|
console.log(" " + C.green("✓") + " " + t().secretOk(BRAINS[flags.brainKey || "claude"].secret));
|
|
@@ -862,6 +868,8 @@ async function deployBot(dir, { flags = {}, rl } = {}) {
|
|
|
862
868
|
console.log(C.yellow("\n ── PARA EL AGENTE ── falta la API key de IA."));
|
|
863
869
|
console.log(" " + C.cyan(`npx wrangler secret put ${BRAINS[flags.brainKey || "claude"].secret}`));
|
|
864
870
|
console.log(" " + m("Pídele la llave al usuario y guárdala tú (entrada oculta).", "Ask the user for the key and save it (hidden input)."));
|
|
871
|
+
} else {
|
|
872
|
+
console.log(" " + C.yellow("⚠") + " " + m("sin API key — el bot no responderá hasta que la pongas desde el panel (Configuración → Modelo de IA).", "no API key — the bot won't reply until you set it from the panel (Settings → AI Model)."));
|
|
865
873
|
}
|
|
866
874
|
|
|
867
875
|
let dash = "";
|
|
@@ -1075,7 +1083,7 @@ async function cmdInit(flags, rest) {
|
|
|
1075
1083
|
|
|
1076
1084
|
// deploy (si no lo deshabilitan)
|
|
1077
1085
|
if (!flags["no-deploy"] && !process.env.KOONI_NO_DEPLOY) {
|
|
1078
|
-
const deployFlags = { ...flags, brainKey: answers.brainKey };
|
|
1086
|
+
const deployFlags = { ...flags, brainKey: answers.brainKey, "api-key": answers.apiKey || "" };
|
|
1079
1087
|
const url = await deployBot(dir, { flags: deployFlags, rl });
|
|
1080
1088
|
if (url) {
|
|
1081
1089
|
console.log("\n " + C.green(C.b(m("🎉 BOT EN LÍNEA", "🎉 BOT LIVE"))));
|
package/package.json
CHANGED