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 +46 -31
- package/package.json +25 -8
- package/src/commands/change.js +44 -54
- package/src/commands/init.js +317 -42
- package/src/commands/refresh.js +59 -47
- package/src/commands/remove.js +46 -22
- package/src/commands/status.js +58 -31
- package/src/constants.js +31 -17
- package/src/index.js +21 -10
- package/src/lib/agents/aider.js +141 -0
- package/src/lib/agents/cline.js +128 -0
- package/src/lib/agents/hermes.js +156 -0
- package/src/lib/agents/index.js +82 -0
- package/src/lib/agents/kilo.js +140 -0
- package/src/lib/agents/kimi.js +149 -0
- package/src/lib/agents/openclaw.js +155 -0
- package/src/lib/agents/opencode.js +177 -0
- package/src/lib/detect.js +286 -0
- package/src/lib/install.js +210 -0
- package/src/messages.js +169 -0
- package/src/octopus.js +254 -0
- package/src/ui.js +230 -0
package/src/commands/refresh.js
CHANGED
|
@@ -1,68 +1,80 @@
|
|
|
1
|
-
import { BANNER, color } from "../constants.js";
|
|
2
|
-
import { validateKey } from "../lib/api.js";
|
|
3
|
-
import {
|
|
4
|
-
readConfig,
|
|
5
|
-
writeConfig,
|
|
6
|
-
hasProvider,
|
|
7
|
-
getProvider,
|
|
8
|
-
upsertProvider,
|
|
9
|
-
buildModels,
|
|
10
|
-
} from "../lib/config.js";
|
|
11
|
-
|
|
12
1
|
/**
|
|
13
|
-
*
|
|
2
|
+
* NOITON refresh command - re-fetch models from the API
|
|
14
3
|
*/
|
|
4
|
+
|
|
5
|
+
import { c } from "../constants.js";
|
|
6
|
+
import { printHeader, spinner, ok, fail, info, box, dim } from "../ui.js";
|
|
7
|
+
import { getConfiguredAgents, getAgent } from "../lib/agents/index.js";
|
|
8
|
+
import { validateKey, maskKey } from "../lib/api.js";
|
|
9
|
+
import { input } from "@inquirer/prompts";
|
|
10
|
+
import { farewellMessage } from "../messages.js";
|
|
11
|
+
|
|
15
12
|
export async function refreshCommand() {
|
|
16
|
-
|
|
13
|
+
printHeader("Atualizar Modelos");
|
|
17
14
|
|
|
18
|
-
const
|
|
15
|
+
const configured = getConfiguredAgents();
|
|
19
16
|
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
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;
|
|
24
20
|
}
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
info(`${configured.length} agente(s) encontrado(s): ${configured.map(a => a.name).join(", ")}`);
|
|
23
|
+
console.log();
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
// We need the API key to refresh. Try to read from any agent.
|
|
26
|
+
let apiKey = null;
|
|
27
|
+
for (const agent of configured) {
|
|
28
|
+
try {
|
|
29
|
+
const status = agent.getStatus();
|
|
30
|
+
if (status.apiKey && !status.apiKey.startsWith("{env:")) {
|
|
31
|
+
apiKey = status.apiKey;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
} catch {}
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
if (!apiKey) {
|
|
38
|
+
dim(" Sua chave não pode ser lida. Cole ela:");
|
|
39
|
+
const key = await input({
|
|
40
|
+
message: " Chave NOITON:",
|
|
41
|
+
validate: (v) => (v.trim().length >= 10 ? true : "Chave muito curta"),
|
|
42
|
+
transformer: (v) => maskKey(v),
|
|
43
|
+
});
|
|
44
|
+
apiKey = key.trim();
|
|
45
|
+
}
|
|
35
46
|
|
|
47
|
+
const s = spinner("Buscando modelos atualizados da API...");
|
|
36
48
|
const { valid, models, error } = await validateKey(apiKey);
|
|
37
49
|
|
|
38
|
-
if (!valid) {
|
|
39
|
-
|
|
40
|
-
console.log(color(" Dica: rode ", "dim") + color("npx noiton change", "cyan") + color(" para trocar a chave.\n", "dim"));
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!models || models.length === 0) {
|
|
45
|
-
console.log(color(" ⚠ Nenhum modelo disponível.\n", "yellow"));
|
|
50
|
+
if (!valid || !models) {
|
|
51
|
+
s.fail(`Erro: ${error || "Resposta inválida"}`);
|
|
46
52
|
return;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const newCount = Object.keys(newModels).length;
|
|
52
|
-
|
|
53
|
-
console.log(color(" ✓ Modelos atualizados!\n", "green"));
|
|
54
|
-
console.log(color(` Antes: ${oldCount} modelo(s) → Agora: ${newCount} modelo(s)\n`, "dim"));
|
|
55
|
+
s.succeed(`${models.length} modelo(s) encontrado(s)!`);
|
|
56
|
+
console.log();
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
let success = 0;
|
|
59
|
+
for (const agent of configured) {
|
|
60
|
+
const sub = spinner(`Atualizando ${agent.name}...`);
|
|
61
|
+
try {
|
|
62
|
+
agent.refresh(models);
|
|
63
|
+
sub.succeed(`${agent.name} atualizado (${models.length} modelos)`);
|
|
64
|
+
success++;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
sub.fail(`${agent.name}: ${err.message}`);
|
|
67
|
+
}
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
console.log();
|
|
71
|
+
box(
|
|
72
|
+
success > 0
|
|
73
|
+
? `✨ Atualização concluída!\n${success} agente(s) atualizado(s).`
|
|
74
|
+
: "⚠️ Nenhum agente pôde ser atualizado.",
|
|
75
|
+
success > 0 ? "success" : "warning"
|
|
76
|
+
);
|
|
65
77
|
|
|
66
|
-
console.log(
|
|
67
|
-
console.log(
|
|
78
|
+
console.log();
|
|
79
|
+
console.log(c.dim(` ${farewellMessage()}`));
|
|
68
80
|
}
|
package/src/commands/remove.js
CHANGED
|
@@ -1,38 +1,62 @@
|
|
|
1
|
-
import { confirm } from "@inquirer/prompts";
|
|
2
|
-
import { BANNER, color } from "../constants.js";
|
|
3
|
-
import {
|
|
4
|
-
readConfig,
|
|
5
|
-
writeConfig,
|
|
6
|
-
hasProvider,
|
|
7
|
-
removeProvider,
|
|
8
|
-
} from "../lib/config.js";
|
|
9
|
-
|
|
10
1
|
/**
|
|
11
|
-
*
|
|
2
|
+
* NOITON remove command - remove NOITON provider from agents
|
|
12
3
|
*/
|
|
4
|
+
|
|
5
|
+
import { confirm } from "@inquirer/prompts";
|
|
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";
|
|
9
|
+
import { farewellMessage, cancelMessage } from "../messages.js";
|
|
10
|
+
|
|
13
11
|
export async function removeCommand() {
|
|
14
|
-
|
|
12
|
+
printHeader("Remover NOITON");
|
|
15
13
|
|
|
16
|
-
const
|
|
14
|
+
const configured = getConfiguredAgents();
|
|
17
15
|
|
|
18
|
-
if (
|
|
19
|
-
|
|
16
|
+
if (configured.length === 0) {
|
|
17
|
+
box("NOITON não está configurado em nenhum agente.", "info");
|
|
20
18
|
return;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
info(`Agentes com NOITON: ${configured.map(a => a.name).join(", ")}`);
|
|
22
|
+
console.log();
|
|
23
|
+
|
|
24
|
+
const sure = await confirm({
|
|
25
|
+
message: `Remover NOITON de ${configured.length} agente(s)?`,
|
|
25
26
|
default: false,
|
|
26
27
|
});
|
|
27
28
|
|
|
28
|
-
if (!
|
|
29
|
-
console.log(
|
|
29
|
+
if (!sure) {
|
|
30
|
+
console.log();
|
|
31
|
+
info(cancelMessage());
|
|
30
32
|
return;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
console.log();
|
|
36
|
+
let success = 0;
|
|
37
|
+
for (const agent of configured) {
|
|
38
|
+
const sub = spinner(`Removendo de ${agent.name}...`);
|
|
39
|
+
try {
|
|
40
|
+
const result = agent.remove();
|
|
41
|
+
if (result.removed) {
|
|
42
|
+
sub.succeed(`${agent.name} limpo!`);
|
|
43
|
+
success++;
|
|
44
|
+
} else {
|
|
45
|
+
sub.info(`${agent.name} já estava limpo`);
|
|
46
|
+
}
|
|
47
|
+
} catch (err) {
|
|
48
|
+
sub.fail(`${agent.name}: ${err.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log();
|
|
53
|
+
box(
|
|
54
|
+
success > 0
|
|
55
|
+
? `🐙 NOITON removido de ${success} agente(s).\n\nAs outras configurações foram preservadas.`
|
|
56
|
+
: "Nada para remover.",
|
|
57
|
+
success > 0 ? "success" : "info"
|
|
58
|
+
);
|
|
35
59
|
|
|
36
|
-
console.log(
|
|
37
|
-
console.log(
|
|
60
|
+
console.log();
|
|
61
|
+
console.log(c.dim(` ${farewellMessage()}`));
|
|
38
62
|
}
|
package/src/commands/status.js
CHANGED
|
@@ -1,41 +1,68 @@
|
|
|
1
|
-
import { BANNER, color } from "../constants.js";
|
|
2
|
-
import { maskKey } from "../lib/api.js";
|
|
3
|
-
import { readConfig, hasProvider, getProvider } from "../lib/config.js";
|
|
4
|
-
|
|
5
1
|
/**
|
|
6
|
-
*
|
|
2
|
+
* NOITON status command - show configuration across all agents
|
|
7
3
|
*/
|
|
4
|
+
|
|
5
|
+
import { c } from "../constants.js";
|
|
6
|
+
import { printHeader, ok, warn, fail, info, dim, divider, checklist, box } from "../ui.js";
|
|
7
|
+
import { getAllStatuses } from "../lib/agents/index.js";
|
|
8
|
+
import { maskKey } from "../lib/api.js";
|
|
9
|
+
import { farewellMessage } from "../messages.js";
|
|
10
|
+
|
|
8
11
|
export async function statusCommand() {
|
|
9
|
-
|
|
12
|
+
printHeader("Status dos Agentes");
|
|
10
13
|
|
|
11
|
-
const
|
|
14
|
+
const statuses = getAllStatuses();
|
|
15
|
+
const detected = statuses.filter((s) => s.detected);
|
|
12
16
|
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
console.log(
|
|
17
|
+
if (detected.length === 0) {
|
|
18
|
+
box("Nenhum agente de IA detectado!\n\nRode 'npx noiton' para configurar.", "warning");
|
|
19
|
+
console.log();
|
|
20
|
+
console.log(c.dim(` ${farewellMessage()}`));
|
|
16
21
|
return;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
for (const s of statuses) {
|
|
25
|
+
if (!s.detected) continue;
|
|
26
|
+
|
|
27
|
+
console.log();
|
|
28
|
+
console.log(c.bold(c.purple(` 🐙 ${s.name}`)));
|
|
29
|
+
|
|
30
|
+
if (s.error) {
|
|
31
|
+
fail(`Erro: ${s.error}`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (s.configured) {
|
|
36
|
+
ok("NOITON configurado");
|
|
37
|
+
if (s.apiKey) {
|
|
38
|
+
dim(`Chave: ${maskKey(s.apiKey)}`);
|
|
39
|
+
}
|
|
40
|
+
if (s.models && s.models.length > 0) {
|
|
41
|
+
dim(`Modelos: ${s.models.join(", ")}`);
|
|
42
|
+
}
|
|
43
|
+
dim(`Config: ${s.path}`);
|
|
44
|
+
if (s.envPath) {
|
|
45
|
+
dim(`Env: ${s.envPath}`);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
warn("NOITON não configurado");
|
|
49
|
+
dim(`Config: ${s.path}`);
|
|
50
|
+
}
|
|
39
51
|
}
|
|
40
|
-
|
|
52
|
+
|
|
53
|
+
// Show not-detected agents
|
|
54
|
+
const notDetected = statuses.filter((s) => !s.detected);
|
|
55
|
+
if (notDetected.length > 0) {
|
|
56
|
+
console.log();
|
|
57
|
+
divider();
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(c.dim(" Outros agentes suportados (não detectados):"));
|
|
60
|
+
for (const s of notDetected) {
|
|
61
|
+
console.log(c.dim(` ○ ${s.name}`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(c.dim(` ${farewellMessage()}`));
|
|
67
|
+
console.log();
|
|
41
68
|
}
|
package/src/constants.js
CHANGED
|
@@ -1,26 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
import picocolors from "picocolors";
|
|
2
|
+
|
|
3
|
+
export const VERSION = "2.0.0";
|
|
2
4
|
export const PROVIDER_ID = "noiton";
|
|
3
5
|
export const PROVIDER_NAME = "NOITON";
|
|
4
6
|
export const BASE_URL = "https://blogs-litellm-app.rwezkp.easypanel.host";
|
|
5
7
|
export const NPM_PACKAGE = "@ai-sdk/openai-compatible";
|
|
8
|
+
export const GITHUB_URL = "https://github.com/tokensilimitados/noiton";
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
║ NOITON - OpenCode Authenticator v${VERSION} ║
|
|
10
|
-
╚══════════════════════════════════════════╝
|
|
11
|
-
`;
|
|
10
|
+
// Octopus says goodbye
|
|
11
|
+
export const sayGoodbye = "O polvo agradece! 🐙";
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Colorize text using picocolors (lightweight, no deps beyond itself).
|
|
15
|
+
* Falls back gracefully when colors are disabled.
|
|
16
|
+
*/
|
|
17
|
+
export const c = {
|
|
18
|
+
purple: (t) => picocolors.magenta(t),
|
|
19
|
+
cyan: (t) => picocolors.cyan(t),
|
|
20
|
+
green: (t) => picocolors.green(t),
|
|
21
|
+
yellow: (t) => picocolors.yellow(t),
|
|
22
|
+
red: (t) => picocolors.red(t),
|
|
23
|
+
blue: (t) => picocolors.blue(t),
|
|
24
|
+
gray: (t) => picocolors.gray(t),
|
|
25
|
+
dim: (t) => picocolors.dim(t),
|
|
26
|
+
bold: (t) => picocolors.bold(t),
|
|
27
|
+
italic: (t) => picocolors.italic(t),
|
|
28
|
+
underline: (t) => picocolors.underline(t),
|
|
29
|
+
magenta: (t) => picocolors.magenta(t),
|
|
30
|
+
white: (t) => picocolors.white(t),
|
|
31
|
+
bgMagenta: (t) => picocolors.bgMagenta(t),
|
|
32
|
+
bgCyan: (t) => picocolors.bgCyan(t),
|
|
33
|
+
bgGreen: (t) => picocolors.bgGreen(t),
|
|
22
34
|
};
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
|
|
36
|
+
/** Backwards-compat alias for the old color() signature */
|
|
37
|
+
export function color(text, col) {
|
|
38
|
+
if (c[col]) return c[col](text);
|
|
39
|
+
return text;
|
|
26
40
|
}
|
package/src/index.js
CHANGED
|
@@ -1,30 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { VERSION, c, GITHUB_URL } from "./constants.js";
|
|
2
|
+
import { printBanner, box } from "./ui.js";
|
|
2
3
|
import { initCommand } from "./commands/init.js";
|
|
3
4
|
import { statusCommand } from "./commands/status.js";
|
|
4
5
|
import { refreshCommand } from "./commands/refresh.js";
|
|
5
6
|
import { changeCommand } from "./commands/change.js";
|
|
6
7
|
import { removeCommand } from "./commands/remove.js";
|
|
8
|
+
import { randomGoodbye } from "./messages.js";
|
|
7
9
|
|
|
8
10
|
const HELP = `
|
|
9
|
-
${
|
|
11
|
+
${c.purple("🐙 NOITON")} ${c.dim("v" + VERSION)} - Universal AI Agent Connector
|
|
10
12
|
|
|
11
|
-
${
|
|
13
|
+
${c.bold("USAGE")}
|
|
12
14
|
npx noiton Wizard interativo (padrão)
|
|
13
15
|
npx noiton init Mesmo que acima
|
|
14
|
-
npx noiton status Mostra configuração
|
|
16
|
+
npx noiton status Mostra configuração de todos os agentes
|
|
15
17
|
npx noiton refresh Re-busca modelos da API
|
|
16
|
-
npx noiton change Troca a chave API
|
|
17
|
-
npx noiton remove Remove
|
|
18
|
+
npx noiton change Troca a chave API em todos
|
|
19
|
+
npx noiton remove Remove NOITON de todos os agentes
|
|
18
20
|
npx noiton help, --help Mostra esta ajuda
|
|
19
21
|
npx noiton --version Mostra versão
|
|
20
22
|
|
|
21
|
-
${
|
|
23
|
+
${c.bold("AGENTES SUPORTADOS")}
|
|
24
|
+
${c.cyan("•")} OpenCode Programador IA (CLI)
|
|
25
|
+
${c.cyan("•")} Hermes Assistente de tarefas (CLI)
|
|
26
|
+
${c.cyan("•")} OpenClaw Agente open-source (CLI)
|
|
27
|
+
${c.cyan("•")} Cline Extensão VS Code
|
|
28
|
+
${c.cyan("•")} Aider Pair programmer de terminal
|
|
29
|
+
${c.cyan("•")} Kilo Code CLI + extensão VS Code
|
|
30
|
+
${c.cyan("•")} Kimi Code CLI + extensão VS Code
|
|
31
|
+
|
|
32
|
+
${c.bold("EXEMPLOS")}
|
|
22
33
|
npx noiton # configura pela primeira vez
|
|
23
34
|
npx noiton refresh # atualiza lista de modelos
|
|
24
35
|
npx noiton status # ver o que está configurado
|
|
25
36
|
|
|
26
|
-
${
|
|
27
|
-
GitHub:
|
|
37
|
+
${c.bold("MAIS INFO")}
|
|
38
|
+
GitHub: ${GITHUB_URL}
|
|
28
39
|
`;
|
|
29
40
|
|
|
30
41
|
/**
|
|
@@ -70,7 +81,7 @@ export async function run(args) {
|
|
|
70
81
|
await removeCommand();
|
|
71
82
|
break;
|
|
72
83
|
default:
|
|
73
|
-
console.error(
|
|
84
|
+
console.error(c.red(`\n ❌ Comando desconhecido: ${command}\n`));
|
|
74
85
|
console.log(HELP);
|
|
75
86
|
process.exit(1);
|
|
76
87
|
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aider Agent Adapter
|
|
3
|
+
*
|
|
4
|
+
* Config: ~/.aider.conf.yml (YAML) + env vars
|
|
5
|
+
* Format:
|
|
6
|
+
* openai-api-base: <BASE_URL>
|
|
7
|
+
* model: <model-id>
|
|
8
|
+
* .env: OPENAI_API_KEY=<key>
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import YAML from "yaml";
|
|
15
|
+
import { BASE_URL } from "../../constants.js";
|
|
16
|
+
|
|
17
|
+
const HOME = homedir();
|
|
18
|
+
const CONFIG_PATH = join(HOME, ".aider.conf.yml");
|
|
19
|
+
const ENV_PATH = join(HOME, ".env");
|
|
20
|
+
|
|
21
|
+
function readYaml() {
|
|
22
|
+
if (!existsSync(CONFIG_PATH)) return { config: {}, path: CONFIG_PATH };
|
|
23
|
+
try {
|
|
24
|
+
const raw = readFileSync(CONFIG_PATH, "utf8");
|
|
25
|
+
return { config: YAML.parse(raw) || {}, path: CONFIG_PATH };
|
|
26
|
+
} catch {
|
|
27
|
+
return { config: {}, path: CONFIG_PATH };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function writeYaml(config) {
|
|
32
|
+
if (!existsSync(dirname(CONFIG_PATH))) {
|
|
33
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
writeFileSync(CONFIG_PATH, YAML.stringify(config, { indent: 2 }), "utf8");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readEnvFile() {
|
|
39
|
+
if (!existsSync(ENV_PATH)) return {};
|
|
40
|
+
const raw = readFileSync(ENV_PATH, "utf8");
|
|
41
|
+
const env = {};
|
|
42
|
+
for (const line of raw.split("\n")) {
|
|
43
|
+
const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
|
|
44
|
+
if (m) env[m[1]] = m[2];
|
|
45
|
+
}
|
|
46
|
+
return env;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function writeEnvFile(env) {
|
|
50
|
+
const lines = Object.entries(env).map(([k, v]) => `${k}=${v}`);
|
|
51
|
+
writeFileSync(ENV_PATH, lines.join("\n") + "\n", "utf8");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ============= ADAPTER INTERFACE =============
|
|
55
|
+
|
|
56
|
+
export const aiderAgent = {
|
|
57
|
+
id: "aider",
|
|
58
|
+
name: "Aider",
|
|
59
|
+
supportsMultipleModels: false,
|
|
60
|
+
|
|
61
|
+
detect() {
|
|
62
|
+
return existsSync(CONFIG_PATH);
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
isConfigured() {
|
|
66
|
+
const { config } = readYaml();
|
|
67
|
+
return Boolean(config?.["openai-api-base"] === BASE_URL);
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
getStatus() {
|
|
71
|
+
const { config, path } = readYaml();
|
|
72
|
+
const env = readEnvFile();
|
|
73
|
+
const apiKey = env.OPENAI_API_KEY;
|
|
74
|
+
const configured = config?.["openai-api-base"] === BASE_URL;
|
|
75
|
+
const models = config?.model ? [config.model] : [];
|
|
76
|
+
return {
|
|
77
|
+
configured,
|
|
78
|
+
path,
|
|
79
|
+
models,
|
|
80
|
+
apiKey: apiKey ? `${apiKey.slice(0, 6)}...${apiKey.slice(-4)}` : null,
|
|
81
|
+
envPath: ENV_PATH,
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
configure({ apiKey, models, primaryModel }) {
|
|
86
|
+
const { config } = readYaml();
|
|
87
|
+
config["openai-api-base"] = `${BASE_URL}/v1`;
|
|
88
|
+
config.model = primaryModel || models[0]?.id || "gpt-4";
|
|
89
|
+
config["weak-model"] = primaryModel || models[0]?.id || "gpt-4";
|
|
90
|
+
writeYaml(config);
|
|
91
|
+
|
|
92
|
+
// Set API key in .env
|
|
93
|
+
const env = readEnvFile();
|
|
94
|
+
env.OPENAI_API_KEY = apiKey;
|
|
95
|
+
writeEnvFile(env);
|
|
96
|
+
|
|
97
|
+
return { path: CONFIG_PATH, envPath: ENV_PATH, modelsCount: 1 };
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
changeKey(apiKey) {
|
|
101
|
+
if (!this.isConfigured()) {
|
|
102
|
+
throw new Error("NOITON não está configurado no Aider");
|
|
103
|
+
}
|
|
104
|
+
const env = readEnvFile();
|
|
105
|
+
env.OPENAI_API_KEY = apiKey;
|
|
106
|
+
writeEnvFile(env);
|
|
107
|
+
return { path: ENV_PATH };
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
refresh(models) {
|
|
111
|
+
const { config } = readYaml();
|
|
112
|
+
if (!config?.["openai-api-base"]) {
|
|
113
|
+
throw new Error("NOITON não está configurado no Aider");
|
|
114
|
+
}
|
|
115
|
+
if (models.length > 0) {
|
|
116
|
+
config.model = models[0].id;
|
|
117
|
+
config["weak-model"] = models[0].id;
|
|
118
|
+
}
|
|
119
|
+
writeYaml(config);
|
|
120
|
+
return { path: CONFIG_PATH, modelsCount: 1 };
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
remove() {
|
|
124
|
+
const { config } = readYaml();
|
|
125
|
+
let removed = false;
|
|
126
|
+
if (config?.["openai-api-base"] === BASE_URL) {
|
|
127
|
+
delete config["openai-api-base"];
|
|
128
|
+
delete config.model;
|
|
129
|
+
delete config["weak-model"];
|
|
130
|
+
removed = true;
|
|
131
|
+
writeYaml(config);
|
|
132
|
+
}
|
|
133
|
+
const env = readEnvFile();
|
|
134
|
+
if (env.OPENAI_API_KEY) {
|
|
135
|
+
delete env.OPENAI_API_KEY;
|
|
136
|
+
writeEnvFile(env);
|
|
137
|
+
removed = true;
|
|
138
|
+
}
|
|
139
|
+
return { path: CONFIG_PATH, envPath: ENV_PATH, removed };
|
|
140
|
+
},
|
|
141
|
+
};
|