noiton 1.0.0 → 2.0.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 CHANGED
@@ -1,6 +1,8 @@
1
- # NOITON
1
+ # 🐙 NOITON
2
2
 
3
- OpenCode authenticator - configure providers with a single key.
3
+ **Universal AI Agent Connector** one key, many agents.
4
+
5
+ NOITON is a CLI wizard that connects your API key to multiple AI coding agents at once. Run it once, pick your agents, paste your key — done.
4
6
 
5
7
  ## Quick Start
6
8
 
@@ -8,51 +10,64 @@ OpenCode authenticator - configure providers with a single key.
8
10
  npx noiton
9
11
  ```
10
12
 
11
- The wizard will:
12
- 1. Ask for your NOITON API key
13
- 2. Validate it against the API
14
- 3. Fetch available models
15
- 4. Write the `noiton` provider to your `opencode.json`
13
+ The octopus will:
14
+ 1. 🔍 Detect which AI agents are installed (OpenCode, Hermes, OpenClaw, Cline, Aider, Kilo Code, Kimi Code)
15
+ 2. 📦 If none found, offer to install OpenCode or Hermes based on your use case
16
+ 3. 🔄 If OpenCode is outdated, offer to update it
17
+ 4. 🔑 Ask for your NOITON API key (one key serves all agents)
18
+ 5. 📋 Fetch available models from the API
19
+ 6. ⚙️ Configure each selected agent in its native format
20
+ 7. 🎉 Show a funny success message (with easter eggs on holidays!)
21
+
22
+ ## Supported Agents
23
+
24
+ | Agent | Type | Config Format | Multiple Models |
25
+ |-------|------|---------------|-----------------|
26
+ | **OpenCode** | CLI | JSON (`opencode.json`) | ✅ |
27
+ | **Hermes** | CLI | YAML (`config.yaml` + `.env`) | Single default |
28
+ | **OpenClaw** | CLI | JSON5 (`openclaw.json`) | ✅ |
29
+ | **Cline** | VS Code ext | JSON (VS Code settings) | Single |
30
+ | **Aider** | CLI | YAML (`.aider.conf.yml` + `.env`) | Single |
31
+ | **Kilo Code** | CLI + VS Code | JSONC (`kilo.jsonc`) | ✅ |
32
+ | **Kimi Code** | CLI + VS Code | TOML (`config.toml`) | Single |
16
33
 
17
34
  ## Commands
18
35
 
19
36
  ```bash
20
37
  npx noiton # Setup wizard (default)
21
- npx noiton status # Show current configuration
38
+ npx noiton status # Show configuration across all agents
22
39
  npx noiton refresh # Re-fetch models from API
23
- npx noiton change # Change API key
24
- npx noiton remove # Remove NOITON from config
40
+ npx noiton change # Change API key in all agents
41
+ npx noiton remove # Remove NOITON from all agents
25
42
  npx noiton help # Show help
43
+ npx noiton --version # Show version
26
44
  ```
27
45
 
28
46
  ## How It Works
29
47
 
30
- NOITON adds a provider to your `~/.config/opencode/opencode.json`:
31
-
32
- ```json
33
- {
34
- "provider": {
35
- "noiton": {
36
- "npm": "@ai-sdk/openai-compatible",
37
- "name": "NOITON",
38
- "options": {
39
- "baseURL": "https://blogs-litellm-app.rwezkp.easypanel.host",
40
- "apiKey": "sk-your-key-here"
41
- },
42
- "models": {
43
- "model-id": { "name": "Model Name", "limit": { ... } }
44
- }
45
- }
46
- }
47
- }
48
- ```
48
+ NOITON detects your installed agents and writes to each one's native config format:
49
+
50
+ - **OpenCode** → `~/.config/opencode/opencode.json` (provider block)
51
+ - **Hermes** → `~/.hermes/config.yaml` (model block) + `~/.hermes/.env` (API key)
52
+ - **OpenClaw** → `~/.openclaw/openclaw.json` (models.providers block)
53
+ - **Cline** → VS Code `settings.json` (cline.* keys)
54
+ - **Aider** → `~/.aider.conf.yml` (openai-api-base) + `~/.env` (OPENAI_API_KEY)
55
+ - **Kilo Code** → `~/.config/kilo/kilo.jsonc` (provider.openai-compatible block)
56
+ - **Kimi Code** → `~/.kimi-code/config.toml` (providers + models blocks)
57
+
58
+ Existing configurations are preserved — NOITON only adds/removes its own entries.
59
+
60
+ ## Auto-Install
61
+
62
+ If no agents are detected, NOITON asks what you want to do:
49
63
 
50
- After running `npx noiton`, restart OpenCode and the models will be available.
64
+ - 💻 **Programar** installs OpenCode (`curl -fsSL https://opencode.ai/install | bash`)
65
+ - 🤖 **Assistente** → installs Hermes (`pip install nous-hermes`)
51
66
 
