@rabts/cli 1.0.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/bin/cli.js ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ import { ensureConfig, configureApiKey } from '../src/config.js';
10
+ import { startInteractiveLoop } from '../src/agent.js';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+ const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8'));
15
+
16
+ // Cor primária da marca
17
+ const rabtsColor = chalk.cyan; // Azul claro para padronizar
18
+
19
+ const logo = rabtsColor(`
20
+ %%%%
21
+ %%%%%%%%%%
22
+ %%%%%%%%%%%%%%%%%@
23
+ %%%%%%%%%%%%%%%%%%%%%%%%%
24
+ %%% %%%%% %%%%%%%%%%%%%%%%%%
25
+ @%%%% %%% %%%%%%%%%%%%%%%%%%
26
+ %%%%%% % %%%%%%%%%%%%%%%%%
27
+ @%%%%%%% %% %%%%%%%%%%%%%%%%%%
28
+ %%%%%%%%%% % %%%%%%%%%%%%%%%%
29
+ %%%%%%%%%%%%% % %%%%%%%%%%%%%%%%
30
+ %%%%%%%%%%%%%%%%% %%%%%%%%%%
31
+ %%%%%%%%%%%%%%%%%% %%% %%%%%%%
32
+ %%%%%%%%%%%%%%%%% %%%%%
33
+ %%%%%%%%%%%%%%%%%% %%%%%%
34
+ %%%%%%%%%%%%%%% %%%%%%%%
35
+ @%%%%%%%%%%%%% %%%%%%%%%
36
+ %%%%%%%%%%% %%%%%%%
37
+ %%%%%%%%%% %%% %%%%%%%
38
+ %%%%%%%@ %%% @%%%%%%
39
+ % %% %%%%%%%
40
+ %%% @%%%%%%
41
+ %%% %%%%%@
42
+ %%%
43
+ `);
44
+
45
+ const program = new Command();
46
+
47
+ program
48
+ .name('rabts')
49
+ .description('Rabts Studio AI CLI')
50
+ .version(packageJson.version)
51
+ .action(async () => {
52
+ console.clear();
53
+ console.log(logo);
54
+ console.log(chalk.gray(`v${packageJson.version} - Seu Agente Inteligente\n`));
55
+
56
+ const config = await ensureConfig();
57
+ await startInteractiveLoop(config);
58
+ });
59
+
60
+ program
61
+ .command('config')
62
+ .description('Força a reconfiguração de provedor e chave de API')
63
+ .action(async () => {
64
+ console.clear();
65
+ console.log(logo);
66
+ const config = await configureApiKey();
67
+ await startInteractiveLoop(config);
68
+ });
69
+
70
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@rabts/cli",
3
+ "version": "1.0.0",
4
+ "description": "Rabts Studio CLI",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "rabts": "bin/cli.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node ./bin/cli.js"
12
+ },
13
+ "dependencies": {
14
+ "@anthropic-ai/sdk": "^0.105.0",
15
+ "@google/genai": "^2.9.0",
16
+ "axios": "^1.7.2",
17
+ "chalk": "^5.3.0",
18
+ "commander": "^12.1.0",
19
+ "dotenv": "^16.4.5",
20
+ "inquirer": "^10.0.0",
21
+ "openai": "^4.52.0"
22
+ }
23
+ }
package/src/agent.js ADDED
@@ -0,0 +1,183 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { exec } from 'child_process';
4
+ import util from 'util';
5
+ import chalk from 'chalk';
6
+ import inquirer from 'inquirer';
7
+ import { sendMessage } from './llm.js';
8
+
9
+ const execPromise = util.promisify(exec);
10
+
11
+ // Tema Rabts
12
+ const themeColor = chalk.cyan; // Azul claro para padronizar
13
+ const infoColor = chalk.gray;
14
+
15
+ const tools = [
16
+ {
17
+ type: "function",
18
+ function: {
19
+ name: "read_file",
20
+ description: "Lê o conteúdo de um arquivo do sistema local.",
21
+ parameters: {
22
+ type: "object",
23
+ properties: { filepath: { type: "string" } },
24
+ required: ["filepath"]
25
+ }
26
+ }
27
+ },
28
+ {
29
+ type: "function",
30
+ function: {
31
+ name: "write_file",
32
+ description: "Escreve ou sobrescreve um arquivo no sistema local.",
33
+ parameters: {
34
+ type: "object",
35
+ properties: {
36
+ filepath: { type: "string" },
37
+ content: { type: "string" }
38
+ },
39
+ required: ["filepath", "content"]
40
+ }
41
+ }
42
+ },
43
+ {
44
+ type: "function",
45
+ function: {
46
+ name: "run_command",
47
+ description: "Executa um comando no terminal local.",
48
+ parameters: {
49
+ type: "object",
50
+ properties: { command: { type: "string" } },
51
+ required: ["command"]
52
+ }
53
+ }
54
+ }
55
+ ];
56
+
57
+ async function executeToolCall(toolCall) {
58
+ const args = JSON.parse(toolCall.function.arguments);
59
+ const cwd = process.cwd();
60
+
61
+ if (toolCall.function.name === 'read_file') {
62
+ try {
63
+ const fullPath = path.resolve(cwd, args.filepath);
64
+ return fs.readFileSync(fullPath, 'utf8');
65
+ } catch (e) {
66
+ return `Erro ao ler arquivo: ${e.message}`;
67
+ }
68
+ }
69
+
70
+ if (toolCall.function.name === 'write_file') {
71
+ try {
72
+ const fullPath = path.resolve(cwd, args.filepath);
73
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
74
+ fs.writeFileSync(fullPath, args.content, 'utf8');
75
+ console.log(themeColor(` ↳ Arquivo salvo: `) + chalk.white(args.filepath));
76
+ return `Arquivo salvo em ${fullPath}`;
77
+ } catch (e) {
78
+ return `Erro ao salvar arquivo: ${e.message}`;
79
+ }
80
+ }
81
+
82
+ if (toolCall.function.name === 'run_command') {
83
+ const { confirm } = await inquirer.prompt([{
84
+ type: 'confirm',
85
+ name: 'confirm',
86
+ message: chalk.yellow(`Permitir comando: '${args.command}'?`),
87
+ default: false
88
+ }]);
89
+
90
+ if (!confirm) return `Usuário negou o comando: ${args.command}`;
91
+
92
+ try {
93
+ console.log(themeColor(` ↳ Executando: `) + chalk.white(args.command));
94
+ const { stdout, stderr } = await execPromise(args.command, { cwd });
95
+ return stdout || stderr || "Sucesso sem output.";
96
+ } catch (e) {
97
+ return `Erro: ${e.message}\nStderr: ${e.stderr || ''}`;
98
+ }
99
+ }
100
+
101
+ return "Função desconhecida.";
102
+ }
103
+
104
+ export async function startInteractiveLoop(config) {
105
+ console.log(infoColor(`\nConectado via ${config.provider} (Modelo: ${config.model})`));
106
+ console.log(infoColor('Você pode conversar ou pedir para ler/criar arquivos. Digite "sair" para encerrar.\n'));
107
+
108
+ let messages = [
109
+ {
110
+ role: 'system',
111
+ content: 'Você é o Rabts AI, um assistente de terminal. Você pode conversar com o usuário ou usar ferramentas para manipular arquivos e comandos quando requisitado.'
112
+ }
113
+ ];
114
+
115
+ const supportsTools = (config.provider === 'rabts' || config.provider === 'openai');
116
+
117
+ while (true) {
118
+ const { userPrompt } = await inquirer.prompt([
119
+ {
120
+ type: 'input',
121
+ name: 'userPrompt',
122
+ message: chalk.cyan('❯')
123
+ }
124
+ ]);
125
+
126
+ if (userPrompt.toLowerCase() === 'sair' || userPrompt.toLowerCase() === 'exit') {
127
+ console.log(infoColor('Até logo!'));
128
+ break;
129
+ }
130
+
131
+ if (!userPrompt.trim()) continue;
132
+
133
+ messages.push({ role: 'user', content: userPrompt });
134
+
135
+ let isProcessingTools = true;
136
+ let totalPromptTokens = 0;
137
+ let totalCompletionTokens = 0;
138
+ let startTime = Date.now();
139
+
140
+ while (isProcessingTools) {
141
+ process.stdout.write(infoColor(' Pensando...'));
142
+
143
+ const responseMessage = await sendMessage(config, messages, supportsTools ? tools : null);
144
+
145
+ process.stdout.clearLine(0);
146
+ process.stdout.cursorTo(0);
147
+
148
+ if (responseMessage.usage) {
149
+ totalPromptTokens += responseMessage.usage.prompt_tokens || 0;
150
+ totalCompletionTokens += responseMessage.usage.completion_tokens || 0;
151
+ }
152
+
153
+ messages.push({
154
+ role: responseMessage.role,
155
+ content: responseMessage.content,
156
+ tool_calls: responseMessage.tool_calls
157
+ });
158
+
159
+ if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
160
+ for (const toolCall of responseMessage.tool_calls) {
161
+ console.log(themeColor(` ✦ Usando ferramenta: `) + chalk.white(toolCall.function.name));
162
+ const result = await executeToolCall(toolCall);
163
+ messages.push({
164
+ tool_call_id: toolCall.id,
165
+ role: "tool",
166
+ name: toolCall.function.name,
167
+ content: result,
168
+ });
169
+ }
170
+ } else {
171
+ const duration = ((Date.now() - startTime) / 1000).toFixed(1);
172
+
173
+ if (responseMessage.content) {
174
+ console.log(themeColor('\nRabts:'));
175
+ console.log(chalk.white(responseMessage.content));
176
+ }
177
+
178
+ console.log(infoColor(`\n ↳ Finalizado em ${duration}s • ${totalPromptTokens + totalCompletionTokens} tokens (${totalPromptTokens} in, ${totalCompletionTokens} out)\n`));
179
+ isProcessingTools = false;
180
+ }
181
+ }
182
+ }
183
+ }
package/src/config.js ADDED
@@ -0,0 +1,97 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import inquirer from 'inquirer';
5
+ import chalk from 'chalk';
6
+
7
+ const CONFIG_FILE_PATH = path.join(os.homedir(), '.rabts-config.json');
8
+
9
+ const RABTS_DEFAULT_KEY = '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';
10
+
11
+ export function getConfig() {
12
+ if (fs.existsSync(CONFIG_FILE_PATH)) {
13
+ try {
14
+ const data = fs.readFileSync(CONFIG_FILE_PATH, 'utf8');
15
+ return JSON.parse(data);
16
+ } catch (e) {
17
+ return null;
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+
23
+ export function saveConfig(config) {
24
+ fs.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(config, null, 2));
25
+ }
26
+
27
+ export async function ensureConfig() {
28
+ let config = getConfig();
29
+ if (config) {
30
+ return config;
31
+ }
32
+ return await configureApiKey();
33
+ }
34
+
35
+ export async function configureApiKey() {
36
+ console.log(chalk.blue('\n=== Bem-vindo ao Rabts Studio CLI ===\n'));
37
+
38
+ const { provider } = await inquirer.prompt([
39
+ {
40
+ type: 'list',
41
+ name: 'provider',
42
+ message: 'Qual provedor de IA você quer usar?',
43
+ choices: ['rabts', 'openai', 'anthropic', 'google']
44
+ }
45
+ ]);
46
+
47
+ let config = { provider };
48
+
49
+ if (provider === 'rabts') {
50
+ config.apiKey = RABTS_DEFAULT_KEY;
51
+ const { model } = await inquirer.prompt([
52
+ {
53
+ type: 'list',
54
+ name: 'model',
55
+ message: 'Escolha um modelo gratuito do Rabts:',
56
+ choices: [
57
+ 'qwen/qwen3-32b',
58
+ 'openai/gpt-oss-120b',
59
+ 'moonshotai/kimi-k2-instruct',
60
+ 'llama-3.1-8b-instant',
61
+ 'openai/gpt-oss-20b',
62
+ 'meta-llama/llama-4-scout-17b-16e-instruct'
63
+ ]
64
+ }
65
+ ]);
66
+ config.model = model;
67
+ console.log(chalk.green('\nConfigurado para usar o Rabts Studio (Modelos Gratuitos).\n'));
68
+ } else {
69
+ const { apiKey } = await inquirer.prompt([
70
+ {
71
+ type: 'input',
72
+ name: 'apiKey',
73
+ message: `Digite a sua chave de API da ${provider}:`
74
+ }
75
+ ]);
76
+ config.apiKey = apiKey.trim();
77
+
78
+ let defaultModel = '';
79
+ if (provider === 'openai') defaultModel = 'gpt-4o';
80
+ if (provider === 'anthropic') defaultModel = 'claude-3-5-sonnet-20240620';
81
+ if (provider === 'google') defaultModel = 'gemini-1.5-pro';
82
+
83
+ const { model } = await inquirer.prompt([
84
+ {
85
+ type: 'input',
86
+ name: 'model',
87
+ message: `Digite o modelo desejado (Padrão: ${defaultModel}):`,
88
+ default: defaultModel
89
+ }
90
+ ]);
91
+ config.model = model.trim();
92
+ console.log(chalk.green('\nConfiguração salva com sucesso!\n'));
93
+ }
94
+
95
+ saveConfig(config);
96
+ return config;
97
+ }
package/src/llm.js ADDED
@@ -0,0 +1,120 @@
1
+ import OpenAI from 'openai';
2
+ import { GoogleGenAI } from '@google/genai';
3
+ import Anthropic from '@anthropic-ai/sdk';
4
+ import chalk from 'chalk';
5
+
6
+ const clients = {};
7
+
8
+ export function getClient(config) {
9
+ if (clients[config.provider]) return clients[config.provider];
10
+
11
+ if (config.provider === 'rabts') {
12
+ clients[config.provider] = new OpenAI({
13
+ baseURL: 'https://api.groq.com/openai/v1',
14
+ apiKey: config.apiKey,
15
+ });
16
+ } else if (config.provider === 'openai') {
17
+ clients[config.provider] = new OpenAI({
18
+ apiKey: config.apiKey,
19
+ });
20
+ } else if (config.provider === 'anthropic') {
21
+ clients[config.provider] = new Anthropic({
22
+ apiKey: config.apiKey,
23
+ });
24
+ } else if (config.provider === 'google') {
25
+ clients[config.provider] = new GoogleGenAI({
26
+ apiKey: config.apiKey,
27
+ });
28
+ }
29
+ return clients[config.provider];
30
+ }
31
+
32
+ export async function sendMessage(config, messages, tools = null) {
33
+ const client = getClient(config);
34
+ let usage = null;
35
+
36
+ try {
37
+ if (config.provider === 'rabts' || config.provider === 'openai') {
38
+ const params = {
39
+ model: config.model,
40
+ messages: messages,
41
+ };
42
+
43
+ if (tools) {
44
+ params.tools = tools;
45
+ params.tool_choice = "auto";
46
+ }
47
+
48
+ const response = await client.chat.completions.create(params);
49
+ const msg = response.choices[0].message;
50
+ usage = response.usage;
51
+
52
+ return {
53
+ role: msg.role,
54
+ content: msg.content || '',
55
+ tool_calls: msg.tool_calls,
56
+ usage
57
+ };
58
+ }
59
+
60
+ else if (config.provider === 'anthropic') {
61
+ const systemMessage = messages.find(m => m.role === 'system')?.content || '';
62
+ const anthropicMessages = messages.filter(m => m.role !== 'system').map(m => ({
63
+ role: m.role === 'user' ? 'user' : 'assistant',
64
+ content: m.content
65
+ }));
66
+
67
+ const response = await client.messages.create({
68
+ model: config.model,
69
+ max_tokens: 4096,
70
+ system: systemMessage,
71
+ messages: anthropicMessages
72
+ });
73
+
74
+ usage = {
75
+ prompt_tokens: response.usage.input_tokens,
76
+ completion_tokens: response.usage.output_tokens,
77
+ };
78
+
79
+ return {
80
+ role: 'assistant',
81
+ content: response.content[0].text,
82
+ usage
83
+ };
84
+ }
85
+
86
+ else if (config.provider === 'google') {
87
+ const systemMessage = messages.find(m => m.role === 'system')?.content || '';
88
+ const googleMessages = messages.filter(m => m.role !== 'system').map(m => ({
89
+ role: m.role === 'user' ? 'user' : 'model',
90
+ parts: [{ text: m.content }]
91
+ }));
92
+
93
+ const response = await client.models.generateContent({
94
+ model: config.model,
95
+ contents: googleMessages,
96
+ config: {
97
+ systemInstruction: systemMessage ? { parts: [{text: systemMessage}]} : undefined
98
+ }
99
+ });
100
+
101
+ // GoogleGenAI might have usageMetadata
102
+ if (response.usageMetadata) {
103
+ usage = {
104
+ prompt_tokens: response.usageMetadata.promptTokenCount,
105
+ completion_tokens: response.usageMetadata.candidatesTokenCount,
106
+ };
107
+ }
108
+
109
+ return {
110
+ role: 'assistant',
111
+ content: response.text,
112
+ usage
113
+ };
114
+ }
115
+
116
+ } catch (error) {
117
+ console.error(chalk.red(`\nErro na comunicação com a API (${config.provider}):`), error.message);
118
+ process.exit(1);
119
+ }
120
+ }