agent-rev 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/dist/api/qwen.d.ts +12 -0
- package/dist/api/qwen.js +150 -0
- package/dist/commands/auth.d.ts +31 -0
- package/dist/commands/auth.js +255 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +42 -0
- package/dist/commands/models.d.ts +2 -0
- package/dist/commands/models.js +27 -0
- package/dist/commands/repl.d.ts +29 -0
- package/dist/commands/repl.js +1167 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +52 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +353 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +46 -0
- package/dist/core/engine.d.ts +36 -0
- package/dist/core/engine.js +905 -0
- package/dist/core/prompts.d.ts +11 -0
- package/dist/core/prompts.js +126 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +171 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.js +120 -0
- package/dist/types.d.ts +73 -0
- package/dist/types.js +58 -0
- package/dist/ui/input.d.ts +24 -0
- package/dist/ui/input.js +244 -0
- package/dist/ui/theme.d.ts +99 -0
- package/dist/ui/theme.js +307 -0
- package/dist/utils/config.d.ts +38 -0
- package/dist/utils/config.js +40 -0
- package/dist/utils/fs.d.ts +7 -0
- package/dist/utils/fs.js +37 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.js +46 -0
- package/dist/utils/qwen-auth.d.ts +12 -0
- package/dist/utils/qwen-auth.js +250 -0
- package/dist/utils/sessions.d.ts +17 -0
- package/dist/utils/sessions.js +46 -0
- package/package.json +44 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface QwenMessage {
|
|
2
|
+
role: 'system' | 'user' | 'assistant';
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function qwenLogin(): Promise<boolean>;
|
|
6
|
+
export declare function qwenAuthStatus(): Promise<{
|
|
7
|
+
authenticated: boolean;
|
|
8
|
+
email?: string;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function qwenChat(messages: QwenMessage[], model?: string): Promise<string>;
|
|
11
|
+
export declare function qwenAsk(prompt: string, model?: string): Promise<string>;
|
|
12
|
+
export {};
|
package/dist/api/qwen.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import open from 'open';
|
|
6
|
+
const QWEN_API_BASE = 'https://dashscope-intl.aliyuncs.com/api/v1';
|
|
7
|
+
const QWEN_AUTH_URL = 'https://oauth.aliyun.com/v1/oauth/authorize';
|
|
8
|
+
const CLIENT_ID = 'your_client_id'; // Reemplazar con el client ID real de Qwen
|
|
9
|
+
const REDIRECT_URI = 'http://localhost:3000/callback';
|
|
10
|
+
let tokenCache = null;
|
|
11
|
+
async function getTokenPath() {
|
|
12
|
+
const homeDir = os.homedir();
|
|
13
|
+
const dir = path.join(homeDir, '.agent');
|
|
14
|
+
await fs.mkdir(dir, { recursive: true });
|
|
15
|
+
return path.join(dir, 'qwen-token.json');
|
|
16
|
+
}
|
|
17
|
+
async function loadToken() {
|
|
18
|
+
try {
|
|
19
|
+
const tokenPath = await getTokenPath();
|
|
20
|
+
const content = await fs.readFile(tokenPath, 'utf-8');
|
|
21
|
+
const token = JSON.parse(content);
|
|
22
|
+
if (token.expiresAt > Date.now()) {
|
|
23
|
+
return token;
|
|
24
|
+
}
|
|
25
|
+
// Token expirado, intentar refresh
|
|
26
|
+
if (token.refreshToken) {
|
|
27
|
+
return refreshAccessToken(token.refreshToken);
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function saveToken(token) {
|
|
36
|
+
const tokenPath = await getTokenPath();
|
|
37
|
+
await fs.writeFile(tokenPath, JSON.stringify(token, null, 2), 'utf-8');
|
|
38
|
+
}
|
|
39
|
+
async function refreshAccessToken(refreshToken) {
|
|
40
|
+
try {
|
|
41
|
+
const response = await axios.post('https://oauth.aliyun.com/v1/oauth/token', new URLSearchParams({
|
|
42
|
+
grant_type: 'refresh_token',
|
|
43
|
+
refresh_token: refreshToken,
|
|
44
|
+
client_id: CLIENT_ID,
|
|
45
|
+
}), {
|
|
46
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
47
|
+
});
|
|
48
|
+
const token = {
|
|
49
|
+
accessToken: response.data.access_token,
|
|
50
|
+
refreshToken: response.data.refresh_token || refreshToken,
|
|
51
|
+
expiresAt: Date.now() + (response.data.expires_in || 3600) * 1000,
|
|
52
|
+
};
|
|
53
|
+
await saveToken(token);
|
|
54
|
+
return token;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.error('Error refreshing token:', error);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export async function qwenLogin() {
|
|
62
|
+
const authUrl = `${QWEN_AUTH_URL}?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&response_type=code&scope=api`;
|
|
63
|
+
console.log('Abriendo navegador para autenticación con Qwen...');
|
|
64
|
+
console.log('URL:', authUrl);
|
|
65
|
+
await open(authUrl);
|
|
66
|
+
console.log('');
|
|
67
|
+
console.log('Por favor completá la autenticación en el navegador.');
|
|
68
|
+
console.log('Cuando obtengas el código de autorización, ingresalo abajo:');
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const readline = await import('readline');
|
|
71
|
+
const rl = readline.createInterface({
|
|
72
|
+
input: process.stdin,
|
|
73
|
+
output: process.stdout,
|
|
74
|
+
});
|
|
75
|
+
rl.question('Código de autorización: ', async (code) => {
|
|
76
|
+
try {
|
|
77
|
+
const response = await axios.post('https://oauth.aliyun.com/v1/oauth/token', new URLSearchParams({
|
|
78
|
+
grant_type: 'authorization_code',
|
|
79
|
+
code,
|
|
80
|
+
redirect_uri: REDIRECT_URI,
|
|
81
|
+
client_id: CLIENT_ID,
|
|
82
|
+
}), {
|
|
83
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
84
|
+
});
|
|
85
|
+
const token = {
|
|
86
|
+
accessToken: response.data.access_token,
|
|
87
|
+
refreshToken: response.data.refresh_token,
|
|
88
|
+
expiresAt: Date.now() + (response.data.expires_in || 3600) * 1000,
|
|
89
|
+
};
|
|
90
|
+
await saveToken(token);
|
|
91
|
+
console.log('¡Autenticación exitosa!');
|
|
92
|
+
rl.close();
|
|
93
|
+
resolve(true);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.error('Error en autenticación:', error);
|
|
97
|
+
rl.close();
|
|
98
|
+
resolve(false);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export async function qwenAuthStatus() {
|
|
104
|
+
const token = await loadToken();
|
|
105
|
+
if (token) {
|
|
106
|
+
return { authenticated: true };
|
|
107
|
+
}
|
|
108
|
+
return { authenticated: false };
|
|
109
|
+
}
|
|
110
|
+
export async function qwenChat(messages, model = 'qwen-coder') {
|
|
111
|
+
const token = await loadToken();
|
|
112
|
+
if (!token) {
|
|
113
|
+
throw new Error('No hay token de Qwen. Ejecutá /qwen-login primero.');
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const response = await axios.post(`${QWEN_API_BASE}/services/aigc/text-generation/generation`, {
|
|
117
|
+
model,
|
|
118
|
+
input: {
|
|
119
|
+
messages,
|
|
120
|
+
},
|
|
121
|
+
parameters: {
|
|
122
|
+
result_format: 'text',
|
|
123
|
+
},
|
|
124
|
+
}, {
|
|
125
|
+
headers: {
|
|
126
|
+
'Authorization': `Bearer ${token.accessToken}`,
|
|
127
|
+
'Content-Type': 'application/json',
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
return response.data.output.text;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error.response?.status === 401) {
|
|
134
|
+
// Token inválido, intentar refresh
|
|
135
|
+
const newToken = token.refreshToken ? await refreshAccessToken(token.refreshToken) : null;
|
|
136
|
+
if (newToken) {
|
|
137
|
+
// Reintentar con el nuevo token
|
|
138
|
+
return qwenChat(messages, model);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export async function qwenAsk(prompt, model = 'qwen-coder') {
|
|
145
|
+
const messages = [
|
|
146
|
+
{ role: 'system', content: 'Sos un asistente útil de programación.' },
|
|
147
|
+
{ role: 'user', content: prompt },
|
|
148
|
+
];
|
|
149
|
+
return qwenChat(messages, model);
|
|
150
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
declare const CONFIG_DIR: string;
|
|
3
|
+
declare const CONFIG_FILE: string;
|
|
4
|
+
interface AuthEntry {
|
|
5
|
+
provider: string;
|
|
6
|
+
method: 'oauth' | 'apikey';
|
|
7
|
+
accessToken?: string;
|
|
8
|
+
refreshToken?: string;
|
|
9
|
+
expiresAt?: number;
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
email?: string;
|
|
12
|
+
}
|
|
13
|
+
interface AuthStore {
|
|
14
|
+
entries: AuthEntry[];
|
|
15
|
+
activeProvider?: string;
|
|
16
|
+
}
|
|
17
|
+
interface CliConfig {
|
|
18
|
+
roles: Record<string, {
|
|
19
|
+
provider: string;
|
|
20
|
+
model: string;
|
|
21
|
+
}>;
|
|
22
|
+
deliberation: {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
max_rounds: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
declare function loadAuth(): Promise<AuthStore>;
|
|
28
|
+
declare function loadCliConfig(): Promise<CliConfig>;
|
|
29
|
+
declare function saveCliConfig(cfg: CliConfig): Promise<void>;
|
|
30
|
+
export declare function authCommand(program: Command): void;
|
|
31
|
+
export { loadAuth, loadCliConfig, saveCliConfig, CONFIG_DIR, CONFIG_FILE };
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import { createServer } from 'http';
|
|
5
|
+
import { createHash, randomBytes } from 'crypto';
|
|
6
|
+
import open from 'open';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { writeJson, ensureDir } from '../utils/fs.js';
|
|
9
|
+
const CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '', '.agent-cli');
|
|
10
|
+
const AUTH_FILE = path.join(CONFIG_DIR, 'auth.json');
|
|
11
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
12
|
+
// ── OAuth providers ────────────────────────────────────────────────────────
|
|
13
|
+
const OAUTH_PROVIDERS = {
|
|
14
|
+
opencode: {
|
|
15
|
+
authorizeUrl: 'https://accounts.opencode.ai/oauth2/auth',
|
|
16
|
+
tokenUrl: 'https://accounts.opencode.ai/oauth2/token',
|
|
17
|
+
clientId: 'claw_code',
|
|
18
|
+
scopes: 'openid email profile offline_access',
|
|
19
|
+
name: 'Opencode',
|
|
20
|
+
},
|
|
21
|
+
google: {
|
|
22
|
+
authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
|
|
23
|
+
tokenUrl: 'https://oauth2.googleapis.com/token',
|
|
24
|
+
clientId: '',
|
|
25
|
+
scopes: 'openid email profile',
|
|
26
|
+
name: 'Google',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
async function loadAuth() {
|
|
30
|
+
try {
|
|
31
|
+
const content = await fs.readFile(AUTH_FILE, 'utf-8');
|
|
32
|
+
return JSON.parse(content);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return { entries: [] };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function saveAuth(auth) {
|
|
39
|
+
await ensureDir(CONFIG_DIR);
|
|
40
|
+
await writeJson(AUTH_FILE, auth);
|
|
41
|
+
}
|
|
42
|
+
async function loadCliConfig() {
|
|
43
|
+
try {
|
|
44
|
+
const content = await fs.readFile(CONFIG_FILE, 'utf-8');
|
|
45
|
+
return JSON.parse(content);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { roles: {}, deliberation: { enabled: false, max_rounds: 4 } };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function saveCliConfig(cfg) {
|
|
52
|
+
await ensureDir(CONFIG_DIR);
|
|
53
|
+
await writeJson(CONFIG_FILE, cfg);
|
|
54
|
+
}
|
|
55
|
+
function base64url(buf) {
|
|
56
|
+
return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
57
|
+
}
|
|
58
|
+
async function oauthFlow(providerId) {
|
|
59
|
+
const provider = OAUTH_PROVIDERS[providerId];
|
|
60
|
+
if (!provider)
|
|
61
|
+
throw new Error(`Unknown OAuth provider: ${providerId}`);
|
|
62
|
+
const port = 9800 + Math.floor(Math.random() * 1000);
|
|
63
|
+
const state = base64url(randomBytes(32));
|
|
64
|
+
const codeVerifier = base64url(randomBytes(32));
|
|
65
|
+
const codeChallenge = base64url(createHash('sha256').update(codeVerifier).digest());
|
|
66
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
67
|
+
const authUrl = new URL(provider.authorizeUrl);
|
|
68
|
+
authUrl.searchParams.set('response_type', 'code');
|
|
69
|
+
authUrl.searchParams.set('client_id', provider.clientId);
|
|
70
|
+
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
71
|
+
authUrl.searchParams.set('scope', provider.scopes);
|
|
72
|
+
authUrl.searchParams.set('state', state);
|
|
73
|
+
authUrl.searchParams.set('code_challenge', codeChallenge);
|
|
74
|
+
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
75
|
+
console.log(chalk.dim(` Opening browser for ${provider.name} login...`));
|
|
76
|
+
await open(authUrl.toString());
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const server = createServer((req, res) => {
|
|
79
|
+
const url = new URL(req.url, `http://127.0.0.1:${port}`);
|
|
80
|
+
if (url.pathname !== '/callback') {
|
|
81
|
+
res.writeHead(404);
|
|
82
|
+
res.end();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const returnedState = url.searchParams.get('state');
|
|
86
|
+
const code = url.searchParams.get('code');
|
|
87
|
+
const error = url.searchParams.get('error');
|
|
88
|
+
if (error) {
|
|
89
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
90
|
+
res.end(`<h1>Login failed</h1><p>${error}</p>`);
|
|
91
|
+
server.close();
|
|
92
|
+
reject(new Error(`OAuth error: ${error}`));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (returnedState !== state) {
|
|
96
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
97
|
+
res.end('<h1>Invalid state</h1>');
|
|
98
|
+
server.close();
|
|
99
|
+
reject(new Error('State mismatch'));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!code) {
|
|
103
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
104
|
+
res.end('<h1>No code</h1>');
|
|
105
|
+
server.close();
|
|
106
|
+
reject(new Error('No authorization code'));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
110
|
+
res.end(`
|
|
111
|
+
<html><body style="font-family:system-ui;text-align:center;padding:60px">
|
|
112
|
+
<h1 style="color:#06b6d4">✓ Login successful</h1>
|
|
113
|
+
<p>You can close this tab and return to the terminal.</p>
|
|
114
|
+
</body></html>
|
|
115
|
+
`);
|
|
116
|
+
server.close();
|
|
117
|
+
// Exchange code for tokens
|
|
118
|
+
exchangeToken(provider, code, redirectUri, codeVerifier).then(resolve).catch(reject);
|
|
119
|
+
});
|
|
120
|
+
server.listen(port, '127.0.0.1', () => {
|
|
121
|
+
console.log(chalk.blue(` → Waiting for callback on port ${port}...`));
|
|
122
|
+
});
|
|
123
|
+
server.on('error', (err) => {
|
|
124
|
+
if (err.code === 'EADDRINUSE') {
|
|
125
|
+
reject(new Error(`Port ${port} is in use. Try again.`));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
reject(err);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async function exchangeToken(provider, code, redirectUri, codeVerifier) {
|
|
134
|
+
const params = new URLSearchParams({
|
|
135
|
+
grant_type: 'authorization_code',
|
|
136
|
+
client_id: provider.clientId,
|
|
137
|
+
code,
|
|
138
|
+
redirect_uri: redirectUri,
|
|
139
|
+
code_verifier: codeVerifier,
|
|
140
|
+
});
|
|
141
|
+
const res = await fetch(provider.tokenUrl, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
144
|
+
body: params.toString(),
|
|
145
|
+
});
|
|
146
|
+
if (!res.ok) {
|
|
147
|
+
const text = await res.text();
|
|
148
|
+
throw new Error(`Token exchange failed (${res.status}): ${text}`);
|
|
149
|
+
}
|
|
150
|
+
const data = await res.json();
|
|
151
|
+
// Decode email from id_token if available
|
|
152
|
+
let email;
|
|
153
|
+
if (data.id_token) {
|
|
154
|
+
try {
|
|
155
|
+
const payload = JSON.parse(Buffer.from(data.id_token.split('.')[1], 'base64').toString());
|
|
156
|
+
email = payload.email;
|
|
157
|
+
}
|
|
158
|
+
catch { }
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
provider: 'opencode',
|
|
162
|
+
method: 'oauth',
|
|
163
|
+
accessToken: data.access_token,
|
|
164
|
+
refreshToken: data.refresh_token,
|
|
165
|
+
expiresAt: data.expires_in ? Date.now() + data.expires_in * 1000 : undefined,
|
|
166
|
+
email,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function ask(rl, q) {
|
|
170
|
+
return new Promise((resolve) => rl.question(q, resolve));
|
|
171
|
+
}
|
|
172
|
+
export function authCommand(program) {
|
|
173
|
+
const auth = program.command('auth')
|
|
174
|
+
.description('Manage authentication');
|
|
175
|
+
auth
|
|
176
|
+
.command('login')
|
|
177
|
+
.description('Login to a provider (opens browser)')
|
|
178
|
+
.option('-p, --provider <name>', 'Provider name (opencode)')
|
|
179
|
+
.action(async (opts) => {
|
|
180
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
181
|
+
console.log(chalk.bold.cyan('\n Agent CLI — Login\n'));
|
|
182
|
+
let providerId = opts.provider;
|
|
183
|
+
if (!providerId) {
|
|
184
|
+
console.log(chalk.dim(' Available providers:'));
|
|
185
|
+
for (const [id, p] of Object.entries(OAUTH_PROVIDERS)) {
|
|
186
|
+
console.log(chalk.dim(` ${id} — ${p.name}`));
|
|
187
|
+
}
|
|
188
|
+
providerId = await ask(rl, '\n Provider: ');
|
|
189
|
+
}
|
|
190
|
+
providerId = providerId.trim();
|
|
191
|
+
try {
|
|
192
|
+
const entry = await oauthFlow(providerId);
|
|
193
|
+
const auth = await loadAuth();
|
|
194
|
+
// Remove existing entry for same provider
|
|
195
|
+
auth.entries = auth.entries.filter((e) => e.provider !== entry.provider);
|
|
196
|
+
auth.entries.push(entry);
|
|
197
|
+
auth.activeProvider = entry.provider;
|
|
198
|
+
await saveAuth(auth);
|
|
199
|
+
console.log(chalk.green(`\n ✓ Logged in as ${entry.email || 'user'} via ${entry.provider}`));
|
|
200
|
+
console.log(chalk.dim(' Auth saved to ~/.agent-cli/auth.json'));
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
console.log(chalk.red(`\n ✗ Login failed: ${err.message}`));
|
|
204
|
+
}
|
|
205
|
+
rl.close();
|
|
206
|
+
});
|
|
207
|
+
auth
|
|
208
|
+
.command('logout')
|
|
209
|
+
.description('Logout from current provider')
|
|
210
|
+
.action(async () => {
|
|
211
|
+
const auth = await loadAuth();
|
|
212
|
+
if (!auth.activeProvider || auth.entries.length === 0) {
|
|
213
|
+
console.log(chalk.yellow(' Not logged in'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
auth.entries = auth.entries.filter((e) => e.provider !== auth.activeProvider);
|
|
217
|
+
auth.activeProvider = undefined;
|
|
218
|
+
await saveAuth(auth);
|
|
219
|
+
console.log(chalk.green(' ✓ Logged out'));
|
|
220
|
+
});
|
|
221
|
+
auth
|
|
222
|
+
.command('status')
|
|
223
|
+
.description('Show current auth status')
|
|
224
|
+
.action(async () => {
|
|
225
|
+
const auth = await loadAuth();
|
|
226
|
+
console.log(chalk.bold('\n Auth Status'));
|
|
227
|
+
console.log(chalk.dim(' ──────────────────────────'));
|
|
228
|
+
if (auth.entries.length === 0) {
|
|
229
|
+
console.log(chalk.yellow(' Not logged in to any provider'));
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
for (const entry of auth.entries) {
|
|
233
|
+
const active = entry.provider === auth.activeProvider ? chalk.green(' (active)') : '';
|
|
234
|
+
const email = entry.email ? ` — ${entry.email}` : '';
|
|
235
|
+
const expired = entry.expiresAt && entry.expiresAt < Date.now() ? chalk.red(' (expired)') : '';
|
|
236
|
+
console.log(` ${entry.provider} (${entry.method})${email}${active}${expired}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
console.log('');
|
|
240
|
+
});
|
|
241
|
+
auth
|
|
242
|
+
.command('apikey')
|
|
243
|
+
.description('Set API key for a provider')
|
|
244
|
+
.argument('<provider>', 'Provider name')
|
|
245
|
+
.argument('<key>', 'API key')
|
|
246
|
+
.action(async (provider, key) => {
|
|
247
|
+
const auth = await loadAuth();
|
|
248
|
+
auth.entries = auth.entries.filter((e) => e.provider !== provider);
|
|
249
|
+
auth.entries.push({ provider, method: 'apikey', apiKey: key });
|
|
250
|
+
auth.activeProvider = provider;
|
|
251
|
+
await saveAuth(auth);
|
|
252
|
+
console.log(chalk.green(` ✓ API key saved for ${provider}`));
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
export { loadAuth, loadCliConfig, saveCliConfig, CONFIG_DIR, CONFIG_FILE };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { loadCliConfig, saveCliConfig } from '../utils/config.js';
|
|
3
|
+
export function configCommand(program) {
|
|
4
|
+
program
|
|
5
|
+
.command('config')
|
|
6
|
+
.description('View or edit CLI configuration')
|
|
7
|
+
.option('--show', 'Show current configuration')
|
|
8
|
+
.option('--set <key=value>', 'Set a config value')
|
|
9
|
+
.action(async (opts) => {
|
|
10
|
+
const config = await loadCliConfig();
|
|
11
|
+
if (opts.show) {
|
|
12
|
+
console.log(chalk.bold('\n Agent CLI Configuration'));
|
|
13
|
+
console.log(chalk.dim(' ──────────────────────────'));
|
|
14
|
+
console.log(chalk.yellow(' Providers:'), Object.keys(config.apiKeys).join(', ') || 'none');
|
|
15
|
+
for (const [role, r] of Object.entries(config.roles)) {
|
|
16
|
+
if (r)
|
|
17
|
+
console.log(chalk.yellow(` ${role}:`), `${r.provider}/${r.model}`);
|
|
18
|
+
}
|
|
19
|
+
if (config.deliberation?.enabled) {
|
|
20
|
+
console.log(chalk.yellow(' Deliberation:'), `enabled (${config.deliberation.max_rounds} rounds)`);
|
|
21
|
+
}
|
|
22
|
+
console.log('');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (opts.set) {
|
|
26
|
+
const [key, ...valParts] = opts.set.split('=');
|
|
27
|
+
const value = valParts.join('=');
|
|
28
|
+
const parts = key.split('.');
|
|
29
|
+
let obj = config;
|
|
30
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
31
|
+
if (!obj[parts[i]])
|
|
32
|
+
obj[parts[i]] = {};
|
|
33
|
+
obj = obj[parts[i]];
|
|
34
|
+
}
|
|
35
|
+
obj[parts[parts.length - 1]] = value;
|
|
36
|
+
await saveCliConfig(config);
|
|
37
|
+
console.log(chalk.green(` ✓ Set ${key} = ${value}`));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
console.log('Use --show or --set key=value');
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
const MODELS = {
|
|
3
|
+
openai: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1', 'o3-mini', 'o4-mini'],
|
|
4
|
+
anthropic: ['claude-sonnet-4-20250514', 'claude-opus-4-20250514', 'claude-haiku-4-20250514'],
|
|
5
|
+
google: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.0-flash'],
|
|
6
|
+
};
|
|
7
|
+
export function modelsCommand(program) {
|
|
8
|
+
program
|
|
9
|
+
.command('models')
|
|
10
|
+
.description('List available models by provider')
|
|
11
|
+
.argument('[provider]', 'Filter by provider')
|
|
12
|
+
.action((provider) => {
|
|
13
|
+
const providers = provider ? [provider] : Object.keys(MODELS);
|
|
14
|
+
for (const p of providers) {
|
|
15
|
+
const models = MODELS[p];
|
|
16
|
+
if (!models) {
|
|
17
|
+
console.log(chalk.yellow(` Unknown: ${p}`));
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
console.log(chalk.bold(`\n ${p}`));
|
|
21
|
+
console.log(chalk.dim(' ' + '-'.repeat(30)));
|
|
22
|
+
for (const m of models)
|
|
23
|
+
console.log(` ${p}/${m}`);
|
|
24
|
+
}
|
|
25
|
+
console.log('');
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import { CliInfo } from '../types.js';
|
|
3
|
+
import { Session } from '../utils/sessions.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared coordinator setup: detect CLIs, pick coordinator, check auth.
|
|
6
|
+
* Returns { coordinatorCmd, activeCli, installed, rl } or null if setup failed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function initCoordinator(): Promise<{
|
|
9
|
+
coordinatorCmd: string;
|
|
10
|
+
activeCli: {
|
|
11
|
+
name: string;
|
|
12
|
+
info: CliInfo;
|
|
13
|
+
path: string;
|
|
14
|
+
};
|
|
15
|
+
installed: Array<{
|
|
16
|
+
name: string;
|
|
17
|
+
info: CliInfo;
|
|
18
|
+
path: string;
|
|
19
|
+
}>;
|
|
20
|
+
rl: readline.Interface;
|
|
21
|
+
} | null>;
|
|
22
|
+
/** REPL mode — interactive loop */
|
|
23
|
+
export declare function runRepl(resumeSession?: Session): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Direct role execution — agent-mp MISMO actúa como rol.
|
|
26
|
+
* NO spawnea CLIs externos. Usa la conexión OAuth de qwen directamente.
|
|
27
|
+
* Prompt estricto: cada rol solo hace lo que debe, sin extras.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runRole(role: string, arg: string): Promise<void>;
|