vault-go 0.9.0 → 0.10.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,3 +1,9 @@
1
+ ## Login no navegador
2
+
3
+ `bunx --bun vault-go@latest` abre o autenticador do AI Vault Memory no navegador. E-mail, senha e MFA são informados apenas no portal. Ao concluir, o terminal recebe um código de uso único por `127.0.0.1` e o troca com PKCE; tokens não aparecem na URL nem no console. O listener fecha ao concluir ou após cinco minutos.
4
+
5
+ Para trocar a conta: `bunx --bun vault-go@latest login --force`. Para chave de API explícita: `bunx --bun vault-go@latest login --force --api-key`. Se a abertura automática falhar, use o endereço exibido no mesmo computador. Requer uma sessão local com navegador para o retorno loopback.
6
+
1
7
  # vault-go
2
8
 
3
9
  Servidor MCP em Bun para pesquisar, contextualizar e registrar memória na
@@ -59,7 +65,7 @@ bunx --bun vault-go@latest
59
65
  O assistente:
60
66
 
61
67
  1. valida uma sessão existente em `~/.memvault`;
62
- 2. se necessário, solicita login por e-mail, senha e MFA ou uma chave de API
68
+ 2. se necessário, abre o login do AI Vault Memory no navegador (chave de API somente com `--api-key`)
63
69
  `vg_live_...`;
64
70
  3. detecta os clientes instalados;
65
71
  4. permite escolher onde registrar o MCP;
package/dist/auth.d.ts CHANGED
@@ -1,9 +1,16 @@
1
1
  export interface AuthenticationResult {
2
2
  email: string;
3
- method: 'password' | 'api-key' | 'existing';
3
+ method: 'password' | 'browser' | 'api-key' | 'existing';
4
4
  }
5
5
  type Fetcher = typeof fetch;
6
6
  export declare function authenticateWithPassword(email: string, password: string, requestMfa: () => Promise<string>, home: string, fetcher?: Fetcher): Promise<AuthenticationResult>;
7
7
  export declare function authenticateWithApiKey(apiKey: string, home: string, fetcher?: Fetcher): Promise<AuthenticationResult>;
8
8
  export declare function validateExistingAuthentication(home: string, fetcher?: Fetcher): Promise<AuthenticationResult | null>;
9
+ export declare function openVaultBrowser(url: string): Promise<void>;
10
+ export declare function authenticateWithBrowser(home: string, options?: {
11
+ fetcher?: Fetcher;
12
+ openBrowser?: (url: string) => Promise<void>;
13
+ timeoutMs?: number;
14
+ report?: (message: string) => void;
15
+ }): Promise<AuthenticationResult>;
9
16
  export {};
package/dist/auth.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { createServer } from 'node:http';
2
+ import { randomBytes, createHash, timingSafeEqual } from 'node:crypto';
3
+ import { execFile } from 'node:child_process';
1
4
  import { loadConfig, loadTokens, saveConfig, saveTokens, } from './config.js';
