vault-go 0.10.1 → 0.12.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/README.md +72 -3
- package/assets/vault-logo-glass.png +0 -0
- package/dist/activity.d.ts +9 -0
- package/dist/activity.js +46 -0
- package/dist/auth.d.ts +4 -0
- package/dist/auth.js +17 -8
- package/dist/callback-page.d.ts +3 -0
- package/dist/callback-page.js +33 -0
- package/dist/cli.d.ts +8 -1
- package/dist/cli.js +135 -26
- package/dist/cloud.d.ts +12 -1
- package/dist/cloud.js +56 -4
- package/dist/context-engines.d.ts +52 -0
- package/dist/context-engines.js +350 -0
- package/dist/index.js +8 -4
- package/dist/local-dashboard.d.ts +3 -0
- package/dist/local-dashboard.js +374 -0
- package/dist/local-device.d.ts +22 -0
- package/dist/local-device.js +185 -0
- package/dist/local-install.d.ts +24 -0
- package/dist/local-install.js +282 -0
- package/dist/local-service.d.ts +26 -0
- package/dist/local-service.js +628 -0
- package/dist/local-worker.d.ts +1 -0
- package/dist/local-worker.js +8 -0
- package/dist/locale.d.ts +97 -0
- package/dist/locale.js +106 -0
- package/dist/private-store.d.ts +8 -0
- package/dist/private-store.js +130 -0
- package/package.json +30 -9
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Para trocar a conta: `bunx --bun vault-go@latest login --force`. Para chave de A
|
|
|
8
8
|
|
|
9
9
|
Servidor MCP em Bun para pesquisar, contextualizar e registrar memória na
|
|
10
10
|
plataforma Vault. A persistência fica no PostgreSQL do serviço Rust executado
|
|
11
|
-
no EasyPanel;
|
|
11
|
+
no EasyPanel; o painel local mantém um cache privado das consultas recentes.
|
|
12
12
|
|
|
13
13
|
## Pré-requisito
|
|
14
14
|
|
|
@@ -69,7 +69,8 @@ O assistente:
|
|
|
69
69
|
`vg_live_...`;
|
|
70
70
|
3. detecta os clientes instalados;
|
|
71
71
|
4. permite escolher onde registrar o MCP;
|
|
72
|
-
5. preserva as outras configurações dos clientes
|
|
72
|
+
5. preserva as outras configurações dos clientes;
|
|
73
|
+
6. instala e abre o painel local na porta 38850 (início automático no macOS).
|
|
73
74
|
|
|
74
75
|
Clientes suportados:
|
|
75
76
|
|
|
@@ -195,7 +196,7 @@ consultas aplicam esse identificador, impedindo leitura entre contas.
|
|
|
195
196
|
- Renova o access token usando o refresh token local com permissão privada.
|
|
196
197
|
- Limita tamanhos, listas e janelas nos schemas das ferramentas.
|
|
197
198
|
- Reserva `stdout` exclusivamente para o protocolo MCP.
|
|
198
|
-
-
|
|
199
|
+
- O painel guarda cache de projetos/memórias e metadados de chamadas MCP em arquivos privados locais.
|
|
199
200
|
|
|
200
201
|
Instale o MCP apenas em clientes confiáveis: o cliente conectado pode pedir
|
|
201
202
|
busca, gravação e exclusão de memórias dentro da conta autenticada.
|
|
@@ -221,3 +222,71 @@ semântica, publicação no npm, tag e GitHub Release:
|
|
|
221
222
|
|
|
222
223
|
O segredo `NPM_TOKEN` existe apenas no GitHub Actions e nunca deve ser salvo no
|
|
223
224
|
repositório ou em logs.
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
## Painel local · localhost:38850
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
bunx --bun vault-go@latest setup --lang pt
|
|
231
|
+
bunx --bun vault-go@latest local install
|
|
232
|
+
bunx --bun vault-go@latest local open
|
|
233
|
+
bunx --bun vault-go@latest local status
|
|
234
|
+
bunx --bun vault-go@latest local stop
|
|
235
|
+
bunx --bun vault-go@latest local uninstall
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
O assistente e a página de retorno do login suportam `--lang pt|en|es`.
|
|
239
|
+
O painel detecta o idioma do navegador e oferece seletor PT/EN/ES, busca
|
|
240
|
+
local, atividade MCP recente, projetos e memórias consultados na nuvem.
|
|
241
|
+
Atualiza a interface a cada 5 segundos enquanto visível e consulta a nuvem
|
|
242
|
+
a cada 15 segundos. Sem conexão, conserva o último cache da mesma conta.
|
|
243
|
+
|
|
244
|
+
Abra http://localhost:38850 e clique em **Conectar ao Vault** para entrar pelo navegador. A senha permanece no portal; o painel libera a sessão somente após confirmar o retorno OAuth com PKCE. A tela oferece próximos passos para configurar clientes e registrar a primeira memória. A logo e as cores são as mesmas do portal.
|
|
245
|
+
|
|
246
|
+
O serviço escuta exclusivamente em 127.0.0.1:38850. `local open` verifica
|
|
247
|
+
uma prova criptográfica do serviço e abre uma sessão privada no navegador;
|
|
248
|
+
o segredo local é removido do fragmento imediatamente e trocado por cookie
|
|
249
|
+
HttpOnly/SameSite=Strict. Não há CORS nem acesso direto da nuvem ao localhost.
|
|
250
|
+
O portal `/app/local` abre o painel da máquina atual.
|
|
251
|
+
|
|
252
|
+
No macOS, `local install` registra um LaunchAgent do usuário. Em Linux e
|
|
253
|
+
Windows, inicia o serviço nesta sessão; após reiniciar, use `local start`.
|
|
254
|
+
O runtime fica em `~/.memvault/local-runtime`, independente do cache bunx.
|
|
255
|
+
`local stop` para o processo; `local uninstall` remove o início automático e
|
|
256
|
+
preserva autenticação, cache e atividade. Execute `local install` novamente
|
|
257
|
+
após atualizar o pacote para atualizar o serviço persistente.
|
|
258
|
+
|
|
259
|
+
A atividade registra método, rota, duração e resultado das chamadas feitas
|
|
260
|
+
pelo MCP Vault, sem argumentos, consultas ou credenciais. Não captura toda a
|
|
261
|
+
atividade da máquina nem importa automaticamente o banco do claude-mem em
|
|
262
|
+
37701. A sincronização deste painel é de leitura da nuvem; as ferramentas
|
|
263
|
+
MCP continuam responsáveis pelas gravações na API.
|
|
264
|
+
|
|
265
|
+
### Sessões, dispositivos e proteção local
|
|
266
|
+
|
|
267
|
+
**Sair** encerra a sessão desta página e cancela suas prévias de contexto.
|
|
268
|
+
**Desconectar dispositivo** pede confirmação, revoga as credenciais do agente
|
|
269
|
+
na nuvem e remove a autenticação e o cache local após sucesso. Os arquivos
|
|
270
|
+
pessoais permanecem intactos. Para reconectar, entre novamente pelo navegador.
|
|
271
|
+
O portal mostra último contato, permissões e permite revogar cada agente.
|
|
272
|
+
|
|
273
|
+
O cache e a credencial de monitoramento usam AES-256-GCM. No macOS, a chave
|
|
274
|
+
fica no Keychain; nos demais sistemas, em arquivo privado `0600`. Os tokens
|
|
275
|
+
MCP existentes continuam em arquivo privado. O painel informa essas diferenças:
|
|
276
|
+
cache cifrado não significa que todas as memórias sejam criptografadas ponta a
|
|
277
|
+
ponta. O cofre de arquivos e o canal do terminal têm seus próprios protocolos.
|
|
278
|
+
|
|
279
|
+
### Motor de contexto
|
|
280
|
+
|
|
281
|
+
Escolha **Vault AI Resume**, **Claude Subscription** ou **OpenAI Subscription**
|
|
282
|
+
no painel local. A disponibilidade é verificada antes da geração. Claude usa
|
|
283
|
+
`claude auth login`; OpenAI usa `codex login` com uma conta ChatGPT. O agente
|
|
284
|
+
não pede nem copia credenciais desses provedores. O modelo gerenciado depende
|
|
285
|
+
de configuração e quota no servidor Vault.
|
|
286
|
+
|
|
287
|
+
A geração exige texto e consentimento explícito, usa diretório temporário,
|
|
288
|
+
limites de entrada/saída e timeout, e permite cancelamento. CLIs recebem apenas
|
|
289
|
+
o texto informado, com ferramentas/configurações locais desativadas conforme
|
|
290
|
+
os recursos suportados. O resultado é uma prévia privada da sessão, disponível
|
|
291
|
+
para copiar; não é salvo automaticamente como memória. Os planos e limites do
|
|
292
|
+
provedor escolhido se aplicam. Cancelar não garante estorno de uso já iniciado.
|
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface Activity {
|
|
2
|
+
id: string;
|
|
3
|
+
time: string;
|
|
4
|
+
operation: string;
|
|
5
|
+
ok: boolean;
|
|
6
|
+
durationMs: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function recordActivity(home: string, operation: string, ok: boolean, durationMs: number): void;
|
|
9
|
+
export declare function readActivity(home: string): Activity[];
|
package/dist/activity.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { appendFileSync, chmodSync, mkdirSync, readFileSync, statSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
export function recordActivity(home, operation, ok, durationMs) {
|
|
5
|
+
try {
|
|
6
|
+
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
7
|
+
const file = join(home, "local-activity.jsonl");
|
|
8
|
+
// Bounded journal contains metadata only: never arguments, content or credentials.
|
|
9
|
+
try {
|
|
10
|
+
if (statSync(file).size > 2 * 1024 * 1024)
|
|
11
|
+
writeFileSync(file, "", { mode: 0o600 });
|
|
12
|
+
}
|
|
13
|
+
catch { }
|
|
14
|
+
appendFileSync(file, JSON.stringify({
|
|
15
|
+
id: randomUUID(),
|
|
16
|
+
time: new Date().toISOString(),
|
|
17
|
+
operation: operation.slice(0, 180),
|
|
18
|
+
ok,
|
|
19
|
+
durationMs: Math.round(durationMs),
|
|
20
|
+
}) + "\n", { mode: 0o600 });
|
|
21
|
+
chmodSync(file, 0o600);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
/* Observability must never prevent an MCP operation. */
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function readActivity(home) {
|
|
28
|
+
try {
|
|
29
|
+
return readFileSync(join(home, "local-activity.jsonl"), "utf8")
|
|
30
|
+
.split("\n")
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.slice(-100)
|
|
33
|
+
.flatMap((line) => {
|
|
34
|
+
try {
|
|
35
|
+
return [JSON.parse(line)];
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
.reverse();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Locale } from './locale.js';
|
|
1
2
|
export interface AuthenticationResult {
|
|
2
3
|
email: string;
|
|
3
4
|
method: 'password' | 'browser' | 'api-key' | 'existing';
|
|
@@ -8,6 +9,9 @@ export declare function authenticateWithApiKey(apiKey: string, home: string, fet
|
|
|
8
9
|
export declare function validateExistingAuthentication(home: string, fetcher?: Fetcher): Promise<AuthenticationResult | null>;
|
|
9
10
|
export declare function openVaultBrowser(url: string): Promise<void>;
|
|
10
11
|
export declare function authenticateWithBrowser(home: string, options?: {
|
|
12
|
+
locale?: Locale;
|
|
13
|
+
callbackHtml?: string;
|
|
14
|
+
signal?: AbortSignal;
|
|
11
15
|
fetcher?: Fetcher;
|
|
12
16
|
openBrowser?: (url: string) => Promise<void>;
|
|
13
17
|
timeoutMs?: number;
|
package/dist/auth.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { callbackPage } from './callback-page.js';
|
|
2
|
+
import { copy, resolveLocale } from './locale.js';
|
|
1
3
|
import { createServer } from 'node:http';
|
|
2
4
|
import { randomBytes, createHash, timingSafeEqual } from 'node:crypto';
|
|
3
5
|
import { execFile } from 'node:child_process';
|
|
@@ -107,6 +109,8 @@ export function openVaultBrowser(url) {
|
|
|
107
109
|
return new Promise((resolve, reject) => { execFile(command, args, { timeout: 10_000 }, error => error ? reject(error) : resolve()); });
|
|
108
110
|
}
|
|
109
111
|
export async function authenticateWithBrowser(home, options = {}) {
|
|
112
|
+
const locale = options.locale ?? resolveLocale();
|
|
113
|
+
const t = copy[locale];
|
|
110
114
|
const fetcher = options.fetcher ?? fetch;
|
|
111
115
|
const report = options.report ?? (message => process.stderr.write(message + '\n'));
|
|
112
116
|
const start = endpoint(home, '/auth/oauth/resolveup/start');
|
|
@@ -124,7 +128,7 @@ export async function authenticateWithBrowser(home, options = {}) {
|
|
|
124
128
|
const server = createServer((req, res) => {
|
|
125
129
|
res.setHeader('Cache-Control', 'no-store');
|
|
126
130
|
res.setHeader('Referrer-Policy', 'no-referrer');
|
|
127
|
-
res.setHeader('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none'");
|
|
131
|
+
res.setHeader('Content-Security-Policy', "default-src 'none'; img-src data:; style-src 'unsafe-inline'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'");
|
|
128
132
|
let url;
|
|
129
133
|
try {
|
|
130
134
|
url = new URL(req.url ?? '/', `http://127.0.0.1:${port}`);
|
|
@@ -147,7 +151,7 @@ export async function authenticateWithBrowser(home, options = {}) {
|
|
|
147
151
|
if (url.searchParams.has('error')) {
|
|
148
152
|
accepted = true;
|
|
149
153
|
res.writeHead(400).end('Login não concluído. Volte ao terminal.');
|
|
150
|
-
rejectClaim?.(new Error(
|
|
154
|
+
rejectClaim?.(new Error(t.denied));
|
|
151
155
|
return;
|
|
152
156
|
}
|
|
153
157
|
if (!code || !/^[A-Za-z0-9_-]{43}$/.test(code) || url.searchParams.getAll('code').length !== 1) {
|
|
@@ -156,11 +160,15 @@ export async function authenticateWithBrowser(home, options = {}) {
|
|
|
156
160
|
}
|
|
157
161
|
accepted = true;
|
|
158
162
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
159
|
-
res.end(
|
|
163
|
+
res.end(options.callbackHtml ?? callbackPage(locale));
|
|
160
164
|
claim?.(code);
|
|
161
165
|
});
|
|
162
166
|
let timer;
|
|
167
|
+
const abort = () => rejectClaim?.(new Error(t.denied));
|
|
168
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
163
169
|
try {
|
|
170
|
+
if (options.signal?.aborted)
|
|
171
|
+
throw new Error(t.denied);
|
|
164
172
|
await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', resolve); });
|
|
165
173
|
const address = server.address();
|
|
166
174
|
if (!address || typeof address === 'string')
|
|
@@ -169,14 +177,14 @@ export async function authenticateWithBrowser(home, options = {}) {
|
|
|
169
177
|
start.searchParams.set('mobile_challenge', createHash('sha256').update(verifier).digest('base64url'));
|
|
170
178
|
start.searchParams.set('mobile_state', state);
|
|
171
179
|
start.searchParams.set('cli_port', String(port));
|
|
172
|
-
timer = setTimeout(() => rejectClaim?.(new Error(
|
|
173
|
-
report(
|
|
174
|
-
report(
|
|
180
|
+
timer = setTimeout(() => rejectClaim?.(new Error(t.timeout)), options.timeoutMs ?? 300_000);
|
|
181
|
+
report(t.opening);
|
|
182
|
+
report(`${t.fallback} ${start.href}`);
|
|
175
183
|
try {
|
|
176
184
|
await (options.openBrowser ?? openVaultBrowser)(start.href);
|
|
177
185
|
}
|
|
178
186
|
catch {
|
|
179
|
-
report(
|
|
187
|
+
report(t.openFailed);
|
|
180
188
|
}
|
|
181
189
|
const code = await received;
|
|
182
190
|
const response = await fetcher(endpoint(home, '/auth/mobile/exchange'), {
|
|
@@ -184,7 +192,7 @@ export async function authenticateWithBrowser(home, options = {}) {
|
|
|
184
192
|
body: JSON.stringify({ code, verifier }), redirect: 'error', signal: AbortSignal.timeout(15_000),
|
|
185
193
|
});
|
|
186
194
|
if (!response.ok)
|
|
187
|
-
throw new Error(
|
|
195
|
+
throw new Error(t.retry);
|
|
188
196
|
const tokens = normalizeTokens(await response.json());
|
|
189
197
|
const account = await accountForToken(tokens.accessToken, home, fetcher);
|
|
190
198
|
if (!account)
|
|
@@ -193,6 +201,7 @@ export async function authenticateWithBrowser(home, options = {}) {
|
|
|
193
201
|
return { email: account.email, method: 'browser' };
|
|
194
202
|
}
|
|
195
203
|
finally {
|
|
204
|
+
options.signal?.removeEventListener('abort', abort);
|
|
196
205
|
if (timer)
|
|
197
206
|
clearTimeout(timer);
|
|
198
207
|
server.closeAllConnections();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { copy } from "./locale.js";
|
|
3
|
+
/** All interpolated content is from the static locale catalog, never the callback URL. */
|
|
4
|
+
export function callbackPage(locale, dashboardUrl) {
|
|
5
|
+
const base = copy[locale];
|
|
6
|
+
const local = {
|
|
7
|
+
pt: {
|
|
8
|
+
body: "Sua conexão está sendo concluída. Volte ao painel local para acompanhar suas memórias.",
|
|
9
|
+
step2: "Retorno seguro recebido nesta máquina",
|
|
10
|
+
step3: "Abra o painel para acompanhar a conexão",
|
|
11
|
+
back: "Voltar ao painel local",
|
|
12
|
+
},
|
|
13
|
+
en: {
|
|
14
|
+
body: "Your connection is being completed. Return to the local dashboard to follow your memories.",
|
|
15
|
+
step2: "Secure callback received on this machine",
|
|
16
|
+
step3: "Open the dashboard to follow the connection",
|
|
17
|
+
back: "Return to local dashboard",
|
|
18
|
+
},
|
|
19
|
+
es: {
|
|
20
|
+
body: "Tu conexión se está completando. Vuelve al panel local para consultar tus memorias.",
|
|
21
|
+
step2: "Retorno seguro recibido en esta máquina",
|
|
22
|
+
step3: "Abre el panel para consultar la conexión",
|
|
23
|
+
back: "Volver al panel local",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const t = dashboardUrl ? { ...base, ...local[locale] } : base;
|
|
27
|
+
if (dashboardUrl && !/^http:\/\/localhost:\d+\/$/.test(dashboardUrl))
|
|
28
|
+
throw new Error("Invalid dashboard callback");
|
|
29
|
+
const logo = readFileSync(new URL("../assets/vault-logo-glass.png", import.meta.url)).toString("base64");
|
|
30
|
+
return `<!doctype html><html lang="${locale}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="dark"><title>AI Vault Memory · ${t.received}</title><style>
|
|
31
|
+
*{box-sizing:border-box}body{margin:0;min-height:100svh;display:grid;place-items:center;padding:24px;color:#f1f3fc;background:radial-gradient(ellipse at 20% 10%,#38305a60,transparent 50%),radial-gradient(ellipse at 90% 90%,#20394c40,transparent 55%),#080b12;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}.panel{width:min(100%,520px);padding:44px;border:1px solid #cdd8ff26;border-radius:32px;background:linear-gradient(135deg,#272c3edb,#131724ed);box-shadow:0 32px 100px #0008,inset 0 1px #ffffff10;animation:arrive .6s cubic-bezier(.2,.8,.2,1) both}.mark{display:block;margin:0 auto 28px;width:92px;height:92px;filter:drop-shadow(0 8px 25px #9990ff40)}.ribbon{stroke-dasharray:180;animation:draw 1.1s ease both}.ribbon:nth-child(2){animation-delay:.12s}.ribbon:nth-child(3){animation-delay:.24s}.brand{text-align:center;color:#b9b6ff;font-size:10px;letter-spacing:.22em;font-weight:650}h1{text-align:center;font-size:34px;letter-spacing:-.045em;line-height:1.1;margin:16px 0}.intro{text-align:center;color:#acb4cb;font-size:15px;line-height:1.7;margin:0 0 30px}.steps{list-style:none;padding:0;margin:0;border-top:1px solid #cdd8ff20}.steps li{display:flex;align-items:center;gap:14px;padding:15px 0;border-bottom:1px solid #cdd8ff15;font-size:13px;animation:arrive .5s both}.steps li:nth-child(2){animation-delay:.15s}.steps li:nth-child(3){animation-delay:.3s}.step{display:grid;place-items:center;flex-shrink:0;width:28px;height:28px;border:1px solid #b9b6ff40;background:#b9b6ff12;border-radius:9px;color:#c8c5ff;font-size:11px}.return{margin:26px 0 0;padding:16px 18px;background:#b9b6ff14;border:1px solid #b9b6ff30;border-radius:16px;color:#d4d1ff;font-size:14px;line-height:1.6}.privacy{margin:22px 0 0;color:#929db6;font-size:11px;line-height:1.7;text-align:center}@keyframes arrive{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes draw{from{stroke-dashoffset:180;opacity:0}to{stroke-dashoffset:0;opacity:1}}@media(max-width:480px){.panel{padding:30px 24px}h1{font-size:30px}}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important}}@media(forced-colors:active){.panel,.step,.return{border:1px solid CanvasText}svg{stroke:CanvasText}}
|
|
32
|
+
</style></head><body><main class="panel"><img class="mark" src="data:image/png;base64,${logo}" alt="AI Vault Memory"><p class="brand">AI VAULT MEMORY</p><h1>${t.received}</h1><p class="intro">${t.body}</p><ol class="steps"><li><span class="step">01</span>${t.step1}</li><li><span class="step">02</span>${t.step2}</li><li><span class="step">03</span>${t.step3}</li></ol><div class="return">${dashboardUrl ? `<a style="color:inherit" href="${dashboardUrl}">↗ ${t.back}</a>` : `↗ ${t.back}`}</div><p class="privacy">${t.privacy}</p></main></body></html>`;
|
|
33
|
+
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { type McpClient } from './installer.js';
|
|
2
|
+
export type CliMode = 'help' | 'version' | 'setup' | 'login' | 'serve' | 'local' | 'invalid';
|
|
2
3
|
export declare function resolveCliMode(argument: string | undefined, stdinIsTTY: boolean, stderrIsTTY: boolean): CliMode;
|
|
4
|
+
export declare function parseNumberedClients(value: string): McpClient[];
|
|
5
|
+
export declare function cliArguments(args: string[]): {
|
|
6
|
+
argument: string | undefined;
|
|
7
|
+
options: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare function runLocal(args: string[]): Promise<number>;
|
|
3
10
|
export declare function runLogin(args: string[]): Promise<number>;
|
|
4
11
|
export declare function runSetup(args: string[]): Promise<number>;
|
|
5
12
|
export declare function helpText(version: string): string;
|
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,8 @@ import { authenticateWithApiKey, authenticateWithBrowser, validateExistingAuthen
|
|
|
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';
|
|
5
|
+
import { copy, resolveLocale } from './locale.js';
|
|
6
|
+
import { installLocal, localStatus, localUrl, openLocal, startLocal, stopLocal, uninstallLocal } from './local-install.js';
|
|
5
7
|
export function resolveCliMode(argument, stdinIsTTY, stderrIsTTY) {
|
|
6
8
|
if (argument === '--help' || argument === '-h')
|
|
7
9
|
return 'help';
|
|
@@ -13,6 +15,8 @@ export function resolveCliMode(argument, stdinIsTTY, stderrIsTTY) {
|
|
|
13
15
|
return 'login';
|
|
14
16
|
if (argument === 'serve')
|
|
15
17
|
return 'serve';
|
|
18
|
+
if (argument === 'local')
|
|
19
|
+
return 'local';
|
|
16
20
|
if (argument === undefined)
|
|
17
21
|
return stdinIsTTY && stderrIsTTY ? 'setup' : 'serve';
|
|
18
22
|
return 'invalid';
|
|
@@ -24,21 +28,22 @@ function optionValue(args, name) {
|
|
|
24
28
|
const prefix = `${name}=`;
|
|
25
29
|
return args.find((argument) => argument.startsWith(prefix))?.slice(prefix.length);
|
|
26
30
|
}
|
|
27
|
-
async function ensureAuthentication(home, force = false, apiKeyMode = false) {
|
|
31
|
+
async function ensureAuthentication(home, force = false, apiKeyMode = false, locale = 'pt') {
|
|
32
|
+
const t = copy[locale];
|
|
28
33
|
if (!force) {
|
|
29
34
|
const existing = await validateExistingAuthentication(home);
|
|
30
35
|
if (existing) {
|
|
31
|
-
process.stderr.write(
|
|
36
|
+
process.stderr.write(` ✓ ${t.connected}: ${existing.email}\n`);
|
|
32
37
|
return existing;
|
|
33
38
|
}
|
|
34
39
|
}
|
|
35
40
|
if (apiKeyMode) {
|
|
36
|
-
const result = await authenticateWithApiKey(await promptSecret(
|
|
37
|
-
process.stderr.write(
|
|
41
|
+
const result = await authenticateWithApiKey(await promptSecret(`${t.key}: `), home);
|
|
42
|
+
process.stderr.write(` ✓ ${t.connected}: ${result.email}\n`);
|
|
38
43
|
return result;
|
|
39
44
|
}
|
|
40
|
-
const result = await authenticateWithBrowser(home);
|
|
41
|
-
process.stderr.write(
|
|
45
|
+
const result = await authenticateWithBrowser(home, { locale });
|
|
46
|
+
process.stderr.write(` ✓ ${t.connected}: ${result.email}\n`);
|
|
42
47
|
return result;
|
|
43
48
|
}
|
|
44
49
|
function defaultClients() {
|
|
@@ -47,55 +52,153 @@ function defaultClients() {
|
|
|
47
52
|
return detected.map((item) => item.client);
|
|
48
53
|
return ['codex', 'claude', 'cursor', 'vscode', 'copilot'];
|
|
49
54
|
}
|
|
50
|
-
|
|
55
|
+
export function parseNumberedClients(value) {
|
|
56
|
+
return parseClientSelection(value.split(',').map(item => {
|
|
57
|
+
const trimmed = item.trim();
|
|
58
|
+
if (!/^\d+$/.test(trimmed))
|
|
59
|
+
return trimmed;
|
|
60
|
+
const client = MCP_CLIENTS[Number(trimmed) - 1];
|
|
61
|
+
if (!client)
|
|
62
|
+
throw new Error(`Invalid assistant number: ${trimmed}`);
|
|
63
|
+
return client;
|
|
64
|
+
}).join(','));
|
|
65
|
+
}
|
|
66
|
+
async function chooseClients(args, locale) {
|
|
67
|
+
const t = copy[locale];
|
|
51
68
|
const configured = optionValue(args, '--clients');
|
|
52
69
|
if (configured)
|
|
53
|
-
return
|
|
70
|
+
return parseNumberedClients(configured);
|
|
54
71
|
const detected = detectMcpClients();
|
|
55
|
-
process.stderr.write('\nClientes MCP disponíveis:\n');
|
|
56
72
|
for (const item of detected) {
|
|
57
|
-
process.stderr.write(`
|
|
73
|
+
process.stderr.write(` ${MCP_CLIENTS.indexOf(item.client) + 1}. ${item.client.padEnd(12)} ${item.detected ? `● ${t.detected}` : `○ ${t.available}`}\n`);
|
|
58
74
|
}
|
|
59
75
|
const defaults = defaultClients();
|
|
60
|
-
const answer = await promptText(
|
|
61
|
-
return answer ?
|
|
76
|
+
const answer = await promptText(`${t.choose} [${defaults.join(',')}]: `);
|
|
77
|
+
return answer ? parseNumberedClients(answer) : defaults;
|
|
78
|
+
}
|
|
79
|
+
export function cliArguments(args) {
|
|
80
|
+
const remaining = [...args];
|
|
81
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
82
|
+
if (remaining[i] === '--lang') {
|
|
83
|
+
if (!remaining[i + 1] || !['pt', 'en', 'es'].includes(remaining[i + 1]))
|
|
84
|
+
throw new Error('--lang: pt, en, es');
|
|
85
|
+
remaining.splice(i, 2);
|
|
86
|
+
i--;
|
|
87
|
+
}
|
|
88
|
+
else if (remaining[i]?.startsWith('--lang=')) {
|
|
89
|
+
if (!['pt', 'en', 'es'].includes(remaining[i].slice(7)))
|
|
90
|
+
throw new Error('--lang: pt, en, es');
|
|
91
|
+
remaining.splice(i, 1);
|
|
92
|
+
i--;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { argument: remaining[0], options: args.filter((_, index) => index !== args.indexOf(remaining[0])) };
|
|
96
|
+
}
|
|
97
|
+
async function chooseLocale(args) {
|
|
98
|
+
const configured = optionValue(args, '--lang');
|
|
99
|
+
if (configured)
|
|
100
|
+
return resolveLocale(configured);
|
|
101
|
+
const detected = resolveLocale();
|
|
102
|
+
const answer = await promptText(` 1. Português 2. English 3. Español [${detected}]: `);
|
|
103
|
+
if (!answer)
|
|
104
|
+
return detected;
|
|
105
|
+
const selected = { '1': 'pt', '2': 'en', '3': 'es' }[answer] ?? answer.toLowerCase();
|
|
106
|
+
if (!['pt', 'en', 'es'].includes(selected))
|
|
107
|
+
throw new Error('--lang: pt, en, es');
|
|
108
|
+
return resolveLocale(selected);
|
|
109
|
+
}
|
|
110
|
+
const localCopy = {
|
|
111
|
+
pt: { step: 'Instalar painel local', startup: 'Início automático ativado', manual: 'Início automático indisponível neste sistema. Após reiniciar, execute: vault-go local start', running: 'Painel local disponível', stopped: 'Serviço local parado', removed: 'Início automático removido; dados e credenciais preservados', retry: 'Tente novamente: vault-go local install; vault-go local open' },
|
|
112
|
+
en: { step: 'Install local dashboard', startup: 'Automatic startup enabled', manual: 'Automatic startup is unavailable on this system. After restarting, run: vault-go local start', running: 'Local dashboard available', stopped: 'Local service stopped', removed: 'Automatic startup removed; data and credentials preserved', retry: 'Retry: vault-go local install; vault-go local open' },
|
|
113
|
+
es: { step: 'Instalar panel local', startup: 'Inicio automático activado', manual: 'Inicio automático no disponible en este sistema. Después de reiniciar, ejecuta: vault-go local start', running: 'Panel local disponible', stopped: 'Servicio local detenido', removed: 'Inicio automático eliminado; datos y credenciales conservados', retry: 'Reintenta: vault-go local install; vault-go local open' },
|
|
114
|
+
};
|
|
115
|
+
export async function runLocal(args) {
|
|
116
|
+
const t = localCopy[resolveLocale(optionValue(args, '--lang'))];
|
|
117
|
+
const command = cliArguments(args).argument ?? 'open';
|
|
118
|
+
if (command === 'status') {
|
|
119
|
+
const status = await localStatus();
|
|
120
|
+
process.stdout.write(status ? `${t.running}: ${localUrl} (v${status.version})\n` : `${t.stopped}\n`);
|
|
121
|
+
return status ? 0 : 1;
|
|
122
|
+
}
|
|
123
|
+
if (command === 'stop') {
|
|
124
|
+
await stopLocal();
|
|
125
|
+
process.stderr.write(`${t.stopped}\n`);
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
if (command === 'uninstall') {
|
|
129
|
+
await uninstallLocal();
|
|
130
|
+
process.stderr.write(`${t.removed}\n`);
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
if (command === 'serve') {
|
|
134
|
+
const { startLocalService } = await import('./local-service.js');
|
|
135
|
+
await startLocalService({ home: vaultHome() });
|
|
136
|
+
process.stderr.write(`${t.running}: ${localUrl}\n`);
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
if (command === 'install') {
|
|
140
|
+
const result = await installLocal();
|
|
141
|
+
process.stderr.write(`${result.startup ? t.startup : t.manual}\n`);
|
|
142
|
+
}
|
|
143
|
+
else if (command === 'start')
|
|
144
|
+
await startLocal();
|
|
145
|
+
else if (command === 'open')
|
|
146
|
+
await openLocal();
|
|
147
|
+
else
|
|
148
|
+
throw new Error('vault-go local [install|start|open|status|stop|uninstall|serve]');
|
|
149
|
+
process.stderr.write(`${t.running}: ${localUrl}\n`);
|
|
150
|
+
return 0;
|
|
62
151
|
}
|
|
63
152
|
export async function runLogin(args) {
|
|
153
|
+
const locale = resolveLocale(optionValue(args, '--lang'));
|
|
64
154
|
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
65
|
-
throw new Error(
|
|
155
|
+
throw new Error(copy[locale].interactive);
|
|
66
156
|
}
|
|
67
|
-
await ensureAuthentication(vaultHome(), args.includes('--force'), args.includes('--api-key'));
|
|
157
|
+
await ensureAuthentication(vaultHome(), args.includes('--force'), args.includes('--api-key'), locale);
|
|
68
158
|
return 0;
|
|
69
159
|
}
|
|
70
160
|
export async function runSetup(args) {
|
|
71
161
|
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
72
162
|
throw new Error('O setup requer um terminal interativo.');
|
|
73
163
|
}
|
|
74
|
-
process.stderr.write('
|
|
75
|
-
await
|
|
76
|
-
const
|
|
164
|
+
process.stderr.write('\n ◆ VAULT GO\n ────────────────────────────────────────\n');
|
|
165
|
+
const locale = await chooseLocale(args);
|
|
166
|
+
const t = copy[locale];
|
|
167
|
+
const local = localCopy[locale];
|
|
168
|
+
process.env['VAULT_GO_LANG'] = locale;
|
|
169
|
+
process.stderr.write(` ${t.subtitle}\n\n [1/4] ${t.auth}\n`);
|
|
170
|
+
await ensureAuthentication(vaultHome(), args.includes('--reauth'), args.includes('--api-key'), locale);
|
|
171
|
+
process.stderr.write(`\n [2/4] ${t.clients}\n`);
|
|
172
|
+
const clients = await chooseClients(args, locale);
|
|
77
173
|
if (clients.length === 0)
|
|
78
|
-
throw new Error(
|
|
79
|
-
process.stderr.write(
|
|
174
|
+
throw new Error(t.none);
|
|
175
|
+
process.stderr.write(`\n [3/4] ${t.install}\n`);
|
|
80
176
|
let failures = 0;
|
|
81
177
|
for (const client of clients) {
|
|
82
178
|
try {
|
|
83
179
|
const result = installMcpClient(client);
|
|
84
180
|
const marker = result.status === 'already-installed' ? '=' : '✓';
|
|
85
|
-
process.stderr.write(
|
|
181
|
+
process.stderr.write(` ${marker} ${client}: ${result.status === 'already-installed' ? t.already : t.installed}\n ${result.destination}\n`);
|
|
86
182
|
}
|
|
87
183
|
catch (error) {
|
|
88
184
|
failures += 1;
|
|
89
185
|
const message = error instanceof Error ? error.message : String(error);
|
|
90
|
-
process.stderr.write(
|
|
186
|
+
process.stderr.write(` ✗ ${t.failed} ${client}: ${message}\n`);
|
|
91
187
|
}
|
|
92
188
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
189
|
+
process.stderr.write(`\n [4/4] ${local.step}\n`);
|
|
190
|
+
try {
|
|
191
|
+
const result = await installLocal();
|
|
192
|
+
process.stderr.write(` ✓ ${result.startup ? local.startup : local.manual}\n`);
|
|
193
|
+
await openLocal();
|
|
194
|
+
process.stderr.write(` ✓ ${local.running}: ${localUrl}\n`);
|
|
96
195
|
}
|
|
97
|
-
|
|
98
|
-
|
|
196
|
+
catch (error) {
|
|
197
|
+
failures++;
|
|
198
|
+
process.stderr.write(` ✗ ${error instanceof Error ? error.message : String(error)}\n ${local.retry}\n`);
|
|
199
|
+
}
|
|
200
|
+
process.stderr.write(`\n ${failures ? `${t.failed}: ${failures}` : `✓ ${t.done}`}\n ${t.restart}\n\n`);
|
|
201
|
+
return failures ? 1 : 0;
|
|
99
202
|
}
|
|
100
203
|
export function helpText(version) {
|
|
101
204
|
return `vault-go ${version}
|
|
@@ -115,6 +218,12 @@ Uso:
|
|
|
115
218
|
bunx --bun vault-go@latest serve
|
|
116
219
|
Inicia o servidor MCP por stdio.
|
|
117
220
|
|
|
221
|
+
vault-go local [install|start|open|status|stop|uninstall|serve]
|
|
222
|
+
Painel local em http://localhost:38850; install ativa início automático no macOS.
|
|
223
|
+
|
|
224
|
+
--lang pt|en|es
|
|
225
|
+
Idioma do assistente (padrão: idioma do sistema).
|
|
226
|
+
|
|
118
227
|
Clientes:
|
|
119
228
|
${MCP_CLIENTS.join(', ')}
|
|
120
229
|
|
package/dist/cloud.d.ts
CHANGED
|
@@ -32,8 +32,14 @@ export interface VaultMemoryApi {
|
|
|
32
32
|
export declare class VaultCloudClient implements VaultMemoryApi {
|
|
33
33
|
private readonly home;
|
|
34
34
|
private readonly cloudFetch;
|
|
35
|
-
|
|
35
|
+
private readonly journal;
|
|
36
|
+
constructor(home?: string, cloudFetch?: typeof fetch, journal?: boolean);
|
|
36
37
|
health(): Promise<unknown>;
|
|
38
|
+
observerStatus(): Promise<unknown>;
|
|
39
|
+
observerGenerate(text: string, signal?: AbortSignal): Promise<unknown>;
|
|
40
|
+
registerDevice(input: Record<string, unknown>): Promise<unknown>;
|
|
41
|
+
rotateDevice(id: string): Promise<unknown>;
|
|
42
|
+
deviceAction(id: string, token: string, action: 'heartbeat' | 'disconnect', body?: Record<string, unknown>): Promise<unknown>;
|
|
37
43
|
projects(): Promise<unknown>;
|
|
38
44
|
createProject(input: Record<string, unknown>): Promise<unknown>;
|
|
39
45
|
startSession(input: Record<string, unknown>): Promise<unknown>;
|
|
@@ -65,4 +71,9 @@ export declare class VaultCloudClient implements VaultMemoryApi {
|
|
|
65
71
|
private endpoint;
|
|
66
72
|
private accessToken;
|
|
67
73
|
private request;
|
|
74
|
+
private performRequest;
|
|
75
|
+
}
|
|
76
|
+
export declare class VaultCloudError extends Error {
|
|
77
|
+
readonly status: number;
|
|
78
|
+
constructor(status: number, message: string);
|
|
68
79
|
}
|