vault-go 0.18.0 → 0.19.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 +117 -5
- package/dist/capabilities.d.ts +33 -0
- package/dist/capabilities.js +31 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +37 -8
- package/dist/cloud.d.ts +16 -0
- package/dist/cloud.js +22 -12
- package/dist/context-engines.js +14 -4
- package/dist/hook-install.js +1 -2
- package/dist/hook-queue.d.ts +19 -5
- package/dist/hook-queue.js +127 -53
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.js +34 -14
- package/dist/index.js +5 -1
- package/dist/local-install.js +37 -28
- package/dist/local-service.d.ts +1 -0
- package/dist/local-service.js +2 -2
- package/dist/local-worker.js +3 -1
- package/dist/mcp-result.d.ts +20 -0
- package/dist/mcp-result.js +21 -0
- package/dist/modes.d.ts +17 -0
- package/dist/modes.js +131 -0
- package/dist/parity-tools.d.ts +3 -0
- package/dist/parity-tools.js +82 -0
- package/dist/server.js +21 -32
- package/dist/smart-code.d.ts +80 -0
- package/dist/smart-code.js +453 -0
- package/dist/update-control.d.ts +24 -0
- package/dist/update-control.js +191 -0
- package/dist/update-runner.d.ts +1 -0
- package/dist/update-runner.js +16 -0
- package/dist/updater.d.ts +34 -0
- package/dist/updater.js +404 -0
- package/package.json +19 -2
package/dist/hook-queue.js
CHANGED
|
@@ -1,72 +1,146 @@
|
|
|
1
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync } from
|
|
2
|
-
import {
|
|
3
|
-
import { join } from
|
|
4
|
-
import { VaultCloudClient } from
|
|
5
|
-
import { vaultHome } from
|
|
6
|
-
import { generateContext, selectedContextEngine
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { VaultCloudClient } from './cloud.js';
|
|
5
|
+
import { loadConfig, loadTokens, vaultHome } from './config.js';
|
|
6
|
+
import { generateContext, selectedContextEngine } from './context-engines.js';
|
|
7
|
+
const queuePath = (home) => join(home, 'generation-queue.json');
|
|
8
|
+
function ownerKey(home) {
|
|
9
|
+
const config = loadConfig(home);
|
|
10
|
+
return createHash('sha256').update(JSON.stringify([config.apiUrl, process.env['VAULT_GO_TOKEN'] ?? config.email ?? loadTokens(home)?.accessToken ?? 'anonymous'])).digest('hex');
|
|
9
11
|
}
|
|
10
12
|
function readQueue(home) {
|
|
11
|
-
|
|
12
|
-
const value = JSON.parse(readFileSync(queuePath(home), "utf8"));
|
|
13
|
-
return Array.isArray(value) ? value : [];
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
13
|
+
if (!existsSync(queuePath(home)))
|
|
16
14
|
return [];
|
|
17
|
-
|
|
15
|
+
const value = JSON.parse(readFileSync(queuePath(home), 'utf8'));
|
|
16
|
+
if (!Array.isArray(value))
|
|
17
|
+
throw new Error('Invalid generation queue; source preserved.');
|
|
18
|
+
return value;
|
|
18
19
|
}
|
|
19
20
|
function writeQueue(home, jobs) {
|
|
20
21
|
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
21
|
-
const destination = queuePath(home)
|
|
22
|
-
|
|
23
|
-
writeFileSync(temporary, JSON.stringify(jobs.slice(0, 50)) + "\n", { mode: 0o600 });
|
|
24
|
-
chmodSync(temporary, 0o600);
|
|
22
|
+
const destination = queuePath(home), temporary = `${destination}.${randomUUID()}.tmp`;
|
|
23
|
+
writeFileSync(temporary, JSON.stringify(jobs) + '\n', { mode: 0o600 });
|
|
25
24
|
renameSync(temporary, destination);
|
|
26
25
|
chmodSync(destination, 0o600);
|
|
27
26
|
}
|
|
28
|
-
|
|
27
|
+
// Separate short storage locks and a long consumer lock let hooks enqueue while a model runs.
|
|
28
|
+
async function lock(home, name, wait = true) {
|
|
29
|
+
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
30
|
+
const path = join(home, `${name}.lock`);
|
|
31
|
+
const token = randomUUID();
|
|
32
|
+
for (let attempt = 0; attempt < (wait ? 100 : 1); attempt++) {
|
|
33
|
+
try {
|
|
34
|
+
writeFileSync(path, JSON.stringify({ pid: process.pid, token }), { flag: 'wx', mode: 0o600 });
|
|
35
|
+
return () => {
|
|
36
|
+
try {
|
|
37
|
+
if (JSON.parse(readFileSync(path, 'utf8')).token === token)
|
|
38
|
+
rmSync(path, { force: true });
|
|
39
|
+
}
|
|
40
|
+
catch { /* already released; never remove another consumer's lock */ }
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error.code !== 'EEXIST')
|
|
45
|
+
throw error;
|
|
46
|
+
try {
|
|
47
|
+
const before = statSync(path);
|
|
48
|
+
const stored = JSON.parse(readFileSync(path, 'utf8'));
|
|
49
|
+
const pid = typeof stored === 'number' ? stored : Number(stored?.pid);
|
|
50
|
+
if (Number.isInteger(pid) && pid > 0) {
|
|
51
|
+
try {
|
|
52
|
+
process.kill(pid, 0);
|
|
53
|
+
}
|
|
54
|
+
catch (probe) {
|
|
55
|
+
if (probe.code === 'ESRCH') {
|
|
56
|
+
const after = statSync(path);
|
|
57
|
+
if (before.ino === after.ino && before.mtimeMs === after.mtimeMs)
|
|
58
|
+
rmSync(path, { force: true });
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch { /* another process may have just released it */ }
|
|
65
|
+
if (wait)
|
|
66
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (wait)
|
|
70
|
+
throw new Error('Generation queue is busy; retry capture.');
|
|
71
|
+
}
|
|
72
|
+
async function mutate(home, change) {
|
|
73
|
+
const release = await lock(home, 'generation-storage');
|
|
74
|
+
try {
|
|
75
|
+
writeQueue(home, change(readQueue(home)));
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
release?.();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export async function enqueueGeneration(home, job) {
|
|
29
82
|
if (!job.text.trim() || job.text.length > 16_000)
|
|
30
83
|
return;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
84
|
+
await mutate(home, jobs => {
|
|
85
|
+
if (jobs.some(item => item.id === job.id))
|
|
86
|
+
return jobs;
|
|
87
|
+
if (jobs.length >= 1000)
|
|
88
|
+
throw new Error('Generation queue is full; existing items preserved.');
|
|
89
|
+
return [...jobs, { ...job, owner: ownerKey(home) }];
|
|
90
|
+
});
|
|
35
91
|
}
|
|
36
|
-
export function
|
|
37
|
-
const jobs = readQueue(home);
|
|
38
|
-
|
|
39
|
-
if (!next)
|
|
40
|
-
return;
|
|
41
|
-
writeQueue(home, jobs.slice(1));
|
|
42
|
-
return next;
|
|
92
|
+
export function generationQueueStatus(home = vaultHome()) {
|
|
93
|
+
const jobs = readQueue(home), owner = ownerKey(home);
|
|
94
|
+
return { pending: jobs.filter(j => j.owner === owner || !j.owner).length, failed: jobs.filter(j => j.owner === owner && j.lastError).length, legacyAwaitingOwnership: jobs.filter(j => !j.owner).length, heldForOtherAccount: jobs.filter(j => j.owner && j.owner !== owner).length };
|
|
43
95
|
}
|
|
44
96
|
export async function processGenerationQueue(home = vaultHome(), cloud = new VaultCloudClient(home), generate = generateContext) {
|
|
45
|
-
const
|
|
46
|
-
if (!
|
|
97
|
+
const release = await lock(home, 'generation-worker', false);
|
|
98
|
+
if (!release)
|
|
47
99
|
return false;
|
|
48
|
-
const engine = selectedContextEngine(home);
|
|
49
100
|
try {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
101
|
+
const owner = ownerKey(home);
|
|
102
|
+
if (readQueue(home).some(item => !item.owner)) {
|
|
103
|
+
// Pre-upgrade jobs have no account fingerprint. Adopt only projects proven
|
|
104
|
+
// to belong to the currently authenticated account; never infer from login.
|
|
105
|
+
const projects = await cloud.projects().catch(() => null);
|
|
106
|
+
if (Array.isArray(projects) && owner === ownerKey(home)) {
|
|
107
|
+
const owned = new Set(projects.flatMap(project => project && typeof project === 'object' && typeof project.id === 'string' ? [project.id] : []));
|
|
108
|
+
await mutate(home, jobs => jobs.map(item => !item.owner && owned.has(item.projectId) ? { ...item, owner } : item));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const job = readQueue(home).find(item => item.owner === owner && (item.nextAttemptAt ?? 0) <= Date.now());
|
|
112
|
+
if (!job)
|
|
113
|
+
return false;
|
|
114
|
+
try {
|
|
115
|
+
const result = job.result ?? (job.generate === false ? { title: job.title ?? 'Memória', content: job.text, facts: [], concepts: [] } : await generate(home, selectedContextEngine(home), job.text, {
|
|
116
|
+
...(cloud.observerGenerate ? { managedGenerate: (text, options) => cloud.observerGenerate(text, options.signal) } : {}),
|
|
117
|
+
}));
|
|
118
|
+
if (owner !== ownerKey(home))
|
|
119
|
+
throw new Error('Account changed.');
|
|
120
|
+
// Cache successful generation before persistence, so a network retry does not pay for the model twice.
|
|
121
|
+
await mutate(home, jobs => jobs.map(item => item.id === job.id ? { ...item, result } : item));
|
|
122
|
+
await cloud.importMemories({ projectId: job.projectId, records: [{
|
|
123
|
+
sourceId: `vault-go:event:${job.id}`,
|
|
124
|
+
...(job.sessionId ? { sessionId: job.sessionId } : {}),
|
|
125
|
+
kind: job.kind,
|
|
126
|
+
memoryType: 'discovery',
|
|
127
|
+
title: result.title || job.title,
|
|
128
|
+
content: result.content.slice(0, job.maxChars ?? 4000),
|
|
129
|
+
facts: result.facts, concepts: result.concepts,
|
|
130
|
+
filesRead: job.filesRead ?? [], filesModified: job.filesModified ?? [],
|
|
131
|
+
}] });
|
|
132
|
+
await mutate(home, jobs => jobs.filter(item => item.id !== job.id));
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
await mutate(home, jobs => jobs.map(item => item.id !== job.id ? item : {
|
|
136
|
+
...item, attempts: (item.attempts ?? 0) + 1,
|
|
137
|
+
nextAttemptAt: Date.now() + Math.min(300_000, 5000 * 2 ** Math.min(item.attempts ?? 0, 6)),
|
|
138
|
+
lastError: 'generation_or_persistence_failed',
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
return readQueue(home).some(item => item.owner === owner);
|
|
67
142
|
}
|
|
68
|
-
|
|
69
|
-
|
|
143
|
+
finally {
|
|
144
|
+
release();
|
|
70
145
|
}
|
|
71
|
-
return existsSync(queuePath(home)) && readQueue(home).length > 0;
|
|
72
146
|
}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type VaultMemoryApi } from "./cloud.js";
|
|
|
2
2
|
export declare const HOOK_EVENTS: readonly ["context", "session-init", "observation", "file-context", "summarize"];
|
|
3
3
|
export type HookEvent = (typeof HOOK_EVENTS)[number];
|
|
4
4
|
type RecordValue = Record<string, unknown>;
|
|
5
|
+
export declare function redact(value: unknown): unknown;
|
|
5
6
|
export declare function normalizeHookInput(adapter: string, value: unknown): RecordValue;
|
|
6
7
|
export declare function hookOutput(event: HookEvent, context?: string, adapter?: string): RecordValue;
|
|
7
8
|
export declare function executeHook(adapter: string, event: HookEvent, value: unknown, cloud?: VaultMemoryApi, home?: string): Promise<RecordValue>;
|
package/dist/hooks.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { basename, dirname, resolve } from "node:path";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { VaultCloudClient } from "./cloud.js";
|
|
@@ -26,10 +26,10 @@ function root(cwd) {
|
|
|
26
26
|
}
|
|
27
27
|
return initial;
|
|
28
28
|
}
|
|
29
|
-
function redact(value) {
|
|
29
|
+
export function redact(value) {
|
|
30
30
|
if (typeof value === "string") {
|
|
31
31
|
return value
|
|
32
|
-
.replace(/<private
|
|
32
|
+
.replace(/<(private|vault-private|claude-mem-context|system_instruction|system-instruction|persisted-output|system-reminder)\b[^>]*>[\s\S]*?(?:<\/\1\s*>|$)/gi, "[redacted]")
|
|
33
33
|
.replace(/(api[_-]?key|secret|token|password)\s*[:=]\s*['"]?[^\s'"]+/gi, "$1=[redacted]");
|
|
34
34
|
}
|
|
35
35
|
if (Array.isArray(value))
|
|
@@ -37,7 +37,7 @@ function redact(value) {
|
|
|
37
37
|
if (value && typeof value === "object") {
|
|
38
38
|
const output = {};
|
|
39
39
|
for (const [key, nested] of Object.entries(value)) {
|
|
40
|
-
output[key] = /api[_-]?key|secret|token|password|authorization/i.test(key)
|
|
40
|
+
output[key] = /api[_-]?key|secret|token|password|authorization|cookie|credential|private[_-]?key/i.test(key)
|
|
41
41
|
? "[redacted]"
|
|
42
42
|
: redact(nested);
|
|
43
43
|
}
|
|
@@ -98,7 +98,7 @@ async function preferences(cloud) {
|
|
|
98
98
|
/* use defaults */
|
|
99
99
|
}
|
|
100
100
|
return {
|
|
101
|
-
automaticCapture:
|
|
101
|
+
automaticCapture: false,
|
|
102
102
|
captureMode: "balanced",
|
|
103
103
|
includeToolResults: false,
|
|
104
104
|
contextItems: 20,
|
|
@@ -110,6 +110,7 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
|
|
|
110
110
|
const toolName = string(input.tool_name);
|
|
111
111
|
if (input.private === true ||
|
|
112
112
|
input.is_private === true ||
|
|
113
|
+
input.isPrivate === true ||
|
|
113
114
|
/vault[_-]go|vault[_-]mem/.test(toolName)) {
|
|
114
115
|
return hookOutput(event, "", adapter);
|
|
115
116
|
}
|
|
@@ -135,7 +136,9 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
|
|
|
135
136
|
throw new Error("Resposta de projetos inválida");
|
|
136
137
|
let project = projects.find((item) => item &&
|
|
137
138
|
typeof item === "object" &&
|
|
138
|
-
|
|
139
|
+
typeof item.rootPath === "string" &&
|
|
140
|
+
Boolean(String(item.rootPath).trim()) &&
|
|
141
|
+
resolve(String(item.rootPath)) === cwd);
|
|
139
142
|
if (!project && reading)
|
|
140
143
|
return hookOutput(event, "", adapter);
|
|
141
144
|
if (!project) {
|
|
@@ -148,8 +151,8 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
|
|
|
148
151
|
throw new Error("Projeto inválido");
|
|
149
152
|
const limits = {
|
|
150
153
|
projectId: project.id,
|
|
151
|
-
limit: 20,
|
|
152
|
-
maxChars: 24_000,
|
|
154
|
+
limit: boundedPreference(prefs.contextItems, 20, 1, 100),
|
|
155
|
+
maxChars: boundedPreference(prefs.contextMaxChars, 24_000, 1_000, 100_000),
|
|
153
156
|
};
|
|
154
157
|
if (reading) {
|
|
155
158
|
const result = record(event === "file-context"
|
|
@@ -158,7 +161,7 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
|
|
|
158
161
|
paths: [...new Set([file, resolve(cwd, file)])],
|
|
159
162
|
})
|
|
160
163
|
: await cloud.context(limits));
|
|
161
|
-
return hookOutput(event, string(result.context).slice(0,
|
|
164
|
+
return hookOutput(event, string(result.context).slice(0, limits.maxChars), adapter);
|
|
162
165
|
}
|
|
163
166
|
const session = record(await cloud.startSession({
|
|
164
167
|
projectId: project.id,
|
|
@@ -169,14 +172,18 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
|
|
|
169
172
|
throw new Error("Sessão inválida");
|
|
170
173
|
const prompt = string(input.prompt);
|
|
171
174
|
const response = string(input.last_assistant_message);
|
|
175
|
+
const toolPayload = record(redact({
|
|
176
|
+
toolName,
|
|
177
|
+
toolInput: tool,
|
|
178
|
+
...(prefs.includeToolResults === true ? {
|
|
179
|
+
toolResponse: input.tool_response ?? input.toolResponse ?? input.tool_result ?? input.toolResult ?? input.result ?? null,
|
|
180
|
+
} : {}),
|
|
181
|
+
}));
|
|
172
182
|
const rawContent = event === "session-init"
|
|
173
183
|
? prompt
|
|
174
184
|
: event === "summarize"
|
|
175
185
|
? response
|
|
176
|
-
: JSON.stringify(
|
|
177
|
-
toolName,
|
|
178
|
-
toolInput: tool,
|
|
179
|
-
}));
|
|
186
|
+
: JSON.stringify(toolPayload);
|
|
180
187
|
const content = String(redact(rawContent));
|
|
181
188
|
if (content.trim()) {
|
|
182
189
|
const eventType = event === "session-init"
|
|
@@ -193,6 +200,8 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
|
|
|
193
200
|
input.tool_use_id,
|
|
194
201
|
input.turn_id,
|
|
195
202
|
input.timestamp,
|
|
203
|
+
string(input.tool_use_id, input.turn_id, input.timestamp) ? undefined : randomUUID(),
|
|
204
|
+
content,
|
|
196
205
|
]))
|
|
197
206
|
.digest("hex");
|
|
198
207
|
await cloud.event({
|
|
@@ -201,7 +210,11 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
|
|
|
201
210
|
sourceAdapter: adapter,
|
|
202
211
|
sourceEventId,
|
|
203
212
|
eventType,
|
|
213
|
+
generate: false,
|
|
204
214
|
payload: {
|
|
215
|
+
...(event === 'observation' ? toolPayload : {}),
|
|
216
|
+
summaryMode: 'off',
|
|
217
|
+
metadata: { summaryMode: 'off' },
|
|
205
218
|
title: toolName ||
|
|
206
219
|
(event === "summarize" ? "Resumo da sessão" : "Solicitação do usuário"),
|
|
207
220
|
content,
|
|
@@ -210,20 +223,27 @@ export async function executeHook(adapter, event, value, cloud = new VaultCloudC
|
|
|
210
223
|
},
|
|
211
224
|
occurredAtEpoch: Date.now(),
|
|
212
225
|
});
|
|
213
|
-
enqueueGeneration(home, {
|
|
226
|
+
await enqueueGeneration(home, {
|
|
214
227
|
id: sourceEventId,
|
|
228
|
+
generate: prefs.summaryMode !== 'off',
|
|
215
229
|
projectId: project.id,
|
|
216
230
|
sessionId: session.id,
|
|
217
231
|
text: content.slice(0, 16_000),
|
|
218
232
|
title: toolName ||
|
|
219
233
|
(event === "summarize" ? "Resumo da sessão" : "Solicitação do usuário"),
|
|
220
234
|
kind: event === "summarize" ? "summary" : event === "session-init" ? "prompt" : "observation",
|
|
235
|
+
maxChars: boundedPreference(prefs.summaryMaxChars, 4_000, 256, 16_000),
|
|
236
|
+
filesRead: !modifies && file ? [file] : [],
|
|
237
|
+
filesModified: modifies && file ? [file] : [],
|
|
221
238
|
});
|
|
222
239
|
}
|
|
223
240
|
if (event === "summarize")
|
|
224
241
|
await cloud.endSession(session.id);
|
|
225
242
|
return hookOutput(event, "", adapter);
|
|
226
243
|
}
|
|
244
|
+
function boundedPreference(value, fallback, min, max) {
|
|
245
|
+
return typeof value === 'number' && Number.isInteger(value) ? Math.max(min, Math.min(max, value)) : fallback;
|
|
246
|
+
}
|
|
227
247
|
export async function readHookInput(stream = process.stdin) {
|
|
228
248
|
const chunks = [];
|
|
229
249
|
let size = 0;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { cliArguments, helpText, resolveCliMode, runEngine, runLocal, runLogin, runSetup } from './cli.js';
|
|
3
|
+
import { cliArguments, helpText, resolveCliMode, runEngine, runLocal, runLogin, runSetup, runUpdate } from './cli.js';
|
|
4
4
|
import { HOOK_EVENTS, runHook } from './hooks.js';
|
|
5
5
|
import { createVaultGoServer, VERSION } from './server.js';
|
|
6
6
|
const { argument, options } = cliArguments(process.argv.slice(2));
|
|
@@ -33,6 +33,10 @@ async function main() {
|
|
|
33
33
|
process.exitCode = await runEngine(options);
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
+
if (mode === 'update') {
|
|
37
|
+
process.exitCode = await runUpdate(options);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
36
40
|
if (mode === 'hook') {
|
|
37
41
|
const adapter = options.find((item) => !item.startsWith('--')) ?? 'auto';
|
|
38
42
|
const event = options.filter((item) => !item.startsWith('--'))[1];
|
package/dist/local-install.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFile, spawn } from "node:child_process";
|
|
2
|
-
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
3
3
|
import { chmodSync, copyFileSync, existsSync, mkdirSync, openSync, closeSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
@@ -46,42 +46,51 @@ export async function localStatus(port = LOCAL_PORT, home = vaultHome()) {
|
|
|
46
46
|
}
|
|
47
47
|
export function stageLocalRuntime(home = vaultHome(), source = dirname(fileURLToPath(import.meta.url))) {
|
|
48
48
|
const manifest = JSON.parse(readFileSync(join(source, "..", "package.json"), "utf8"));
|
|
49
|
-
const root = join(home, "local-runtime");
|
|
50
|
-
const target = join(root, "dist");
|
|
51
|
-
mkdirSync(target, { recursive: true, mode: 0o700 });
|
|
52
|
-
chmodSync(root, 0o700);
|
|
53
|
-
chmodSync(target, 0o700);
|
|
54
49
|
if (!existsSync(join(source, "local-worker.js")))
|
|
55
50
|
throw new Error("Local worker is missing. Build or reinstall vault-go first.");
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
if (!existsSync(join(source, "hook-runner.js")))
|
|
52
|
+
throw new Error("Hook runner is missing. Build or reinstall vault-go first.");
|
|
53
|
+
const root = join(home, "local-runtime"), candidate = join(home, `.runtime-${randomUUID()}`), previous = join(home, "local-runtime.previous");
|
|
54
|
+
const target = join(candidate, "dist");
|
|
55
|
+
mkdirSync(target, { recursive: true, mode: 0o700 });
|
|
56
|
+
chmodSync(candidate, 0o700);
|
|
57
|
+
try {
|
|
58
|
+
for (const file of readdirSync(source)) {
|
|
59
|
+
if (!file.endsWith(".js") || file.endsWith(".test.js"))
|
|
60
|
+
continue;
|
|
61
|
+
copyFileSync(join(source, file), join(target, file));
|
|
62
|
+
chmodSync(join(target, file), 0o600);
|
|
63
|
+
}
|
|
64
|
+
const logo = join(source, "..", "assets", "vault-logo-glass.png");
|
|
65
|
+
if (existsSync(logo)) {
|
|
66
|
+
mkdirSync(join(candidate, "assets"), { mode: 0o700 });
|
|
67
|
+
copyFileSync(logo, join(candidate, "assets", "vault-logo-glass.png"));
|
|
68
|
+
chmodSync(join(candidate, "assets", "vault-logo-glass.png"), 0o600);
|
|
69
|
+
}
|
|
70
|
+
writeFileSync(join(candidate, "package.json"), JSON.stringify({ name: "vault-go-local-runtime", version: manifest.version, type: "module" }), { mode: 0o600 });
|
|
71
|
+
rmSync(previous, { recursive: true, force: true });
|
|
72
|
+
if (existsSync(root))
|
|
73
|
+
renameSync(root, previous);
|
|
74
|
+
try {
|
|
75
|
+
renameSync(candidate, root);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (existsSync(previous))
|
|
79
|
+
renameSync(previous, root);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
63
82
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const logo = join(source, "..", "assets", "vault-logo-glass.png");
|
|
67
|
-
if (existsSync(logo)) {
|
|
68
|
-
copyFileSync(logo, join(assets, "vault-logo-glass.png"));
|
|
69
|
-
chmodSync(join(assets, "vault-logo-glass.png"), 0o600);
|
|
83
|
+
finally {
|
|
84
|
+
rmSync(candidate, { recursive: true, force: true });
|
|
70
85
|
}
|
|
71
|
-
|
|
72
|
-
name: "vault-go-local-runtime",
|
|
73
|
-
version: manifest.version,
|
|
74
|
-
type: "module",
|
|
75
|
-
}), { mode: 0o600 });
|
|
76
|
-
if (!existsSync(join(target, "hook-runner.js")))
|
|
77
|
-
throw new Error("Hook runner is missing. Build or reinstall vault-go first.");
|
|
78
|
-
return join(target, "local-worker.js");
|
|
86
|
+
return join(root, "dist", "local-worker.js");
|
|
79
87
|
}
|
|
80
88
|
export async function startLocal(home = vaultHome()) {
|
|
81
89
|
const running = await localStatus(LOCAL_PORT, home);
|
|
82
90
|
if (running)
|
|
83
91
|
return running;
|
|
84
|
-
const
|
|
92
|
+
const stagedWorker = join(home, "local-runtime", "dist", "local-worker.js");
|
|
93
|
+
const worker = existsSync(stagedWorker) ? stagedWorker : stageLocalRuntime(home);
|
|
85
94
|
const log = openSync(join(home, "local-service.log"), "a", 0o600);
|
|
86
95
|
try {
|
|
87
96
|
const child = spawn(process.execPath, [worker], {
|
package/dist/local-service.d.ts
CHANGED
package/dist/local-service.js
CHANGED
|
@@ -260,7 +260,7 @@ export async function startLocalService(options = {}) {
|
|
|
260
260
|
cloudError = undefined;
|
|
261
261
|
storage?.write(cache, { owner, projects, memories, updatedAt });
|
|
262
262
|
if (!generating && !options.cloud) {
|
|
263
|
-
generating = processGenerationQueue(home, accountCloud).finally(() => {
|
|
263
|
+
generating = processGenerationQueue(home, accountCloud).catch(() => { cloudError = "generation_queue_unavailable"; }).finally(() => {
|
|
264
264
|
generating = null;
|
|
265
265
|
});
|
|
266
266
|
}
|
|
@@ -657,5 +657,5 @@ export async function startLocalService(options = {}) {
|
|
|
657
657
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
658
658
|
storage?.close();
|
|
659
659
|
}
|
|
660
|
-
return { server, port, close };
|
|
660
|
+
return { server, port, close, isIdle: () => !generating && !syncing && login?.status !== "pending" && [...jobs.values()].every(job => job.status !== "running") };
|
|
661
661
|
}
|
package/dist/local-worker.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { startLocalService } from './local-service.js';
|
|
2
|
+
import { scheduleAutomaticUpdates } from './update-control.js';
|
|
2
3
|
const port = Number(process.env['VAULT_GO_LOCAL_PORT'] ?? 38850);
|
|
3
4
|
if (!Number.isInteger(port) || port < 1024 || port > 65535)
|
|
4
5
|
throw new Error('Invalid local port');
|
|
5
6
|
const local = await startLocalService({ port });
|
|
7
|
+
const stopUpdates = scheduleAutomaticUpdates(local.isIdle);
|
|
6
8
|
for (const signal of ['SIGINT', 'SIGTERM'])
|
|
7
|
-
process.once(signal, () => void local.close());
|
|
9
|
+
process.once(signal, () => { stopUpdates(); void local.close(); });
|
|
8
10
|
console.log(`AI Vault Memory local service · http://localhost:${local.port}`);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function jsonResult(value: unknown): {
|
|
2
|
+
content: {
|
|
3
|
+
type: "text";
|
|
4
|
+
text: string;
|
|
5
|
+
}[];
|
|
6
|
+
structuredContent: Record<string, unknown>;
|
|
7
|
+
};
|
|
8
|
+
export declare function run(operation: () => Promise<unknown>): Promise<{
|
|
9
|
+
content: {
|
|
10
|
+
type: "text";
|
|
11
|
+
text: string;
|
|
12
|
+
}[];
|
|
13
|
+
structuredContent: Record<string, unknown>;
|
|
14
|
+
} | {
|
|
15
|
+
content: {
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}[];
|
|
19
|
+
isError: boolean;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function jsonResult(value) {
|
|
2
|
+
const structuredContent = value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
3
|
+
? value
|
|
4
|
+
: { value };
|
|
5
|
+
return {
|
|
6
|
+
content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],
|
|
7
|
+
structuredContent,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function errorResult(error) {
|
|
11
|
+
const message = error instanceof Error ? error.message : 'Erro inesperado.';
|
|
12
|
+
return { content: [{ type: 'text', text: message }], isError: true };
|
|
13
|
+
}
|
|
14
|
+
export async function run(operation) {
|
|
15
|
+
try {
|
|
16
|
+
return jsonResult(await operation());
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return errorResult(error);
|
|
20
|
+
}
|
|
21
|
+
}
|
package/dist/modes.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
export declare const MODE_NAMES: readonly ["babysit", "mem-search", "how-it-works", "make-plan", "do", "smart-explore", "knowledge-agent", "standup", "timeline-report", "weekly-digests", "learn-codebase", "pathfinder", "design-is", "what-the", "version-bump", "oh-my-issues", "cloud-sync", "mode-creator", "ccs-align", "wowerpoint"];
|
|
3
|
+
export type VaultModeName = (typeof MODE_NAMES)[number];
|
|
4
|
+
export type VaultModeStatus = 'native' | 'instructional' | 'limited';
|
|
5
|
+
export interface VaultMode {
|
|
6
|
+
name: VaultModeName;
|
|
7
|
+
promptName: string;
|
|
8
|
+
title: string;
|
|
9
|
+
summary: string;
|
|
10
|
+
status: VaultModeStatus;
|
|
11
|
+
readOnlyDefault: boolean;
|
|
12
|
+
runtimeRequires: readonly string[];
|
|
13
|
+
limitations: readonly string[];
|
|
14
|
+
steps: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
export declare const VAULT_MODES: readonly VaultMode[];
|
|
17
|
+
export declare function registerVaultModes(server: McpServer): void;
|