vault-go 0.14.0 → 0.16.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 +3 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +52 -8
- package/dist/cloud.d.ts +2 -0
- package/dist/cloud.js +14 -0
- package/dist/context-engines.d.ts +13 -0
- package/dist/context-engines.js +98 -18
- package/dist/hook-queue.d.ts +13 -0
- package/dist/hook-queue.js +72 -0
- package/dist/hooks.d.ts +9 -0
- package/dist/hooks.js +227 -0
- package/dist/index.js +10 -0
- package/dist/installer.d.ts +3 -0
- package/dist/installer.js +78 -2
- package/dist/local-dashboard.js +14 -1
- package/dist/local-install.js +1 -1
- package/dist/local-service.js +23 -1
- package/dist/locale.d.ts +6 -0
- package/dist/locale.js +6 -0
- package/dist/server.js +13 -4
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -88,6 +88,7 @@ Comandos explícitos:
|
|
|
88
88
|
bunx --bun vault-go@latest setup
|
|
89
89
|
bunx --bun vault-go@latest setup --clients codex,claude,copilot
|
|
90
90
|
bunx --bun vault-go@latest setup --engine openrouter
|
|
91
|
+
bunx --bun vault-go@latest setup --engine claude --model sonnet
|
|
91
92
|
bunx --bun vault-go@latest login --force
|
|
92
93
|
bunx --bun vault-go@latest serve
|
|
93
94
|
bunx --bun vault-go@latest engine list
|
|
@@ -169,6 +170,8 @@ limitados.
|
|
|
169
170
|
|
|
170
171
|
O seletor de motor segue o mesmo modelo do claude-mem: Claude (CLI da assinatura), OpenRouter (chave), Gemini (chave) ou o Vault AI Resume da conta. Grave chaves com `vault-go engine key openrouter|gemini` ou no painel local; as ferramentas MCP não aceitam segredos.
|
|
171
172
|
|
|
173
|
+
Na instalação, o Vault Go registra hooks no Claude Code e no Codex. Eles injetam contexto no início da sessão, capturam prompts e ferramentas, e o serviço local (`http://localhost:38850`) gera memórias com o motor escolhido — o mesmo ciclo do claude-mem, persistindo no Vault.
|
|
174
|
+
|
|
172
175
|
O recurso `vault-go://status` fornece o resumo sanitizado em JSON.
|
|
173
176
|
|
|
174
177
|
Para reduzir o uso de contexto, prefira a recuperação em três camadas:
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type McpClient } from './installer.js';
|
|
2
2
|
import { type ContextEngineId } from './context-engines.js';
|
|
3
|
-
export type CliMode = 'help' | 'version' | 'setup' | 'login' | 'serve' | 'local' | 'engine' | 'invalid';
|
|
3
|
+
export type CliMode = 'help' | 'version' | 'setup' | 'login' | 'serve' | 'local' | 'engine' | 'hook' | 'invalid';
|
|
4
4
|
export declare function resolveCliMode(argument: string | undefined, stdinIsTTY: boolean, stderrIsTTY: boolean): CliMode;
|
|
5
5
|
export declare function parseEngineChoice(value: string): ContextEngineId;
|
|
6
6
|
export declare function parseNumberedClients(value: string): McpClient[];
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@ 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 { CONTEXT_ENGINE_IDS, ENGINE_NAMES, isApiEngine, listContextEngines, loadEngineKey, saveEngineKey, selectContextEngine, } from './context-engines.js';
|
|
5
|
+
import { CONTEXT_ENGINE_IDS, ENGINE_NAMES, engineModels, isApiEngine, listContextEngines, loadEngineKey, parseEngineModel, saveEngineKey, selectContextEngine, selectEngineModel, selectedContextEngine, selectedEngineModel, } from './context-engines.js';
|
|
6
6
|
import { VaultCloudClient } from './cloud.js';
|
|
7
7
|
import { copy, resolveLocale } from './locale.js';
|
|
8
8
|
import { installLocal, localStatus, localUrl, openLocal, startLocal, stopLocal, uninstallLocal } from './local-install.js';
|
|
@@ -21,6 +21,8 @@ export function resolveCliMode(argument, stdinIsTTY, stderrIsTTY) {
|
|
|
21
21
|
return 'local';
|
|
22
22
|
if (argument === 'engine')
|
|
23
23
|
return 'engine';
|
|
24
|
+
if (argument === 'hook')
|
|
25
|
+
return 'hook';
|
|
24
26
|
if (argument === undefined)
|
|
25
27
|
return stdinIsTTY && stderrIsTTY ? 'setup' : 'serve';
|
|
26
28
|
return 'invalid';
|
|
@@ -110,6 +112,7 @@ async function chooseEngine(args, locale, home) {
|
|
|
110
112
|
const engine = configured ? parseEngineChoice(configured) : await promptEngine(home, locale);
|
|
111
113
|
selectContextEngine(home, engine);
|
|
112
114
|
process.stderr.write(` ✓ ${t.engineSaved}: ${ENGINE_NAMES[engine]}\n`);
|
|
115
|
+
await chooseModel(args, locale, home, engine);
|
|
113
116
|
if (isApiEngine(engine) && !loadEngineKey(home, engine)) {
|
|
114
117
|
try {
|
|
115
118
|
const key = await promptSecret(` ${t.engineKey} (${ENGINE_NAMES[engine]}): `);
|
|
@@ -145,6 +148,29 @@ async function promptEngine(home, locale) {
|
|
|
145
148
|
const answer = await promptText(`${t.choose} [1. ${ENGINE_NAMES['vault-ai-resume']}]: `);
|
|
146
149
|
return answer ? parseEngineChoice(answer) : 'vault-ai-resume';
|
|
147
150
|
}
|
|
151
|
+
async function chooseModel(args, locale, home, engine) {
|
|
152
|
+
const catalog = engineModels(engine);
|
|
153
|
+
if (catalog.length === 0)
|
|
154
|
+
return;
|
|
155
|
+
const t = copy[locale];
|
|
156
|
+
const configured = optionValue(args, '--model');
|
|
157
|
+
const model = configured
|
|
158
|
+
? parseEngineModel(engine, configured)
|
|
159
|
+
: await promptModel(home, engine, locale);
|
|
160
|
+
selectEngineModel(home, engine, model);
|
|
161
|
+
process.stderr.write(` ✓ ${t.modelSaved}: ${catalog.find((item) => item.id === model)?.label ?? model}\n`);
|
|
162
|
+
}
|
|
163
|
+
async function promptModel(home, engine, locale) {
|
|
164
|
+
const t = copy[locale];
|
|
165
|
+
const catalog = engineModels(engine);
|
|
166
|
+
process.stderr.write(` ${t.modelHelp}\n`);
|
|
167
|
+
for (const [index, item] of catalog.entries()) {
|
|
168
|
+
process.stderr.write(` ${index + 1}. ${item.label}\n`);
|
|
169
|
+
}
|
|
170
|
+
const fallback = selectedEngineModel(home, engine) || catalog[0].id;
|
|
171
|
+
const answer = await promptText(`${t.choose} [${catalog[0].label}]: `);
|
|
172
|
+
return answer ? parseEngineModel(engine, answer) : fallback;
|
|
173
|
+
}
|
|
148
174
|
export function cliArguments(args) {
|
|
149
175
|
const remaining = [...args];
|
|
150
176
|
for (let i = 0; i < remaining.length; i++) {
|
|
@@ -280,9 +306,10 @@ Uso:
|
|
|
280
306
|
bunx --bun vault-go@latest
|
|
281
307
|
Abre o login Vault no navegador e instala nos clientes detectados.
|
|
282
308
|
|
|
283
|
-
bunx --bun vault-go@latest setup [--clients lista] [--engine motor] [--reauth]
|
|
309
|
+
bunx --bun vault-go@latest setup [--clients lista] [--engine motor] [--model modelo] [--reauth]
|
|
284
310
|
Executa o assistente de autenticação e instalação. Pergunta o motor
|
|
285
|
-
(Vault AI, Claude, OpenRouter, Gemini ou ChatGPT/Codex)
|
|
311
|
+
(Vault AI, Claude, OpenRouter, Gemini ou ChatGPT/Codex) e o modelo
|
|
312
|
+
(Haiku, Sonnet, Opus no Claude).
|
|
286
313
|
|
|
287
314
|
bunx --bun vault-go@latest login [--force] [--api-key]
|
|
288
315
|
Autentica sem alterar clientes MCP.
|
|
@@ -293,9 +320,12 @@ Uso:
|
|
|
293
320
|
vault-go local [install|start|open|status|stop|uninstall|serve]
|
|
294
321
|
Painel local em http://localhost:38850; install ativa início automático no macOS.
|
|
295
322
|
|
|
296
|
-
vault-go engine list|use <motor
|
|
297
|
-
Escolhe o motor de contexto:
|
|
298
|
-
|
|
323
|
+
vault-go engine list|use <motor> [--model haiku|sonnet|opus]|key openrouter|gemini
|
|
324
|
+
Escolhe o motor e o modelo de contexto. No Claude: haiku, sonnet ou opus.
|
|
325
|
+
|
|
326
|
+
vault-go hook <adaptador> <evento>
|
|
327
|
+
Usado pelos hooks do Claude/Codex: injeta contexto, captura atividade
|
|
328
|
+
e enfileira geração com o motor escolhido.
|
|
299
329
|
|
|
300
330
|
--lang pt|en|es
|
|
301
331
|
Idioma do assistente (padrão: idioma do sistema).
|
|
@@ -344,7 +374,21 @@ export async function runEngine(args) {
|
|
|
344
374
|
if (!engine || !CONTEXT_ENGINE_IDS.includes(engine)) {
|
|
345
375
|
throw new Error(`Motor inválido. Use: ${CONTEXT_ENGINE_IDS.join(', ')}`);
|
|
346
376
|
}
|
|
347
|
-
|
|
377
|
+
const selected = parseEngineChoice(engine);
|
|
378
|
+
selectContextEngine(home, selected);
|
|
379
|
+
const modelFlag = optionValue(args, '--model');
|
|
380
|
+
const model = modelFlag
|
|
381
|
+
? selectEngineModel(home, selected, parseEngineModel(selected, modelFlag))
|
|
382
|
+
: selectedEngineModel(home, selected);
|
|
383
|
+
process.stdout.write(`${JSON.stringify({ selected, ...(model ? { model } : {}) })}\n`);
|
|
384
|
+
return 0;
|
|
385
|
+
}
|
|
386
|
+
if (action === 'model') {
|
|
387
|
+
const engine = selectContextEngine(home, selectedContextEngine(home));
|
|
388
|
+
const value = positional[1] ?? optionValue(args, '--model');
|
|
389
|
+
if (!value)
|
|
390
|
+
throw new Error('Use vault-go engine model haiku|sonnet|opus');
|
|
391
|
+
process.stdout.write(`${selectEngineModel(home, engine, parseEngineModel(engine, value))}\n`);
|
|
348
392
|
return 0;
|
|
349
393
|
}
|
|
350
394
|
if (action === 'key') {
|
|
@@ -355,5 +399,5 @@ export async function runEngine(args) {
|
|
|
355
399
|
process.stderr.write(`Chave ${engine} gravada com permissão privada.\n`);
|
|
356
400
|
return 0;
|
|
357
401
|
}
|
|
358
|
-
throw new Error('vault-go engine list|use <motor>|key openrouter|gemini');
|
|
402
|
+
throw new Error('vault-go engine list|use <motor> [--model haiku|sonnet|opus]|model <modelo>|key openrouter|gemini');
|
|
359
403
|
}
|
package/dist/cloud.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface VaultMemoryApi {
|
|
2
2
|
observerStatus?(): Promise<unknown>;
|
|
3
3
|
observerGenerate?(text: string, signal?: AbortSignal): Promise<unknown>;
|
|
4
|
+
preferences?(): Promise<unknown>;
|
|
4
5
|
health(): Promise<unknown>;
|
|
5
6
|
projects(): Promise<unknown>;
|
|
6
7
|
createProject(input: Record<string, unknown>): Promise<unknown>;
|
|
@@ -38,6 +39,7 @@ export declare class VaultCloudClient implements VaultMemoryApi {
|
|
|
38
39
|
constructor(home?: string, cloudFetch?: typeof fetch, journal?: boolean);
|
|
39
40
|
health(): Promise<unknown>;
|
|
40
41
|
observerStatus(): Promise<unknown>;
|
|
42
|
+
preferences(): Promise<unknown>;
|
|
41
43
|
observerGenerate(text: string, signal?: AbortSignal): Promise<unknown>;
|
|
42
44
|
registerDevice(input: Record<string, unknown>): Promise<unknown>;
|
|
43
45
|
rotateDevice(id: string): Promise<unknown>;
|
package/dist/cloud.js
CHANGED
|
@@ -13,6 +13,20 @@ export class VaultCloudClient {
|
|
|
13
13
|
return this.request('/health', { authenticated: false });
|
|
14
14
|
}
|
|
15
15
|
async observerStatus() { return this.request('/memory/observer/status'); }
|
|
16
|
+
async preferences() {
|
|
17
|
+
try {
|
|
18
|
+
return await this.request('/preferences');
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {
|
|
22
|
+
automaticCapture: true,
|
|
23
|
+
captureMode: 'balanced',
|
|
24
|
+
includeToolResults: false,
|
|
25
|
+
contextItems: 20,
|
|
26
|
+
contextMaxChars: 24_000,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
16
30
|
async observerGenerate(text, signal) {
|
|
17
31
|
return this.request('/memory/observer/generate', { method: 'POST', body: { text }, timeoutMs: 60_000, ...(signal ? { signal } : {}) });
|
|
18
32
|
}
|
|
@@ -3,6 +3,12 @@ export declare const API_ENGINE_IDS: readonly ["openrouter", "gemini"];
|
|
|
3
3
|
export type ContextEngineId = (typeof CONTEXT_ENGINE_IDS)[number];
|
|
4
4
|
export type ApiEngineId = (typeof API_ENGINE_IDS)[number];
|
|
5
5
|
export declare const ENGINE_NAMES: Record<ContextEngineId, string>;
|
|
6
|
+
export interface EngineModelOption {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
aliases?: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
export declare const ENGINE_MODELS: Record<ContextEngineId, readonly EngineModelOption[]>;
|
|
6
12
|
export interface ContextResult {
|
|
7
13
|
title: string;
|
|
8
14
|
content: string;
|
|
@@ -18,6 +24,8 @@ export interface EngineAvailability {
|
|
|
18
24
|
export interface ContextEngine extends EngineAvailability {
|
|
19
25
|
id: ContextEngineId;
|
|
20
26
|
name: string;
|
|
27
|
+
models: EngineModelOption[];
|
|
28
|
+
model?: string;
|
|
21
29
|
}
|
|
22
30
|
export interface CommandRequest {
|
|
23
31
|
command: "claude" | "codex";
|
|
@@ -38,6 +46,10 @@ export declare function selectedContextEngine(home: string): ContextEngineId;
|
|
|
38
46
|
export declare function selectContextEngine(home: string, engine: unknown): ContextEngineId;
|
|
39
47
|
export declare function saveEngineKey(home: string, engine: unknown, key: unknown): ApiEngineId;
|
|
40
48
|
export declare function loadEngineKey(home: string, engine: ApiEngineId, environment?: NodeJS.ProcessEnv): string | undefined;
|
|
49
|
+
export declare function engineModels(engine: ContextEngineId): EngineModelOption[];
|
|
50
|
+
export declare function parseEngineModel(engine: unknown, value: unknown): string;
|
|
51
|
+
export declare function selectedEngineModel(home: string, engine: ContextEngineId): string;
|
|
52
|
+
export declare function selectEngineModel(home: string, engine: unknown, model: unknown): string;
|
|
41
53
|
export declare function contextEnvironment(source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
42
54
|
/** Never includes process stderr or model input in an error message. */
|
|
43
55
|
export declare const runContextCommand: CommandRunner;
|
|
@@ -46,6 +58,7 @@ export declare function listContextEngines(home: string, managedStatus?: EngineA
|
|
|
46
58
|
env?: NodeJS.ProcessEnv;
|
|
47
59
|
}): Promise<{
|
|
48
60
|
selected: ContextEngineId;
|
|
61
|
+
model?: string;
|
|
49
62
|
engines: ContextEngine[];
|
|
50
63
|
}>;
|
|
51
64
|
export declare function validateContextResult(value: unknown, engine: ContextEngineId): ContextResult;
|
package/dist/context-engines.js
CHANGED
|
@@ -17,10 +17,37 @@ export const ENGINE_NAMES = {
|
|
|
17
17
|
openrouter: "OpenRouter",
|
|
18
18
|
gemini: "Gemini",
|
|
19
19
|
};
|
|
20
|
+
export const ENGINE_MODELS = {
|
|
21
|
+
"vault-ai-resume": [],
|
|
22
|
+
"claude-subscription": [
|
|
23
|
+
{ id: "haiku", label: "Haiku" },
|
|
24
|
+
{ id: "sonnet", label: "Sonnet" },
|
|
25
|
+
{ id: "opus", label: "Opus" },
|
|
26
|
+
],
|
|
27
|
+
"openai-subscription": [
|
|
28
|
+
{ id: "gpt-5-mini", label: "GPT-5 mini" },
|
|
29
|
+
{ id: "gpt-5", label: "GPT-5" },
|
|
30
|
+
{ id: "o3", label: "o3" },
|
|
31
|
+
],
|
|
32
|
+
openrouter: [
|
|
33
|
+
{ id: "xiaomi/mimo-v2-flash:free", label: "Free", aliases: ["free"] },
|
|
34
|
+
{ id: "anthropic/claude-haiku-4.5", label: "Haiku", aliases: ["haiku"] },
|
|
35
|
+
{ id: "anthropic/claude-sonnet-4.5", label: "Sonnet", aliases: ["sonnet"] },
|
|
36
|
+
{ id: "anthropic/claude-opus-4.1", label: "Opus", aliases: ["opus"] },
|
|
37
|
+
],
|
|
38
|
+
gemini: [
|
|
39
|
+
{ id: "gemini-flash-latest", label: "Flash", aliases: ["flash"] },
|
|
40
|
+
{ id: "gemini-2.5-pro", label: "Pro", aliases: ["pro"] },
|
|
41
|
+
],
|
|
42
|
+
};
|
|
20
43
|
const DEFAULT_MODELS = {
|
|
44
|
+
"vault-ai-resume": "",
|
|
45
|
+
"claude-subscription": "haiku",
|
|
46
|
+
"openai-subscription": "gpt-5-mini",
|
|
21
47
|
openrouter: "xiaomi/mimo-v2-flash:free",
|
|
22
48
|
gemini: "gemini-flash-latest",
|
|
23
49
|
};
|
|
50
|
+
const CUSTOM_MODEL = /^[a-zA-Z0-9][a-zA-Z0-9_.:/-]{0,199}$/;
|
|
24
51
|
const KEY_ENV = {
|
|
25
52
|
openrouter: ["VAULT_GO_OPENROUTER_API_KEY", "OPENROUTER_API_KEY"],
|
|
26
53
|
gemini: ["VAULT_GO_GEMINI_API_KEY", "GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
@@ -87,14 +114,61 @@ export function loadEngineKey(home, engine, environment = process.env) {
|
|
|
87
114
|
const stored = readJson(secretsPath(home))[engine];
|
|
88
115
|
return typeof stored === "string" && stored.trim() ? stored.trim() : undefined;
|
|
89
116
|
}
|
|
90
|
-
function
|
|
117
|
+
export function engineModels(engine) {
|
|
118
|
+
return ENGINE_MODELS[engine].map(({ id, label }) => ({ id, label }));
|
|
119
|
+
}
|
|
120
|
+
export function parseEngineModel(engine, value) {
|
|
121
|
+
if (!isContextEngine(engine))
|
|
122
|
+
throw new Error("Unknown context engine.");
|
|
123
|
+
const catalog = ENGINE_MODELS[engine];
|
|
124
|
+
if (catalog.length === 0)
|
|
125
|
+
throw new Error("This engine does not select a model.");
|
|
126
|
+
if (typeof value !== "string" || !value.trim())
|
|
127
|
+
throw new Error("Unknown model.");
|
|
128
|
+
const input = value.trim();
|
|
129
|
+
const index = /^\d+$/.test(input) ? Number(input) - 1 : -1;
|
|
130
|
+
const numbered = catalog[index];
|
|
131
|
+
if (numbered)
|
|
132
|
+
return numbered.id;
|
|
133
|
+
const lower = input.toLowerCase();
|
|
134
|
+
const match = catalog.find((item) => item.id.toLowerCase() === lower ||
|
|
135
|
+
item.label.toLowerCase() === lower ||
|
|
136
|
+
item.aliases?.some((alias) => alias.toLowerCase() === lower));
|
|
137
|
+
if (match)
|
|
138
|
+
return match.id;
|
|
139
|
+
if ((engine === "openrouter" || engine === "gemini") && CUSTOM_MODEL.test(input))
|
|
140
|
+
return input;
|
|
141
|
+
throw new Error("Unknown model.");
|
|
142
|
+
}
|
|
143
|
+
export function selectedEngineModel(home, engine) {
|
|
91
144
|
const models = readJson(settingsPath(home)).models;
|
|
92
145
|
const configured = models && typeof models === "object" && !Array.isArray(models)
|
|
93
146
|
? models[engine]
|
|
94
147
|
: undefined;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
148
|
+
if (typeof configured === "string" && configured.trim()) {
|
|
149
|
+
try {
|
|
150
|
+
return parseEngineModel(engine, configured);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return DEFAULT_MODELS[engine];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return DEFAULT_MODELS[engine];
|
|
157
|
+
}
|
|
158
|
+
export function selectEngineModel(home, engine, model) {
|
|
159
|
+
if (!isContextEngine(engine))
|
|
160
|
+
throw new Error("Unknown context engine.");
|
|
161
|
+
const resolved = parseEngineModel(engine, model);
|
|
162
|
+
const current = readJson(settingsPath(home));
|
|
163
|
+
const models = current.models && typeof current.models === "object" && !Array.isArray(current.models)
|
|
164
|
+
? { ...current.models }
|
|
165
|
+
: {};
|
|
166
|
+
models[engine] = resolved;
|
|
167
|
+
writePrivateJson(settingsPath(home), { ...current, engine, models });
|
|
168
|
+
return resolved;
|
|
169
|
+
}
|
|
170
|
+
function engineModel(home, engine) {
|
|
171
|
+
return selectedEngineModel(home, engine);
|
|
98
172
|
}
|
|
99
173
|
export function contextEnvironment(source = process.env) {
|
|
100
174
|
const env = {};
|
|
@@ -252,22 +326,24 @@ export async function listContextEngines(home, managedStatus = {
|
|
|
252
326
|
checkSubscription("claude-subscription", cwd, runner, env),
|
|
253
327
|
checkSubscription("openai-subscription", cwd, runner, env),
|
|
254
328
|
]);
|
|
329
|
+
const selected = selectedContextEngine(home);
|
|
330
|
+
const withModels = (id, availability) => ({
|
|
331
|
+
id,
|
|
332
|
+
name: ENGINE_NAMES[id],
|
|
333
|
+
...availability,
|
|
334
|
+
models: engineModels(id),
|
|
335
|
+
...(engineModels(id).length > 0 ? { model: selectedEngineModel(home, id) } : {}),
|
|
336
|
+
});
|
|
337
|
+
const selectedModel = selectedEngineModel(home, selected);
|
|
255
338
|
return {
|
|
256
|
-
selected
|
|
339
|
+
selected,
|
|
340
|
+
...(selectedModel ? { model: selectedModel } : {}),
|
|
257
341
|
engines: [
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
name: ENGINE_NAMES.openrouter,
|
|
264
|
-
...apiAvailability(home, "openrouter", options.env),
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
id: "gemini",
|
|
268
|
-
name: ENGINE_NAMES.gemini,
|
|
269
|
-
...apiAvailability(home, "gemini", options.env),
|
|
270
|
-
},
|
|
342
|
+
withModels("vault-ai-resume", managedStatus),
|
|
343
|
+
withModels("claude-subscription", claude),
|
|
344
|
+
withModels("openai-subscription", codex),
|
|
345
|
+
withModels("openrouter", apiAvailability(home, "openrouter", options.env)),
|
|
346
|
+
withModels("gemini", apiAvailability(home, "gemini", options.env)),
|
|
271
347
|
],
|
|
272
348
|
};
|
|
273
349
|
}
|
|
@@ -469,6 +545,8 @@ export async function generateContext(home, engine, text, options = {}) {
|
|
|
469
545
|
args = [
|
|
470
546
|
"--safe-mode",
|
|
471
547
|
"-p",
|
|
548
|
+
"--model",
|
|
549
|
+
engineModel(home, engine),
|
|
472
550
|
"--tools",
|
|
473
551
|
"",
|
|
474
552
|
"--strict-mcp-config",
|
|
@@ -498,6 +576,8 @@ export async function generateContext(home, engine, text, options = {}) {
|
|
|
498
576
|
"-c",
|
|
499
577
|
'forced_login_method="chatgpt"',
|
|
500
578
|
"-c",
|
|
579
|
+
`model=${JSON.stringify(engineModel(home, engine))}`,
|
|
580
|
+
"-c",
|
|
501
581
|
'approval_policy="never"',
|
|
502
582
|
"-c",
|
|
503
583
|
'web_search="disabled"',
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { VaultCloudClient } from "./cloud.js";
|
|
2
|
+
import { generateContext } from "./context-engines.js";
|
|
3
|
+
export interface GenerationJob {
|
|
4
|
+
id: string;
|
|
5
|
+
projectId: string;
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
text: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
kind: "observation" | "summary" | "prompt";
|
|
10
|
+
}
|
|
11
|
+
export declare function enqueueGeneration(home: string, job: GenerationJob): void;
|
|
12
|
+
export declare function takeGeneration(home: string): GenerationJob | undefined;
|
|
13
|
+
export declare function processGenerationQueue(home?: string, cloud?: VaultCloudClient, generate?: typeof generateContext): Promise<boolean>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync } from "node:fs";
|
|
2
|
+
import { writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { VaultCloudClient } from "./cloud.js";
|
|
5
|
+
import { vaultHome } from "./config.js";
|
|
6
|
+
import { generateContext, selectedContextEngine, } from "./context-engines.js";
|
|
7
|
+
function queuePath(home) {
|
|
8
|
+
return join(home, "generation-queue.json");
|
|
9
|
+
}
|
|
10
|
+
function readQueue(home) {
|
|
11
|
+
try {
|
|
12
|
+
const value = JSON.parse(readFileSync(queuePath(home), "utf8"));
|
|
13
|
+
return Array.isArray(value) ? value : [];
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function writeQueue(home, jobs) {
|
|
20
|
+
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
21
|
+
const destination = queuePath(home);
|
|
22
|
+
const temporary = destination + "." + process.pid + ".tmp";
|
|
23
|
+
writeFileSync(temporary, JSON.stringify(jobs.slice(0, 50)) + "\n", { mode: 0o600 });
|
|
24
|
+
chmodSync(temporary, 0o600);
|
|
25
|
+
renameSync(temporary, destination);
|
|
26
|
+
chmodSync(destination, 0o600);
|
|
27
|
+
}
|
|
28
|
+
export function enqueueGeneration(home, job) {
|
|
29
|
+
if (!job.text.trim() || job.text.length > 16_000)
|
|
30
|
+
return;
|
|
31
|
+
const jobs = readQueue(home);
|
|
32
|
+
if (jobs.some((item) => item.id === job.id))
|
|
33
|
+
return;
|
|
34
|
+
writeQueue(home, [...jobs, job]);
|
|
35
|
+
}
|
|
36
|
+
export function takeGeneration(home) {
|
|
37
|
+
const jobs = readQueue(home);
|
|
38
|
+
const next = jobs[0];
|
|
39
|
+
if (!next)
|
|
40
|
+
return;
|
|
41
|
+
writeQueue(home, jobs.slice(1));
|
|
42
|
+
return next;
|
|
43
|
+
}
|
|
44
|
+
export async function processGenerationQueue(home = vaultHome(), cloud = new VaultCloudClient(home), generate = generateContext) {
|
|
45
|
+
const job = takeGeneration(home);
|
|
46
|
+
if (!job)
|
|
47
|
+
return false;
|
|
48
|
+
const engine = selectedContextEngine(home);
|
|
49
|
+
try {
|
|
50
|
+
const result = await generate(home, engine, job.text.slice(0, 16_000), {
|
|
51
|
+
...(cloud.observerGenerate
|
|
52
|
+
? {
|
|
53
|
+
managedGenerate: (text, options) => cloud.observerGenerate(text, options.signal),
|
|
54
|
+
}
|
|
55
|
+
: {}),
|
|
56
|
+
});
|
|
57
|
+
await cloud.remember({
|
|
58
|
+
projectId: job.projectId,
|
|
59
|
+
...(job.sessionId ? { sessionId: job.sessionId } : {}),
|
|
60
|
+
kind: job.kind === "summary" ? "summary" : "observation",
|
|
61
|
+
memoryType: "discovery",
|
|
62
|
+
title: result.title || job.title,
|
|
63
|
+
content: result.content,
|
|
64
|
+
facts: result.facts,
|
|
65
|
+
concepts: result.concepts,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Drop poison items so the watcher keeps draining.
|
|
70
|
+
}
|
|
71
|
+
return existsSync(queuePath(home)) && readQueue(home).length > 0;
|
|
72
|
+
}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type VaultMemoryApi } from "./cloud.js";
|
|
2
|
+
export declare const HOOK_EVENTS: readonly ["context", "session-init", "observation", "file-context", "summarize"];
|
|
3
|
+
export type HookEvent = (typeof HOOK_EVENTS)[number];
|
|
4
|
+
type RecordValue = Record<string, unknown>;
|
|
5
|
+
export declare function hookOutput(event: HookEvent, context?: string): RecordValue;
|
|
6
|
+
export declare function executeHook(adapter: string, event: HookEvent, value: unknown, cloud?: VaultMemoryApi, home?: string): Promise<RecordValue>;
|
|
7
|
+
export declare function readHookInput(stream?: NodeJS.ReadableStream): Promise<unknown>;
|
|
8
|
+
export declare function runHook(adapter: string, event: string): Promise<void>;
|
|
9
|
+
export {};
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { basename, dirname, resolve } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { VaultCloudClient } from "./cloud.js";
|
|
5
|
+
import { vaultHome } from "./config.js";
|
|
6
|
+
import { enqueueGeneration } from "./hook-queue.js";
|
|
7
|
+
export const HOOK_EVENTS = [
|
|
8
|
+
"context",
|
|
9
|
+
"session-init",
|
|
10
|
+
"observation",
|
|
11
|
+
"file-context",
|
|
12
|
+
"summarize",
|
|
13
|
+
];
|
|
14
|
+
const record = (value) => value && typeof value === "object" && !Array.isArray(value)
|
|
15
|
+
? value
|
|
16
|
+
: {};
|
|
17
|
+
const string = (...values) => values.find((value) => typeof value === "string" && value.trim()) ||
|
|
18
|
+
"";
|
|
19
|
+
function root(cwd) {
|
|
20
|
+
const initial = resolve(cwd);
|
|
21
|
+
let dir = initial;
|
|
22
|
+
while (dirname(dir) !== dir) {
|
|
23
|
+
if (existsSync(resolve(dir, ".git")))
|
|
24
|
+
return dir;
|
|
25
|
+
dir = dirname(dir);
|
|
26
|
+
}
|
|
27
|
+
return initial;
|
|
28
|
+
}
|
|
29
|
+
function redact(value) {
|
|
30
|
+
if (typeof value === "string") {
|
|
31
|
+
return value
|
|
32
|
+
.replace(/<private>[\s\S]*?<\/private>/gi, "[redacted]")
|
|
33
|
+
.replace(/(api[_-]?key|secret|token|password)\s*[:=]\s*['"]?[^\s'"]+/gi, "$1=[redacted]");
|
|
34
|
+
}
|
|
35
|
+
if (Array.isArray(value))
|
|
36
|
+
return value.map(redact);
|
|
37
|
+
if (value && typeof value === "object") {
|
|
38
|
+
const output = {};
|
|
39
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
40
|
+
output[key] = /api[_-]?key|secret|token|password|authorization/i.test(key)
|
|
41
|
+
? "[redacted]"
|
|
42
|
+
: redact(nested);
|
|
43
|
+
}
|
|
44
|
+
return output;
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
export function hookOutput(event, context = "") {
|
|
49
|
+
const result = { continue: true, suppressOutput: true };
|
|
50
|
+
const name = event === "context"
|
|
51
|
+
? "SessionStart"
|
|
52
|
+
: event === "file-context"
|
|
53
|
+
? "PreToolUse"
|
|
54
|
+
: event === "session-init"
|
|
55
|
+
? "UserPromptSubmit"
|
|
56
|
+
: "";
|
|
57
|
+
if (name && context) {
|
|
58
|
+
result.hookSpecificOutput = {
|
|
59
|
+
hookEventName: name,
|
|
60
|
+
additionalContext: context,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
async function preferences(cloud) {
|
|
66
|
+
try {
|
|
67
|
+
if (cloud.preferences)
|
|
68
|
+
return record(await cloud.preferences());
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
/* use defaults */
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
automaticCapture: true,
|
|
75
|
+
captureMode: "balanced",
|
|
76
|
+
includeToolResults: false,
|
|
77
|
+
contextItems: 20,
|
|
78
|
+
contextMaxChars: 24_000,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export async function executeHook(adapter, event, value, cloud = new VaultCloudClient(), home = vaultHome()) {
|
|
82
|
+
const input = record(value);
|
|
83
|
+
const toolName = string(input.tool_name, input.toolName);
|
|
84
|
+
if (input.private === true ||
|
|
85
|
+
input.is_private === true ||
|
|
86
|
+
/vault[_-]go|vault[_-]mem/.test(toolName)) {
|
|
87
|
+
return hookOutput(event);
|
|
88
|
+
}
|
|
89
|
+
const prefs = await preferences(cloud);
|
|
90
|
+
const reading = event === "context" || event === "file-context";
|
|
91
|
+
if (!reading && prefs.automaticCapture !== true)
|
|
92
|
+
return hookOutput(event);
|
|
93
|
+
const tool = record(input.tool_input ?? input.toolInput);
|
|
94
|
+
const file = string(tool.file_path, tool.path, tool.notebook_path);
|
|
95
|
+
const modifies = /write|edit|patch|create|delete|move|rename/i.test(toolName);
|
|
96
|
+
if (!reading && prefs.captureMode === "focused" && event !== "summarize")
|
|
97
|
+
return hookOutput(event);
|
|
98
|
+
if (event === "observation" && prefs.captureMode === "balanced" && !modifies)
|
|
99
|
+
return hookOutput(event);
|
|
100
|
+
if (event === "file-context" && !file)
|
|
101
|
+
return hookOutput(event);
|
|
102
|
+
const sessionId = string(input.session_id, input.sessionId, input.thread_id, input.conversation_id);
|
|
103
|
+
if (!reading && !sessionId)
|
|
104
|
+
return hookOutput(event);
|
|
105
|
+
const cwd = root(string(input.cwd, input.workspace) || process.cwd());
|
|
106
|
+
const projects = await cloud.projects();
|
|
107
|
+
if (!Array.isArray(projects))
|
|
108
|
+
throw new Error("Resposta de projetos inválida");
|
|
109
|
+
let project = projects.find((item) => item &&
|
|
110
|
+
typeof item === "object" &&
|
|
111
|
+
resolve(String(item.rootPath || "")) === cwd);
|
|
112
|
+
if (!project && reading)
|
|
113
|
+
return hookOutput(event);
|
|
114
|
+
if (!project) {
|
|
115
|
+
project = record(await cloud.createProject({
|
|
116
|
+
name: (basename(cwd) || "workspace").padEnd(3, "_").slice(0, 80),
|
|
117
|
+
rootPath: cwd,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
if (typeof project.id !== "string")
|
|
121
|
+
throw new Error("Projeto inválido");
|
|
122
|
+
const limits = {
|
|
123
|
+
projectId: project.id,
|
|
124
|
+
limit: 20,
|
|
125
|
+
maxChars: 24_000,
|
|
126
|
+
};
|
|
127
|
+
if (reading) {
|
|
128
|
+
const result = record(event === "file-context"
|
|
129
|
+
? await cloud.fileContext({
|
|
130
|
+
...limits,
|
|
131
|
+
paths: [...new Set([file, resolve(cwd, file)])],
|
|
132
|
+
})
|
|
133
|
+
: await cloud.context(limits));
|
|
134
|
+
return hookOutput(event, string(result.context).slice(0, 24_000));
|
|
135
|
+
}
|
|
136
|
+
const session = record(await cloud.startSession({
|
|
137
|
+
projectId: project.id,
|
|
138
|
+
externalSessionId: sessionId,
|
|
139
|
+
platformSource: adapter,
|
|
140
|
+
}));
|
|
141
|
+
if (typeof session.id !== "string")
|
|
142
|
+
throw new Error("Sessão inválida");
|
|
143
|
+
const prompt = string(input.prompt, input.user_prompt);
|
|
144
|
+
const response = string(input.last_assistant_message, input.response, input.summary);
|
|
145
|
+
const rawContent = event === "session-init"
|
|
146
|
+
? prompt
|
|
147
|
+
: event === "summarize"
|
|
148
|
+
? response
|
|
149
|
+
: JSON.stringify(redact({
|
|
150
|
+
toolName,
|
|
151
|
+
toolInput: tool,
|
|
152
|
+
}));
|
|
153
|
+
const content = String(redact(rawContent));
|
|
154
|
+
if (content.trim()) {
|
|
155
|
+
const eventType = event === "session-init"
|
|
156
|
+
? "user_prompt"
|
|
157
|
+
: event === "summarize"
|
|
158
|
+
? "session_summary"
|
|
159
|
+
: "tool_use";
|
|
160
|
+
const sourceEventId = createHash("sha256")
|
|
161
|
+
.update(JSON.stringify([
|
|
162
|
+
adapter,
|
|
163
|
+
event,
|
|
164
|
+
cwd,
|
|
165
|
+
sessionId,
|
|
166
|
+
input.tool_use_id,
|
|
167
|
+
input.turn_id,
|
|
168
|
+
input.timestamp,
|
|
169
|
+
]))
|
|
170
|
+
.digest("hex");
|
|
171
|
+
await cloud.event({
|
|
172
|
+
projectId: project.id,
|
|
173
|
+
sessionId: session.id,
|
|
174
|
+
sourceAdapter: adapter,
|
|
175
|
+
sourceEventId,
|
|
176
|
+
eventType,
|
|
177
|
+
payload: {
|
|
178
|
+
title: toolName ||
|
|
179
|
+
(event === "summarize" ? "Resumo da sessão" : "Solicitação do usuário"),
|
|
180
|
+
content,
|
|
181
|
+
filesRead: !modifies && file ? [file] : [],
|
|
182
|
+
filesModified: modifies && file ? [file] : [],
|
|
183
|
+
},
|
|
184
|
+
occurredAtEpoch: Date.now(),
|
|
185
|
+
});
|
|
186
|
+
enqueueGeneration(home, {
|
|
187
|
+
id: sourceEventId,
|
|
188
|
+
projectId: project.id,
|
|
189
|
+
sessionId: session.id,
|
|
190
|
+
text: content.slice(0, 16_000),
|
|
191
|
+
title: toolName ||
|
|
192
|
+
(event === "summarize" ? "Resumo da sessão" : "Solicitação do usuário"),
|
|
193
|
+
kind: event === "summarize" ? "summary" : event === "session-init" ? "prompt" : "observation",
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (event === "summarize")
|
|
197
|
+
await cloud.endSession(session.id);
|
|
198
|
+
return hookOutput(event);
|
|
199
|
+
}
|
|
200
|
+
export async function readHookInput(stream = process.stdin) {
|
|
201
|
+
const chunks = [];
|
|
202
|
+
let size = 0;
|
|
203
|
+
for await (const chunk of stream) {
|
|
204
|
+
const buffer = Buffer.from(chunk);
|
|
205
|
+
size += buffer.length;
|
|
206
|
+
if (size > 1_000_000)
|
|
207
|
+
throw new Error("Hook excede o limite de entrada");
|
|
208
|
+
chunks.push(buffer);
|
|
209
|
+
}
|
|
210
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
|
|
211
|
+
}
|
|
212
|
+
export async function runHook(adapter, event) {
|
|
213
|
+
if (!HOOK_EVENTS.includes(event)) {
|
|
214
|
+
throw new Error("Evento de hook desconhecido.");
|
|
215
|
+
}
|
|
216
|
+
if (event === "context") {
|
|
217
|
+
try {
|
|
218
|
+
const { startLocal } = await import("./local-install.js");
|
|
219
|
+
await startLocal();
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
/* dashboard already running or unavailable */
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const result = await executeHook(adapter === "auto" ? "claude" : adapter, event, await readHookInput());
|
|
226
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
227
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
3
|
import { cliArguments, helpText, resolveCliMode, runEngine, runLocal, runLogin, runSetup } from './cli.js';
|
|
4
|
+
import { HOOK_EVENTS, runHook } from './hooks.js';
|
|
4
5
|
import { createVaultGoServer, VERSION } from './server.js';
|
|
5
6
|
const { argument, options } = cliArguments(process.argv.slice(2));
|
|
6
7
|
const mode = resolveCliMode(argument, process.stdin.isTTY === true, process.stderr.isTTY === true);
|
|
@@ -32,6 +33,15 @@ async function main() {
|
|
|
32
33
|
process.exitCode = await runEngine(options);
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
36
|
+
if (mode === 'hook') {
|
|
37
|
+
const adapter = options.find((item) => !item.startsWith('--')) ?? 'auto';
|
|
38
|
+
const event = options.filter((item) => !item.startsWith('--'))[1];
|
|
39
|
+
if (!event || !HOOK_EVENTS.includes(event)) {
|
|
40
|
+
throw new Error('Uso: vault-go hook <adaptador> context|session-init|observation|file-context|summarize');
|
|
41
|
+
}
|
|
42
|
+
await runHook(adapter, event);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
35
45
|
const server = createVaultGoServer();
|
|
36
46
|
const transport = new StdioServerTransport();
|
|
37
47
|
await server.connect(transport);
|
package/dist/installer.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ export declare const MCP_SERVER_NAME = "vault-go";
|
|
|
2
2
|
export declare const MCP_SERVER_COMMAND: readonly ["bunx", "--bun", "vault-go@latest", "serve"];
|
|
3
3
|
export declare const MCP_CLIENTS: readonly ["codex", "claude", "claude-desktop", "cursor", "vscode", "copilot", "windsurf", "roo", "opencode"];
|
|
4
4
|
export type McpClient = (typeof MCP_CLIENTS)[number];
|
|
5
|
+
export declare function quoteShell(value: string): string;
|
|
6
|
+
export declare function hookEntryPath(configHome?: string): string;
|
|
7
|
+
export declare function hookCommand(adapter: string, event: string, configHome?: string): string;
|
|
5
8
|
export interface DetectedClient {
|
|
6
9
|
client: McpClient;
|
|
7
10
|
detected: boolean;
|
package/dist/installer.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync, } from 'node:fs';
|
|
2
2
|
import { homedir, platform } from 'node:os';
|
|
3
3
|
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { vaultHome } from './config.js';
|
|
7
|
+
import { stageLocalRuntime } from './local-install.js';
|
|
5
8
|
export const MCP_SERVER_NAME = 'vault-go';
|
|
6
9
|
export const MCP_SERVER_COMMAND = ['bunx', '--bun', 'vault-go@latest', 'serve'];
|
|
7
10
|
export const MCP_CLIENTS = [
|
|
@@ -15,6 +18,75 @@ export const MCP_CLIENTS = [
|
|
|
15
18
|
'roo',
|
|
16
19
|
'opencode',
|
|
17
20
|
];
|
|
21
|
+
const HOOK_OWNER = 'Vault Go: captura e contexto';
|
|
22
|
+
const HOOK_EVENTS = {
|
|
23
|
+
SessionStart: 'context',
|
|
24
|
+
UserPromptSubmit: 'session-init',
|
|
25
|
+
PreToolUse: 'file-context',
|
|
26
|
+
PostToolUse: 'observation',
|
|
27
|
+
Stop: 'summarize',
|
|
28
|
+
};
|
|
29
|
+
export function quoteShell(value) {
|
|
30
|
+
return "'" + value.replaceAll("'", "'\\''") + "'";
|
|
31
|
+
}
|
|
32
|
+
export function hookEntryPath(configHome = vaultHome()) {
|
|
33
|
+
const staged = join(configHome, 'local-runtime', 'dist', 'index.js');
|
|
34
|
+
if (existsSync(staged))
|
|
35
|
+
return staged;
|
|
36
|
+
return join(dirname(fileURLToPath(import.meta.url)), 'index.js');
|
|
37
|
+
}
|
|
38
|
+
export function hookCommand(adapter, event, configHome = vaultHome()) {
|
|
39
|
+
return `${quoteShell(process.execPath)} ${quoteShell(hookEntryPath(configHome))} hook ${adapter} ${event}`;
|
|
40
|
+
}
|
|
41
|
+
function hookManifest(adapter, configHome = vaultHome()) {
|
|
42
|
+
return {
|
|
43
|
+
hooks: Object.fromEntries(Object.entries(HOOK_EVENTS).map(([name, event]) => [
|
|
44
|
+
name,
|
|
45
|
+
[
|
|
46
|
+
{
|
|
47
|
+
hooks: [
|
|
48
|
+
{
|
|
49
|
+
type: 'command',
|
|
50
|
+
command: hookCommand(adapter, event, configHome),
|
|
51
|
+
timeout: event === 'context' ? 60 : 20,
|
|
52
|
+
statusMessage: HOOK_OWNER,
|
|
53
|
+
...(event === 'observation' || event === 'summarize' || event === 'file-context'
|
|
54
|
+
? { async: true }
|
|
55
|
+
: {}),
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
])),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function installClientHooks(client, userHome, configHome = vaultHome()) {
|
|
64
|
+
try {
|
|
65
|
+
stageLocalRuntime(configHome);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
/* tests and incomplete installs still write the hook command */
|
|
69
|
+
}
|
|
70
|
+
const path = join(userHome, client === 'claude' ? '.claude/settings.json' : '.codex/hooks.json');
|
|
71
|
+
const config = readJsonObject(path);
|
|
72
|
+
const hooks = { ...(config.hooks && typeof config.hooks === 'object' && !Array.isArray(config.hooks) ? config.hooks : {}) };
|
|
73
|
+
const desired = hookManifest(client, configHome).hooks;
|
|
74
|
+
for (const event of Object.keys(HOOK_EVENTS)) {
|
|
75
|
+
const current = hooks[event];
|
|
76
|
+
const retained = Array.isArray(current)
|
|
77
|
+
? current
|
|
78
|
+
.map((group) => {
|
|
79
|
+
if (!group || typeof group !== 'object' || !Array.isArray(group.hooks))
|
|
80
|
+
return group;
|
|
81
|
+
const filtered = group.hooks.filter((hook) => !hook || typeof hook !== 'object' || hook.statusMessage !== HOOK_OWNER);
|
|
82
|
+
return { ...group, hooks: filtered };
|
|
83
|
+
})
|
|
84
|
+
.filter((group) => !group || typeof group !== 'object' || !Array.isArray(group.hooks) || group.hooks.length > 0)
|
|
85
|
+
: [];
|
|
86
|
+
hooks[event] = [...retained, ...(desired[event] || [])];
|
|
87
|
+
}
|
|
88
|
+
atomicWriteJson(path, { ...config, hooks });
|
|
89
|
+
}
|
|
18
90
|
const runCommand = (command, args) => spawnSync(command, [...args], { stdio: 'ignore' });
|
|
19
91
|
function readJsonObject(path) {
|
|
20
92
|
if (!existsSync(path))
|
|
@@ -83,10 +155,12 @@ export function installMcpClient(client, options = {}) {
|
|
|
83
155
|
const cwd = options.cwd ?? process.cwd();
|
|
84
156
|
const runner = options.runner ?? runCommand;
|
|
85
157
|
if (client === 'codex') {
|
|
86
|
-
|
|
158
|
+
const result = installViaCli(client, 'codex', ['mcp', 'get', MCP_SERVER_NAME], ['mcp', 'add', MCP_SERVER_NAME, '--', ...MCP_SERVER_COMMAND], runner);
|
|
159
|
+
installClientHooks('codex', home);
|
|
160
|
+
return result;
|
|
87
161
|
}
|
|
88
162
|
if (client === 'claude') {
|
|
89
|
-
|
|
163
|
+
const result = installViaCli(client, 'claude', ['mcp', 'get', MCP_SERVER_NAME], [
|
|
90
164
|
'mcp',
|
|
91
165
|
'add',
|
|
92
166
|
'--scope',
|
|
@@ -97,6 +171,8 @@ export function installMcpClient(client, options = {}) {
|
|
|
97
171
|
'--',
|
|
98
172
|
...MCP_SERVER_COMMAND,
|
|
99
173
|
], runner);
|
|
174
|
+
installClientHooks('claude', home);
|
|
175
|
+
return result;
|
|
100
176
|
}
|
|
101
177
|
if (client === 'vscode') {
|
|
102
178
|
const payload = JSON.stringify({
|
package/dist/local-dashboard.js
CHANGED
|
@@ -5,7 +5,7 @@ export function dashboardHtml() {
|
|
|
5
5
|
|
|
6
6
|
/* Same identity tokens and glass surfaces as the Vault portal. */
|
|
7
7
|
:root{--c-canvas:8 11 18;--c-surface:24 29 45;--c-surface-2:32 38 56;--c-line:53 61 81;--c-fg:241 243 252;--c-muted:172 180 203;--c-faint:148 159 187;--c-accent:185 182 255;color:rgb(var(--c-fg));background:rgb(var(--c-canvas))}body{background:radial-gradient(ellipse at 85% 8%,#534ca02e,transparent 45%),linear-gradient(145deg,#080b12,#101321 58%,#080b12)}[hidden]{display:none!important}.shell{grid-template-columns:258px 1fr;max-width:1600px}.rail{background:linear-gradient(160deg,#1c2135f5,#101522f5);border-color:#cdd8ff26}.brand{font-size:15px;letter-spacing:-.3px;gap:10px;white-space:nowrap}.brand img,.onboarding-logo{object-fit:contain;filter:drop-shadow(0 5px 12px #7775c533)}.brand-sub{display:block;font-size:9px;letter-spacing:1.7px;color:#acb4cb;margin-top:7px}.card,.panel,.onboarding,.step,.notice{background:linear-gradient(135deg,#272c3ed1,#131724e0);border-color:#cdd8ff26;box-shadow:0 24px 70px -30px #0009,inset 0 1px 0 #ffffff0c;backdrop-filter:blur(28px) saturate(135%)}.nav a.active,.nav a:hover{background:#b9b6ff20;color:#d3d1ff}.eyebrow,.step-number,.empty-symbol{color:#b9b6ff}p,.card-label{color:#acb4cb}.card-note,.row-meta,.rail-footer,.rail-label,.footer{color:#949fbb}.pulse{color:#b9b6ff;border-color:#b9b6ff30;background:#b9b6ff0d}.button{background:#202638;color:#f1f3fc;border-color:#cdd8ff26;text-decoration:none;justify-content:center}.button:hover{border-color:#b9b6ff88}.primary{background:linear-gradient(135deg,#c8c5ff,#9d9bed);color:#15152f;border-color:transparent;font-weight:650;padding:13px 20px;font-size:14px}.primary:hover{background:#d3d1ff}.onboarding{border:1px solid #cdd8ff26;border-radius:20px;overflow:hidden;margin-top:30px;animation:arrive .6s ease both}.onboarding-hero{position:relative;padding:38px;background:radial-gradient(ellipse at 100% 0%,#7370d02c,transparent 62%)}.onboarding-logo{width:86px;height:86px;margin-bottom:24px}.onboarding h1{font-size:36px;max-width:600px;line-height:1.12;margin:12px 0 18px}.onboarding-hero>p{max-width:570px;font-size:14px;line-height:1.8}.actions{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:25px}.link{color:#d3d1ff;text-decoration:none;font-size:12px}.link:hover{text-decoration:underline}.onboarding-hint{font-size:11px!important;color:#949fbb;margin-top:15px!important}.steps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border-top:1px solid #cdd8ff26}.step{padding:25px;background:#080b122b;box-shadow:none;border-right:1px solid #cdd8ff16}.step:last-child{border:0}.step-number{font-family:monospace;font-size:11px;margin-bottom:13px}.step h2{margin-bottom:9px}.step p{font-size:12px}.terminal-fallback{padding:22px 30px;border-top:1px solid #cdd8ff26;background:#080b1233}.terminal-fallback p{font-size:12px}.terminal-fallback code{display:block;margin-top:10px;font-size:12px;color:#d3d1ff;overflow-wrap:anywhere}.connection-message{margin-top:18px;font-size:12px;color:#d3d1ff;min-height:18px}.next-step{display:flex;justify-content:space-between;align-items:center;gap:20px;margin-top:22px;padding:23px;border:1px solid #b9b6ff30;border-radius:14px;background:#b9b6ff09}.next-step h2{margin-bottom:8px}.next-step p{font-size:12px}.next-step .actions{margin:0;flex-shrink:0}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible{outline-color:#b9b6ff}.notice code{color:#d3d1ff}.search{border-color:#cdd8ff26;background:#0307117a}.breadcrumb b{color:#f1f3fc}.rail-footer .dot{background:#b9b6ff}.footer{margin-top:28px}@media(max-width:1100px){.shell{grid-template-columns:225px 1fr}.brand{font-size:13px}.main{padding:25px}.onboarding-hero{padding:30px}.next-step{align-items:flex-start;flex-direction:column}}@media(max-width:680px){.shell{display:block}.brand{font-size:16px}.onboarding{margin-top:22px}.onboarding-hero{padding:26px}.onboarding h1{font-size:30px}.onboarding-logo{width:65px;height:65px;margin-bottom:15px}.steps{grid-template-columns:1fr}.step{padding:20px 26px;border-right:0;border-bottom:1px solid #cdd8ff16}.step-number{margin-bottom:8px}.terminal-fallback{padding:22px 26px}.main{padding:20px}.next-step .actions{flex-shrink:1}.onboarding .actions .button{width:100%}.onboarding .actions .link{padding:8px 0}.card-value{overflow-wrap:anywhere}.nav{padding-bottom:2px}}
|
|
8
|
-
.danger{border-color:#ed9a9a66;color:#ffb5b5;background:#53272d55}.danger:hover{border-color:#ffb5b5}.danger-title{color:#ffb5b5}.pagination{display:flex;gap:9px;align-items:center;flex-wrap:wrap;border-top:1px solid #cdd8ff20;margin-top:15px;padding-top:15px;color:#acb4cb;font-size:11px}.pagination label{display:flex;align-items:center;gap:7px}.pagination .page-summary{flex:1;min-width:95px}.pagination select{padding:7px}.pagination .button{padding:7px 10px}.pagination button:disabled{cursor:default}.row-open{background:transparent;border:0;text-align:left;padding:0;color:#d3d1ff;cursor:pointer;font:inherit}.row-open:hover{text-decoration:underline}.row-actions{padding-top:5px}.detail-dialog{color:#f1f3fc;background:linear-gradient(135deg,#272c3e,#131724);border:1px solid #cdd8ff40;border-radius:18px;max-width:800px;width:calc(100% - 32px);max-height:85vh;padding:26px;box-shadow:0 30px 100px #0009}.detail-dialog::backdrop{background:#050710bd;backdrop-filter:blur(5px)}.detail-dialog h2{font-size:20px;overflow-wrap:anywhere}.detail-fields{margin:0}.detail-fields dt{color:#acb4cb;font-size:11px;margin:20px 0 8px}.detail-fields dd{margin:0;font-size:13px;line-height:1.7;white-space:pre-wrap;overflow-wrap:anywhere}.detail-fields pre{font:inherit;white-space:pre-wrap;margin:0}.engine-options{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-top:18px}.engine-keys{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-top:16px}.engine-keys label{display:block;font-size:12px;margin-bottom:8px}.engine-keys input{width:100%;background:#0307117a;color:#f1f3fc;border:1px solid #cdd8ff26;border-radius:9px;padding:10px 12px;font:inherit;font-size:13px;margin-bottom:8px}.engine-option{display:block;padding:17px;border:1px solid #cdd8ff26;border-radius:10px;background:#080b1233;cursor:pointer}.engine-option:has(input:checked){border-color:#b9b6ff;background:#b9b6ff12}.engine-option:has(input:disabled){opacity:.7;cursor:default}.engine-option input{accent-color:#b9b6ff;margin-right:8px}.engine-option strong{font-size:12px}.engine-option p{font-size:11px;margin-top:9px}.engine-message{margin-top:13px;font-size:12px;color:#d3d1ff}.context-editor{margin-top:20px}.context-editor label{display:block;font-size:12px;margin-bottom:8px}.context-editor textarea{width:100%;min-height:130px;resize:vertical;background:#0307117a;color:#f1f3fc;border:1px solid #cdd8ff26;border-radius:9px;padding:12px;font:inherit;font-size:13px}.context-editor textarea:focus-visible{outline:2px solid #b9b6ff;outline-offset:3px}@media(max-width:800px){.engine-options{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){.detail-dialog::backdrop{backdrop-filter:none}}</style><script src="/app.js" defer></script></head><body><div class="shell"><aside class="rail"><div class="brand"><img src="/logo.png" alt="" width="46" height="46"><div>AI Vault Memory<span class="brand-sub">LOCAL AGENT</span></div></div><div class="rail-label" data-i18n="workspace">WORKSPACE</div><nav class="nav" aria-label="Navigation"><a class="active" href="#overview"><span class="icon">◈</span><span data-i18n="overview">Overview</span></a><a href="#activity"><span class="icon">⌁</span><span data-i18n="activity">Activity</span></a><a href="#memories"><span class="icon">▤</span><span data-i18n="memories">Memories</span></a><a href="#context"><span class="icon">✧</span><span data-i18n="contextEngine">Context engine</span></a><a href="#projects"><span class="icon">▱</span><span data-i18n="projects">Projects</span></a></nav><div class="rail-footer"><span class="dot"></span><span data-i18n="localWorkspace">Your local workspace</span><br><span data-i18n="privacy">Activity metadata stays local.</span></div></aside><main class="main" id="overview"><header class="top"><div class="breadcrumb">Vault <span aria-hidden="true"> / </span><b data-i18n="localMonitor">Local monitor</b></div><div class="controls"><select id="locale" aria-label="Language"><option value="en">English</option><option value="pt">Português</option><option value="es">Español</option></select><button class="button" id="logout" type="button" hidden data-i18n="logout">Sign out of dashboard</button><button class="button" id="reconnect" type="button" hidden data-i18n="reconnect">Reconnect account</button><button class="button" id="refresh" type="button"><span aria-hidden="true">↻</span><span data-i18n="refresh">Refresh cloud</span></button></div></header><div id="error" class="error" role="status" aria-live="polite"></div><section class="onboarding" id="onboarding"><div class="onboarding-hero"><img class="onboarding-logo" src="/logo.png" alt="" width="86" height="86"><div class="eyebrow" data-i18n="connectEyebrow">AI VAULT MEMORY · LOCAL</div><h1 data-i18n="connectTitle">Your memory. Connected to this machine.</h1><p data-i18n="connectIntro">Connect your Vault account to see your memories, projects and recent activity in one place.</p><div class="actions"><button type="button" class="button primary" id="connect" data-i18n="connect">Connect to Vault</button><a class="link" href="https://vault.resolveup.com.br/app" target="_blank" rel="noopener noreferrer" data-i18n="openPortal">Open Vault portal ↗</a></div><p class="onboarding-hint" data-i18n="connectHint">Sign in securely in a separate window. Your password stays with the Vault portal.</p><div class="connection-message" id="connection-message" role="status" aria-live="polite"></div><a id="auth-fallback" class="button" hidden target="_blank" rel="noopener noreferrer" data-i18n="continueAuth">Continue sign-in ↗</a></div><div class="steps"><article class="step"><div class="step-number">01 / VAULT</div><h2 data-i18n="stepAccount">Connect your account</h2><p data-i18n="stepAccountBody">Sign in to Vault and authorize this local agent.</p></article><article class="step"><div class="step-number">02 / MCP</div><h2 data-i18n="stepClient">Connect your AI client</h2><p data-i18n="stepClientBody">Configure Vault MCP in Codex, Cursor or another compatible client.</p></article><article class="step"><div class="step-number">03 / MEMORY</div><h2 data-i18n="stepMemory">Build your first memory</h2><p data-i18n="stepMemoryBody">Ask your connected client to save useful context, then follow its activity here.</p></article></div><div class="terminal-fallback"><button class="button" id="verify-connection" type="button" data-i18n="verifyConnection">Already signed in? Check connection</button><p style="margin-top:16px" data-i18n="terminalFallback">Already connected through the CLI? Open a secure local session:</p><code>bunx --bun vault-go@latest local open</code></div></section><div id="dashboard" hidden><div class="welcome"><div><div class="eyebrow" data-i18n="eyebrow">YOUR KNOWLEDGE, CONNECTED</div><h1 data-i18n="title">Your local workspace.</h1><p data-i18n="subtitle">Your Vault account, memories and activity on this machine.</p></div><img src="/logo.png" alt="" width="56" height="56"></div><div id="session-notice" hidden></div><section class="cards" aria-label="Status"><article class="card"><div class="card-label"><span data-i18n="localService">Local service</span><span aria-hidden="true">◈</span></div><div class="card-value" id="local-status">—</div><div class="card-note" id="local-note">localhost:38850</div></article><article class="card"><div class="card-label"><span data-i18n="cloudConnection">Cloud connection</span><span aria-hidden="true">↗</span></div><div class="card-value" id="cloud-status">—</div><div class="card-note" id="cloud-note">—</div></article><article class="card"><div class="card-label"><span data-i18n="recentOperations">Recent operations</span><span aria-hidden="true">⌁</span></div><div class="card-value" id="activity-total">—</div><div class="card-note" data-i18n="operationsNote">Recent MCP request metadata</div></article></section><section class="next-step" id="first-memory"><div><h2 data-i18n="firstTitle">Create your first memory</h2><p data-i18n="firstBody">Connect Vault MCP to your AI client, then ask it to save context. Your memories will appear here after a cloud refresh.</p><code id="mcp-command" style="display:block;margin-top:12px;font-size:12px;overflow-wrap:anywhere"></code><p data-i18n="rememberPrompt" style="margin-top:10px"></p></div><div class="actions"><a class="button" href="https://vault.resolveup.com.br/mcp" target="_blank" rel="noopener noreferrer" data-i18n="setupMcp">Set up MCP ↗</a><a class="link" href="https://vault.resolveup.com.br/app/memory" target="_blank" rel="noopener noreferrer" data-i18n="openMemories">Open memories ↗</a></div></section><div class="layout"><section class="panel wide" id="security"><div class="panel-head"><h2 data-i18n="securityTitle">Security on this machine</h2></div><div id="security-facts"></div><div class="actions"><button class="button danger" id="disconnect-device" type="button" data-i18n="disconnectDevice">Disconnect device</button></div></section><section class="panel wide" id="context"><div class="panel-head"><h2 data-i18n="contextEngine">Context engine</h2></div><p data-i18n="engineIntro">Choose how Vault prepares your context.</p><div class="engine-options" id="engine-options" role="radiogroup" aria-label="Context engine"></div><div class="engine-keys" id="engine-keys"></div><div class="engine-message" id="engine-message" role="status" aria-live="polite"></div><div class="context-editor"><label for="context-text" data-i18n="contextText">Text to prepare</label><textarea id="context-text" maxlength="16000" aria-describedby="context-note"></textarea><p id="context-note" data-i18n="contextNote"></p><div class="actions"><button class="button primary" id="context-preview" type="button" data-i18n="contextPreview"></button><button class="button" id="context-stop" type="button" data-i18n="cancel" hidden></button></div><p id="context-progress" role="status" aria-live="polite"></p></div></section><section class="panel" id="activity"><div class="panel-head"><h2 data-i18n="activity">Activity</h2><span class="count" id="activity-count">0</span></div><input class="search" id="activity-search" type="search" data-placeholder="searchActivity" aria-label="Search activity"><div id="activity-list"></div><div class="pagination" id="activity-pagination"></div></section><section class="panel" id="memories"><div class="panel-head"><h2 data-i18n="memories">Memories</h2><span class="count" id="memory-count">0</span></div><input class="search" id="memory-search" type="search" data-placeholder="searchMemories" aria-label="Search memories"><div id="memory-list"></div><div class="pagination" id="memory-pagination"></div></section><section class="panel wide" id="projects"><div class="panel-head"><h2 data-i18n="projects">Projects</h2><span class="count" id="project-count">0</span></div><input class="search" id="project-search" type="search" data-placeholder="searchProjects" aria-label="Search projects"><div id="project-list"></div><div class="pagination" id="project-pagination"></div></section></div></div><footer class="footer"><span data-i18n="footer">Vault Local · Context with a place to call home.</span><span id="updated" aria-live="off">—</span></footer></main></div><dialog class="detail-dialog" id="detail-dialog" aria-labelledby="detail-title"><div class="panel-head"><h2 id="detail-title"></h2><button class="button" type="button" id="detail-copy" hidden data-i18n="copyResult">Copy result</button><button class="button" type="button" id="detail-close" data-i18n="close">Close</button></div><dl class="detail-fields" id="detail-fields"></dl></dialog><dialog class="detail-dialog" id="logout-dialog" aria-labelledby="logout-title" aria-describedby="logout-description"><h2 id="logout-title" data-i18n="logout"></h2><p id="logout-description" data-i18n="logoutDescription" style="margin-top:16px"></p><div class="actions"><button class="button" type="button" id="logout-cancel" data-i18n="cancel"></button><button class="button primary" type="button" id="logout-confirm" data-i18n="logoutConfirm"></button></div></dialog><dialog class="detail-dialog" id="context-confirm-dialog" aria-labelledby="context-confirm-title" aria-describedby="context-confirm-note"><h2 id="context-confirm-title" data-i18n="contextPreview"></h2><p id="context-confirm-engine" style="margin-top:16px"></p><p id="context-confirm-billing"></p><p id="context-confirm-note" data-i18n="contextConsentNote" style="margin-top:16px"></p><pre id="context-confirm-text" style="white-space:pre-wrap;overflow-wrap:anywhere;max-height:32vh;overflow:auto;font-size:12px;line-height:1.7"></pre><label style="display:flex;gap:10px;align-items:flex-start;font-size:12px"><input id="context-consent" type="checkbox"><span data-i18n="contextConsent"></span></label><div class="actions"><button class="button" id="context-cancel" type="button" data-i18n="cancel"></button><button class="button primary" id="context-generate" type="button" disabled data-i18n="contextGenerate"></button></div></dialog><dialog class="detail-dialog" id="disconnect-dialog" aria-labelledby="disconnect-title" aria-describedby="disconnect-description"><h2 class="danger-title" id="disconnect-title" data-i18n="disconnectDevice"></h2><p id="disconnect-description" data-i18n="disconnectDescription" style="margin-top:16px"></p><p id="disconnect-error" class="error" role="alert"></p><div class="actions"><button class="button" type="button" id="disconnect-cancel" data-i18n="cancel"></button><button class="button danger" type="button" id="disconnect-confirm" data-i18n="disconnectConfirm"></button></div></dialog></body></html>`;
|
|
8
|
+
.danger{border-color:#ed9a9a66;color:#ffb5b5;background:#53272d55}.danger:hover{border-color:#ffb5b5}.danger-title{color:#ffb5b5}.pagination{display:flex;gap:9px;align-items:center;flex-wrap:wrap;border-top:1px solid #cdd8ff20;margin-top:15px;padding-top:15px;color:#acb4cb;font-size:11px}.pagination label{display:flex;align-items:center;gap:7px}.pagination .page-summary{flex:1;min-width:95px}.pagination select{padding:7px}.pagination .button{padding:7px 10px}.pagination button:disabled{cursor:default}.row-open{background:transparent;border:0;text-align:left;padding:0;color:#d3d1ff;cursor:pointer;font:inherit}.row-open:hover{text-decoration:underline}.row-actions{padding-top:5px}.detail-dialog{color:#f1f3fc;background:linear-gradient(135deg,#272c3e,#131724);border:1px solid #cdd8ff40;border-radius:18px;max-width:800px;width:calc(100% - 32px);max-height:85vh;padding:26px;box-shadow:0 30px 100px #0009}.detail-dialog::backdrop{background:#050710bd;backdrop-filter:blur(5px)}.detail-dialog h2{font-size:20px;overflow-wrap:anywhere}.detail-fields{margin:0}.detail-fields dt{color:#acb4cb;font-size:11px;margin:20px 0 8px}.detail-fields dd{margin:0;font-size:13px;line-height:1.7;white-space:pre-wrap;overflow-wrap:anywhere}.detail-fields pre{font:inherit;white-space:pre-wrap;margin:0}.engine-options{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-top:18px}.engine-keys{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-top:16px}.engine-keys label{display:block;font-size:12px;margin-bottom:8px}.engine-keys input{width:100%;background:#0307117a;color:#f1f3fc;border:1px solid #cdd8ff26;border-radius:9px;padding:10px 12px;font:inherit;font-size:13px;margin-bottom:8px}.engine-option{display:block;padding:17px;border:1px solid #cdd8ff26;border-radius:10px;background:#080b1233;cursor:pointer}.engine-option:has(input:checked){border-color:#b9b6ff;background:#b9b6ff12}.engine-option:has(input:disabled){opacity:.7;cursor:default}.engine-option input{accent-color:#b9b6ff;margin-right:8px}.engine-option strong{font-size:12px}.engine-option p{font-size:11px;margin-top:9px}.engine-message{margin-top:13px;font-size:12px;color:#d3d1ff}.context-editor{margin-top:20px}.context-editor label{display:block;font-size:12px;margin-bottom:8px}.context-editor textarea{width:100%;min-height:130px;resize:vertical;background:#0307117a;color:#f1f3fc;border:1px solid #cdd8ff26;border-radius:9px;padding:12px;font:inherit;font-size:13px}.context-editor textarea:focus-visible{outline:2px solid #b9b6ff;outline-offset:3px}@media(max-width:800px){.engine-options{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){.detail-dialog::backdrop{backdrop-filter:none}}</style><script src="/app.js" defer></script></head><body><div class="shell"><aside class="rail"><div class="brand"><img src="/logo.png" alt="" width="46" height="46"><div>AI Vault Memory<span class="brand-sub">LOCAL AGENT</span></div></div><div class="rail-label" data-i18n="workspace">WORKSPACE</div><nav class="nav" aria-label="Navigation"><a class="active" href="#overview"><span class="icon">◈</span><span data-i18n="overview">Overview</span></a><a href="#activity"><span class="icon">⌁</span><span data-i18n="activity">Activity</span></a><a href="#memories"><span class="icon">▤</span><span data-i18n="memories">Memories</span></a><a href="#context"><span class="icon">✧</span><span data-i18n="contextEngine">Context engine</span></a><a href="#projects"><span class="icon">▱</span><span data-i18n="projects">Projects</span></a></nav><div class="rail-footer"><span class="dot"></span><span data-i18n="localWorkspace">Your local workspace</span><br><span data-i18n="privacy">Activity metadata stays local.</span></div></aside><main class="main" id="overview"><header class="top"><div class="breadcrumb">Vault <span aria-hidden="true"> / </span><b data-i18n="localMonitor">Local monitor</b></div><div class="controls"><select id="locale" aria-label="Language"><option value="en">English</option><option value="pt">Português</option><option value="es">Español</option></select><button class="button" id="logout" type="button" hidden data-i18n="logout">Sign out of dashboard</button><button class="button" id="reconnect" type="button" hidden data-i18n="reconnect">Reconnect account</button><button class="button" id="refresh" type="button"><span aria-hidden="true">↻</span><span data-i18n="refresh">Refresh cloud</span></button></div></header><div id="error" class="error" role="status" aria-live="polite"></div><section class="onboarding" id="onboarding"><div class="onboarding-hero"><img class="onboarding-logo" src="/logo.png" alt="" width="86" height="86"><div class="eyebrow" data-i18n="connectEyebrow">AI VAULT MEMORY · LOCAL</div><h1 data-i18n="connectTitle">Your memory. Connected to this machine.</h1><p data-i18n="connectIntro">Connect your Vault account to see your memories, projects and recent activity in one place.</p><div class="actions"><button type="button" class="button primary" id="connect" data-i18n="connect">Connect to Vault</button><a class="link" href="https://vault.resolveup.com.br/app" target="_blank" rel="noopener noreferrer" data-i18n="openPortal">Open Vault portal ↗</a></div><p class="onboarding-hint" data-i18n="connectHint">Sign in securely in a separate window. Your password stays with the Vault portal.</p><div class="connection-message" id="connection-message" role="status" aria-live="polite"></div><a id="auth-fallback" class="button" hidden target="_blank" rel="noopener noreferrer" data-i18n="continueAuth">Continue sign-in ↗</a></div><div class="steps"><article class="step"><div class="step-number">01 / VAULT</div><h2 data-i18n="stepAccount">Connect your account</h2><p data-i18n="stepAccountBody">Sign in to Vault and authorize this local agent.</p></article><article class="step"><div class="step-number">02 / MCP</div><h2 data-i18n="stepClient">Connect your AI client</h2><p data-i18n="stepClientBody">Configure Vault MCP in Codex, Cursor or another compatible client.</p></article><article class="step"><div class="step-number">03 / MEMORY</div><h2 data-i18n="stepMemory">Build your first memory</h2><p data-i18n="stepMemoryBody">Ask your connected client to save useful context, then follow its activity here.</p></article></div><div class="terminal-fallback"><button class="button" id="verify-connection" type="button" data-i18n="verifyConnection">Already signed in? Check connection</button><p style="margin-top:16px" data-i18n="terminalFallback">Already connected through the CLI? Open a secure local session:</p><code>bunx --bun vault-go@latest local open</code></div></section><div id="dashboard" hidden><div class="welcome"><div><div class="eyebrow" data-i18n="eyebrow">YOUR KNOWLEDGE, CONNECTED</div><h1 data-i18n="title">Your local workspace.</h1><p data-i18n="subtitle">Your Vault account, memories and activity on this machine.</p></div><img src="/logo.png" alt="" width="56" height="56"></div><div id="session-notice" hidden></div><section class="cards" aria-label="Status"><article class="card"><div class="card-label"><span data-i18n="localService">Local service</span><span aria-hidden="true">◈</span></div><div class="card-value" id="local-status">—</div><div class="card-note" id="local-note">localhost:38850</div></article><article class="card"><div class="card-label"><span data-i18n="cloudConnection">Cloud connection</span><span aria-hidden="true">↗</span></div><div class="card-value" id="cloud-status">—</div><div class="card-note" id="cloud-note">—</div></article><article class="card"><div class="card-label"><span data-i18n="recentOperations">Recent operations</span><span aria-hidden="true">⌁</span></div><div class="card-value" id="activity-total">—</div><div class="card-note" data-i18n="operationsNote">Recent MCP request metadata</div></article></section><section class="next-step" id="first-memory"><div><h2 data-i18n="firstTitle">Create your first memory</h2><p data-i18n="firstBody">Connect Vault MCP to your AI client, then ask it to save context. Your memories will appear here after a cloud refresh.</p><code id="mcp-command" style="display:block;margin-top:12px;font-size:12px;overflow-wrap:anywhere"></code><p data-i18n="rememberPrompt" style="margin-top:10px"></p></div><div class="actions"><a class="button" href="https://vault.resolveup.com.br/mcp" target="_blank" rel="noopener noreferrer" data-i18n="setupMcp">Set up MCP ↗</a><a class="link" href="https://vault.resolveup.com.br/app/memory" target="_blank" rel="noopener noreferrer" data-i18n="openMemories">Open memories ↗</a></div></section><div class="layout"><section class="panel wide" id="security"><div class="panel-head"><h2 data-i18n="securityTitle">Security on this machine</h2></div><div id="security-facts"></div><div class="actions"><button class="button danger" id="disconnect-device" type="button" data-i18n="disconnectDevice">Disconnect device</button></div></section><section class="panel wide" id="context"><div class="panel-head"><h2 data-i18n="contextEngine">Context engine</h2></div><p data-i18n="engineIntro">Choose how Vault prepares your context.</p><div class="engine-options" id="engine-options" role="radiogroup" aria-label="Context engine"></div><div class="engine-options" id="model-options" role="radiogroup" aria-label="Context model"></div><div class="engine-keys" id="engine-keys"></div><div class="engine-message" id="engine-message" role="status" aria-live="polite"></div><div class="context-editor"><label for="context-text" data-i18n="contextText">Text to prepare</label><textarea id="context-text" maxlength="16000" aria-describedby="context-note"></textarea><p id="context-note" data-i18n="contextNote"></p><div class="actions"><button class="button primary" id="context-preview" type="button" data-i18n="contextPreview"></button><button class="button" id="context-stop" type="button" data-i18n="cancel" hidden></button></div><p id="context-progress" role="status" aria-live="polite"></p></div></section><section class="panel" id="activity"><div class="panel-head"><h2 data-i18n="activity">Activity</h2><span class="count" id="activity-count">0</span></div><input class="search" id="activity-search" type="search" data-placeholder="searchActivity" aria-label="Search activity"><div id="activity-list"></div><div class="pagination" id="activity-pagination"></div></section><section class="panel" id="memories"><div class="panel-head"><h2 data-i18n="memories">Memories</h2><span class="count" id="memory-count">0</span></div><input class="search" id="memory-search" type="search" data-placeholder="searchMemories" aria-label="Search memories"><div id="memory-list"></div><div class="pagination" id="memory-pagination"></div></section><section class="panel wide" id="projects"><div class="panel-head"><h2 data-i18n="projects">Projects</h2><span class="count" id="project-count">0</span></div><input class="search" id="project-search" type="search" data-placeholder="searchProjects" aria-label="Search projects"><div id="project-list"></div><div class="pagination" id="project-pagination"></div></section></div></div><footer class="footer"><span data-i18n="footer">Vault Local · Context with a place to call home.</span><span id="updated" aria-live="off">—</span></footer></main></div><dialog class="detail-dialog" id="detail-dialog" aria-labelledby="detail-title"><div class="panel-head"><h2 id="detail-title"></h2><button class="button" type="button" id="detail-copy" hidden data-i18n="copyResult">Copy result</button><button class="button" type="button" id="detail-close" data-i18n="close">Close</button></div><dl class="detail-fields" id="detail-fields"></dl></dialog><dialog class="detail-dialog" id="logout-dialog" aria-labelledby="logout-title" aria-describedby="logout-description"><h2 id="logout-title" data-i18n="logout"></h2><p id="logout-description" data-i18n="logoutDescription" style="margin-top:16px"></p><div class="actions"><button class="button" type="button" id="logout-cancel" data-i18n="cancel"></button><button class="button primary" type="button" id="logout-confirm" data-i18n="logoutConfirm"></button></div></dialog><dialog class="detail-dialog" id="context-confirm-dialog" aria-labelledby="context-confirm-title" aria-describedby="context-confirm-note"><h2 id="context-confirm-title" data-i18n="contextPreview"></h2><p id="context-confirm-engine" style="margin-top:16px"></p><p id="context-confirm-billing"></p><p id="context-confirm-note" data-i18n="contextConsentNote" style="margin-top:16px"></p><pre id="context-confirm-text" style="white-space:pre-wrap;overflow-wrap:anywhere;max-height:32vh;overflow:auto;font-size:12px;line-height:1.7"></pre><label style="display:flex;gap:10px;align-items:flex-start;font-size:12px"><input id="context-consent" type="checkbox"><span data-i18n="contextConsent"></span></label><div class="actions"><button class="button" id="context-cancel" type="button" data-i18n="cancel"></button><button class="button primary" id="context-generate" type="button" disabled data-i18n="contextGenerate"></button></div></dialog><dialog class="detail-dialog" id="disconnect-dialog" aria-labelledby="disconnect-title" aria-describedby="disconnect-description"><h2 class="danger-title" id="disconnect-title" data-i18n="disconnectDevice"></h2><p id="disconnect-description" data-i18n="disconnectDescription" style="margin-top:16px"></p><p id="disconnect-error" class="error" role="alert"></p><div class="actions"><button class="button" type="button" id="disconnect-cancel" data-i18n="cancel"></button><button class="button danger" type="button" id="disconnect-confirm" data-i18n="disconnectConfirm"></button></div></dialog></body></html>`;
|
|
9
9
|
}
|
|
10
10
|
export function dashboardScript() {
|
|
11
11
|
return `"use strict";
|
|
@@ -146,6 +146,19 @@ export function dashboardScript() {
|
|
|
146
146
|
label.append(input,name,node('p','',engineReason(engine)));if(engine.billing)label.append(node('p','',engineBilling(engine)));target.append(label);
|
|
147
147
|
}
|
|
148
148
|
text('engine-message',engineMessage?t(engineMessage):!engines?t('engineLoading'):'');
|
|
149
|
+
const models=el('model-options');models.replaceChildren();models.setAttribute('aria-label',t('contextEngine'));
|
|
150
|
+
const current=array(engines&&engines.engines).find(engine=>engine.id===engines.selected);
|
|
151
|
+
for(const item of array(current&¤t.models)) {
|
|
152
|
+
const label=node('label','engine-option'),input=node('input',''),name=node('strong','',scalar(item.label)||scalar(item.id));
|
|
153
|
+
input.type='radio';input.name='context-model';input.value=item.id;input.checked=(current.model||engines.model)===item.id;input.disabled=engineBusy||!!contextJob||contextStarting;
|
|
154
|
+
input.addEventListener('change',async()=>{
|
|
155
|
+
if(!input.checked||engineBusy)return;const epoch=sessionEpoch;engineBusy=true;engineMessage='';renderEngines();
|
|
156
|
+
try {const result=await request('/api/context/model',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({engine:current.id,model:item.id})});if(result&&!sessionRequired&&epoch===sessionEpoch&&engines){current.model=item.id;engines.model=item.id;engineMessage='engineSaved';}}
|
|
157
|
+
catch {if(epoch===sessionEpoch)engineMessage='engineFailed';}
|
|
158
|
+
finally{engineBusy=false;render();}
|
|
159
|
+
});
|
|
160
|
+
label.append(input,name);models.append(label);
|
|
161
|
+
}
|
|
149
162
|
const keys=el('engine-keys');keys.replaceChildren();
|
|
150
163
|
if(engines) for(const provider of ['openrouter','gemini']) {
|
|
151
164
|
const wrap=node('div',''),label=node('label','',t(provider==='openrouter'?'openrouterKey':'geminiKey')),input=node('input',''),button=node('button','button',t('saveKey'));
|
package/dist/local-install.js
CHANGED
|
@@ -51,7 +51,7 @@ export function stageLocalRuntime(home = vaultHome(), source = dirname(fileURLTo
|
|
|
51
51
|
mkdirSync(target, { recursive: true, mode: 0o700 });
|
|
52
52
|
chmodSync(root, 0o700);
|
|
53
53
|
chmodSync(target, 0o700);
|
|
54
|
-
const excluded = new Set(["
|
|
54
|
+
const excluded = new Set(["server.js", "installer.js", "cli.js"]);
|
|
55
55
|
if (!existsSync(join(source, "local-worker.js")))
|
|
56
56
|
throw new Error("Local worker is missing. Build or reinstall vault-go first.");
|
|
57
57
|
for (const file of readdirSync(source)) {
|
package/dist/local-service.js
CHANGED
|
@@ -12,7 +12,8 @@ import { resolveLocale } from "./locale.js";
|
|
|
12
12
|
import { callbackPage } from "./callback-page.js";
|
|
13
13
|
import { privateStore } from "./private-store.js";
|
|
14
14
|
import { localDevice } from "./local-device.js";
|
|
15
|
-
import {
|
|
15
|
+
import { processGenerationQueue } from "./hook-queue.js";
|
|
16
|
+
import { generateContext, isApiEngine, isContextEngine, listContextEngines, parseEngineModel, saveEngineKey, selectContextEngine, selectEngineModel, selectedContextEngine, } from "./context-engines.js";
|
|
16
17
|
export const LOCAL_PORT = 38850;
|
|
17
18
|
const safeEqual = (a, b) => a.length === b.length &&
|
|
18
19
|
/^[A-Za-z0-9_-]{43}$/.test(a) &&
|
|
@@ -258,6 +259,11 @@ export async function startLocalService(options = {}) {
|
|
|
258
259
|
connected = true;
|
|
259
260
|
cloudError = undefined;
|
|
260
261
|
storage?.write(cache, { owner, projects, memories, updatedAt });
|
|
262
|
+
if (!generating && !options.cloud) {
|
|
263
|
+
generating = processGenerationQueue(home, accountCloud).finally(() => {
|
|
264
|
+
generating = null;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
261
267
|
}
|
|
262
268
|
catch {
|
|
263
269
|
if (requestOwner === identity()) {
|
|
@@ -271,6 +277,7 @@ export async function startLocalService(options = {}) {
|
|
|
271
277
|
})();
|
|
272
278
|
return syncing;
|
|
273
279
|
}
|
|
280
|
+
let generating = null;
|
|
274
281
|
const server = createServer(async (req, res) => {
|
|
275
282
|
res.setHeader("Cache-Control", "no-store");
|
|
276
283
|
res.setHeader("Referrer-Policy", "no-referrer");
|
|
@@ -467,6 +474,21 @@ export async function startLocalService(options = {}) {
|
|
|
467
474
|
json(200, { selected: data.engine });
|
|
468
475
|
return;
|
|
469
476
|
}
|
|
477
|
+
if (url.pathname === "/api/context/model" && req.method === "PUT") {
|
|
478
|
+
const data = (await readBody(req));
|
|
479
|
+
const engine = isContextEngine(data.engine)
|
|
480
|
+
? data.engine
|
|
481
|
+
: selectedContextEngine(home);
|
|
482
|
+
try {
|
|
483
|
+
const model = selectEngineModel(home, engine, parseEngineModel(engine, data.model));
|
|
484
|
+
engineCache = undefined;
|
|
485
|
+
json(200, { selected: engine, model });
|
|
486
|
+
}
|
|
487
|
+
catch {
|
|
488
|
+
json(400, { error: "invalid_model" });
|
|
489
|
+
}
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
470
492
|
if (url.pathname === "/api/context/secret" && req.method === "PUT") {
|
|
471
493
|
const data = (await readBody(req));
|
|
472
494
|
if (!isApiEngine(data.provider) || typeof data.key !== "string") {
|
package/dist/locale.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export declare const copy: {
|
|
|
17
17
|
engineKey: string;
|
|
18
18
|
engineKeySaved: string;
|
|
19
19
|
engineKeyLater: string;
|
|
20
|
+
modelHelp: string;
|
|
21
|
+
modelSaved: string;
|
|
20
22
|
choose: string;
|
|
21
23
|
detected: string;
|
|
22
24
|
available: string;
|
|
@@ -54,6 +56,8 @@ export declare const copy: {
|
|
|
54
56
|
engineKey: string;
|
|
55
57
|
engineKeySaved: string;
|
|
56
58
|
engineKeyLater: string;
|
|
59
|
+
modelHelp: string;
|
|
60
|
+
modelSaved: string;
|
|
57
61
|
choose: string;
|
|
58
62
|
detected: string;
|
|
59
63
|
available: string;
|
|
@@ -91,6 +95,8 @@ export declare const copy: {
|
|
|
91
95
|
engineKey: string;
|
|
92
96
|
engineKeySaved: string;
|
|
93
97
|
engineKeyLater: string;
|
|
98
|
+
modelHelp: string;
|
|
99
|
+
modelSaved: string;
|
|
94
100
|
choose: string;
|
|
95
101
|
detected: string;
|
|
96
102
|
available: string;
|
package/dist/locale.js
CHANGED
|
@@ -26,6 +26,8 @@ export const copy = {
|
|
|
26
26
|
engineKey: "Cole a chave (não será exibida). Enter para pular",
|
|
27
27
|
engineKeySaved: "Chave gravada com permissão privada.",
|
|
28
28
|
engineKeyLater: "Sem chave ainda. Depois execute:",
|
|
29
|
+
modelHelp: "Haiku é mais rápido; Sonnet é o equilíbrio; Opus é o mais capaz.",
|
|
30
|
+
modelSaved: "Modelo selecionado",
|
|
29
31
|
choose: "Números ou nomes, separados por vírgula",
|
|
30
32
|
detected: "detectado",
|
|
31
33
|
available: "disponível",
|
|
@@ -63,6 +65,8 @@ export const copy = {
|
|
|
63
65
|
engineKey: "Paste the API key (it will not be shown). Enter to skip",
|
|
64
66
|
engineKeySaved: "Key stored with private file permissions.",
|
|
65
67
|
engineKeyLater: "No key yet. Later run:",
|
|
68
|
+
modelHelp: "Haiku is faster; Sonnet is the balance; Opus is the most capable.",
|
|
69
|
+
modelSaved: "Model selected",
|
|
66
70
|
choose: "Numbers or names, separated by commas",
|
|
67
71
|
detected: "detected",
|
|
68
72
|
available: "available",
|
|
@@ -100,6 +104,8 @@ export const copy = {
|
|
|
100
104
|
engineKey: "Pega la clave (no se mostrará). Enter para omitir",
|
|
101
105
|
engineKeySaved: "Clave guardada con permisos privados.",
|
|
102
106
|
engineKeyLater: "Aún no hay clave. Después ejecuta:",
|
|
107
|
+
modelHelp: "Haiku es más rápido; Sonnet es el equilibrio; Opus es el más capaz.",
|
|
108
|
+
modelSaved: "Modelo seleccionado",
|
|
103
109
|
choose: "Números o nombres, separados por comas",
|
|
104
110
|
detected: "detectado",
|
|
105
111
|
available: "disponible",
|
package/dist/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { VaultCloudClient } from './cloud.js';
|
|
4
4
|
import { getStatus, vaultHome } from './config.js';
|
|
5
|
-
import { CONTEXT_ENGINE_IDS, generateContext, listContextEngines, selectContextEngine, selectedContextEngine, } from './context-engines.js';
|
|
5
|
+
import { CONTEXT_ENGINE_IDS, generateContext, listContextEngines, parseEngineModel, selectContextEngine, selectEngineModel, selectedContextEngine, selectedEngineModel, } from './context-engines.js';
|
|
6
6
|
import packageJson from '../package.json' with { type: 'json' };
|
|
7
7
|
export const VERSION = packageJson.version;
|
|
8
8
|
function jsonResult(value) {
|
|
@@ -70,10 +70,19 @@ export function createVaultGoServer(home = vaultHome(), cloud = new VaultCloudCl
|
|
|
70
70
|
}, async () => run(async () => listContextEngines(home, await managedEngineStatus(cloud))));
|
|
71
71
|
server.registerTool('vault_go_engine_select', {
|
|
72
72
|
title: 'Escolher motor de contexto',
|
|
73
|
-
description: 'Define o motor local
|
|
74
|
-
inputSchema: {
|
|
73
|
+
description: 'Define o motor local e, opcionalmente, o modelo (no Claude: haiku, sonnet ou opus). Chaves de API não são aceitas por esta ferramenta.',
|
|
74
|
+
inputSchema: {
|
|
75
|
+
engine: z.enum(CONTEXT_ENGINE_IDS),
|
|
76
|
+
model: z.string().trim().min(1).max(200).optional(),
|
|
77
|
+
},
|
|
75
78
|
annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: false },
|
|
76
|
-
}, async ({ engine }) =>
|
|
79
|
+
}, async ({ engine, model }) => {
|
|
80
|
+
selectContextEngine(home, engine);
|
|
81
|
+
const selectedModel = model
|
|
82
|
+
? selectEngineModel(home, engine, parseEngineModel(engine, model))
|
|
83
|
+
: selectedEngineModel(home, engine);
|
|
84
|
+
return jsonResult({ selected: engine, ...(selectedModel ? { model: selectedModel } : {}) });
|
|
85
|
+
});
|
|
77
86
|
server.registerTool('vault_go_generate_context', {
|
|
78
87
|
title: 'Gerar contexto',
|
|
79
88
|
description: 'Gera título, conteúdo, fatos e conceitos com o motor selecionado (Claude, OpenRouter, Gemini ou Vault AI). Não persiste memória automaticamente.',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vault-go",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
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": {
|
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
"dist/config.d.ts",
|
|
35
35
|
"dist/context-engines.js",
|
|
36
36
|
"dist/context-engines.d.ts",
|
|
37
|
+
"dist/hook-queue.js",
|
|
38
|
+
"dist/hook-queue.d.ts",
|
|
39
|
+
"dist/hooks.js",
|
|
40
|
+
"dist/hooks.d.ts",
|
|
37
41
|
"dist/index.js",
|
|
38
42
|
"dist/index.d.ts",
|
|
39
43
|
"dist/installer.js",
|