52
67
  ## Requirements
53
68
 
54
69
  - Node.js >= 18
55
- - OpenCode CLI installed
70
+ - At least one supported agent (or let NOITON install one for you)
56
71
 
57
72
  ## License
58
73
 
package/package.json CHANGED
@@ -1,17 +1,26 @@
1
1
  {
2
2
  "name": "noiton",
3
- "version": "1.0.0",
4
- "description": "OpenCode authenticator - configure providers with a single key",
3
+ "version": "2.0.1",
4
+ "description": "🐙 Universal AI Agent Connector - one key, many agents (OpenCode, Hermes, OpenClaw, Cline, Aider, Kilo, Kimi)",
5
5
  "keywords": [
6
+ "noiton",
6
7
  "opencode",
7
- "auth",
8
+ "hermes",
9
+ "openclaw",
10
+ "cline",
11
+ "aider",
12
+ "kilo",
13
+ "kimi",
14
+ "ai",
8
15
  "cli",
9
- "noiton",
10
- "provider",
11
- "ai"
16
+ "agent",
17
+ "wizard",
18
+ "setup",
19
+ "auth"
12
20
  ],
13
21
  "license": "MIT",
14
22
  "author": "NOITON",
23
+ "homepage": "https://github.com/tokensilimitados/noiton",
15
24
  "type": "module",
16
25
  "bin": {
17
26
  "noiton": "./bin/noiton.js"
@@ -27,11 +36,19 @@
27
36
  "node": ">=18"
28
37
  },
29
38
  "dependencies": {
30
- "@inquirer/prompts": "^7.0.0"
39
+ "@inquirer/prompts": "^7.0.0",
40
+ "@iarna/toml": "^2.2.5",
41
+ "boxen": "^8.0.0",
42
+ "gradient-string": "^3.0.0",
43
+ "json5": "^2.2.3",
44
+ "ora": "^8.0.0",
45
+ "picocolors": "^1.1.0",
46
+ "yaml": "^2.8.4"
31
47
  },
32
48
  "scripts": {
33
49
  "test": "node --test tests/",
34
- "prepublishOnly": "echo 'no build step - pure JS'"
50
+ "prepublishOnly": "echo 'no build step - pure JS'",
51
+ "view-octopus": "node -e \"import('./src/octopus.js').then(m => console.log(m.octopusHappy()))\""
35
52
  },
36
53
  "publishConfig": {
37
54
  "access": "public",
@@ -1,77 +1,67 @@
1
+ /**
2
+ * NOITON change command - swap the API key across all configured agents
3
+ */
4
+
1
5
  import { input, confirm } from "@inquirer/prompts";
2
- import { BANNER, color } from "../constants.js";
6
+ import { c } from "../constants.js";
7
+ import { printHeader, spinner, ok, fail, info, box, dim } from "../ui.js";
8
+ import { getConfiguredAgents } from "../lib/agents/index.js";
3
9
  import { validateKey, maskKey } from "../lib/api.js";
4
- import {
5
- readConfig,
6
- writeConfig,
7
- hasProvider,
8
- getProvider,
9
- upsertProvider,
10
- buildModels,
11
- } from "../lib/config.js";
10
+ import { farewellMessage } from "../messages.js";
12
11
 
13
- /**
14
- * Change API key (re-runs wizard with new key).
15
- */
16
12
  export async function changeCommand() {
17
- console.log(BANNER);
13
+ printHeader("Trocar Chave API");
18
14
 
19
- const { config, path } = readConfig();
15
+ const configured = getConfiguredAgents();
20
16
 
21
- if (!hasProvider(config)) {
22
- console.log(color("NOITON não está configurado.\n", "red"));
23
- console.log(color(" Rode ", "dim") + color("npx noiton", "cyan") + color(" para configurar.\n", "dim"));
24
- process.exit(1);
17
+ if (configured.length === 0) {
18
+ box("NOITON não está configurado em nenhum agente.\n\nRode 'npx noiton' para configurar.", "warning");
19
+ return;
25
20
  }
26
21
 
27
- const provider = getProvider(config);
28
- const oldKey = provider?.options?.apiKey || "";
29
-
30
- console.log(color(" Chave atual: ", "dim") + maskKey(oldKey) + "\n");
22
+ info(`Agentes configurados: ${configured.map(a => a.name).join(", ")}`);
23
+ console.log();
31
24
 
32
25
  const newKey = await input({
33
- message: " Nova chave:",
26
+ message: " Nova chave NOITON:",
34
27
  validate: (v) => (v.trim().length >= 10 ? true : "Chave muito curta"),
35
28
  transformer: (v) => maskKey(v),
36
29
  });
37
30
 
38
- const trimmedKey = newKey.trim();
31
+ const trimmed = newKey.trim();
39
32
 
40
- console.log(color("\n ⏳ Validando nova chave...", "dim"));
41
-
42
- const { valid, models, error } = await validateKey(trimmedKey);
33
+ // Validate the new key
34
+ const s = spinner("Validando nova chave...");
35
+ const { valid, error } = await validateKey(trimmed);
43
36
 
44
37
  if (!valid) {
45
- console.log(color(`\n ❌ Erro: ${error}\n`, "red"));
46
- process.exit(1);
47
- }
48
-
49
- console.log(color(" ✓ Chave válida!\n", "green"));
50
-
51
- if (!models || models.length === 0) {
52
- console.log(color(" ⚠ Nenhum modelo disponível para esta chave.\n", "yellow"));
53
- process.exit(1);
54
- }
55
-
56
- console.log(color(` 📦 ${models.length} modelo(s) encontrado(s):\n`, "cyan"));
57
- for (const m of models) {
58
- console.log(` • ${m.id}`);
38
+ s.fail(`Chave inválida: ${error}`);
39
+ return;
59
40
  }
60
41
 
61
- const proceed = await confirm({
62
- message: "\n Atualizar configuração com a nova chave?",
63
- default: true,
64
- });
65
-
66
- if (!proceed) {
67
- console.log(color("\n ✋ Operação cancelada.\n", "dim"));
68
- return;
42
+ s.succeed("Nova chave válida!");
43
+ console.log();
44
+
45
+ let success = 0;
46
+ for (const agent of configured) {
47
+ const sub = spinner(`Atualizando ${agent.name}...`);
48
+ try {
49
+ agent.changeKey(trimmed);
50
+ sub.succeed(`${agent.name} atualizado!`);
51
+ success++;
52
+ } catch (err) {
53
+ sub.fail(`${agent.name}: ${err.message}`);
54
+ }
69
55
  }
70
56
 
71
- const modelsObj = buildModels(models);
72
- const newConfig = upsertProvider(config, trimmedKey, modelsObj);
73
- writeConfig(newConfig, path);
57
+ console.log();
58
+ box(
59
+ success > 0
60
+ ? `✨ Chave trocada em ${success} agente(s)!`
61
+ : "⚠️ Nenhum agente pôde ser atualizado.",
62
+ success > 0 ? "success" : "warning"
63
+ );
74
64
 
75
- console.log(color("\n ✓ Chave atualizada!\n", "green"));
76
- console.log(color(" 🚀 Reinicie o OpenCode para usar a nova chave.\n", "cyan"));
65
+ console.log();
66
+ console.log(c.dim(` ${farewellMessage()}`));
77
67
  }
@@ -1,35 +1,222 @@
1
- import { input, confirm } from "@inquirer/prompts";
2
- import { BANNER, color } from "../constants.js";
1
+ /**
2
+ * NOITON init command - the universal multi-agent wizard
3
+ *
4
+ * Flow (with proper pacing and step-by-step reveals):
5
+ * 1. Print banner with octopus mascot
6
+ * 2. Detect all installed agents (with spinner + gradual reveal)
7
+ * 3. If no agents, offer to install OpenCode or Hermes
8
+ * 4. If OpenCode installed, check version & offer update
9
+ * 5. Show checklist of detected agents (one by one)
10
+ * 6. Ask which agents to configure
11
+ * 7. Check if already configured, ask to overwrite
12
+ * 8. Ask for NOITON API key (once)
13
+ * 9. Validate key + fetch models (with spinner)
14
+ * 10. Show models list
15
+ * 11. Ask which model to use as primary
16
+ * 12. Configure each selected agent (with per-agent spinner)
17
+ * 13. Show summary + funny success message + easter egg
18
+ */
19
+
20
+ import { input, confirm, select, checkbox } from "@inquirer/prompts";
21
+ import { c } from "../constants.js";
22
+ import {
23
+ printBanner,
24
+ box,
25
+ spinner,
26
+ ok,
27
+ warn,
28
+ fail,
29
+ info,
30
+ dim,
31
+ step,
32
+ checklist,
33
+ divider,
34
+ sleep,
35
+ line,
36
+ } from "../ui.js";
37
+ import {
38
+ successMessage,
39
+ noAgentMessage,
40
+ thinkingMessage,
41
+ cancelMessage,
42
+ farewellMessage,
43
+ getEasterEgg,
44
+ } from "../messages.js";
3
45
  import { validateKey, maskKey } from "../lib/api.js";
46
+ import { getDetectedAgents, getAgent } from "../lib/agents/index.js";
4
47
  import {
5
- readConfig,
6
- writeConfig,
7
- hasProvider,
8
- upsertProvider,
9
- buildModels,
10
- } from "../lib/config.js";
48
+ detectAgents,
49
+ getOpenCodeStatus,
50
+ } from "../lib/detect.js";
51
+ import { installAgent, updateAgent, offerInstall } from "../lib/install.js";
11
52
 
12
- /**
13
- * Main wizard command - interactive setup.
14
- */
15
53
  export async function initCommand() {
16
- console.log(BANNER);
54
+ // ============= STEP 1: BANNER =============
55
+ printBanner("happy");
56
+ await sleep(400);
57
+
58
+ // ============= STEP 2: DETECT AGENTS =============
59
+ step(1, "Procurando agentes de IA instalados...");
60
+ await sleep(300);
61
+
62
+ const detectSpinner = spinner("Escaneando seu sistema...");
63
+ await sleep(800);
64
+ const detected = getDetectedAgents();
65
+ const opencodeStatus = getOpenCodeStatus();
66
+ detectSpinner.succeed("Escaneamento concluído!");
67
+ await sleep(300);
68
+
69
+ // ============= STEP 2a: NO AGENTS FOUND =============
70
+ if (detected.length === 0) {
71
+ console.log();
72
+ await sleep(200);
73
+ box(noAgentMessage(), "warning");
74
+ await sleep(500);
75
+ console.log();
76
+
77
+ const installChoice = await offerInstall();
78
+ if (!installChoice) {
79
+ console.log();
80
+ info(cancelMessage());
81
+ await sleep(200);
82
+ console.log();
83
+ console.log(c.dim(" 💡 Dica: instale OpenCode ou Hermes e rode noiton novamente!"));
84
+ console.log(c.dim(" OpenCode: curl -fsSL https://opencode.ai/install | bash"));
85
+ console.log(c.dim(" Hermes: pip install nous-hermes"));
86
+ console.log();
87
+ return;
88
+ }
89
+
90
+ // Install the chosen agent
91
+ const result = await installAgent(installChoice);
92
+ if (!result.ok) {
93
+ console.log();
94
+ fail(`Não foi possível instalar: ${result.error}`);
95
+ console.log(c.dim(" Tente instalar manualmente e rode noiton novamente."));
96
+ return;
97
+ }
98
+
99
+ await sleep(500);
100
+ // Re-detect after install
101
+ const reDetected = getDetectedAgents();
102
+ if (reDetected.length === 0) {
103
+ console.log();
104
+ warn("Instalação concluída, mas não foi possível detectar o agente.");
105
+ info("Reinicie o terminal e rode 'npx noiton' novamente.");
106
+ return;
107
+ }
108
+ detected.push(...reDetected);
109
+ }
110
+
111
+ // ============= STEP 3: CHECK OPENCODE VERSION =============
112
+ if (opencodeStatus?.updateAvailable) {
113
+ await sleep(400);
114
+ console.log();
115
+ box(
116
+ `🔄 OpenCode v${opencodeStatus.latest} disponível!\n` +
117
+ ` Você tem v${opencodeStatus.version}.\n\n` +
118
+ ` Atualizar agora?`,
119
+ "warning"
120
+ );
121
+ await sleep(300);
122
+ const wantUpdate = await confirm({
123
+ message: "Atualizar OpenCode?",
124
+ default: true,
125
+ });
126
+ if (wantUpdate) {
127
+ const updateResult = await updateAgent("opencode");
128
+ if (updateResult.ok) {
129
+ ok("OpenCode atualizado!");
130
+ } else {
131
+ warn(`Não foi possível atualizar: ${updateResult.error}`);
132
+ }
133
+ await sleep(300);
134
+ }
135
+ }
136
+
137
+ // ============= STEP 4: SHOW DETECTED AGENTS ONE BY ONE =============
138
+ step(2, "Agentes detectados");
139
+ await sleep(300);
140
+
141
+ // Reveal each agent with a small delay
142
+ const items = detected.map((a) => ({
143
+ label: a.name,
144
+ ok: true,
145
+ detail: a.version ? `v${a.version}` : "(config encontrado)",
146
+ }));
147
+
148
+ // Print one by one for a "checking" feel
149
+ for (const item of items) {
150
+ const mark = c.green("✓");
151
+ const label = c.white(item.label);
152
+ const detail = c.dim(` ${item.detail}`);
153
+ process.stdout.write(` ${mark} ${label}${detail}`);
154
+ await sleep(250);
155
+ process.stdout.write("\n");
156
+ }
157
+ console.log();
158
+ await sleep(300);
159
+
160
+ // ============= STEP 5: CHOOSE AGENTS TO CONFIGURE =============
161
+ step(3, "Escolha quais agentes configurar");
162
+ await sleep(200);
163
+
164
+ const agentChoices = detected.map((a) => ({
165
+ name: `${a.name}${a.version ? ` (v${a.version})` : ""}`,
166
+ value: a.id,
167
+ checked: true,
168
+ }));
169
+
170
+ let selectedIds;
171
+ if (detected.length === 1) {
172
+ selectedIds = [detected[0].id];
173
+ info(`Configurando: ${detected[0].name}`);
174
+ await sleep(200);
175
+ } else {
176
+ selectedIds = await checkbox({
177
+ message: "Quais agentes você quer conectar ao NOITON?",
178
+ choices: agentChoices,
179
+ required: true,
180
+ });
181
+ }
182
+
183
+ const selectedAgents = selectedIds
184
+ .map((id) => getAgent(id))
185
+ .filter(Boolean);
17
186
 
18
- const { config, path } = readConfig();
187
+ // ============= STEP 6: CHECK EXISTING CONFIG =============
188
+ const alreadyConfigured = selectedAgents.filter((a) => {
189
+ try {
190
+ return a.isConfigured();
191
+ } catch {
192
+ return false;
193
+ }
194
+ });
19
195
 
20
- if (hasProvider(config)) {
21
- console.log(color(" ⚠ NOITON já está configurado!\n", "yellow"));
196
+ if (alreadyConfigured.length > 0) {
197
+ await sleep(300);
198
+ console.log();
199
+ warn(
200
+ `NOITON já está configurado em: ${alreadyConfigured.map((a) => a.name).join(", ")}`
201
+ );
202
+ await sleep(200);
22
203
  const overwrite = await confirm({
23
- message: "Deseja sobrescrever a configuração existente?",
204
+ message: "Sobrescrever configuração existente?",
24
205
  default: false,
25
206
  });
26
207
  if (!overwrite) {
27
- console.log(color("\n ✋ Operação cancelada.\n", "dim"));
208
+ console.log();
209
+ info(cancelMessage());
28
210
  return;
29
211
  }
30
212
  }
31
213
 
32
- console.log(color(" Cole sua chave NOITON para começar.\n", "dim"));
214
+ // ============= STEP 7: ASK FOR API KEY =============
215
+ step(4, "Cole sua chave NOITON");
216
+ await sleep(200);
217
+ console.log(c.dim(" Uma chave só serve para todos os agentes! 🐙"));
218
+ await sleep(200);
219
+ console.log();
33
220
 
34
221
  const apiKey = await input({
35
222
  message: " Chave:",
@@ -39,47 +226,135 @@ export async function initCommand() {
39
226
 
40
227
  const trimmedKey = apiKey.trim();
41
228
 
42
- console.log(color("\n ⏳ Validando chave...", "dim"));
43
-
229
+ // ============= STEP 8: VALIDATE KEY + FETCH MODELS =============
230
+ console.log();
231
+ await sleep(200);
232
+ const s = spinner(`${thinkingMessage()} Validando chave...`);
233
+ await sleep(500);
44
234
  const { valid, models, error } = await validateKey(trimmedKey);
45
235
 
46
236
  if (!valid) {
47
- console.log(color(`\n ❌ Erro: ${error}\n`, "red"));
48
- process.exit(1);
237
+ s.fail(`Erro: ${error}`);
238
+ console.log();
239
+ fail("Não foi possível validar a chave. Verifique e tente novamente.");
240
+ return;
49
241
  }
50
242
 
51
- console.log(color("Chave válida!\n", "green"));
243
+ s.succeed("Chave válida!");
244
+ await sleep(300);
52
245
 
53
246
  if (!models || models.length === 0) {
54
- console.log(color(" ⚠ Nenhum modelo disponível para esta chave.\n", "yellow"));
55
- process.exit(1);
247
+ console.log();
248
+ warn("Nenhum modelo disponível para esta chave.");
249
+ return;
56
250
  }
57
251
 
58
- console.log(color(` 📦 ${models.length} modelo(s) encontrado(s):\n`, "cyan"));
252
+ // ============= STEP 9: SHOW MODELS =============
253
+ step(5, `Modelos disponíveis (${models.length})`);
254
+ await sleep(200);
255
+
59
256
  for (const m of models) {
60
257
  const ctx = m.context_window
61
- ? color(` (${Math.round(m.context_window / 1000)}K ctx)`, "dim")
258
+ ? c.dim(` (${Math.round(m.context_window / 1000)}K ctx)`)
62
259
  : "";
63
- console.log(` • ${m.id}${ctx}`);
260
+ process.stdout.write(` ${c.cyan("")} ${m.id}${ctx}`);
261
+ await sleep(120);
262
+ process.stdout.write("\n");
64
263
  }
264
+ console.log();
265
+ await sleep(300);
65
266
 
66
- const proceed = await confirm({
67
- message: "\n Configurar OpenCode com estes modelos?",
68
- default: true,
69
- });
267
+ // ============= STEP 10: CHOOSE PRIMARY MODEL =============
268
+ const needsPrimaryModel = selectedAgents.some((a) => !a.supportsMultipleModels);
70
269
 
71
- if (!proceed) {
72
- console.log(color("\n Operação cancelada.\n", "dim"));
73
- return;
270
+ let primaryModel = null;
271
+ if (needsPrimaryModel) {
272
+ step(6, "Escolha o modelo principal");
273
+ await sleep(200);
274
+ console.log(c.dim(" Alguns agentes usam apenas 1 modelo por vez."));
275
+ await sleep(200);
276
+ console.log();
277
+
278
+ const modelChoices = models.map((m) => ({
279
+ name: `${m.id}${m.context_window ? ` (${Math.round(m.context_window / 1000)}K ctx)` : ""}`,
280
+ value: m.id,
281
+ }));
282
+
283
+ primaryModel = await select({
284
+ message: "Qual modelo usar como principal?",
285
+ choices: modelChoices,
286
+ default: models[0]?.id,
287
+ });
288
+ console.log();
289
+ await sleep(200);
74
290
  }
75
291
 
76
- console.log(color("\n ⏳ Configurando OpenCode...", "dim"));
292
+ // ============= STEP 11: CONFIGURE EACH AGENT =============
293
+ step(needsPrimaryModel ? 7 : 6, "Configurando agentes...");
294
+ await sleep(300);
295
+
296
+ const results = [];
297
+ for (const agent of selectedAgents) {
298
+ const s = spinner(`Configurando ${agent.name}...`);
299
+ await sleep(400);
300
+ try {
301
+ const result = agent.configure({
302
+ apiKey: trimmedKey,
303
+ models,
304
+ primaryModel,
305
+ });
306
+ s.succeed(`${agent.name} configurado!`);
307
+ results.push({ agent: agent.name, ok: true, ...result });
308
+ } catch (err) {
309
+ s.fail(`Erro em ${agent.name}: ${err.message}`);
310
+ results.push({ agent: agent.name, ok: false, error: err.message });
311
+ }
312
+ await sleep(200);
313
+ }
77
314
 
78
- const modelsObj = buildModels(models);
79
- const newConfig = upsertProvider(config, trimmedKey, modelsObj);
80
- writeConfig(newConfig, path);
315
+ // ============= STEP 12: SUMMARY =============
316
+ console.log();
317
+ divider();
318
+ await sleep(300);
319
+ console.log();
320
+ printBanner("dancing");
321
+ await sleep(400);
322
+
323
+ const successCount = results.filter((r) => r.ok).length;
324
+ if (successCount > 0) {
325
+ box(
326
+ `${successMessage()}\n\n` +
327
+ `${successCount} de ${selectedAgents.length} agente(s) configurado(s):`,
328
+ "success"
329
+ );
330
+ console.log();
331
+ await sleep(200);
332
+ for (const r of results) {
333
+ if (r.ok) {
334
+ ok(`${r.agent} ${c.dim(`→ ${r.path}`)}`);
335
+ } else {
336
+ fail(`${r.agent} ${c.dim(`→ ${r.error}`)}`);
337
+ }
338
+ await sleep(150);
339
+ }
340
+ } else {
341
+ box("Nenhum agente foi configurado. Tente novamente!", "error");
342
+ return;
343
+ }
344
+
345
+ // ============= STEP 13: EASTER EGG + FAREWELL =============
346
+ const egg = getEasterEgg();
347
+ if (egg) {
348
+ console.log();
349
+ await sleep(200);
350
+ console.log(c.yellow(` 🎁 ${egg}`));
351
+ }
81
352
 
82
- console.log(color(" ✓ Provider 'noiton' adicionado!\n", "green"));
83
- console.log(color(` 📁 Arquivo: ${path}\n`, "dim"));
84
- console.log(color(" 🚀 Reinicie o OpenCode e use os modelos!\n", "cyan"));
353
+ console.log();
354
+ await sleep(200);
355
+ console.log(c.dim(" 💡 Reinicie os agentes para aplicar as mudanças."));
356
+ console.log();
357
+ await sleep(200);
358
+ console.log(c.purple(` ${farewellMessage()}`));
359
+ console.log();
85
360
  }