2
5
  function endpoint(home, path) {
3
6
  const apiUrl = new URL(loadConfig(home).apiUrl);
@@ -10,6 +13,7 @@ async function errorMessage(response) {
10
13
  async function accountForToken(token, home, fetcher) {
11
14
  const response = await fetcher(endpoint(home, '/me'), {
12
15
  headers: { authorization: `Bearer ${token}` },
16
+ redirect: 'error',
13
17
  signal: AbortSignal.timeout(10_000),
14
18
  });
15
19
  if (response.status === 401 || response.status === 403)
@@ -97,3 +101,101 @@ export async function validateExistingAuthentication(home, fetcher = fetch) {
97
101
  persist(tokens, account.email, home);
98
102
  return { email: account.email, method: 'existing' };
99
103
  }
104
+ export function openVaultBrowser(url) {
105
+ const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'rundll32' : 'xdg-open';
106
+ const args = process.platform === 'win32' ? ['url.dll,FileProtocolHandler', url] : [url];
107
+ return new Promise((resolve, reject) => { execFile(command, args, { timeout: 10_000 }, error => error ? reject(error) : resolve()); });
108
+ }
109
+ export async function authenticateWithBrowser(home, options = {}) {
110
+ const fetcher = options.fetcher ?? fetch;
111
+ const report = options.report ?? (message => process.stderr.write(message + '\n'));
112
+ const start = endpoint(home, '/auth/oauth/resolveup/start');
113
+ if (start.protocol !== 'https:' && !(start.protocol === 'http:' && ['127.0.0.1', 'localhost', '[::1]'].includes(start.hostname)))
114
+ throw new Error('A autenticação Vault requer HTTPS.');
115
+ const verifier = randomBytes(32).toString('base64url');
116
+ const state = randomBytes(32).toString('base64url');
117
+ let port = 0;
118
+ let claim;
119
+ let rejectClaim;
120
+ let accepted = false;
121
+ const received = new Promise((resolve, reject) => { claim = resolve; rejectClaim = reject; });
122
+ // A timeout can occur while the browser opener is still running.
123
+ void received.catch(() => { });
124
+ const server = createServer((req, res) => {
125
+ res.setHeader('Cache-Control', 'no-store');
126
+ res.setHeader('Referrer-Policy', 'no-referrer');
127
+ res.setHeader('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'");
128
+ let url;
129
+ try {
130
+ url = new URL(req.url ?? '/', `http://127.0.0.1:${port}`);
131
+ }
132
+ catch {
133
+ res.writeHead(400).end();
134
+ return;
135
+ }
136
+ if (req.method !== 'GET' || req.headers.host !== `127.0.0.1:${port}` || url.pathname !== '/callback') {
137
+ res.writeHead(404).end();
138
+ return;
139
+ }
140
+ const incoming = url.searchParams.get('state') ?? '';
141
+ const validState = /^[A-Za-z0-9_-]{43}$/.test(incoming) && timingSafeEqual(Buffer.from(incoming), Buffer.from(state));
142
+ const code = url.searchParams.get('code');
143
+ if (!validState || url.searchParams.getAll('state').length !== 1 || accepted) {
144
+ res.writeHead(400).end('Retorno de autenticação inválido.');
145
+ return;
146
+ }
147
+ if (url.searchParams.has('error')) {
148
+ accepted = true;
149
+ res.writeHead(400).end('Login não concluído. Volte ao terminal.');
150
+ rejectClaim?.(new Error('Login cancelado ou recusado no Vault.'));
151
+ return;
152
+ }
153
+ if (!code || !/^[A-Za-z0-9_-]{43}$/.test(code) || url.searchParams.getAll('code').length !== 1) {
154
+ res.writeHead(400).end('Código inválido.');
155
+ return;
156
+ }
157
+ accepted = true;
158
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
159
+ res.end('<!doctype html><html lang="pt-BR"><title>AI Vault Memory</title><h1>AI Vault Memory</h1><p>Retorno recebido. Volte ao terminal para concluir.</p></html>');
160
+ claim?.(code);
161
+ });
162
+ let timer;
163
+ try {
164
+ await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', resolve); });
165
+ const address = server.address();
166
+ if (!address || typeof address === 'string')
167
+ throw new Error('Não foi possível iniciar o retorno local.');
168
+ port = address.port;
169
+ start.searchParams.set('mobile_challenge', createHash('sha256').update(verifier).digest('base64url'));
170
+ start.searchParams.set('mobile_state', state);
171
+ start.searchParams.set('cli_port', String(port));
172
+ timer = setTimeout(() => rejectClaim?.(new Error('Tempo de login esgotado. Execute vault-go login novamente.')), options.timeoutMs ?? 300_000);
173
+ report('Abrindo o autenticador do AI Vault Memory no navegador…');
174
+ report(`Se o navegador não abrir, acesse neste computador: ${start.href}`);
175
+ try {
176
+ await (options.openBrowser ?? openVaultBrowser)(start.href);
177
+ }
178
+ catch {
179
+ report('Não foi possível abrir o navegador automaticamente. Use o endereço acima.');
180
+ }
181
+ const code = await received;
182
+ const response = await fetcher(endpoint(home, '/auth/mobile/exchange'), {
183
+ method: 'POST', headers: { 'content-type': 'application/json' },
184
+ body: JSON.stringify({ code, verifier }), redirect: 'error', signal: AbortSignal.timeout(15_000),
185
+ });
186
+ if (!response.ok)
187
+ throw new Error('Não foi possível concluir o login do Vault. Tente novamente.');
188
+ const tokens = normalizeTokens(await response.json());
189
+ const account = await accountForToken(tokens.accessToken, home, fetcher);
190
+ if (!account)
191
+ throw new Error('O Vault não confirmou a conta autenticada.');
192
+ persist(tokens, account.email, home);
193
+ return { email: account.email, method: 'browser' };
194
+ }
195
+ finally {
196
+ if (timer)
197
+ clearTimeout(timer);
198
+ server.closeAllConnections();
199
+ await new Promise(resolve => server.close(() => resolve()));
200
+ }
201
+ }
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- import { authenticateWithApiKey, authenticateWithPassword, validateExistingAuthentication, } from './auth.js';
1
+ import { authenticateWithApiKey, authenticateWithBrowser, validateExistingAuthentication, } from './auth.js';
2
2
  import { vaultHome } from './config.js';
3
3
  import { detectMcpClients, installMcpClient, MCP_CLIENTS, parseClientSelection, } from './installer.js';
4
4
  import { promptSecret, promptText } from './prompt.js';
@@ -24,7 +24,7 @@ function optionValue(args, name) {
24
24
  const prefix = `${name}=`;
25
25
  return args.find((argument) => argument.startsWith(prefix))?.slice(prefix.length);
26
26
  }
27
- async function ensureAuthentication(home, force = false) {
27
+ async function ensureAuthentication(home, force = false, apiKeyMode = false) {
28
28
  if (!force) {
29
29
  const existing = await validateExistingAuthentication(home);
30
30
  if (existing) {
@@ -32,21 +32,12 @@ async function ensureAuthentication(home, force = false) {
32
32
  return existing;
33
33
  }
34
34
  }
35
- process.stderr.write('\nAutenticação Vault\n');
36
- process.stderr.write(' 1. E-mail, senha e MFA\n');
37
- process.stderr.write(' 2. Chave de API (vg_live_...)\n');
38
- const method = (await promptText('Escolha [1]: ')) || '1';
39
- if (method === '2') {
40
- const apiKey = await promptSecret('Chave de API: ');
41
- const result = await authenticateWithApiKey(apiKey, home);
35
+ if (apiKeyMode) {
36
+ const result = await authenticateWithApiKey(await promptSecret('Chave de API: '), home);
42
37
  process.stderr.write(`✓ Chave validada para ${result.email}.\n`);
43
38
  return result;
44
39
  }
45
- if (method !== '1')
46
- throw new Error('Método de autenticação inválido.');
47
- const email = await promptText('E-mail: ');
48
- const password = await promptSecret('Senha: ');
49
- const result = await authenticateWithPassword(email, password, () => promptSecret('Código MFA ou backup: '), home);
40
+ const result = await authenticateWithBrowser(home);
50
41
  process.stderr.write(`✓ Login realizado como ${result.email}.\n`);
51
42
  return result;
52
43
  }
@@ -73,7 +64,7 @@ export async function runLogin(args) {
73
64
  if (!process.stdin.isTTY || !process.stderr.isTTY) {
74
65
  throw new Error('O login requer um terminal interativo.');
75
66
  }
76
- await ensureAuthentication(vaultHome(), args.includes('--force'));
67
+ await ensureAuthentication(vaultHome(), args.includes('--force'), args.includes('--api-key'));
77
68
  return 0;
78
69
  }
79
70
  export async function runSetup(args) {
@@ -81,7 +72,7 @@ export async function runSetup(args) {
81
72
  throw new Error('O setup requer um terminal interativo.');
82
73
  }
83
74
  process.stderr.write('vault-go — configuração MCP universal\n');
84
- await ensureAuthentication(vaultHome(), args.includes('--reauth'));
75
+ await ensureAuthentication(vaultHome(), args.includes('--reauth'), args.includes('--api-key'));
85
76
  const clients = await chooseClients(args);
86
77
  if (clients.length === 0)
87
78
  throw new Error('Nenhum cliente MCP selecionado.');
@@ -113,12 +104,12 @@ Servidor MCP universal para a plataforma Vault.
113
104
 
114
105
  Uso:
115
106
  bunx --bun vault-go@latest
116
- Autentica no Vault e instala nos clientes detectados.
107
+ Abre o login Vault no navegador e instala nos clientes detectados.
117
108
 
118
109
  bunx --bun vault-go@latest setup [--clients lista] [--reauth]
119
110
  Executa o assistente de autenticação e instalação.
120
111
 
121
- bunx --bun vault-go@latest login [--force]
112
+ bunx --bun vault-go@latest login [--force] [--api-key]
122
113
  Autentica sem alterar clientes MCP.
123
114
 
124
115
  bunx --bun vault-go@latest serve
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vault-go",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "description": "Servidor MCP universal com autenticação e instalação multi-cliente para a plataforma Vault.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -58,10 +58,10 @@
58
58
  },
59
59
  "repository": {
60
60
  "type": "git",
61
- "url": "git+https://github.com/GutierrezHenrique/vault-go.git"
61
+ "url": "git+https://github.com/resolveup-cloud/vault-go.git"
62
62
  },
63
63
  "bugs": {
64
- "url": "https://github.com/GutierrezHenrique/vault-go/issues"
64
+ "url": "https://github.com/resolveup-cloud/vault-go/issues"
65
65
  },
66
66
  "homepage": "https://vault.resolveup.com.br",
67
67
  "keywords": [